Chris@1
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@1
|
2
|
Chris@1
|
3 #include <ladspa.h>
|
Chris@1
|
4 #include <alloca.h>
|
Chris@1
|
5 #include <iostream>
|
Chris@1
|
6 #include <cmath>
|
Chris@1
|
7
|
Chris@1
|
8 #define FFTSIZE 1024
|
Chris@1
|
9
|
Chris@1
|
10 class Devuvuzelator
|
Chris@1
|
11 {
|
Chris@1
|
12 public:
|
Chris@1
|
13 static const LADSPA_Descriptor *getDescriptor(unsigned long index);
|
Chris@1
|
14
|
Chris@1
|
15 private:
|
Chris@1
|
16 Devuvuzelator(int sampleRate);
|
Chris@1
|
17 ~Devuvuzelator();
|
Chris@1
|
18
|
Chris@1
|
19 enum {
|
Chris@1
|
20 InputPort = 0,
|
Chris@1
|
21 OutputPort = 1,
|
Chris@1
|
22 LowPort = 2,
|
Chris@1
|
23 HighPort = 3,
|
Chris@1
|
24 FundamentalPort = 4,
|
Chris@1
|
25 BandwidthPort = 5,
|
Chris@1
|
26 HarmonicsPort = 6,
|
Chris@1
|
27 ReductionPort = 7,
|
Chris@1
|
28 PortCount = 8,
|
Chris@1
|
29 };
|
Chris@1
|
30
|
Chris@1
|
31 static const char *const portNames[PortCount];
|
Chris@1
|
32 static const LADSPA_PortDescriptor ports[PortCount];
|
Chris@1
|
33 static const LADSPA_PortRangeHint hints[PortCount];
|
Chris@1
|
34 static const LADSPA_Properties properties;
|
Chris@1
|
35 static const LADSPA_Descriptor ladspaDescriptor;
|
Chris@1
|
36
|
Chris@1
|
37 static LADSPA_Handle instantiate(const LADSPA_Descriptor *, unsigned long);
|
Chris@1
|
38 static void connectPort(LADSPA_Handle, unsigned long, LADSPA_Data *);
|
Chris@1
|
39 static void activate(LADSPA_Handle);
|
Chris@1
|
40 static void run(LADSPA_Handle, unsigned long);
|
Chris@1
|
41 static void deactivate(LADSPA_Handle);
|
Chris@1
|
42 static void cleanup(LADSPA_Handle);
|
Chris@1
|
43
|
Chris@1
|
44 void reset();
|
Chris@1
|
45 void window(float *);
|
Chris@1
|
46 void runImpl(unsigned long);
|
Chris@1
|
47 void processFrame();
|
Chris@1
|
48 void processSpectralFrame();
|
Chris@1
|
49
|
Chris@1
|
50 static void fft(unsigned int n, bool inverse,
|
Chris@1
|
51 double *ri, double *ii, double *ro, double *io);
|
Chris@1
|
52
|
Chris@1
|
53 int m_sampleRate;
|
Chris@1
|
54 float *m_input;
|
Chris@1
|
55 float *m_output;
|
Chris@1
|
56 float *m_low;
|
Chris@1
|
57 float *m_high;
|
Chris@1
|
58 float *m_fundamental;
|
Chris@1
|
59 float *m_bandwidth;
|
Chris@1
|
60 float *m_harmonics;
|
Chris@1
|
61 float *m_reduction;
|
Chris@1
|
62
|
Chris@1
|
63 const int m_fftsize;
|
Chris@1
|
64 const int m_increment;
|
Chris@1
|
65 int m_fill;
|
Chris@1
|
66 int m_read;
|
Chris@1
|
67 float *m_buffer;
|
Chris@1
|
68 float *m_outacc;
|
Chris@1
|
69 double *m_real;
|
Chris@1
|
70 double *m_imag;
|
Chris@1
|
71 double *m_window;
|
Chris@1
|
72 };
|
Chris@1
|
73
|
Chris@1
|
74 const char *const
|
Chris@1
|
75 Devuvuzelator::portNames[PortCount] =
|
Chris@1
|
76 {
|
Chris@1
|
77 "Input",
|
Chris@1
|
78 "Output",
|
Chris@1
|
79 "Low threshold (dB)",
|
Chris@1
|
80 "High threshold (dB)",
|
Chris@1
|
81 "Fundamental frequency (Hz)",
|
Chris@1
|
82 "Bandwidth of fundamental (Hz)",
|
Chris@1
|
83 "Number of partials",
|
Chris@1
|
84 "Reduction (dB)",
|
Chris@1
|
85 };
|
Chris@1
|
86
|
Chris@1
|
87 const LADSPA_PortDescriptor
|
Chris@1
|
88 Devuvuzelator::ports[PortCount] =
|
Chris@1
|
89 {
|
Chris@1
|
90 LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO,
|
Chris@1
|
91 LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO,
|
Chris@1
|
92 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
|
Chris@1
|
93 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
|
Chris@1
|
94 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
|
Chris@1
|
95 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
|
Chris@1
|
96 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
|
Chris@1
|
97 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
|
Chris@1
|
98 };
|
Chris@1
|
99
|
Chris@1
|
100 const LADSPA_PortRangeHint
|
Chris@1
|
101 Devuvuzelator::hints[PortCount] =
|
Chris@1
|
102 {
|
Chris@1
|
103 { 0, 0, 0 },
|
Chris@1
|
104 { 0, 0, 0 },
|
Chris@1
|
105 { LADSPA_HINT_DEFAULT_MIDDLE |
|
Chris@1
|
106 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, -80, 0 },
|
Chris@1
|
107 { LADSPA_HINT_DEFAULT_HIGH |
|
Chris@1
|
108 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, -80, 0 },
|
Chris@1
|
109 { LADSPA_HINT_DEFAULT_LOW |
|
Chris@1
|
110 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 110, 550 },
|
Chris@1
|
111 { LADSPA_HINT_DEFAULT_MIDDLE |
|
Chris@1
|
112 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 20, 100 },
|
Chris@1
|
113 { LADSPA_HINT_DEFAULT_MIDDLE | LADSPA_HINT_INTEGER |
|
Chris@1
|
114 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 6 },
|
Chris@1
|
115 { LADSPA_HINT_DEFAULT_MIDDLE |
|
Chris@1
|
116 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 20 },
|
Chris@1
|
117 };
|
Chris@1
|
118
|
Chris@1
|
119 const LADSPA_Properties
|
Chris@1
|
120 Devuvuzelator::properties = LADSPA_PROPERTY_HARD_RT_CAPABLE;
|
Chris@1
|
121
|
Chris@1
|
122 const LADSPA_Descriptor
|
Chris@1
|
123 Devuvuzelator::ladspaDescriptor =
|
Chris@1
|
124 {
|
Chris@1
|
125 0, // "Unique" ID
|
Chris@1
|
126 "devuvuzelator", // Label
|
Chris@1
|
127 properties,
|
Chris@1
|
128 "Devuvuzelator", // Name
|
Chris@1
|
129 "Queen Mary, University of London", // Maker
|
Chris@1
|
130 "All Rights Reserved", // Copyright
|
Chris@1
|
131 PortCount,
|
Chris@1
|
132 ports,
|
Chris@1
|
133 portNames,
|
Chris@1
|
134 hints,
|
Chris@1
|
135 0, // Implementation data
|
Chris@1
|
136 instantiate,
|
Chris@1
|
137 connectPort,
|
Chris@1
|
138 activate,
|
Chris@1
|
139 run,
|
Chris@1
|
140 0, // Run adding
|
Chris@1
|
141 0, // Set run adding gain
|
Chris@1
|
142 deactivate,
|
Chris@1
|
143 cleanup
|
Chris@1
|
144 };
|
Chris@1
|
145
|
Chris@1
|
146 const LADSPA_Descriptor *
|
Chris@1
|
147 Devuvuzelator::getDescriptor(unsigned long index)
|
Chris@1
|
148 {
|
Chris@1
|
149 if (index == 0) return &ladspaDescriptor;
|
Chris@1
|
150 return 0;
|
Chris@1
|
151 }
|
Chris@1
|
152
|
Chris@1
|
153 Devuvuzelator::Devuvuzelator(int sampleRate) :
|
Chris@1
|
154 m_sampleRate(sampleRate),
|
Chris@1
|
155 m_input(0),
|
Chris@1
|
156 m_output(0),
|
Chris@1
|
157 m_low(0),
|
Chris@1
|
158 m_high(0),
|
Chris@1
|
159 m_fftsize(FFTSIZE),
|
Chris@1
|
160 m_increment(m_fftsize/4),
|
Chris@1
|
161 m_fill(0),
|
Chris@1
|
162 m_read(0)
|
Chris@1
|
163 {
|
Chris@1
|
164 m_buffer = new float[m_fftsize];
|
Chris@1
|
165 m_outacc = new float[m_fftsize * 2];
|
Chris@1
|
166 m_real = new double[m_fftsize];
|
Chris@1
|
167 m_imag = new double[m_fftsize];
|
Chris@1
|
168 m_window = new double[m_fftsize];
|
Chris@1
|
169
|
Chris@1
|
170 for (int i = 0; i < m_fftsize; ++i) {
|
Chris@1
|
171 m_window[i] = 0.5 - 0.5 * cos(2 * M_PI * i / m_fftsize);
|
Chris@1
|
172 }
|
Chris@1
|
173
|
Chris@1
|
174 reset();
|
Chris@1
|
175 }
|
Chris@1
|
176
|
Chris@1
|
177 Devuvuzelator::~Devuvuzelator()
|
Chris@1
|
178 {
|
Chris@1
|
179 delete[] m_buffer;
|
Chris@1
|
180 delete[] m_outacc;
|
Chris@1
|
181 delete[] m_real;
|
Chris@1
|
182 delete[] m_imag;
|
Chris@1
|
183 delete[] m_window;
|
Chris@1
|
184 }
|
Chris@1
|
185
|
Chris@1
|
186 LADSPA_Handle
|
Chris@1
|
187 Devuvuzelator::instantiate(const LADSPA_Descriptor *, unsigned long rate)
|
Chris@1
|
188 {
|
Chris@1
|
189 Devuvuzelator *devuvu = new Devuvuzelator(rate);
|
Chris@1
|
190 return devuvu;
|
Chris@1
|
191 }
|
Chris@1
|
192
|
Chris@1
|
193 void
|
Chris@1
|
194 Devuvuzelator::connectPort(LADSPA_Handle handle,
|
Chris@1
|
195 unsigned long port, LADSPA_Data *location)
|
Chris@1
|
196 {
|
Chris@1
|
197 Devuvuzelator *devuvu = (Devuvuzelator *)handle;
|
Chris@1
|
198
|
Chris@1
|
199 float **ports[PortCount] = {
|
Chris@1
|
200 &devuvu->m_input,
|
Chris@1
|
201 &devuvu->m_output,
|
Chris@1
|
202 &devuvu->m_low,
|
Chris@1
|
203 &devuvu->m_high,
|
Chris@1
|
204 &devuvu->m_fundamental,
|
Chris@1
|
205 &devuvu->m_bandwidth,
|
Chris@1
|
206 &devuvu->m_harmonics,
|
Chris@1
|
207 &devuvu->m_reduction,
|
Chris@1
|
208 };
|
Chris@1
|
209
|
Chris@1
|
210 *ports[port] = (float *)location;
|
Chris@1
|
211 }
|
Chris@1
|
212
|
Chris@1
|
213 void
|
Chris@1
|
214 Devuvuzelator::activate(LADSPA_Handle handle)
|
Chris@1
|
215 {
|
Chris@1
|
216 Devuvuzelator *devuvu = (Devuvuzelator *)handle;
|
Chris@1
|
217 devuvu->reset();
|
Chris@1
|
218 }
|
Chris@1
|
219
|
Chris@1
|
220 void
|
Chris@1
|
221 Devuvuzelator::run(LADSPA_Handle handle, unsigned long samples)
|
Chris@1
|
222 {
|
Chris@1
|
223 Devuvuzelator *devuvu = (Devuvuzelator *)handle;
|
Chris@1
|
224 devuvu->runImpl(samples);
|
Chris@1
|
225 }
|
Chris@1
|
226
|
Chris@1
|
227 void
|
Chris@1
|
228 Devuvuzelator::deactivate(LADSPA_Handle handle)
|
Chris@1
|
229 {
|
Chris@1
|
230 activate(handle); // both functions just reset the plugin
|
Chris@1
|
231 }
|
Chris@1
|
232
|
Chris@1
|
233 void
|
Chris@1
|
234 Devuvuzelator::cleanup(LADSPA_Handle handle)
|
Chris@1
|
235 {
|
Chris@1
|
236 delete (Devuvuzelator *)handle;
|
Chris@1
|
237 }
|
Chris@1
|
238
|
Chris@1
|
239 void
|
Chris@1
|
240 Devuvuzelator::reset()
|
Chris@1
|
241 {
|
Chris@1
|
242 for (int i = 0; i < m_fftsize; ++i) {
|
Chris@1
|
243 m_buffer[i] = 0.f;
|
Chris@1
|
244 }
|
Chris@1
|
245 for (int i = 0; i < m_fftsize*2; ++i) {
|
Chris@1
|
246 m_outacc[i] = 0.f;
|
Chris@1
|
247 }
|
Chris@1
|
248 m_fill = 0;
|
Chris@1
|
249 m_read = 0;
|
Chris@1
|
250 }
|
Chris@1
|
251
|
Chris@1
|
252 void
|
Chris@1
|
253 Devuvuzelator::runImpl(unsigned long sampleCount)
|
Chris@1
|
254 {
|
Chris@1
|
255 if (!m_input || !m_output) return;
|
Chris@1
|
256
|
Chris@1
|
257 int ii = 0;
|
Chris@1
|
258 int oi = 0;
|
Chris@1
|
259 const int sc = sampleCount;
|
Chris@1
|
260
|
Chris@1
|
261 while (ii < sc) {
|
Chris@1
|
262
|
Chris@1
|
263 m_output[oi++] = m_outacc[m_read++] / 1.5f;
|
Chris@1
|
264
|
Chris@1
|
265 if (m_fill == m_fftsize) {
|
Chris@1
|
266
|
Chris@1
|
267 processFrame();
|
Chris@1
|
268
|
Chris@1
|
269 for (int j = m_increment; j < m_fftsize; ++j) {
|
Chris@1
|
270 m_buffer[j - m_increment] = m_buffer[j];
|
Chris@1
|
271 }
|
Chris@1
|
272
|
Chris@1
|
273 for (int j = m_increment; j < m_fftsize*2; ++j) {
|
Chris@1
|
274 m_outacc[j - m_increment] = m_outacc[j];
|
Chris@1
|
275 }
|
Chris@1
|
276
|
Chris@1
|
277 for (int j = m_fftsize*2 - m_increment; j < m_fftsize*2; ++j) {
|
Chris@1
|
278 m_outacc[j] = 0.f;
|
Chris@1
|
279 }
|
Chris@1
|
280
|
Chris@1
|
281 m_fill -= m_increment;
|
Chris@1
|
282 m_read -= m_increment;
|
Chris@1
|
283 }
|
Chris@1
|
284
|
Chris@1
|
285 m_buffer[m_fill++] = m_input[ii++];
|
Chris@1
|
286 }
|
Chris@1
|
287 }
|
Chris@1
|
288
|
Chris@1
|
289 void
|
Chris@1
|
290 Devuvuzelator::processFrame()
|
Chris@1
|
291 {
|
Chris@1
|
292 double *frame = (double *)alloca(m_fftsize * sizeof(double));
|
Chris@1
|
293 int ix = m_fftsize/2;
|
Chris@1
|
294 for (int i = 0; i < m_fftsize; ++i) {
|
Chris@1
|
295 frame[ix++] = m_buffer[i] * m_window[i];
|
Chris@1
|
296 if (ix == m_fftsize) ix = 0;
|
Chris@1
|
297 }
|
Chris@1
|
298
|
Chris@1
|
299 fft(m_fftsize, false, frame, 0, m_real, m_imag);
|
Chris@1
|
300
|
Chris@1
|
301 processSpectralFrame();
|
Chris@1
|
302
|
Chris@1
|
303 for (int i = 0; i < m_fftsize/2-1; ++i) {
|
Chris@1
|
304 m_real[m_fftsize-i] = m_real[i+1];
|
Chris@1
|
305 m_imag[m_fftsize-i] = -m_imag[i+1];
|
Chris@1
|
306 }
|
Chris@1
|
307
|
Chris@1
|
308 double *spare = (double *)alloca(m_fftsize * sizeof(double));
|
Chris@1
|
309 fft(m_fftsize, true, m_real, m_imag, frame, spare);
|
Chris@1
|
310
|
Chris@1
|
311 ix = m_fftsize/2;
|
Chris@1
|
312 for (int i = 0; i < m_fftsize; ++i) {
|
Chris@1
|
313 m_outacc[m_fftsize + i] += frame[ix++] * m_window[i];
|
Chris@1
|
314 if (ix == m_fftsize) ix = 0;
|
Chris@1
|
315 }
|
Chris@1
|
316 }
|
Chris@1
|
317
|
Chris@1
|
318 // FFT implementation by Don Cross, public domain.
|
Chris@1
|
319 // This version scales the forward transform.
|
Chris@1
|
320
|
Chris@1
|
321 void Devuvuzelator::fft(unsigned int n, bool inverse,
|
Chris@1
|
322 double *ri, double *ii, double *ro, double *io)
|
Chris@1
|
323 {
|
Chris@1
|
324 if (!ri || !ro || !io) return;
|
Chris@1
|
325
|
Chris@1
|
326 unsigned int bits;
|
Chris@1
|
327 unsigned int i, j, k, m;
|
Chris@1
|
328 unsigned int blockSize, blockEnd;
|
Chris@1
|
329
|
Chris@1
|
330 double tr, ti;
|
Chris@1
|
331
|
Chris@1
|
332 if (n < 2) return;
|
Chris@1
|
333 if (n & (n-1)) return;
|
Chris@1
|
334
|
Chris@1
|
335 double angle = 2.0 * M_PI;
|
Chris@1
|
336 if (inverse) angle = -angle;
|
Chris@1
|
337
|
Chris@1
|
338 for (i = 0; ; ++i) {
|
Chris@1
|
339 if (n & (1 << i)) {
|
Chris@1
|
340 bits = i;
|
Chris@1
|
341 break;
|
Chris@1
|
342 }
|
Chris@1
|
343 }
|
Chris@1
|
344
|
Chris@1
|
345 static unsigned int tableSize = 0;
|
Chris@1
|
346 static int *table = 0;
|
Chris@1
|
347
|
Chris@1
|
348 if (tableSize != n) {
|
Chris@1
|
349
|
Chris@1
|
350 delete[] table;
|
Chris@1
|
351
|
Chris@1
|
352 table = new int[n];
|
Chris@1
|
353
|
Chris@1
|
354 for (i = 0; i < n; ++i) {
|
Chris@1
|
355
|
Chris@1
|
356 m = i;
|
Chris@1
|
357
|
Chris@1
|
358 for (j = k = 0; j < bits; ++j) {
|
Chris@1
|
359 k = (k << 1) | (m & 1);
|
Chris@1
|
360 m >>= 1;
|
Chris@1
|
361 }
|
Chris@1
|
362
|
Chris@1
|
363 table[i] = k;
|
Chris@1
|
364 }
|
Chris@1
|
365
|
Chris@1
|
366 tableSize = n;
|
Chris@1
|
367 }
|
Chris@1
|
368
|
Chris@1
|
369 if (ii) {
|
Chris@1
|
370 for (i = 0; i < n; ++i) {
|
Chris@1
|
371 ro[table[i]] = ri[i];
|
Chris@1
|
372 io[table[i]] = ii[i];
|
Chris@1
|
373 }
|
Chris@1
|
374 } else {
|
Chris@1
|
375 for (i = 0; i < n; ++i) {
|
Chris@1
|
376 ro[table[i]] = ri[i];
|
Chris@1
|
377 io[table[i]] = 0.0;
|
Chris@1
|
378 }
|
Chris@1
|
379 }
|
Chris@1
|
380
|
Chris@1
|
381 blockEnd = 1;
|
Chris@1
|
382
|
Chris@1
|
383 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
|
Chris@1
|
384
|
Chris@1
|
385 double delta = angle / (double)blockSize;
|
Chris@1
|
386 double sm2 = -sin(-2 * delta);
|
Chris@1
|
387 double sm1 = -sin(-delta);
|
Chris@1
|
388 double cm2 = cos(-2 * delta);
|
Chris@1
|
389 double cm1 = cos(-delta);
|
Chris@1
|
390 double w = 2 * cm1;
|
Chris@1
|
391 double ar[3], ai[3];
|
Chris@1
|
392
|
Chris@1
|
393 for (i = 0; i < n; i += blockSize) {
|
Chris@1
|
394
|
Chris@1
|
395 ar[2] = cm2;
|
Chris@1
|
396 ar[1] = cm1;
|
Chris@1
|
397
|
Chris@1
|
398 ai[2] = sm2;
|
Chris@1
|
399 ai[1] = sm1;
|
Chris@1
|
400
|
Chris@1
|
401 for (j = i, m = 0; m < blockEnd; j++, m++) {
|
Chris@1
|
402
|
Chris@1
|
403 ar[0] = w * ar[1] - ar[2];
|
Chris@1
|
404 ar[2] = ar[1];
|
Chris@1
|
405 ar[1] = ar[0];
|
Chris@1
|
406
|
Chris@1
|
407 ai[0] = w * ai[1] - ai[2];
|
Chris@1
|
408 ai[2] = ai[1];
|
Chris@1
|
409 ai[1] = ai[0];
|
Chris@1
|
410
|
Chris@1
|
411 k = j + blockEnd;
|
Chris@1
|
412 tr = ar[0] * ro[k] - ai[0] * io[k];
|
Chris@1
|
413 ti = ar[0] * io[k] + ai[0] * ro[k];
|
Chris@1
|
414
|
Chris@1
|
415 ro[k] = ro[j] - tr;
|
Chris@1
|
416 io[k] = io[j] - ti;
|
Chris@1
|
417
|
Chris@1
|
418 ro[j] += tr;
|
Chris@1
|
419 io[j] += ti;
|
Chris@1
|
420 }
|
Chris@1
|
421 }
|
Chris@1
|
422
|
Chris@1
|
423 blockEnd = blockSize;
|
Chris@1
|
424 }
|
Chris@1
|
425
|
Chris@1
|
426 if (!inverse) {
|
Chris@1
|
427
|
Chris@1
|
428 double denom = (double)n;
|
Chris@1
|
429
|
Chris@1
|
430 for (i = 0; i < n; i++) {
|
Chris@1
|
431 ro[i] /= denom;
|
Chris@1
|
432 io[i] /= denom;
|
Chris@1
|
433 }
|
Chris@1
|
434 }
|
Chris@1
|
435 }
|
Chris@1
|
436
|
Chris@1
|
437 const LADSPA_Descriptor *
|
Chris@1
|
438 ladspa_descriptor(unsigned long ix)
|
Chris@1
|
439 {
|
Chris@1
|
440 return Devuvuzelator::getDescriptor(ix);
|
Chris@1
|
441 }
|
Chris@1
|
442
|
Chris@1
|
443 #include "devuvuzelator.cpp"
|
Chris@1
|
444
|