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