Package topo :: Package pattern :: Module audio
[hide private]
[frames] | no frames]

Source Code for Module topo.pattern.audio

  1  """ 
  2  Pattern generators for audio signals. 
  3   
  4   
  5  $Id: audio.py 11155 2010-07-09 23:21:06Z jbednar $ 
  6  """ 
  7  __version__='$Revision: 11155 $' 
  8   
  9  # JABALERT: Needs cleanup so that default parameters, etc. are reasonable 
 10   
 11  import numpy 
 12  import param 
 13  import os 
 14   
 15  from param.parameterized import ParamOverrides 
 16  from topo.base.sheetcoords import SheetCoordinateSystem 
 17   
 18  from topo.pattern.basic import Spectrogram 
 19  from numpy import float32, multiply, round, shape, hstack 
 20  from numpy import hanning, fft, log10, logspace 
 21   
 22  try: 
 23      import scikits.audiolab as pyaudiolab 
 24   
 25  except ImportError: 
 26      param.Parameterized().warning("audio.py classes will not be usable; scikits.audiolab (pyaudiolab) is not available.") 
 27   
 28   
 29  # CEBALERT: should make super's window_length, window_overlap, sample_rate,  
 30  # and windowing_function be read-only/hidden for users of this class. 
31 -class AudioFile(Spectrogram):
32 """ 33 Returns a spectrogram, i.e. the spectral density over time 34 of a rolling window of the input audio signal. 35 """ 36 37 filename=param.Filename(default='sounds/complex/daisy.wav', doc=""" 38 File path (can be relative to Topographica's base path) to an 39 audio file. The audio can be in any format accepted by pyaudiolab, 40 e.g. WAV, AIFF, or FLAC.""") 41 42 amplify_from_frequency=param.Number(default=1500.0, doc=""" 43 The lower bound of the frequency range to be amplified.""") 44 45 amplify_till_frequency=param.Number(default=7000.0, doc=""" 46 The upper bound of the frequency range to be amplified.""") 47 48 amplify_by=param.Number(default=5.0, doc=""" 49 The percentage by which to amplify the signal between the specified 50 frequency range.""") 51
52 - def __init__(self, **params):
53 for parameter,value in params.items(): 54 if parameter == "filename" or\ 55 parameter == "amplify_from_frequency" or \ 56 parameter == "amplify_till_frequency" or \ 57 parameter == "amplify_by": 58 setattr(self,parameter,value) 59 60 self._source = pyaudiolab.Sndfile(self.filename, 'r') 61 super(AudioFile, self).__init__(signal=self._source.read_frames(self._source.nframes, dtype=float32), 62 sample_rate=self._source.samplerate, **params)
63
64 - def _create_spacing(self, mini, maxi):
65 # frequency spacing to use, i.e. mapping of frequencies to sheet rows, 66 self._frequency_indices = round(logspace(log10(maxi), log10(mini), num=(maxi-mini), 67 endpoint=True, base=10)).astype(int)
68
69 - def __call__(self, **params_to_override):
70 # override defaults with user defined parameters. 71 p = ParamOverrides(self, params_to_override) 72 73 # get the dimensions of the generator sheet. 74 self._sheet_dimensions = SheetCoordinateSystem(p.bounds, p.xdensity, p.ydensity).shape 75 76 # calculate frequency bin divisions. 77 self._create_indices(p) 78 79 # perform a fft to get amplitudes of the composite frequencies. 80 amplitudes = self._get_amplitudes(p) 81 82 # convert output to decibels 83 for frequency in range(shape(amplitudes)[0]): 84 if amplitudes[frequency] > 0.0: 85 multiply(20.0,log10(amplitudes[frequency])) 86 87 # amplifies specified frequency range by a hanning smoothed dB window, 88 if self.amplify_by > 0.0: 89 if (self.amplify_from_frequency < self.min_frequency) or (self.amplify_from_frequency > self.max_frequency): 90 raise ValueError("Lower bound of frequency to amplify is outside the global frequency range.") 91 92 if (self.amplify_till_frequency < self.min_frequency) or (self.amplify_till_frequency > self.max_frequency): 93 raise ValueError("Upper bound of frequency to amplify is outside the global frequency range.") 94 95 amplify_indices = [0,0] 96 frequency_bins = logspace(log10(self.max_frequency), log10(self.min_frequency), 97 num=shape(amplitudes)[0], endpoint=True, base=10) 98 frequency_indices = range(shape(frequency_bins)[0]) 99 100 # index of end point (highest freq) 101 for index in frequency_indices: 102 if frequency_bins[index] <= self.amplify_till_frequency: 103 amplify_indices[1] = index; break 104 105 # index of start point (lowest freq) 106 for index in reversed(frequency_indices): 107 if frequency_bins[index] >= self.amplify_from_frequency: 108 amplify_indices[0] = index; break 109 110 # get a smoothed amplification window of the correct size 111 amplify_indices.sort() 112 assert amplify_indices[1] > amplify_indices[0] 113 amplification = hanning(amplify_indices[1]-amplify_indices[0])*self.amplify_by 114 115 # add it to current amplitudes if not 0, 116 # (practically - above a minimum threshold, 0.1) 117 for unit in range(shape(amplification)[0]): 118 amplitudes[amplify_indices[0]+unit] *= amplification[unit]+1.0 119 120 # first make sure arrays are of compatible size, then add on 121 # latest spectral information to the spectrograms leftmost edge. 122 assert shape(amplitudes)[0] == shape(self._spectrogram)[0] 123 self._spectrogram = hstack((amplitudes, self._spectrogram)) 124 125 # knock off the column on the spectrograms right-most edge, 126 # i.e. the oldest spectral information. 127 self._spectrogram = self._spectrogram[0:, 0:self._spectrogram.shape[1]-1] 128 129 # the following print statements are very useful when calibrating sheets, 130 # allowing you to calculate how much time history a particular generator 131 # sheets x dimension corresponds to. 132 #print shape(amplitudes); print shape(self._spectrogram) 133 134 return self._spectrogram
135 136 137
138 -class AudioFolder(AudioFile):
139 """ 140 Returns a spectrogram, i.e. the spectral density over time 141 of a rolling window of the input audio signal, for all files 142 in the specified folder. 143 """ 144 145 folderpath=param.String(default='sounds/complex/', doc=""" 146 Folder path (can be relative to Topographica's base path) to a 147 folder containing audio files. The audio can be in any format 148 accepted by pyaudiolab, e.g. WAV, AIFF, or FLAC.""") 149 150 gap_between_sounds=param.Number(default=0.0, doc=""" 151 The gap in seconds to insert between consecutive soundfiles.""") 152
153 - def __init__(self, **params):
154 for parameter,value in params.items(): 155 if parameter == "folderpath" or \ 156 parameter == "gap_between_sounds": 157 setattr(self,parameter,value) 158 159 all_files = os.listdir(self.folderpath) 160 self._sound_files = [] 161 for file in all_files: 162 if file[-4:]==".wav" or file[-3:]==".wv" or \ 163 file[-5:]==".aiff" or file[-4:]==".aif" or \ 164 file[-5:]==".flac": 165 self._sound_files.append(self.folderpath+file) 166 167 self._next_file = 1 168 super(AudioFolder, self).__init__(filename=self._sound_files[0], **params)
169
170 - def _extract_sample_window(self, p):
171 # add inter-signal gap to end of current signal 172 if self._window_start == 0: 173 self.signal = hstack((self.signal, [0.0]*int(self.gap_between_sounds*self.sample_rate))) 174 175 start = self._window_start 176 end = start+self._samples_per_window 177 178 # move window forward for next cycle 179 self._window_start += int(self.window_increment * self.sample_rate) 180 181 if (end > self.signal.size) and (self._next_file < len(self._sound_files)): 182 next_source = pyaudiolab.Sndfile(self._sound_files[self._next_file], 'r') 183 self._next_file = self._next_file + 1 184 185 if next_source.samplerate != self.sample_rate: 186 raise ValueError("All sound files must be of the same sample rate") 187 188 next_signal = next_source.read_frames(next_source.nframes, dtype=float32) 189 self.signal = hstack((self.signal[start:len(self.signal)], next_signal)) 190 191 self._window_start = 0 192 return self.signal[0:end-start] 193 194 elif end > self.signal.size: 195 raise ValueError("Reached the end of the signal.") 196 197 return self.signal[start:end]
198 199 200 201 202 if __name__=='__main__' or __name__=='__mynamespace__': 203 204 from topo import sheet 205 import topo 206 207 topo.sim['C']=sheet.GeneratorSheet( 208 input_generator=AudioFile(filename='sounds/sine_waves/20000.wav',sample_window=0.3, 209 seconds_per_timestep=0.1,min_frequency=20,max_frequency=20000), 210 nominal_bounds=sheet.BoundingBox(points=((-0.1,-0.5),(0.0,0.5))), 211 nominal_density=10,period=1.0,phase=0.05) 212