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