Vuo  2.3.2
VuoDsp.mm
Go to the documentation of this file.
1 
10 #include "module.h"
11 #include "VuoDsp.h"
12 #include <Accelerate/Accelerate.h>
13 
14 #include "VuoMacOSSDKWorkaround.h"
15 #import <Foundation/Foundation.h>
16 
17 #ifdef VUO_COMPILER
19  "title" : "VuoDsp",
20  "dependencies" : [
21  "VuoAudioSamples",
22  "VuoList_VuoReal",
23  "Accelerate.framework"
24  ]
25  });
26 #endif
27 
31 @interface VuoDspObject : NSObject
32 {
33  FFTSetup _fftSetup;
34  DSPSplitComplex _split;
35  float* _frequency;
36  unsigned int _frameSize;
37  VuoWindowing _windowMode;
38  float* _window;
40 }
41 
42 - (VuoReal *) frequenciesForSampleData:(float *) sampleData numFrames:(int)frames mode:(VuoAudioBinAverageType)frequencyMode outputCount:(unsigned int *)count newSumming:(bool)newSumming;
43 - (id) initWithSize:(unsigned int)frameSize windowing:(VuoWindowing)windowMode;
44 @end
45 
46 @implementation VuoDspObject
47 
52 - (id) initWithSize:(unsigned int)frameSize windowing:(VuoWindowing)windowMode
53 {
54  if (self = [super init])
55  {
56 
57  _fftSetup = vDSP_create_fftsetup( log2f(frameSize), kFFTRadix2 );
58  // WARNING: we're minimizing allocations here, but we _have_ to keep in mind that _split.realp and _split.imagp _MUST_ by
59  // 16-byte aligned. malloc/calloc will automatically do this for us, but pointer math will NOT.
60  // float is 4 bytes, so frameSize _MUST_ be a multiple of 4 to ensure that we're always 16-byte aligned later on.
61  // This is currently guaranteed, with frameSize being between 256 and 65536.
62  _frequency = (float*)calloc(sizeof(float)*frameSize * 2, 1);
63  _split.realp = _frequency + frameSize;
64  _split.imagp = _split.realp + frameSize / 2;
65  _frameSize = frameSize;
67 
68  // https://developer.apple.com/library/prerelease/ios/documentation/Accelerate/Reference/vDSPRef/index.html#//apple_ref/c/func/vDSP_blkman_window
69  // https://stackoverflow.com/questions/12642916/fft-output-with-float-buffer-audiounit
70 
71  switch(windowMode)
72  {
73  case VuoWindowing_Hamming:
74  _window = (float *) malloc(sizeof(float) * frameSize);
75  vDSP_hamm_window(_window, frameSize, 0);
76  break;
77 
78  case VuoWindowing_Hann:
79  _window = (float *) malloc(sizeof(float) * frameSize);
80  vDSP_hann_window(_window, frameSize, 0);
81  break;
82 
83  case VuoWindowing_Blackman:
84  _window = (float *) malloc(sizeof(float) * frameSize);
85  vDSP_blkman_window(_window, frameSize, 0);
86  break;
87 
88  default:
89  _window = NULL;
90  break;
91  }
92  }
93 
94  return self;
95 }
96 
97 static void VuoDsp_showFrequencies(int frameCount, VuoAudioBinAverageType mode, int lowBin, int highBin, int displayBin)
98 {
99  double nyquist = VuoAudioSamples_sampleRate / 2.;
100  double width = nyquist / (frameCount/2);
101 
102  double lowFrequency = ( lowBin - .5) * width;
103  double highFrequency = (highBin + .5) * width;
104  double centerFrequency = (((double)lowBin + highBin) / 2.) * width;
105 
106  VUserLog("Bin %4d %8.2f Hz ± %7.2f Hz (%8.2f Hz to %8.2f Hz)", displayBin, centerFrequency, (highFrequency - lowFrequency) / 2., lowFrequency, highFrequency);
107 }
108 
112 - (VuoReal *)frequenciesForSampleData:(float *)sampleData numFrames:(int)frames mode:(VuoAudioBinAverageType)frequencyMode outputCount:(unsigned int *)count newSumming:(bool)newSumming
113 {
114  bool showTable = false;
115  if (frames != _frameSize || frequencyMode != priorFrequencyMode)
116  {
117  _frameSize = frames;
118  priorFrequencyMode = frequencyMode;
119  showTable = VuoIsDebugEnabled();
120  }
121 
122  VuoReal *freqChannel = (VuoReal *)malloc(sizeof(VuoReal) * frames/2);
123  // see vDSP_Library.pdf, page 20
124 
125  // apply windowing
126  if(_window != NULL)
127  vDSP_vmul(sampleData, 1, _window, 1, sampleData, 1, frames);
128 
129  // turn channel of (real) sampleData into a (real) even-odd array (despite the DSPSplitComplex datatype).
130  unsigned int offset = 0;
131  unsigned int i;
132  DSPSplitComplex lSplit = _split;
133 
134  for( i=0; i < frames/2; ++i)
135  {
136  lSplit.realp[i] = sampleData[offset];
137  offset++;
138  lSplit.imagp[i] = sampleData[offset];
139  offset++;
140  }
141 
142  // perform real-to-complex FFT.
143  vDSP_fft_zrip( _fftSetup, &lSplit, 1, log2f(_frameSize), kFFTDirection_Forward );
144 
145  // scale by 1/2*n because vDSP_fft_zrip doesn't use the right scaling factors natively ("for better performances")
146  if(_window == NULL || newSumming)
147  {
148  // const float scale = 1.0f/(2.0f*(float)frames);
149  const float scale = 1.0f/frames;
150 
151  vDSP_vsmul( lSplit.realp, 1, &scale, lSplit.realp, 1, frames/2 );
152  vDSP_vsmul( lSplit.imagp, 1, &scale, lSplit.imagp, 1, frames/2 );
153  }
154 
155  // vDSP_zvmags(&lSplit, 1, lSplit.realp, 1, frames/2);
156 
157  // collapse split complex array into a real array.
158  // split[0] contains the DC, and the values we're interested in are split[1] to split[len/2] (since the rest are complex conjugates)
159  float *lFrequency = _frequency;
160  vDSP_zvabs( &lSplit, 1, lFrequency, 1, frames/2 );
161 
162  switch(frequencyMode)
163  {
164  case VuoAudioBinAverageType_None: // Linear Raw
165  {
166  if (newSumming)
167  for( i=1; i<frames/2; ++i )
168  freqChannel[i-1] = lFrequency[i];
169  else
170  for( i=1; i<frames/2; ++i )
171  freqChannel[i-1] = lFrequency[i] * ((float)sqrtf(i)*2.f + 1.f);
172  *count = frames/2 - 1;
173 
174  if (showTable)
175  for (i = 1; i < frames/2; ++i)
176  VuoDsp_showFrequencies(frames, frequencyMode, i, i, i);
177 
178  break;
179  }
180  case VuoAudioBinAverageType_Quadratic: // Quadratic Average
181  {
182  int lowerFrequency = 1, upperFrequency;
183  int k;
184  float sum;
185  bool done=NO;
186  i=0;
187  while(!done)
188  {
189  upperFrequency = lowerFrequency + i;
190  sum=0.f;
191  if( upperFrequency >= frames/2 )
192  {
193  upperFrequency = frames/2-1;
194  done=YES;
195  }
196  for( k=lowerFrequency; k<=upperFrequency; ++k )
197  sum += lFrequency[k];
198  sum /= (float)(upperFrequency-lowerFrequency+1);
199  sum *= (float)i*2.f + 1.f;
200  freqChannel[i] = sum;
201 
202  if (showTable)
203  VuoDsp_showFrequencies(frames, frequencyMode, lowerFrequency, upperFrequency, i + 1);
204 
205  lowerFrequency = upperFrequency + 1;
206  ++i;
207  }
208  *count = i;
209  break;
210  }
211  case VuoAudioBinAverageType_Logarithmic: // Logarithmic Average
212  {
213  const float log2FrameSize = log2f(_frameSize);
214  int numBuckets = log2FrameSize;
215  int lowerFrequency, upperFrequency;
216  int k;
217  float sum;
218  for( i=0; i<numBuckets; ++i)
219  {
220  lowerFrequency = (frames/2) / powf(2.f,log2FrameSize-i )+1;
221  upperFrequency = (frames/2) / powf(2.f,log2FrameSize-i-1);
222  sum=0.f;
223  if(upperFrequency>=frames/2)
224  upperFrequency=frames/2-1;
225  for( k=lowerFrequency; k<=upperFrequency; ++k )
226  sum += lFrequency[k];
227  sum /= (float)(upperFrequency-lowerFrequency+1);
228  sum *= (float)powf(i,1.5f) + 1.f;
229  freqChannel[i] = sum;
230 
231  if (showTable)
232  VuoDsp_showFrequencies(frames, frequencyMode, lowerFrequency, upperFrequency, i + 1);
233  }
234  *count = numBuckets;
235  break;
236  }
237  }
238 
239  return freqChannel;
240 }
241 
245 - (void)dealloc
246 {
247  vDSP_destroy_fftsetup(_fftSetup);
248  free(_frequency);
249 
250  if(_window)
251  free(_window);
252 
253  [super dealloc];
254 }
255 
256 @end
257 
261 void VuoDsp_free(VuoDsp dspObject);
262 
263 VuoDsp VuoDsp_make(unsigned int frameSize, VuoWindowing windowing)
264 {
265  VuoDspObject* fft = [[VuoDspObject alloc] initWithSize:frameSize windowing:windowing];
266  VuoRegister(fft, VuoDsp_free);
267  return (void*) fft;
268 }
269 
273 VuoReal* VuoDsp_frequenciesForSamples(VuoDsp dspObject, VuoReal* audio, unsigned int sampleCount, VuoAudioBinAverageType binAveraging, unsigned int* spectrumSize, bool newSumming)
274 {
275  VuoDspObject* dsp = (VuoDspObject*)dspObject;
276 
277  if(dsp == NULL)
278  return NULL;
279 
280  float* vals = (float*)malloc(sizeof(float)*sampleCount);
281  for(int i = 0; i < sampleCount; i++) vals[i] = (float)audio[i];
282 
283  VuoReal *freq = [dsp frequenciesForSampleData:vals numFrames:sampleCount mode:binAveraging outputCount:spectrumSize newSumming:newSumming];
284  free(vals);
285 
286  return freq;
287 }
288 
292 void VuoDsp_free(VuoDsp dspObject)
293 {
294  VuoDspObject* fft = (VuoDspObject*)dspObject;
295 
296  if(fft)
297  [fft release];
298 }