History

implemented basic DSP for Spectacle

Joel Matthys committed 12 years ago

Commit 4325a601 parent 0ecc72732 files · +3252 −1434Browse files

Unified · Split

32 files
  1. Spectacle/DLineN.cpp +0 −105
  2. Spectacle/DLineN.h +0 −45
  3. Spectacle/JGFilter.cpp +0 −24
  4. Spectacle/JGFilter.h +0 −25
  5. Spectacle/SPECTACLE_BASE.cpp +0 −577
  6. Spectacle/SPECTACLE_BASE.h +0 −85
  7. Spectacle/SpectEQ.cpp +117 −0
  8. Spectacle/SpectEQ.h +36 −0
  9. Spectacle/Spectacle-dsp.cpp +382 −0
  10. Spectacle/Spectacle-dsp.h +56 −0
  11. Spectacle/Spectacle.cpp +84 −375
  12. Spectacle/SpectacleBase.cpp +646 −0
  13. Spectacle/SpectacleBase.h +161 −0
  14. Spectacle/fft.c +0 −142
  15. Spectacle/fft.h +0 −24
  16. Spectacle/genlib/FFTReal.cpp +615 −0
  17. Spectacle/genlib/FFTReal.h +139 −0
  18. Spectacle/genlib/Obucket.cpp +33 −0
  19. Spectacle/genlib/Obucket.h +46 −0
  20. Spectacle/genlib/Odelay.cpp +145 −0
  21. Spectacle/genlib/Odelay.h +85 −0
  22. Spectacle/genlib/Offt.cpp +157 −0
  23. Spectacle/genlib/Offt.h +58 −0
  24. Spectacle/genlib/Ooscil.cpp +48 −0
  25. Spectacle/genlib/Ooscil.h +33 −0
  26. Spectacle/genlib/Ougens.h +30 −0
  27. Spectacle/genlib/RandGen.cpp +224 −0
  28. Spectacle/genlib/RandGen.h +136 −0
  29. Spectacle/genlib/ampdb.h +15 −0
  30. Spectacle/makefile +3 −2
  31. Spectacle/objdefs.h +0 −28
  32. Spectacle/spectacle-help.ck +3 −2
Spectacle/DLineN.cpp+0−105
@@ -1,105 +0,0 @@
1/* Non-Interpolating Delay Line Object by Perry R. Cook 1995-96
2 This one uses a delay line of maximum length specified on creation.
3 A non-interpolating delay line should be used in non-time varying
4 (reverb) or non-critical (????) applications.
5
6 JGG added alternative API, to be used in place of setDelay / tick.
7 Useful mainly for situations where you want multiple taps, which you
8 can't do with the other API. To summarize the two API's:
9
10 (1) setDelay / tick:
11 setDelay(lag);
12 output = tick(input); // output is <lag> samps older than input
13
14 (2) putSample / getSample:
15 putSample(input); // like tick, but no output returned
16 output = getSample(lag); // output is lag samps older than last input
17 out2 = getSample(lag2); // can call again before next putSample
18
19 Note that in the interest of efficiency, putSample does not maintain the
20 correct value for outPoint. (If you want to use the first API after having
21 used the second one for a DLineN object, call setDelay to init outPoint.)
22*/
23#include "DLineN.h"
24
25
26DLineN :: DLineN(long max_length) : JGFilter(0)
27{
28 /* Add 2 to let user call setDelay(max_length) next without worrying
29 about outPoint stepping on inPoint. -JGG
30 */
31 length = max_length + 2;
32
33 inputs = new double [length];
34 outPoint = 0;
35 inPoint = length >> 1; // by default, .5 of max delay -JGG
36 this->clear();
37 outputs = NULL; // unused
38}
39
40
41DLineN :: ~DLineN()
42{
43 delete [] inputs;
44}
45
46
47void DLineN :: clear()
48{
49 for (long i = 0; i < length; i++)
50 inputs[i] = 0.0;
51 lastOutput = 0.0;
52}
53
54
55void DLineN :: setDelay(double lag) // in samples! ignores fract. part
56{
57// assert((long)lag < length);
58
59 outPoint = inPoint - (long) lag; // read chases write
60 while (outPoint < 0)
61 outPoint += length; // modulo maximum length
62}
63
64
65/* Puts <sample> into delay line, returns oldest sample in delay line.
66*/
67double DLineN :: tick(double input) // take one, yield one
68{
69 inputs[inPoint++] = input;
70 if (inPoint == length) // check for end condition
71 inPoint -= length;
72 lastOutput = inputs[outPoint++];
73 if (outPoint >= length)
74 outPoint -= length;
75 return lastOutput;
76}
77
78
79/* Like tick, but doesn't bother maintaining outPoint. Use this in
80 conjunction with getSample only!
81*/
82void DLineN :: putSample(double input)
83{
84 inputs[inPoint++] = input; // input next sample
85 if (inPoint == length) // check for end condition
86 inPoint -= length;
87}
88
89
90/* Get sample from delay line that is <lag> samples behind the most recent
91 sample to enter the delay line. If <lag> is longer than length of delay
92 line, it wraps around. (So check first.)
93 (Note: this is like the cmix genlib delget, except it takes the number
94 of samples of delay rather than a delay time in seconds.)
95*/
96double DLineN :: getSample(long lag)
97{
98 long outpt = inPoint - lag;
99 while (outpt < 0)
100 outpt += length;
101 lastOutput = inputs[outpt];
102 return lastOutput;
103}
104
105
Spectacle/DLineN.h+0−45
@@ -1,45 +0,0 @@
1/* Non-Interpolating Delay Line Object by Perry R. Cook 1995-96
2 This one uses a delay line of maximum length specified on creation.
3 A non-interpolating delay line should be used in non-time varying
4 (reverb) or non-critical (????) applications.
5
6 JGG added alternative API, to be used in place of setDelay / tick.
7 Useful mainly for situations where you want multiple taps, which you
8 can't do with the other API. To summarize the two API's:
9
10 (1) setDelay / tick:
11 setDelay(lag);
12 output = tick(input); // output is <lag> samps older than input
13
14 (2) putSample / getSample:
15 putSample(input); // like tick, but no output returned
16 output = getSample(lag); // output is lag samps older than last input
17 out2 = getSample(lag2); // can call again before next putSample
18
19 Note that in the interest of efficiency, putSample does not maintain the
20 correct value for outPoint. (If you want to use the first API after having
21 used the second one for a DLineN object, call setDelay to init outPoint.)
22*/
23#if !defined(__DLineN_h)
24#define __DLineN_h
25
26#include "JGFilter.h"
27
28class DLineN : public JGFilter
29{
30 protected:
31 long inPoint;
32 long outPoint;
33 long length;
34 public:
35 DLineN(long max_length);
36 ~DLineN();
37 void clear();
38 void setDelay(double lag);
39 double tick(double input);
40 void putSample(double input);
41 double getSample(long lag);
42};
43
44#endif
45
Spectacle/JGFilter.cpp+0−24
@@ -1,24 +0,0 @@
1/* JGFilter Class, by Perry R. Cook, 1995-96
2 This is the base class for all filters. To me, most anything is a
3 filter, but I'll be a little less general here, and define a filter
4 as something which has input(s), output(s), and gain.
5*/
6
7#include "JGFilter.h"
8
9
10JGFilter :: JGFilter(double srate) : _sr(srate)
11{
12}
13
14
15JGFilter :: ~JGFilter()
16{
17}
18
19
20double JGFilter :: lastOut()
21{
22 return lastOutput;
23}
24
Spectacle/JGFilter.h+0−25
@@ -1,25 +0,0 @@
1/* Filter Class, by Perry R. Cook, 1995-96
2 This is the base class for all filters. To me, most anything is a
3 filter, but I'll be a little less general here, and define a filter
4 as something which has input(s), output(s), and gain.
5*/
6#if !defined(__JGFilter_h)
7#define __JGFilter_h
8
9#include "objdefs.h"
10
11class JGFilter
12{
13 protected:
14 double _sr;
15 double gain;
16 double *outputs;
17 double *inputs;
18 double lastOutput;
19 public:
20 JGFilter(double srate);
21 virtual ~JGFilter();
22 double lastOut();
23};
24
25#endif
Spectacle/SPECTACLE_BASE.cpp+0−577
@@ -1,577 +0,0 @@
1/* Base class for SPECTACLE* instruments
2 John Gibson <johgibso at indiana dot edu>, 11/28/02.
3*/
4
5//#define DUMP
6//#define DEBUG
7#include "SPECTACLE_BASE.h"
8
9
10/* -------------------------------------------------------- SPECTACLE_BASE -- */
11SPECTACLE_BASE :: SPECTACLE_BASE()
12{
13 first_time = 1;
14 iamp_branch = 0;
15 oamp_branch = 0;
16}
17
18
19/* ------------------------------------------------------- ~SPECTACLE_BASE -- */
20SPECTACLE_BASE :: ~SPECTACLE_BASE()
21{
22 delete [] input;
23 delete [] output;
24 delete [] anal_window;
25 delete [] synth_window;
26 delete [] fft_buf;
27 delete [] anal_chans;
28 delete [] drybuf;
29 delete [] inbuf;
30 delete [] outbuf;
31 delete dry_delay;
32}
33
34
35/* ---------------------------------------------------------- make_windows -- */
36/* Make balanced pair of analysis and synthesis windows.
37*/
38int SPECTACLE_BASE :: make_windows()
39{
40 switch (window_type) {
41 case Hamming:
42 for (int i = 0; i < window_len; i++)
43 anal_window[i] = synth_window[i] = 0.54 - 0.46
44 * cos(TWO_PI * i / (window_len - 1));
45 break;
46 case Hanning:
47 return die(instname(), "Hanning window not implemented.");
48 break;
49 case Rectangle:
50//FIXME: is this right?
51 for (int i = 0; i < window_len; i++)
52 anal_window[i] = synth_window[i] = 1.0;
53 break;
54 case Triangle:
55 return die(instname(), "Triangle window not implemented.");
56 break;
57 case Blackman:
58 return die(instname(), "Blackman window not implemented.");
59 break;
60 case Kaiser:
61 return die(instname(), "Kaiser window not implemented.");
62 break;
63 }
64
65 /* When window_len > fft_len, also apply interpolating (sinc) windows to
66 ensure that window are 0 at increments of fft_len away from the center
67 of the analysis window and of decimation away from the center of the
68 synthesis window.
69 */
70 if (window_len > fft_len) {
71 float x;
72
73 /* Take care to create symmetrical windows. */
74 x = -(window_len - 1) / 2.;
75 for (int i = 0; i < window_len; i++, x += 1.)
76 if (x != 0.) {
77 anal_window[i] *= fft_len * sin(PI * x / fft_len) / (PI * x);
78 if (decimation)
79 synth_window[i] *= decimation * sin(PI * x / decimation)
80 / (PI * x);
81 }
82 }
83
84 /* Normalize windows for unity gain across unmodified
85 analysis-synthesis procedure.
86 */
87 float sum = 0.0;
88 for (int i = 0; i < window_len; i++)
89 sum += anal_window[i];
90
91 for (int i = 0; i < window_len; i++) {
92 float afac = 2. / sum;
93 float sfac = window_len > fft_len ? 1. / afac : afac;
94 anal_window[i] *= afac;
95 synth_window[i] *= sfac;
96 }
97
98 if (window_len <= fft_len && decimation) {
99 sum = 0.0;
100 for (int i = 0; i < window_len; i += decimation)
101 sum += synth_window[i] * synth_window[i];
102 sum = 1.0 / sum;
103 for (int i = 0; i < window_len; i++)
104 synth_window[i] *= sum;
105 }
106
107 return 0;
108}
109
110
111/* ---------------------------------------------------- resample_functable -- */
112/* Accepts a function table of <oldsize> elements, describing a series of
113 line segments. Resamples this table using linear interpolation, so that
114 its shape is described by <newsize> elements. Returns a new table,
115 allocated with new, containing this resampled data. Caller is responsible
116 for deleting this table.
117*/
118double * SPECTACLE_BASE :: resample_functable(double *table, int oldsize,
119 int newsize)
120{
121 double *newtable = new double [newsize];
122
123 if (newsize == oldsize) { // straight copy
124 for (int i = 0; i < newsize; i++)
125 newtable[i] = table[i];
126 }
127 else {
128 double incr = (double) oldsize / (double) newsize;
129 double f = 0.0;
130 for (int i = 0; i < newsize; i++) {
131 int n = (int) f;
132 double frac = f - (double) n;
133 double diff = 0.0;
134 if (frac) {
135 double next = (n + 1 < oldsize) ? table[n + 1] : table[oldsize - 1];
136 diff = next - table[n];
137 }
138 newtable[i] = table[n] + (diff * frac);
139 f += incr;
140 }
141 }
142 return newtable;
143}
144
145
146/* ------------------------------------------------------------------ init -- */
147WindowType SPECTACLE_BASE :: getWindowType(double pval)
148{
149 int intval = int(pval);
150 WindowType type = Hamming;
151
152 switch (intval) {
153 case 0:
154 type = Hamming;
155 break;
156 case 1:
157 type = Hanning;
158 break;
159 case 2:
160 type = Rectangle;
161 break;
162 case 3:
163 type = Triangle;
164 break;
165 case 4:
166 type = Blackman;
167 break;
168 case 5:
169 type = Kaiser;
170 break;
171 default:
172 die(instname(), "Invalid window type %d\n.", intval);
173 break;
174 }
175 return type;
176}
177
178int SPECTACLE_BASE :: init(double p[], int n_args)
179{
180 float outskip = p[0];
181 float inskip = p[1];
182 inputdur = p[2];
183 amp = p[3];
184 ringdur = p[4];
185 fft_len = (int) p[5];
186 window_len = (int) p[6];
187 window_type = getWindowType(p[7]);
188 float overlap = p[8];
189
190 /* Make sure FFT length is a power of 2 <= MAXFFTLEN and <= RTBUFSAMPS. */
191 bool valid = false;
192 for (int x = 1; x <= MAXFFTLEN; x *= 2) {
193 if (fft_len == x) {
194 valid = true;
195 break;
196 }
197 }
198 if (!valid || fft_len > MAXFFTLEN)
199 return die(instname(), "FFT length must be a power of two <= %d",
200 MAXFFTLEN);
201
202// FIXME: now this isn't a problem; instead, decimation can't be larger
203// than RTBUFSAMPS. But must couch errmsg in terms of overlap and fft length,
204// not decimation...
205#if 0
206 if (fft_len > RTBUFSAMPS)
207 return die(instname(),
208 "FFT length must be a power of two less than or equal\n"
209 "to the output buffer size set in rtsetparams (currently %d).",
210 RTBUFSAMPS);
211#endif
212 half_fft_len = fft_len / 2;
213 fund_anal_freq = SR / (float) fft_len;
214
215 /* Make sure window length is a power of 2 >= FFT length. */
216 valid = false;
217 for (int x = fft_len; x <= MAXWINDOWLEN; x *= 2) {
218 if (window_len == x) {
219 valid = true;
220 break;
221 }
222 }
223 if (!valid)
224 return die(instname(),
225 "Window length must be a power of two >= FFT length\n"
226 "(currently %d) and <= %d.", fft_len, MAXWINDOWLEN);
227
228 /* Make sure overlap is a power of 2 in our safety range. */
229 valid = false;
230//FIXME: need to adjust MINOVERLAP so that iterations is never 0 in run()
231// This might depend upon window_len??
232 for (float x = MINOVERLAP; x <= MAXOVERLAP; x *= 2.0) {
233 if (overlap == x) {
234 valid = true;
235 break;
236 }
237 }
238 if (!valid)
239 return die(instname(),
240 "Overlap must be a power of two between %g and %g.",
241 MINOVERLAP, MAXOVERLAP);
242 int_overlap = (int) overlap;
243
244 /* derive decimation from overlap */
245 decimation = (int) (fft_len / overlap);
246
247 DPRINT2("fft_len=%d, decimation=%d\n", fft_len, decimation);
248
249 if (pre_init(p, n_args) != 0) /* can modify ringdur */
250 return DONT_SCHEDULE;
251
252 iamparray = floc(1);
253 if (iamparray) {
254 int lenamp = fsize(1);
255 tableset(SR, inputdur, lenamp, iamptabs);
256 }
257 else
258 rtcmix_advise(instname(), "Setting input amplitude curve to all 1's.");
259
260 oamparray = floc(2);
261 if (oamparray) {
262 int lenamp = fsize(2);
263 tableset(SR, inputdur + ringdur, lenamp, oamptabs);
264 }
265 else
266 rtcmix_advise(instname(), "Setting output amplitude curve to all 1's.");
267
268 if (rtsetinput(inskip, this) == -1)
269 return DONT_SCHEDULE;
270 if (inchan >= inputChannels())
271 return die(instname(), "You asked for channel %d of a %d-channel file.",
272 inchan, inputChannels());
273
274 /* <latency> is the delay before the FFT looks at actual input rather than
275 zero-padding. Need to let inst run long enough to compensate for this.
276 */
277 window_len_minus_decimation = window_len - decimation;
278 latency = window_len + window_len_minus_decimation;
279 float latency_dur = latency * (1.0 / SR);
280 if (rtsetoutput(outskip, latency_dur + inputdur + ringdur, this) == -1)
281 return DONT_SCHEDULE;
282 total_insamps = (int)(inputdur * SR); /* without latency_dur */
283 input_end_frame = total_insamps + latency;
284 DPRINT1("input_end_frame=%d\n", input_end_frame);
285
286 input = new float [window_len]; /* input buffer */
287 output = new float [window_len]; /* output buffer */
288 anal_window = new float [window_len]; /* analysis window */
289 synth_window = new float [window_len]; /* synthesis window */
290 fft_buf = new float [fft_len]; /* FFT buffer */
291 anal_chans = new float [fft_len + 2]; /* analysis channels */
292
293 if (make_windows() != 0)
294 return DONT_SCHEDULE;
295
296 /* Delay dry output by window_len - decimation to sync with wet sig. */
297 drybuf = new float [decimation];
298 dry_delay = new DLineN(window_len);
299 dry_delay->setDelay((float) window_len_minus_decimation);
300
301 /* Init iamp and oamp to starting amplitudes. */
302 iamp = (iamparray == NULL) ? 1.0 : iamparray[0];
303 oamp = (oamparray == NULL) ? amp : oamparray[0];
304
305 skip = (int) (SR / (float) resetval);
306
307 if (post_init(p, n_args) != 0)
308 return DONT_SCHEDULE;
309
310 return nSamps();
311}
312
313
314/* ---------------------------------------------------------------- shiftin -- */
315void SPECTACLE_BASE :: shiftin()
316{
317 DPRINT1("shiftin (cursamp=%d)\n", currentFrame());
318
319 int _cursamp = currentFrame();
320
321 /* Shift samples in <input> from right to left by <decimation>. */
322 for (int i = 0; i < window_len_minus_decimation; i++)
323 input[i] = input[i + decimation];
324
325 /* Copy <decimation> samples from <inbuf> to right end of <input> and
326 to left end of <drybuf>.
327 */
328 int j = 0;
329 for (int i = window_len_minus_decimation; i < window_len; i++, j++) {
330 if (--iamp_branch <= 0) {
331 /* Since inbuf_readptr trails inbuf_writeptr, we delay the start of
332 the envelope until we reach actual input rather than zero padding.
333 */
334 if (iamparray && _cursamp >= window_len)
335 iamp = tablei(_cursamp - window_len, iamparray, iamptabs);
336 iamp_branch = skip;
337 }
338 float sig = inbuf_readptr[inchan] * iamp;
339 drybuf[j] = dry_delay->tick(sig);
340 input[i] = sig;
341 inbuf_readptr += inputChannels();
342 if (inbuf_readptr >= inbuf_endptr) {
343 inbuf_readptr = inbuf_startptr;
344 /* <inbuf_startptr> might not == <inbuf> in first call to run(). */
345 inbuf_startptr = inbuf;
346 DPRINT1("shiftin: resetting inbuf_readptr=%p\n", inbuf_readptr);
347 }
348 _cursamp++;
349 }
350}
351
352
353/* ------------------------------------------------------------------ fold -- */
354/* Multiply input array by the analysis window, both of length <window_len>.
355 Using modulus arithmetic, fold and rotate windowed input into FFT buffer
356 of length <fft_len>, according to current input time <n>.
357*/
358void SPECTACLE_BASE :: fold(int n)
359{
360 for (int i = 0; i < fft_len; i++)
361 fft_buf[i] = 0.0;
362
363 n %= fft_len;
364
365 for (int i = 0; i < window_len; i++) {
366 fft_buf[n] += input[i] * anal_window[i];
367 if (++n == fft_len)
368 n = 0;
369 }
370}
371
372
373/* ----------------------------------------------------------- leanconvert -- */
374/* <fft_buf> is a spectrum in JGrfft format, i.e. it contains <half_fft_len> * 2
375 real values, arranged in pairs of real and imaginary values, except for
376 the first two values, which are the real parts of 0 and Nyquist frequencies.
377 Converts these into <half_fft_len> + 1 pairs of magnitude and phase values,
378 and stores them into the output array <anal_chans>.
379*/
380void SPECTACLE_BASE :: leanconvert()
381{
382 int real_index, imag_index, amp_index, phase_index;
383 float a, b;
384
385 for (int i = 0; i <= half_fft_len; i++) {
386 real_index = amp_index = i << 1;
387 imag_index = phase_index = real_index + 1;
388 if (i == half_fft_len) {
389 a = fft_buf[1];
390 b = 0.0;
391 }
392 else {
393 a = fft_buf[real_index];
394 b = (i == 0) ? 0.0 : fft_buf[imag_index];
395 }
396 anal_chans[amp_index] = hypot(a, b);
397 anal_chans[phase_index] = -atan2(b, a);
398 }
399}
400
401
402/* ------------------------------------------------------- flush_dry_delay -- */
403void SPECTACLE_BASE :: flush_dry_delay()
404{
405 if (currentFrame() < input_end_frame + window_len_minus_decimation) {
406 for (int i = 0; i < decimation; i++)
407 drybuf[i] = dry_delay->tick(0.0);
408 }
409}
410
411
412/* --------------------------------------------------------- leanunconvert -- */
413/* leanunconvert essentially undoes what leanconvert does, i.e., it turns
414 <half_fft_len> + 1 pairs of amplitude and phase values in <anal_chans>
415 into <half_fft_len> pairs of complex spectrum data (in JGrfft format) in
416 output array <fft_buf>.
417*/
418void SPECTACLE_BASE :: leanunconvert()
419{
420 int real_index, imag_index, amp_index, phase_index;
421 float mag, phase;
422
423 for (int i = 0; i <= half_fft_len; i++) {
424 real_index = amp_index = i << 1;
425 imag_index = phase_index = real_index + 1;
426 if (i == half_fft_len)
427 real_index = 1;
428 mag = anal_chans[amp_index];
429 phase = anal_chans[phase_index];
430 fft_buf[real_index] = mag * cos(phase);
431 if (i != half_fft_len)
432 fft_buf[imag_index] = -mag * sin(phase);
433 }
434}
435
436
437/* ------------------------------------------------------------ overlapadd -- */
438/* <fft_buf> is a folded spectrum of length <fft_len>. <output> and
439 <synth_window> are of length <window_len>. Overlap-add windowed,
440 unrotated, unfolded <fft_buf> data into <output>.
441*/
442void SPECTACLE_BASE :: overlapadd(int n)
443{
444 n %= fft_len;
445 for (int i = 0; i < window_len; i++) {
446 output[i] += fft_buf[n] * synth_window[i];
447 if (++n == fft_len)
448 n = 0;
449 }
450}
451
452
453/* -------------------------------------------------------------- shiftout -- */
454void SPECTACLE_BASE :: shiftout()
455{
456 int _cursamp = currentFrame();
457
458 for (int i = 0; i < decimation; i++) {
459 if (--oamp_branch <= 0) {
460 if (oamparray && (_cursamp >= latency))
461 oamp = tablei(_cursamp - latency, oamparray, oamptabs) * amp;
462 oamp_branch = skip;
463 }
464 float sig;
465 if (_cursamp < input_end_frame)
466 sig = ((output[i] * wetdry) + ((1.0 - wetdry) * drybuf[i])) * oamp;
467 else
468 sig = output[i] * wetdry * oamp;
469 if (outputChannels() == 2) {
470 *outbuf_writeptr++ = sig * pctleft;
471 *outbuf_writeptr++ = sig * (1.0 - pctleft);
472 }
473 else
474 *outbuf_writeptr++ = sig;
475 if (outbuf_writeptr >= outbuf_endptr) {
476 outbuf_writeptr = outbuf;
477 DPRINT1("shiftout: resetting outbuf_writeptr=%p\n", outbuf_writeptr);
478 }
479 _cursamp++;
480 }
481
482 /* shift samples in <output> from right to left by <decimation> */
483 for (int i = 0; i < window_len_minus_decimation; i++)
484 output[i] = output[i + decimation];
485 for (int i = window_len_minus_decimation; i < window_len; i++)
486 output[i] = 0.0;
487}
488
489
490/* ------------------------------------------------------------------- run -- */
491int SPECTACLE_BASE :: run()
492{
493 if (first_time) {
494 /* create segmented input buffer */
495 int num_segments = (window_len / RTBUFSAMPS) + 2;
496 int extrasamps = RTBUFSAMPS - framesToRun();
497 if (extrasamps)
498 num_segments++;
499 int inbuf_samps = num_segments * RTBUFSAMPS * inputChannels();
500 inbuf = new float [inbuf_samps];
501 for (int i = 0; i < inbuf_samps; i++)
502 inbuf[i] = 0.0;
503
504 /* Read ptr chases write ptr by <window_len>. Set read ptr to a position
505 <window_len> frames from right end of input buffer. Set write ptr to
506 beginning of buffer, or, if framesToRun() is not the same as the RTcmix
507 buffer size, set it so that the next run invocation after this one
508 will find the write ptr at the start of the second buffer segment.
509 */
510 inbuf_readptr = inbuf + (inbuf_samps - (window_len * inputChannels()));
511 inbuf_startptr = inbuf + (extrasamps * inputChannels());
512 inbuf_writeptr = inbuf_startptr;
513 inbuf_endptr = inbuf + inbuf_samps;
514
515 int outbuf_samps = num_segments * RTBUFSAMPS * outputChannels();
516 outbuf = new float [outbuf_samps];
517 for (int i = 0; i < outbuf_samps; i++)
518 outbuf[i] = 0.0;
519
520 outbuf_readptr = outbuf + (outbuf_samps
521 - (framesToRun() * outputChannels()));
522 outbuf_writeptr = outbuf_readptr;
523 outbuf_endptr = outbuf + outbuf_samps;
524 first_time = 0;
525
526 DPRINT3("framesToRun()=%d, extrasamps=%d, num_segments=%d\n",
527 framesToRun(), extrasamps, num_segments);
528 DPRINT3("inbuf_samps=%d, inbuf_readptr=%p, inbuf_writeptr=%p\n",
529 inbuf_samps, inbuf_readptr, inbuf_writeptr);
530 DPRINT3("outbuf_samps=%d, outbuf_readptr=%p, outbuf_writeptr=%p\n",
531 outbuf_samps, outbuf_readptr, outbuf_writeptr);
532 }
533
534 const int insamps = framesToRun() * inputChannels();
535 if (currentFrame() < total_insamps)
536 rtgetin(inbuf_writeptr, this, insamps);
537
538 int iterations = framesToRun() / decimation;
539 if (framesToRun() < RTBUFSAMPS)
540 iterations++;
541
542 DPRINT1("iterations=%d\n", iterations);
543
544 for (int i = 0; i < iterations; i++) {
545 if (currentFrame() < input_end_frame) {
546 DPRINT1("taking input...cursamp=%d\n", currentFrame());
547 shiftin();
548 fold(currentFrame());
549 JGrfft(fft_buf, half_fft_len, FORWARD);
550 leanconvert();
551 }
552 else
553 flush_dry_delay();
554 modify_analysis();
555 leanunconvert();
556 JGrfft(fft_buf, half_fft_len, INVERSE);
557 overlapadd(currentFrame());
558 shiftout();
559
560 increment(decimation);
561 }
562
563 if (currentFrame() < input_end_frame) {
564 inbuf_writeptr += insamps;
565 if (inbuf_writeptr >= inbuf_endptr)
566 inbuf_writeptr = inbuf;
567 }
568
569 rtbaddout(outbuf_readptr, framesToRun());
570 outbuf_readptr += framesToRun() * outputChannels();
571 if (outbuf_readptr >= outbuf_endptr)
572 outbuf_readptr = outbuf;
573
574 return framesToRun();
575}
576
577
Spectacle/SPECTACLE_BASE.h+0−85
@@ -1,85 +0,0 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <math.h>
4//#include <ugens.h>
5//#include <mixerr.h>
6//#include <Instrument.h>
7//#include <rt.h>
8//#include <rtdefs.h>
9//#include <assert.h>
10//#include <objlib.h>
11#include "fft.h"
12
13#define MAXFFTLEN 4096
14#define MAXWINDOWLEN MAXFFTLEN * 8
15#define MINOVERLAP 0.25
16#define MAXOVERLAP 64.0
17#define MAXDELTIME 20.0
18
19#ifdef DEBUG
20 #define DPRINT(msg) printf((msg))
21 #define DPRINT1(msg, arg) printf((msg), (arg))
22 #define DPRINT2(msg, arg1, arg2) printf((msg), (arg1), (arg2))
23 #define DPRINT3(msg, arg1, arg2, arg3) printf((msg), (arg1), (arg2), (arg3))
24#else
25 #define DPRINT(msg)
26 #define DPRINT1(msg, arg)
27 #define DPRINT2(msg, arg1, arg2)
28 #define DPRINT3(msg, arg1, arg2, arg3)
29#endif
30
31typedef enum {
32 Hamming = 0,
33 Hanning,
34 Rectangle,
35 Triangle,
36 Blackman,
37 Kaiser
38} WindowType;
39
40class SPECTACLE_BASE {
41
42private:
43 int iamp_branch, oamp_branch;
44 float amp, iamp, oamp;
45 float *anal_window, *synth_window, *input, *output, *fft_buf, *drybuf;
46 float *inbuf, *inbuf_startptr, *inbuf_readptr, *inbuf_writeptr,
47 *inbuf_endptr;
48 float *outbuf, *outbuf_startptr, *outbuf_readptr, *outbuf_writeptr,
49 *outbuf_endptr;
50 float iamptabs[2], oamptabs[2];
51 double *iamparray, *oamparray;
52 DLineN *dry_delay;
53 WindowType window_type;
54protected:
55 int inchan, skip, total_insamps, first_time, int_overlap;
56 int fft_len, window_len, decimation, window_len_minus_decimation,
57 half_fft_len, latency, input_end_frame;
58 float wetdry, pctleft, inputdur, ringdur, fund_anal_freq;
59 float *anal_chans;
60
61public:
62 SPECTACLE_BASE();
63 virtual ~SPECTACLE_BASE();
64 virtual int init(double p[], int n_args);
65 virtual int run();
66protected:
67 double *resample_functable(double *, int, int);
68 int make_windows();
69 void shiftin();
70 void fold(int);
71 void leanconvert();
72 void flush_dry_delay();
73 void leanunconvert();
74 void overlapadd(int);
75 void shiftout();
76 virtual int pre_init(double p[], int n_args) = 0;
77 virtual int post_init(double p[], int n_args) = 0;
78 virtual void modify_analysis() = 0;
79 virtual const char *instname() = 0;
80private:
81 WindowType getWindowType(double pval);
82};
83
84inline int odd(long x) { return (x & 0x00000001); }
85
Spectacle/SpectEQ.cpp+117−0
@@ -0,0 +1,117 @@
1/*
2 * Copyright (C) 2007 John Gibson
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2, as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14// derived from RTcmix SPECTEQ2.cpp, (C) John Gibson 2005.
15
16//#define DEBUG
17#include "SpectEQ.h"
18#include <cstdlib>
19
20// ------------------------------------------------------------------ SpectEQ --
21SpectEQ::SpectEQ()
22 : _eqconst(0.0f), _eqtable(NULL)
23{
24}
25
26
27// ----------------------------------------------------------------- ~SpectEQ --
28SpectEQ::~SpectEQ()
29{
30 // We don't own eq table.
31}
32
33
34// --------------------------------------------------------------------- init --
35// Invoke base init, and perform any initialization specific to this subclass.
36// Return 0 if okay, -1 if not.
37
38int SpectEQ::init(int fftlen, int windowlen, int overlap, float srate)
39{
40 return SpectacleBase::init(fftlen, windowlen, overlap, srate);
41}
42
43
44// ---------------------------------------------------------------- set_srate --
45void SpectEQ::set_srate(float srate)
46{
47 SpectacleBase::set_srate(srate);
48}
49
50
51// -------------------------------------------------------------- set_eqtable --
52void SpectEQ::set_eqtable(float *table, int len)
53{
54 if (len == 0) { // no table set yet by caller
55 _eqconst = 0.0;
56 _eqtable = NULL;
57 _control_table_size = 0;
58 }
59 else if (len == 1) {
60 _eqconst = table[0];
61 _eqtable = NULL;
62 _control_table_size = 0;
63 }
64 else {
65 _eqconst = 0.0;
66 _eqtable = table;
67 if (len != _control_table_size) {
68 _control_table_size = len;
69 update_bin_groups(_bin_groups, NULL, get_minfreq(), get_maxfreq(),
70 _control_table_size);
71 }
72 }
73}
74
75
76// --------------------------------------------------------- set_binmap_table --
77// Set pointer to externally allocated table. Pass table=NULL for no table.
78// <len> must be the same as the current control table size, or else we use
79// no table, without warning -- responsibility of caller to handle this.
80
81void SpectEQ::set_binmap_table(int *table, int len)
82{
83 if (len != _control_table_size)
84 _binmaptable = NULL;
85 else
86 _binmaptable = table;
87 update_bin_groups(_bin_groups, NULL, get_minfreq(), get_maxfreq(),
88 _control_table_size);
89}
90
91
92// ---------------------------------------------------------- modify_analysis --
93void SpectEQ::modify_analysis(bool)
94{
95 DPRINT("modify_analysis: .....................");
96
97 // NB: check EQ table size, rather than table pointer, to determine whether
98 // we should use an EQ constant. Otherwise, there's a danger we could read
99 // a null EQ table pointer, if set_eqtable called by non-perf thread.
100 if (_control_table_size == 0) { // constant gain across bins
101 const float eq = _ampdb(_eqconst);
102 const int len = get_fftlen();
103 for (int i = 0; i < len; i++)
104 _fft_buf[i] *= eq;
105 }
106 else {
107 for (int i = 0; i < _half_fftlen; i++) {
108 const float eq = _ampdb(_eqtable[_bin_groups[i]]);
109 const int index = i << 1;
110 _fft_buf[index] *= eq; // real
111 _fft_buf[index + 1] *= eq; // imag
112 }
113 }
114 _fft_buf[1] = 0.0f; // clear Nyquist real value
115}
116
117
Spectacle/SpectEQ.h+36−0
@@ -0,0 +1,36 @@
1/*
2 * Copyright (C) 2007 John Gibson
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2, as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14//#define DEBUG
15
16#include "SpectacleBase.h"
17
18class SpectEQ : public SpectacleBase {
19
20public:
21 SpectEQ();
22 virtual ~SpectEQ();
23 int init(int fftlen, int windowlen, int overlap, float srate);
24 void set_srate(float srate);
25 void set_eqtable(float *table, int len);
26 void set_binmap_table(int *table, int len);
27
28protected:
29 virtual void modify_analysis(bool reading_input);
30 virtual const char *instname() { return "specteq~"; }
31
32private:
33 float _eqconst;
34 float *_eqtable;
35};
36
Spectacle/Spectacle-dsp.cpp+382−0
@@ -0,0 +1,382 @@
1/*
2 * Copyright (C) 2007 John Gibson
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2, as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14// derived from RTcmix SPECTACLE2.cpp, (C) John Gibson 2005.
15
16
17#include "Spectacle-dsp.h"
18#include <float.h>
19#include <cstddef>
20
21//#define DEBUG
22//#define PRINT_DELTIMES
23//#define PRINT_DELTIME_CHANGES
24
25#if defined(PRINT_DELTIME_CHANGES) && !defined(PRINT_DELTIMES)
26 #define PRINT_DELTIMES
27#endif
28
29const float kMaxDelayTime = 20.0f;
30#ifdef ANTI_DENORM
31 const float kAntiDenormConstant = 1e-18f;
32#endif
33
34
35// ---------------------------------------------------------------- Spectacle_dsp --
36Spectacle_dsp::Spectacle_dsp()
37 : _maxdelsamps(0L), _maxdeltime(0.0f), _eqconst(0.0f), _deltimeconst(0.0f),
38 _feedbackconst(0.0f), _delay_minfreq(-FLT_MAX), _delay_maxfreq(-FLT_MAX),
39 _eqtable(NULL), _deltimetable(NULL), _feedbacktable(NULL),
40 _delay_bin_groups(NULL), _delay_binmap_table(NULL), _delay_table_size(0)
41{
42#ifdef ANTI_DENORM
43 _antidenorm_offset = kAntiDenormConstant;
44#endif
45}
46
47
48// --------------------------------------------------------------- ~Spectacle_dsp --
49Spectacle_dsp::~Spectacle_dsp()
50{
51 // NB: we don't own the EQ, delay time, and feedback tables.
52
53 for (int i = 0; i < _half_fftlen; i++) {
54 if (_real_delay[i])
55 delete _real_delay[i];
56 if (_imag_delay[i])
57 delete _imag_delay[i];
58 }
59
60 delete [] _delay_bin_groups;
61}
62
63
64// --------------------------------------------------------------------- init --
65// Invoke base init, and perform any initialization specific to this subclass.
66// Return 0 if okay, -1 if not.
67
68int Spectacle_dsp::init(int fftlen, int windowlen, int overlap, float srate,
69 float maxdeltime)
70{
71 if (SpectacleBase::init(fftlen, windowlen, overlap, srate) != 0)
72 return -1;
73
74 // Compute maximum delay lag and create delay lines for FFT real and
75 // imaginary values. Remember that these delays function at the decimation
76 // rate, not at the audio rate, so the memory footprint is not as large
77 // as you would expect -- about 44100 samples per second at fftlen=1024,
78 // overlap=2 and SR=44100.
79
80 _maxdeltime = maxdeltime;
81 _maxdelsamps = long(maxdeltime * get_srate() / float(_decimation) + 0.5);
82
83 for (int i = 0; i < _half_fftlen; i++) {
84 Odelay *delay = new Odelay(_maxdelsamps);
85 if (delay == NULL)
86 goto bad_alloc;
87 _real_delay[i] = delay;
88 delay = new Odelay(_maxdelsamps);
89 if (delay == NULL)
90 goto bad_alloc;
91 _imag_delay[i] = delay;
92 }
93
94 _delay_bin_groups = new int [_half_fftlen];
95
96 set_delay_freqrange(0.0f, 0.0f);
97
98 return 0;
99bad_alloc:
100 //error("%s: Not enough memory for delay lines.", instname());
101 return -1;
102}
103
104
105// -------------------------------------------------------------------- clear --
106void Spectacle_dsp::clear()
107{
108 for (int i = 0; i < _half_fftlen; i++) {
109 _real_delay[i]->clear();
110 _imag_delay[i]->clear();
111 }
112 SpectacleBase::clear();
113}
114
115
116// ---------------------------------------------------------------- set_srate --
117void Spectacle_dsp::set_srate(float srate)
118{
119 if (_delay_maxfreq == _nyquist)
120 _delay_maxfreq = 0.0f;
121 SpectacleBase::set_srate(srate);
122 if (_delay_maxfreq == 0.0f) // nyquist might have changed
123 _delay_maxfreq = _nyquist;
124 set_maxdeltime(_maxdeltime);
125 update_bin_groups(_delay_bin_groups, _delay_binmap_table,
126 _delay_minfreq, _delay_maxfreq, _delay_table_size);
127}
128
129
130// ----------------------------------------------------------- set_maxdeltime --
131// Reset delay lines to accommodate a maximum delay of <time> seconds at the
132// current sampling rate. Assumes that caller constrains all delay times to
133// fit this new maximum.
134void Spectacle_dsp::set_maxdeltime(float time)
135{
136 _maxdeltime = time;
137 _maxdelsamps = long(time * get_srate() / float(_decimation) + 0.5);
138 for (int i = 0; i < _half_fftlen; i++) {
139 _real_delay[i]->resize(_maxdelsamps);
140 _imag_delay[i]->resize(_maxdelsamps);
141 }
142}
143
144
145// -------------------------------------------------------------- set_eqtable --
146void Spectacle_dsp::set_eqtable(float *table, int len)
147{
148 if (len == 0) { // no table set yet by caller
149 _eqconst = 0.0;
150 _eqtable = NULL;
151 _control_table_size = 0;
152 }
153 else if (len == 1) {
154 _eqconst = table[0];
155 _eqtable = NULL;
156 _control_table_size = 0;
157 }
158 else {
159 _eqconst = 0.0;
160 _eqtable = table;
161 if (len != _control_table_size) {
162 _control_table_size = len;
163 update_bin_groups(_bin_groups, NULL, get_minfreq(), get_maxfreq(),
164 _control_table_size);
165 }
166 }
167}
168
169
170// ------------------------------------------------------------- set_deltable --
171void Spectacle_dsp::set_deltable(float *table, int len)
172{
173 if (len == 0) { // no table set yet by caller
174 _deltimeconst = 0.0;
175 _deltimetable = NULL;
176 }
177 else if (len == 1) {
178 _deltimeconst = table[0];
179 _deltimetable = NULL;
180 // NB: don't set _delay_table_size to zero
181 }
182 else {
183 _deltimeconst = 0.0;
184 _deltimetable = table;
185 if (len != _delay_table_size) {
186 _delay_table_size = len;
187 update_bin_groups(_delay_bin_groups, _delay_binmap_table,
188 _delay_minfreq, _delay_maxfreq, _delay_table_size);
189 }
190 }
191}
192
193
194// ------------------------------------------------------------ set_feedtable --
195void Spectacle_dsp::set_feedtable(float *table, int len)
196{
197 if (len == 0) { // no table set yet by caller
198 _feedbackconst = 0.0;
199 _feedbacktable = NULL;
200 }
201 else if (len == 1) {
202 _feedbackconst = table[0];
203 _feedbacktable = NULL;
204 }
205 else if (len == _delay_table_size) {
206 _feedbackconst = 0.0;
207 _feedbacktable = table;
208 }
209 // else don't do anything -- spectacle_fb_msg insures we never get here
210}
211
212
213// ------------------------------------------------------ set_delay_freqrange --
214// Similar to set_freqrange in base class, but for _delay_bin_groups.
215bool Spectacle_dsp::set_delay_freqrange(float min, float max)
216{
217 if (min != _delay_minfreq || max != _delay_maxfreq) {
218 if (max == 0.0f)
219 max = _nyquist;
220 if (max < min)
221 _fswap(&min, &max);
222 if (max > _nyquist)
223 max = _nyquist;
224 _delay_maxfreq = max;
225 _delay_minfreq = _fclamp(0.0f, min, max);
226 update_bin_groups(_delay_bin_groups, _delay_binmap_table,
227 _delay_minfreq, max, _delay_table_size);
228 return true;
229 }
230 return false;
231}
232
233
234// --------------------------------------------------------- set_binmap_table --
235// Set pointer to externally allocated table. Pass table=NULL for no table.
236// <len> must be the same as the current control table size, or else we use
237// no table, without warning -- responsibility of caller to handle this.
238
239void Spectacle_dsp::set_binmap_table(int *table, int len)
240{
241 if (table == NULL || len == 0 || len != _control_table_size)
242 _binmaptable = NULL;
243 else
244 _binmaptable = table;
245 update_bin_groups(_bin_groups, NULL, get_minfreq(), get_maxfreq(),
246 _control_table_size);
247}
248
249
250// --------------------------------------------------- set_delay_binmap_table --
251// Set pointer to externally allocated table. Pass table=NULL for no table.
252// <len> must be the same as the current delay table size, or else we use
253// no table, without warning -- responsibility of caller to handle this.
254
255void Spectacle_dsp::set_delay_binmap_table(int *table, int len)
256{
257 if (table == NULL || len == 0 || len != _delay_table_size)
258 _delay_binmap_table = NULL;
259 else
260 _delay_binmap_table = table;
261 update_bin_groups(_delay_bin_groups, _delay_binmap_table,
262 _delay_minfreq, _delay_maxfreq, _delay_table_size);
263}
264
265
266// ---------------------------------------------------------- modify_analysis --
267void Spectacle_dsp::modify_analysis(bool reading_input)
268{
269 DPRINT("modify_analysis: .....................");
270
271#ifdef PRINT_DELTIMES
272 static float *prevdeltimes = NULL;
273 if (prevdeltimes == NULL) {
274 prevdeltimes = new float [_delay_table_size];
275 post("\nmodify_analysis: delay times --------------------------");
276 for (int i = 0; i < _delay_table_size; i++) {
277 prevdeltimes[i] = _deltimetable[i];
278 post("[%d] %f", i, _deltimetable[i]);
279 }
280 #ifdef PRINT_DELTIME_CHANGES
281 post("\nmodify_analysis: delay time changes -------------------");
282 #endif
283 }
284#endif
285
286 const bool posteq = get_posteq();
287 float eq;
288
289 // NB: check EQ table size, rather than table pointer, to determine whether
290 // we should use an EQ constant. Otherwise, there's a danger we could read
291 // a null EQ table pointer, if set_eqtable called by non-perf thread.
292 if (_control_table_size == 0)
293 eq = _ampdb(_eqconst);
294
295 for (int i = 0; i < _half_fftlen; i++) {
296 int index = i << 1;
297
298 if (_control_table_size > 0) {
299 // EQ uses base class bin groups array.
300 const int bg = _bin_groups[i];
301 eq = _ampdb(_eqtable[bg]);
302 }
303
304 float real, imag;
305 if (reading_input) {
306 if (posteq) {
307 real = _fft_buf[index];
308 imag = _fft_buf[index + 1];
309 }
310 else {
311 real = _fft_buf[index] * eq;
312 imag = _fft_buf[index + 1] * eq;
313 }
314 }
315 else {
316 real = 0.0f;
317 imag = 0.0f;
318 }
319
320 const int bg = _delay_bin_groups[i];
321
322 // NB: caller must assure that deltime is in range
323 const float deltime = _deltimetable ? _deltimetable[bg] : _deltimeconst;
324
325#ifdef PRINT_DELTIME_CHANGES
326 if (deltime != prevdeltimes[bg]) {
327 post("[%d] %f", bg, deltime);
328 prevdeltimes[bg] = deltime;
329 }
330#endif
331
332 if (deltime == 0.0f) {
333 if (posteq) {
334 _fft_buf[index] = real * eq;
335 _fft_buf[index + 1] = imag * eq;
336 }
337 else {
338 _fft_buf[index] = real;
339 _fft_buf[index + 1] = imag;
340 }
341 }
342 else {
343 const long delsamps = long((deltime * get_srate()) + 0.5)
344 / _decimation;
345 const float newreal = _real_delay[i]->getsamp(delsamps);
346 const float newimag = _imag_delay[i]->getsamp(delsamps);
347 const float feedback = _feedbacktable ? _feedbacktable[bg]
348 : _feedbackconst;
349 if (feedback != 0.0) {
350#ifdef ANTI_DENORM
351 float fbsig = (newreal * feedback) + _antidenorm_offset;
352 _real_delay[i]->putsamp(real + fbsig);
353 fbsig = (newimag * feedback) + _antidenorm_offset;
354 _imag_delay[i]->putsamp(imag + fbsig);
355#else
356 _real_delay[i]->putsamp(real + (newreal * feedback));
357 _imag_delay[i]->putsamp(imag + (newimag * feedback));
358#endif
359 }
360 else {
361 _real_delay[i]->putsamp(real);
362 _imag_delay[i]->putsamp(imag);
363 }
364 if (posteq) {
365 _fft_buf[index] = newreal * eq;
366 _fft_buf[index + 1] = newimag * eq;
367 }
368 else {
369 _fft_buf[index] = newreal;
370 _fft_buf[index + 1] = newimag;
371 }
372 }
373 }
374
375 _fft_buf[1] = 0.0f; // clear Nyquist real value
376
377#ifdef ANTI_DENORM
378 _antidenorm_offset = -_antidenorm_offset;
379#endif
380}
381
382
Spectacle/Spectacle-dsp.h+56−0
@@ -0,0 +1,56 @@
1/*
2 * Copyright (C) 2007 John Gibson
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2, as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14//#define DEBUG
15
16#include "SpectacleBase.h"
17
18#if defined(i386)
19 #define ANTI_DENORM
20#endif
21
22class Spectacle_dsp : public SpectacleBase {
23
24public:
25 Spectacle_dsp();
26 virtual ~Spectacle_dsp();
27 int init(int fftlen, int windowlen, int overlap, float srate,
28 float maxdeltime);
29 void clear();
30 void set_srate(float srate);
31 void set_maxdeltime(float time);
32 void set_eqtable(float *table, int len);
33 void set_deltable(float *table, int len);
34 void set_feedtable(float *table, int len);
35 void set_binmap_table(int *table, int len);
36 void set_delay_binmap_table(int *table, int len);
37 bool set_delay_freqrange(float min, float max);
38
39protected:
40 virtual void modify_analysis(bool reading_input);
41 virtual const char *instname() { return "spectacle~"; }
42
43private:
44 long _maxdelsamps;
45 float _maxdeltime;
46 float _eqconst, _deltimeconst, _feedbackconst;
47 float _delay_minfreq, _delay_maxfreq;
48 float *_eqtable, *_deltimetable, *_feedbacktable;
49 int *_delay_bin_groups, *_delay_binmap_table, _delay_table_size;
50 Odelay *_real_delay[kMaxFFTLen / 2];
51 Odelay *_imag_delay[kMaxFFTLen / 2];
52#ifdef ANTI_DENORM
53 float _antidenorm_offset;
54#endif
55};
56
Spectacle/Spectacle.cpp+84−375
@@ -7,10 +7,11 @@ · 6 unmodified lines
7#include "chuck_dl.h"7#include "chuck_dl.h"
8#include "chuck_def.h"8#include "chuck_def.h"
9 9
10#include "Spectacle-dsp.h"
11
10// general includes12// general includes
11#include <stdio.h>13#include <stdio.h>
12#include <limits.h>14#include <limits.h>
13#include "DLineN.h"
14 15
15// declaration of chugin constructor16// declaration of chugin constructor
16CK_DLL_CTOR(spectacle_ctor);17CK_DLL_CTOR(spectacle_ctor);
@@ -21,23 +22,42 @@ CK_DLL_DTOR(spectacle_dtor); · 4 unmodified lines
21CK_DLL_MFUN(spectacle_setParam);22CK_DLL_MFUN(spectacle_setParam);
22CK_DLL_MFUN(spectacle_getParam);23CK_DLL_MFUN(spectacle_getParam);
23 24
24// for Chugins extending UGen, this is stereo synthesis for 8 samples25CK_DLL_MFUN(spectacle_setFFTlen);
26CK_DLL_MFUN(spectacle_getFFTlen);
27
28CK_DLL_MFUN(spectacle_setOverlap);
29CK_DLL_MFUN(spectacle_getOverlap);
30
31CK_DLL_MFUN(spectacle_setMaxDelay);
32CK_DLL_MFUN(spectacle_getMaxDelay);
33
34CK_DLL_MFUN(spectacle_clear);
35CK_DLL_MFUN(spectacle_hold);
36CK_DLL_MFUN(spectacle_posteq);
37
38CK_DLL_MFUN(spectacle_setMinFreq);
39CK_DLL_MFUN(spectacle_getMinFreq);
40
41CK_DLL_MFUN(spectacle_setMaxFreq);
42CK_DLL_MFUN(spectacle_getMaxFreq);
43
44
45// for Chugins extending UGen, this is mono synthesis function for 1 sample
25CK_DLL_TICKF(spectacle_tick);46CK_DLL_TICKF(spectacle_tick);
26 47
27// this is a special offset reserved for Chugin internal data48// this is a special offset reserved for Chugin internal data
28t_CKINT spectacle_data_offset = 0;49t_CKINT spectacle_data_offset = 0;
29 50
30#define MAXFFTLEN 409651#define CONFORM_INPUT
31#define MAXWINDOWLEN MAXFFTLEN * 852#define CLIP(a, lo, hi) ( (a)>(lo)?( (a)<(hi)?(a):(hi) ):(lo) )
32#define MINOVERLAP 0.2553
33#define MAXOVERLAP 64.054#define kDefaultFFTLen 1024
34#define MAXDELTIME 20.055#define kDefaultWindowLen 2048
35#define FFTLEN 102456#define kDefaultOverlap 2
36 57#define kDefaultMaxDelTime 6.0f
37typedef enum {58#define kMinMaxDelTime 0.1f
38 Hamming = 0,59#define kMaxMaxDelTime 20.0f
39 Rectangle60#define kMaxTableLen 512
40} WindowType;
41 61
42// class definition for internal Chugin data62// class definition for internal Chugin data
43// (note: this isn't strictly necessary, but serves as example63// (note: this isn't strictly necessary, but serves as example
@@ -48,87 +68,52 @@ public: · 4 unmodified lines
48 // constructor68 // constructor
49 Spectacle( t_CKFLOAT fs)69 Spectacle( t_CKFLOAT fs)
50 {70 {
51 /* create segmented input buffer */71 m_param = 0;
52 int num_segments = (window_len / FFTLEN) + 2;72 fftlen = kDefaultFFTLen;
53 int extrasamps = FFTLEN - framesToRun();73 windowlen = kDefaultWindowLen;
54 if (extrasamps)74 overlap = kDefaultOverlap;
55 num_segments++;75 maxdeltime = kDefaultMaxDelTime;
56 int inbuf_samps = num_segments * FFTLEN * inputChannels();76 maxdeltime = CLIP(maxdeltime, kMinMaxDelTime, kMaxMaxDelTime);
57 inbuf = new float [inbuf_samps];77 srate = fs;
58 for (int i = 0; i < inbuf_samps; i++)78 spectdelay = NULL;
59 inbuf[i] = 0.0;79 float *del = dttable;
60 80 float *eq = eqtable;
61 /* Read ptr chases write ptr by <window_len>. Set read ptr to a position81 float *feed = fbtable;
62 <window_len> frames from right end of input buffer. Set write ptr to82 int *bm = eqbinmap;
63 beginning of buffer, or, if framesToRun() is not the same as the RTcmix83 int *dbm = delaybinmap;
64 buffer size, set it so that the next run invocation after this one84 for (int i = 0; i < kMaxTableLen; i++)
65 will find the write ptr at the start of the second buffer segment.85 {
66 */86 *del++ = 0.0;
67 inbuf_readptr = inbuf + (inbuf_samps - (window_len * inputChannels()));87 *eq++ = 0.0;
68 inbuf_startptr = inbuf + (extrasamps * inputChannels());88 *feed++ = 0.0;
69 inbuf_writeptr = inbuf_startptr;89 *bm++ = 0;
70 inbuf_endptr = inbuf + inbuf_samps;90 *dbm++ = 0;
71 91 }
72 int outbuf_samps = num_segments * FFTLEN * outputChannels();92 dttablen = eqtablen = fbtablen = 0;
73 outbuf = new float [outbuf_samps];93 eqbinmaplen = delaybinmaplen = 0;
74 for (int i = 0; i < outbuf_samps; i++)94 dminfreq = dmaxfreq = minfreq = maxfreq = 0.0;
75 outbuf[i] = 0.0;95 posteq = hold = false;
76 96
77 outbuf_readptr = outbuf + (outbuf_samps97 spectdelay = new Spectacle_dsp();
78 - (framesToRun() * outputChannels()));98 spectdelay->init(fftlen, windowlen, overlap, srate, maxdeltime);
79 outbuf_writeptr = outbuf_readptr;
80 outbuf_endptr = outbuf + outbuf_samps;
81 first_time = 0;
82 }99 }
83 100
84 ~Spectacle()101 ~Spectacle()
85 {102 {
86 delete [] anal_window;103 delete spectdelay;
87 delete [] synth_window;
88 delete [] fft_buf;
89 delete [] anal_chans;
90 delete [] drybuf;
91 delete [] inbuf;
92 delete [] outbuf;
93 delete dry_delay;
94 }104 }
95 105
96 // for Chugins extending UGen106 // for Chugins extending UGen
97 void tick( SAMPLE *in, SAMPLE *out, int nframes )107 void tick( SAMPLE* in, SAMPLE* out, int nframes)
98 { 108 {
99 memset(out, 0, sizeof(SAMPLE)*nframes);109 memset (out, 0, sizeof(SAMPLE)*nframes);
100 110 // process a block of samples
101 for (int i=0; i < nframes; i++)111 spectdelay->run(in, out, nframes);
102 {
103 shiftin();
104 fold();
105 JGrfft(fft_buf, half_fft_len, FORWARD);
106 leanconvert();
107 }
108 flush_dry_delay();
109 modify_analysis();
110 leanunconvert();
111 JGrfft(fft_buf, half_fft_len, INVERSE);
112 overlapadd(currentFrame());
113 shiftout();
114
115 increment(decimation);
116 }112 }
117 113
118 if (currentFrame() < input_end_frame) {114 void clear()
119 inbuf_writeptr += insamps;115 {
120 if (inbuf_writeptr >= inbuf_endptr)116 spectdelay->clear();
121 inbuf_writeptr = inbuf;
122 }
123
124 rtbaddout(outbuf_readptr, framesToRun());
125 outbuf_readptr += framesToRun() * outputChannels();
126 if (outbuf_readptr >= outbuf_endptr)
127 outbuf_readptr = outbuf;
128
129 return framesToRun();
130
131 // sythesis function here
132 }117 }
133 118
134 // set parameter example119 // set parameter example
@@ -141,297 +126,21 @@ public: · 6 unmodified lines
141 // get parameter example126 // get parameter example
142 float getParam() { return m_param; }127 float getParam() { return m_param; }
143 128
144 /* ---------------------------------------------------------- make_windows -- */
145/* Make balanced pair of analysis and synthesis windows.
146*/
147 int make_windows()
148 {
149 switch (window_type) {
150 case Hamming:
151 for (int i = 0; i < window_len; i++)
152 anal_window[i] = synth_window[i] = 0.54 - 0.46
153 * cos(TWO_PI * i / (window_len - 1));
154 break;
155 case Rectangle:
156 //FIXME: is this right?
157 for (int i = 0; i < window_len; i++)
158 anal_window[i] = synth_window[i] = 1.0;
159 break;
160 }
161
162 /* When window_len > fft_len, also apply interpolating (sinc) windows to
163 ensure that window are 0 at increments of fft_len away from the center
164 of the analysis window and of decimation away from the center of the
165 synthesis window.
166 */
167 if (window_len > fft_len) {
168 float x;
169
170 /* Take care to create symmetrical windows. */
171 x = -(window_len - 1) / 2.;
172 for (int i = 0; i < window_len; i++, x += 1.)
173 if (x != 0.) {
174 anal_window[i] *= fft_len * sin(PI * x / fft_len) / (PI * x);
175 if (decimation)
176 synth_window[i] *= decimation * sin(PI * x / decimation)
177 / (PI * x);
178 }
179 }
180
181 /* Normalize windows for unity gain across unmodified
182 analysis-synthesis procedure.
183 */
184 float sum = 0.0;
185 for (int i = 0; i < window_len; i++)
186 sum += anal_window[i];
187
188 for (int i = 0; i < window_len; i++) {
189 float afac = 2. / sum;
190 float sfac = window_len > fft_len ? 1. / afac : afac;
191 anal_window[i] *= afac;
192 synth_window[i] *= sfac;
193 }
194
195 if (window_len <= fft_len && decimation) {
196 sum = 0.0;
197 for (int i = 0; i < window_len; i += decimation)
198 sum += synth_window[i] * synth_window[i];
199 sum = 1.0 / sum;
200 for (int i = 0; i < window_len; i++)
201 synth_window[i] *= sum;
202 }
203
204 return 0;
205 }
206
207 double * resample_functable(double *table, int oldsize, int newsize)
208 {
209 double *newtable = new double [newsize];
210
211 if (newsize == oldsize) { // straight copy
212 for (int i = 0; i < newsize; i++)
213 newtable[i] = table[i];
214 }
215 else {
216 double incr = (double) oldsize / (double) newsize;
217 double f = 0.0;
218 for (int i = 0; i < newsize; i++) {
219 int n = (int) f;
220 double frac = f - (double) n;
221 double diff = 0.0;
222 if (frac) {
223 double next = (n + 1 < oldsize) ? table[n + 1] : table[oldsize - 1];
224 diff = next - table[n];
225 }
226 newtable[i] = table[n] + (diff * frac);
227 f += incr;
228 }
229 }
230 return newtable;
231 }
232
233 /* ------------------------------------------------------------------ init -- */
234 WindowType getWindowType(double pval)
235 {
236 int intval = int(pval);
237 WindowType type;
238
239 switch (intval) {
240 case 1:
241 type = Rectangle;
242 break;
243 default:
244 type = Hamming;
245 }
246 return type;
247 }
248
249 /* ---------------------------------------------------------------- shiftin -- */
250 void shiftin()
251 {
252 int _cursamp = currentFrame();
253
254 /* Shift samples in <input> from right to left by <decimation>. */
255 for (int i = 0; i < window_len_minus_decimation; i++)
256 input[i] = input[i + decimation];
257
258 /* Copy <decimation> samples from <inbuf> to right end of <input> and
259 to left end of <drybuf>.
260 */
261 int j = 0;
262 for (int i = window_len_minus_decimation; i < window_len; i++, j++)
263 {
264 if (--iamp_branch <= 0)
265 {
266 /* Since inbuf_readptr trails inbuf_writeptr, we delay the start of
267 the envelope until we reach actual input rather than zero padding.
268 */
269 if (iamparray && _cursamp >= window_len)
270 iamp = tablei(_cursamp - window_len, iamparray, iamptabs);
271 iamp_branch = skip;
272 }
273 float sig = inbuf_readptr[inchan] * iamp;
274 drybuf[j] = dry_delay->tick(sig);
275 input[i] = sig;
276 inbuf_readptr += inputChannels();
277 if (inbuf_readptr >= inbuf_endptr)
278 {
279 inbuf_readptr = inbuf_startptr;
280 /* <inbuf_startptr> might not == <inbuf> in first call to run(). */
281 inbuf_startptr = inbuf;
282 }
283 _cursamp++;
284 }
285 }
286
287 void fold(int n)
288 {
289 for (int i = 0; i < fft_len; i++)
290 fft_buf[i] = 0.0;
291
292 n %= fft_len;
293
294 for (int i = 0; i < window_len; i++) {
295 fft_buf[n] += input[i] * anal_window[i];
296 if (++n == fft_len)
297 n = 0;
298 }
299 }
300
301/* ----------------------------------------------------------- leanconvert -- */
302/* <fft_buf> is a spectrum in JGrfft format, i.e. it contains <half_fft_len> * 2
303 real values, arranged in pairs of real and imaginary values, except for
304 the first two values, which are the real parts of 0 and Nyquist frequencies.
305 Converts these into <half_fft_len> + 1 pairs of magnitude and phase values,
306 and stores them into the output array <anal_chans>.
307*/
308 void leanconvert()
309 {
310 int real_index, imag_index, amp_index, phase_index;
311 float a, b;
312
313 for (int i = 0; i <= half_fft_len; i++) {
314 real_index = amp_index = i << 1;
315 imag_index = phase_index = real_index + 1;
316 if (i == half_fft_len) {
317 a = fft_buf[1];
318 b = 0.0;
319 }
320 else {
321 a = fft_buf[real_index];
322 b = (i == 0) ? 0.0 : fft_buf[imag_index];
323 }
324 anal_chans[amp_index] = hypot(a, b);
325 anal_chans[phase_index] = -atan2(b, a);
326 }
327 }
328
329 /* ------------------------------------------------------- flush_dry_delay -- */
330 void flush_dry_delay()
331 {
332 if (currentFrame() < input_end_frame + window_len_minus_decimation) {
333 for (int i = 0; i < decimation; i++)
334 drybuf[i] = dry_delay->tick(0.0);
335 }
336 }
337
338
339 /* --------------------------------------------------------- leanunconvert -- */
340 /* leanunconvert essentially undoes what leanconvert does, i.e., it turns
341 <half_fft_len> + 1 pairs of amplitude and phase values in <anal_chans>
342 into <half_fft_len> pairs of complex spectrum data (in JGrfft format) in
343 output array <fft_buf>.
344 */
345 void leanunconvert()
346 {
347 int real_index, imag_index, amp_index, phase_index;
348 float mag, phase;
349
350 for (int i = 0; i <= half_fft_len; i++) {
351 real_index = amp_index = i << 1;
352 imag_index = phase_index = real_index + 1;
353 if (i == half_fft_len)
354 real_index = 1;
355 mag = anal_chans[amp_index];
356 phase = anal_chans[phase_index];
357 fft_buf[real_index] = mag * cos(phase);
358 if (i != half_fft_len)
359 fft_buf[imag_index] = -mag * sin(phase);
360 }
361 }
362
363
364 /* ------------------------------------------------------------ overlapadd -- */
365 /* <fft_buf> is a folded spectrum of length <fft_len>. <output> and
366 <synth_window> are of length <window_len>. Overlap-add windowed,
367 unrotated, unfolded <fft_buf> data into <output>.
368 */
369 void overlapadd(int n)
370 {
371 n %= fft_len;
372 for (int i = 0; i < window_len; i++) {
373 output[i] += fft_buf[n] * synth_window[i];
374 if (++n == fft_len)
375 n = 0;
376 }
377 }
378
379
380 /* -------------------------------------------------------------- shiftout -- */
381 void shiftout()
382 {
383 int _cursamp = currentFrame();
384
385 for (int i = 0; i < decimation; i++) {
386 if (--oamp_branch <= 0) {
387 if (oamparray && (_cursamp >= latency))
388 oamp = tablei(_cursamp - latency, oamparray, oamptabs) * amp;
389 oamp_branch = skip;
390 }
391 float sig;
392 if (_cursamp < input_end_frame)
393 sig = ((output[i] * wetdry) + ((1.0 - wetdry) * drybuf[i])) * oamp;
394 else
395 sig = output[i] * wetdry * oamp;
396 if (outputChannels() == 2) {
397 *outbuf_writeptr++ = sig * pctleft;
398 *outbuf_writeptr++ = sig * (1.0 - pctleft);
399 }
400 else
401 *outbuf_writeptr++ = sig;
402 if (outbuf_writeptr >= outbuf_endptr) {
403 outbuf_writeptr = outbuf;
404 }
405 _cursamp++;
406 }
407
408 /* shift samples in <output> from right to left by <decimation> */
409 for (int i = 0; i < window_len_minus_decimation; i++)
410 output[i] = output[i + decimation];
411 for (int i = window_len_minus_decimation; i < window_len; i++)
412 output[i] = 0.0;
413 }
414
415
416private:129private:
417 // instance data130 // instance data
418 float m_param;131 float m_param;
419 float *anal_window, *synth_window, *input, *output, *fft_buf, *drybuf;132
420 float *inbuf, *inbuf_startptr, *inbuf_readptr, *inbuf_writeptr,133 int fftlen, windowlen, overlap;
421 *inbuf_endptr;134 float srate, maxdeltime;
422 float *outbuf, *outbuf_startptr, *outbuf_readptr, *outbuf_writeptr,135 Spectacle_dsp *spectdelay;
423 *outbuf_endptr;136 float eqtable[kMaxTableLen];
424 float iamptabs[2], oamptabs[2];137 float dttable[kMaxTableLen];
425 double *iamparray, *oamparray;138 float fbtable[kMaxTableLen];
426 DLineN *dry_delay;139 int eqbinmap[kMaxTableLen];
427 WindowType window_type;140 int delaybinmap[kMaxTableLen];
428protected:141 int eqtablen, dttablen, fbtablen, eqbinmaplen, delaybinmaplen;
429 int inchan, skip, total_insamps, first_time, int_overlap;142 float minfreq, maxfreq, dminfreq, dmaxfreq;
430 int fft_len, window_len, decimation, window_len_minus_decimation,143 bool hold, posteq;
431 half_fft_len, latency, input_end_frame;
432 float wetdry, pctleft, inputdur, ringdur, fund_anal_freq;
433 float *anal_chans;
434
435};144};
436 145
437 146
@@ -518,7 +227,7 @@ CK_DLL_TICKF(spectacle_tick) · 80 unmodified lines
518 227
519 // invoke our tick function; store in the magical out variable228 // invoke our tick function; store in the magical out variable
520 if(c) c->tick(in,out, nframes);229 if(c) c->tick(in,out, nframes);
521 230
522 // yes231 // yes
523 return TRUE;232 return TRUE;
524}233}
Spectacle/SpectacleBase.cpp+646−0
@@ -0,0 +1,646 @@
1/*
2 * Copyright (C) 2007 John Gibson
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2, as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14// Base class for the various spectacle externals, derived from
15// my SPECTACLE2 RTcmix instruments.
16// John Gibson <johgibso at indiana dot edu>, 11/23/07.
17
18//#define DEBUG
19//#define DEBUG_BINGROUPS
20//#define CHECK_BINGROUPS
21
22#include "SpectacleBase.h"
23#include <float.h>
24#include <cstddef>
25
26#ifndef cosf
27#define cosf(x) cos((x))
28#endif
29#ifndef sinf
30#define sinf(x) sin((x))
31#endif
32#ifndef hypotf
33#define hypotf(x, y) hypot((x), (y))
34#endif
35#ifndef atan2f
36#define atan2f(y, x) atan2((y), (x))
37#endif
38
39const int kMaxWindowLen = kMaxFFTLen * 8;
40const int kMinOverlap = 1;
41const int kMaxOverlap = 64;
42
43
44// ------------------------------------------------------------ SpectacleBase --
45SpectacleBase::SpectacleBase() :
46 _control_table_size(0),
47 _bin_groups(NULL),
48 _binmaptable(NULL),
49 _print_stats(false),
50 _reading_input(true),
51 _posteq(false),
52 _prev_bg_ignorevals(-1),
53 _srate(-FLT_MAX),
54 _minfreq(-FLT_MAX), _maxfreq(-FLT_MAX),
55 _anal_window(NULL), _synth_window(NULL),
56 _input(NULL), _output(NULL),
57 _outbuf(NULL),
58 _cursamp(0L),
59 _fft(NULL),
60 _bucket(NULL)
61{
62}
63
64
65// ----------------------------------------------------------- ~SpectacleBase --
66SpectacleBase::~SpectacleBase()
67{
68 delete [] _outbuf;
69 delete [] _input;
70 delete [] _output;
71 delete [] _anal_window;
72 delete [] _synth_window;
73 delete [] _bin_groups;
74 delete _bucket;
75 delete _fft;
76}
77
78
79// ------------------------------------------------------------- make_windows --
80int SpectacleBase::make_windows()
81{
82 // Hamming window
83 for (int i = 0; i < _window_len; i++)
84 _anal_window[i] = _synth_window[i] = 0.54f - 0.46f
85 * cosf(2.0f * M_PI * i / (_window_len - 1));
86
87 // When _window_len > _fftlen, also apply interpolating (sinc) windows to
88 // ensure that window is 0 at increments of _fftlen away from the center
89 // of the analysis window and of decimation away from the center of the
90 // synthesis window.
91
92 if (_window_len > _fftlen) {
93 float x = -(_window_len - 1) / 2.0;
94 for (int i = 0; i < _window_len; i++, x += 1.0f)
95 if (x != 0.0f) {
96 _anal_window[i] *= _fftlen * sin(M_PI * x / _fftlen) / (M_PI * x);
97 if (_decimation)
98 _synth_window[i] *= _decimation * sin(M_PI * x / _decimation)
99 / (M_PI * x);
100 }
101 }
102
103 // Normalize windows for unity gain across unmodified
104 // analysis-synthesis procedure.
105
106 float sum = 0.0f;
107 for (int i = 0; i < _window_len; i++)
108 sum += _anal_window[i];
109
110 for (int i = 0; i < _window_len; i++) {
111 float afac = 2.0f / sum;
112 float sfac = _window_len > _fftlen ? 1.0f / afac : afac;
113 _anal_window[i] *= afac;
114 _synth_window[i] *= sfac;
115 }
116
117 if (_window_len <= _fftlen && _decimation) {
118 sum = 0.0f;
119 for (int i = 0; i < _window_len; i += _decimation)
120 sum += _synth_window[i] * _synth_window[i];
121 sum = 1.0f / sum;
122 for (int i = 0; i < _window_len; i++)
123 _synth_window[i] *= sum;
124 }
125
126 return 0;
127}
128
129
130// ---------------------------------------------------------------- set_srate --
131// Must call this before anything that updates bin groups in subclass.
132void SpectacleBase::set_srate(float srate)
133{
134 if (srate != _srate) {
135 _srate = srate;
136 if (_maxfreq == _nyquist)
137 _maxfreq = 0.0f;
138 _nyquist = srate / 2;
139 if (_maxfreq == 0.0f)
140 _maxfreq = _nyquist;
141 _fund_anal_freq = srate / float(_fftlen);
142 update_bin_groups(_bin_groups, NULL, _minfreq, _maxfreq,
143 _control_table_size);
144 }
145}
146
147
148// --------------------------------------------------------------------- init --
149int SpectacleBase::init(int fftlen, int windowlen, int overlap, float srate)
150{
151 _fftlen = fftlen;
152 _window_len = windowlen;
153 _overlap = overlap;
154
155 // Make sure FFT length is a power of 2 <= kMaxFFTLen.
156 bool valid = false;
157 for (int x = 1; x <= kMaxFFTLen; x *= 2) {
158 if (_fftlen == x) {
159 valid = true;
160 break;
161 }
162 }
163 if (!valid) {
164 //post("%s: FFT length must be a power of two <= %d. Setting to 1024...",
165 //instname(), kMaxFFTLen);
166 _fftlen = 1024;
167 }
168
169 _half_fftlen = _fftlen / 2;
170
171 // Make sure window length is a power of 2 >= FFT length.
172 valid = false;
173 for (int x = _fftlen; x <= kMaxWindowLen; x *= 2) {
174 if (_window_len == x) {
175 valid = true;
176 break;
177 }
178 }
179 if (!valid) {
180 //post("%s: Window length must be a power of two >= FFT length (%d)\n"
181 //"and <= %d. Setting to 2048...",
182 //instname(), _fftlen, kMaxWindowLen);
183 _window_len = _fftlen * 2;
184 }
185
186 // Make sure _overlap is a power of 2 in allowed range.
187 valid = false;
188 for (int x = kMinOverlap; x <= kMaxOverlap; x *= 2) {
189 if (_overlap == x) {
190 valid = true;
191 break;
192 }
193 }
194 if (!valid) {
195 //post("%s: Overlap must be a power of two between %d and %d. "
196 //"Setting to 2...",
197 //instname(), kMinOverlap, kMaxOverlap);
198 _overlap = 2;
199 }
200
201 _bin_groups = new int [_half_fftlen];
202
203 set_srate(srate);
204 set_freqrange(0.0f, 0.0f);
205
206 // derive decimation from overlap
207 _decimation = int(_fftlen / _overlap);
208
209 _window_len_minus_decimation = _window_len - _decimation;
210
211 DPRINT2("_fftlen=%d, _decimation=%d", _fftlen, _decimation);
212
213 _input = new float [_window_len]; // interior input buffer
214 _output = new float [_window_len]; // interior output buffer
215 if (_input == NULL || _output == NULL)
216 return -1;
217 for (int i = 0; i < _window_len; i++)
218 _input[i] = _output[i] = 0.0f;
219
220 // Read index chases write index by _decimation; add 2 extra locations to
221 // keep read point from stepping on write point. Verify with asserts in
222 // increment_out_*_index().
223 _outframes = _decimation + 2;
224 _out_read_index = _outframes - _decimation;
225 _out_write_index = 0;
226 _outbuf = new float [_outframes];
227 if (_outbuf == NULL)
228 return -1;
229 for (int i = 0; i < _outframes; i++)
230 _outbuf[i] = 0.0f;
231 DPRINT1("_outframes: %d", _outframes);
232
233 _anal_window = new float [_window_len];
234 _synth_window = new float [_window_len];
235 if (_anal_window == NULL || _synth_window == NULL)
236 return -1;
237 if (make_windows() != 0)
238 return -1;
239
240 _bucket = new Obucket(_decimation, process_wrapper, (void *) this);
241
242 _fft = new Offt(_fftlen);
243 _fft_buf = _fft->getbuf();
244
245 return 0;
246}
247
248
249// -------------------------------------------------------------------- clear --
250void SpectacleBase::clear()
251{
252 for (int i = 0; i < _fftlen; i++)
253 _fft_buf[i] = 0.0f;
254 for (int i = 0; i < _window_len; i++)
255 _input[i] = _output[i] = 0.0f;
256 for (int i = 0; i < _outframes; i++)
257 _outbuf[i] = 0.0f;
258 _bucket->clear();
259}
260
261
262// ------------------------------------------------------------ prepare_input --
263// <buf> contains <_decimation> samples (mono) from most recent input.
264
265void SpectacleBase::prepare_input(const float buf[])
266{
267 DPRINT("prepare_input");
268
269 // Shift samples in <_input> from right to left by <_decimation>,
270 // leaving a hole of <_decimation> slots at right end.
271
272 for (int i = 0; i < _window_len_minus_decimation; i++)
273 _input[i] = _input[i + _decimation];
274
275 // Copy <_decimation> samples from <buf> to right end of <_input>.
276
277 for (int i = _window_len_minus_decimation, j = 0; i < _window_len; i++, j++)
278 _input[i] = buf[j];
279
280 // Multiply input array by analysis window, both of length <_window_len>.
281 // Fold and rotate windowed real input into FFT buffer.
282
283 for (int i = 0; i < _fftlen; i++)
284 _fft_buf[i] = 0.0f;
285 int j = _cursamp % _fftlen;
286 for (int i = 0; i < _window_len; i++) {
287 _fft_buf[j] += _input[i] * _anal_window[i];
288 if (++j == _fftlen)
289 j = 0;
290 }
291}
292
293
294// ----------------------------------------------------------- prepare_output --
295void SpectacleBase::prepare_output()
296{
297 DPRINT("prepare_output");
298
299 // overlap-add <_fft_buf> real data into <_output>
300 int j = _cursamp % _fftlen;
301 for (int i = 0; i < _window_len; i++) {
302 _output[i] += _fft_buf[j] * _synth_window[i];
303 if (++j == _fftlen)
304 j = 0;
305 }
306
307 // transfer samples from <_output> to outer output buffer.
308 for (int i = 0; i < _decimation; i++) {
309 _outbuf[_out_write_index] = _output[i];
310 increment_out_write_index();
311 }
312
313 // shift samples in <_output> from right to left by <_decimation>
314 for (int i = 0; i < _window_len_minus_decimation; i++)
315 _output[i] = _output[i + _decimation];
316 for (int i = _window_len_minus_decimation; i < _window_len; i++)
317 _output[i] = 0.0f;
318}
319
320
321// ---------------------------------------------------------- process_wrapper --
322// Called by Obucket whenever the input bucket is full (i.e., has _decimation
323// samps). This static member wrapper function lets us call a non-static member
324// function, which we can't pass directly as a callback to Obucket.
325// See http://www.newty.de/fpt/callback.html for one explanation of this.
326
327void SpectacleBase::process_wrapper(const float buf[], const int len,
328 void *obj)
329{
330 SpectacleBase *myself = (SpectacleBase *) obj;
331 myself->process(buf, len);
332}
333
334
335// ------------------------------------------------------------------ process --
336void SpectacleBase::process(const float *buf, const int)
337{
338 DPRINT("SpectacleBase::process");
339
340 if (_reading_input) {
341 prepare_input(buf);
342 _fft->r2c();
343 }
344 modify_analysis(_reading_input);
345 _fft->c2r();
346 prepare_output();
347}
348
349
350// ------------------------------------------------------------ set_freqrange --
351// Update the frequency range within which control tables operate. Return true
352// if range has actually changed, resulting in an update_bin_groups call.
353// Otherwise, return false.
354
355bool SpectacleBase::set_freqrange(float min, float max)
356{
357 if (min != _minfreq || max != _maxfreq) {
358 if (max == 0.0f)
359 max = _nyquist;
360 if (max < min)
361 _fswap(&min, &max);
362 if (max > _nyquist)
363 max = _nyquist;
364 _maxfreq = max;
365 _minfreq = _fclamp(0.0f, min, max);
366 update_bin_groups(_bin_groups, NULL, _minfreq, max, _control_table_size);
367 return true;
368 }
369 return false;
370}
371
372
373// -------------------------------------------------------- update_bin_groups --
374// Update a bin groups array. (See .h for a description of bin groups.) Note
375// that this is called from set_freqrange and any subclass equivalents.
376// Three methods of updating bin groups array: (1) use a binmap table,
377// (2) use a one-to-one mapping of bin groups to bins, or (3) use a non-linear
378// mapping that starts as one-to-one, but combines bins in ever larger
379// groups as bin frequency increases. The <binmaptable> argument is an
380// optional binmap table to use in place of the base class <_binmaptable>.
381// If not overriding the base class table, pass NULL.
382
383void SpectacleBase::update_bin_groups(
384 int bin_groups[],
385 int binmaptable[],
386 const float minfreq,
387 const float maxfreq,
388 const int control_table_size)
389{
390 if (control_table_size == 0) // no control table
391 return;
392
393 const int nbins = _half_fftlen;
394 const int *bmtable = binmaptable ? binmaptable : _binmaptable;
395
396 // The optional <bmtable> has <control_table_size> elements, each giving
397 // the number of adjacent FFT bins controlled by that element in any of the
398 // control tables (e.g., EQ). Copy this information into the <bin_groups>
399 // array, which is sized to <nbins>. We ignore minfreq and maxfreq.
400
401 if (bmtable != NULL) {
402#ifdef DEBUG_BINGROUPS
403 //post("using binmap table -----------------------------");
404 for (int i = 0; i < control_table_size; i++)
405 //post("[%d] %d", i, bmtable[i]);
406#endif
407 int bidx = 0;
408 for (int i = 0; i < control_table_size; i++) {
409 int bincount = bmtable[i];
410 while (bincount-- > 0) {
411 if (bidx == nbins)
412 goto highcount;
413 bin_groups[bidx++] = i;
414 }
415 }
416 highcount:
417 if (bidx < nbins) {
418 const int last = control_table_size - 1;
419 while (bidx < nbins)
420 bin_groups[bidx++] = last;
421 }
422 }
423
424 // If there is no binmap table...
425 // If freq. range is full bandwidth, and control table size is >= to the
426 // number of bins, then use linear mapping of control table slots to bins,
427 // as in SPECTACLE v1. If table is larger than the number of bins, warn
428 // about ignoring the extra table slots.
429
430 else if (control_table_size >= nbins
431 && minfreq == 0 && maxfreq == _nyquist) {
432 for (int i = 0; i < nbins; i++)
433 bin_groups[i] = i;
434
435 if (control_table_size > nbins) {
436 const int ignorevals = control_table_size - nbins;
437 if (ignorevals != _prev_bg_ignorevals) {
438 //post("%s: Control table of size %d too large for "
439 //"frequency range...ignoring last %d values",
440 //instname(), control_table_size, ignorevals);
441 _prev_bg_ignorevals = ignorevals;
442 }
443 }
444 }
445
446 // Otherwise, there is either a freq. range that is not full bandwidth, or
447 // the control table size is less than the number of bins. If the former,
448 // we insure that the first array slot affects all bins whose frequencies
449 // are below (and possibly a little above) the min. freq., and that the last
450 // array slot affects all bins whose frequencies are above (and possibly a
451 // little below) the max. freq. If the control table is smaller than the
452 // number of bins within the freq. range, then we construct a one-to-one or
453 // one-to-many mapping of control table slots to bins, arranged so that we
454 // can control lower frequencies with greater resolution.
455 //
456 // Depending on the difference between the control table size and the number
457 // of FFT bins, we create a mapping that begins linearly and grows
458 // arithmetically after a certain point. The purpose is to get higher
459 // resolution in the lower frequencies of the range, where it matters most.
460 // So near the low end of the range, there is a one-to-one mapping of control
461 // array slots to FFT bins. Near the high end of the range, one control
462 // array slot affects many FFT bins. We attempt to transition smoothly
463 // between these two extremes. As an example, the number of FFT bins
464 // controlled by each slot of a control table array might look like this:
465 //
466 // number of bins spanned by each cntl slot
467 // (lowshelf) 1 1 1 1 1 1 1 1 1 1 1 2 3 4 5 6 7 8 9 10 14 (highshelf)
468 //
469 // The scheme isn't perfect -- e.g., the penultimate slot might control more
470 // than you would expect -- but it does guarantee that each control table
471 // slot affects at least one FFT bin.
472
473 else {
474 const int lowshelfbin = closest_bin(minfreq);
475 int highshelfbin = closest_bin(maxfreq);
476 bool maxfreq_is_nyquist = false;
477 if (highshelfbin == nbins) {
478 maxfreq_is_nyquist = true;
479 highshelfbin--; // we don't control the nyquist bin
480 }
481 int cntltablen = control_table_size;
482 const float endflatrange = minfreq + (cntltablen * _fund_anal_freq);
483 bool linear_map = true;
484 if (endflatrange > maxfreq - _fund_anal_freq) {
485 const int ignorevals = int((endflatrange - maxfreq)
486 / _fund_anal_freq);
487 if (ignorevals > 0) {
488 cntltablen = control_table_size - ignorevals;
489 if (ignorevals != _prev_bg_ignorevals) {
490 //post("%s: Control table of size %d too large for "
491 //"frequency range...ignoring last %d values",
492 //instname(), control_table_size, ignorevals);
493 _prev_bg_ignorevals = ignorevals;
494 }
495 }
496 }
497 else
498 linear_map = false;
499#ifdef DEBUG_BINGROUPS
500 //post("lowbin=%d, highbin=%d, endflatrange=%f, map=%s",
501 //lowshelfbin, highshelfbin, endflatrange,
502 //linear_map ? "linear" : "nonlinear");
503#endif
504
505 // assign low shelf group
506 int bidx = 0;
507 while (bidx <= lowshelfbin)
508 bin_groups[bidx++] = 0;
509
510 // assign interior groups
511 if (linear_map) {
512 const int lastbingroup = maxfreq_is_nyquist ? cntltablen - 1
513 : cntltablen - 2;
514 int bingroup = 1;
515 while (bidx < highshelfbin) {
516 bin_groups[bidx++] = bingroup;
517 if (bingroup < lastbingroup)
518 bingroup++;
519 }
520 }
521 else { // higher array slots control more and more bins
522 const int extrabins = (highshelfbin - lowshelfbin) - cntltablen;
523 //if (extrabins <= 0)
524 //error("%s: program error - contact johgibso@gmail.com", instname());
525 const int nsums = int(sqrt(2 * extrabins) + 2);
526
527 // Compute the sum of integers for n in [nsums-2, nsums);
528 // formula is the closed-form equation: x = n * (n + 1) / 2.
529 const int an = nsums - 2;
530 const int bn = nsums - 1;
531 const int a = (an * (an + 1)) / 2;
532 const int b = (bn * (bn + 1)) / 2;
533 int cntlspan = 0, binspan = 0;
534 if (a > extrabins) {
535 cntlspan = an;
536 binspan = a;
537 }
538 else if (b > extrabins) {
539 cntlspan = bn;
540 binspan = b;
541 }
542 //else // this should never happen
543 //error("%s: program error - contact johgibso@gmail.com", instname());
544
545#ifdef DEBUG_BINGROUPS
546 //post("nbins=%d, lowbin=%d, highbin=%d, tablen=%d, extrabins=%d, "
547 //"cntlspan=%d, binspan=%d, maxfreqisnyq=%d\n",
548 //nbins, lowshelfbin, highshelfbin, cntltablen, extrabins,
549 //cntlspan, binspan, maxfreq_is_nyquist);
550#endif
551
552 // first, linear mapping
553 const int end = (cntltablen - cntlspan) - 1;
554 int bingroup = 1;
555 while (bingroup < end)
556 bin_groups[bidx++] = bingroup++;
557
558 // then map using sum-of-integers series
559 const int lastbingroup = maxfreq_is_nyquist ? cntltablen - 1
560 : cntltablen - 2;
561 int incr = 1;
562 int count = 0;
563 while (bidx < highshelfbin) {
564 bin_groups[bidx++] = bingroup;
565 if (++count == incr) {
566 count = 0;
567 if (bingroup < lastbingroup)
568 bingroup++;
569 incr++;
570 }
571 }
572 }
573
574 // assign high shelf group
575 const int last = cntltablen - 1;
576 while (bidx < nbins)
577 bin_groups[bidx++] = last;
578}
579
580#ifdef DEBUG_BINGROUPS
581//post("bin_groups[] -----------------------------");
582for (int i = 0; i < nbins; i++)
583 //post("[%d] %d (%f)", i, bin_groups[i], i * _fund_anal_freq);
584#endif
585#ifdef CHECK_BINGROUPS
586 for (int i = 1; i < nbins; i++) {
587 int diff = bin_groups[i] - bin_groups[i - 1];
588 if (diff < 0 || diff > 1)
589 //error("%s: bin group (%p) index %d not 0 or 1 greater than prev entry",
590 instname(), bin_groups, i);
591}
592#endif
593}
594
595
596// --------------------------------------------------------- print_bin_groups --
597// print a user-readable listing of bin groups
598void SpectacleBase::print_bin_groups(
599 int bin_groups[],
600 const char *type) // to identify bin groups to user
601{
602 const int nbins = _half_fftlen;
603
604 //post("\n%s: %s table bin groups", instname(), type);
605 //post("------------------------------------------");
606 int cntltabslot = 0;
607 int startbin = 0;
608 for (int i = 0; i < nbins; i++) {
609 const int thisslot = bin_groups[i];
610 if (thisslot > cntltabslot) {
611 // print stats for previous control table slot <cntltabslot>
612 const int startfreq = int(_fund_anal_freq * startbin + 0.5f);
613 if (i - startbin > 1) {
614 const int endfreq = int(_fund_anal_freq * (i - 1) + 0.5f);
615 //post(" [%d]\t%d-%d Hz (%d bins)",
616 //cntltabslot, startfreq, endfreq, i - startbin);
617 }
618 else
619 //post(" [%d]\t%d Hz", cntltabslot, startfreq);
620 cntltabslot = thisslot;
621 startbin = i;
622 }
623}
624}
625
626
627// ---------------------------------------------------------------------- run --
628void SpectacleBase::run(float in[], float out[], int nframes)
629{
630 if (_reading_input) {
631 for (int i = 0; i < nframes; i++) {
632 _bucket->drop(in[i]); // may process <_decimation> input frames
633 out[i] = _outbuf[_out_read_index];
634 increment_out_read_index();
635 _cursamp++;
636 }
637 }
638 else {
639 for (int i = 0; i < nframes; i++) {
640 _bucket->drop(0.0f); // may process <_decimation> input frames
641 out[i] = _outbuf[_out_read_index];
642 increment_out_read_index();
643 _cursamp++;
644 }
645 }
646}
Spectacle/SpectacleBase.h+161−0
@@ -0,0 +1,161 @@
1/*
2 * Copyright (C) 2007 John Gibson
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2, as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <math.h>
15#include "genlib/Ougens.h"
16//#include "ext.h" // for Max/MSP post and error functions
17
18//#define NDEBUG
19#include <assert.h>
20
21const int kMaxFFTLen = 16384;
22
23#ifndef powf
24#define powf(val, exp) pow((val), (exp))
25#endif
26
27#ifdef DEBUG
28#define DPRINT(msg) post((msg))
29#define DPRINT1(msg, arg) post((msg), (arg))
30#define DPRINT2(msg, arg1, arg2) post((msg), (arg1), (arg2))
31#define DPRINT3(msg, arg1, arg2, arg3) post((msg), (arg1), (arg2), (arg3))
32#else
33#define DPRINT(msg)
34#define DPRINT1(msg, arg)
35#define DPRINT2(msg, arg1, arg2)
36#define DPRINT3(msg, arg1, arg2, arg3)
37#endif
38
39class SpectacleBase {
40
41 public:
42 SpectacleBase();
43 virtual ~SpectacleBase();
44 void run(float in[], float out[], int nframes);
45 bool set_freqrange(float min, float max);
46
47 // set hold to true to suppress input
48 void set_hold(bool hold) { _reading_input = !hold; }
49
50 // set posteq to true to apply EQ after delay, rather than before
51 void set_posteq(bool posteq) { _posteq = posteq; }
52
53 protected:
54 int init(int fftlen, int windowlen, int overlap, float srate);
55 void clear();
56 void set_srate(float srate);
57 virtual void modify_analysis(bool reading_input) = 0;
58 virtual const char *instname() = 0;
59 inline int closest_bin(const float freq);
60 void update_bin_groups(int bin_groups[], int binmaptable[],
61 const float minfreq, const float maxfreq,
62 const int control_table_size);
63 void print_bin_groups(int bin_groups[], const char *type);
64
65 // helper functions
66 float _ampdb(float db) { return powf(10.0f, db * 0.05f); }
67 float _fclamp(float min, float val, float max) {
68 return (val < min) ? min : ((val > max) ? max : val); }
69 void _fswap(float *a, float *b) { float tmp = *a; *a = *b; *b = tmp; }
70 int _imax(int x, int y) { return x > y ? x : y; }
71 int _imin(int x, int y) { return x < y ? x : y; }
72 int _odd(long x) { return (x & 0x00000001); }
73
74 // accessors for subclasses
75 void set_fftlen(int fftlen) { _fftlen = fftlen; }
76 int get_fftlen() const { return _fftlen; }
77 void set_window_len(int window_len) { _window_len = window_len; }
78 int get_window_len() const { return _window_len; }
79 void set_overlap(int overlap) { _overlap = overlap; }
80 int get_overlap() const { return _overlap; }
81 float get_minfreq() const { return _minfreq; }
82 float get_maxfreq() const { return _maxfreq; }
83 float get_srate() const { return _srate; }
84 float get_posteq() const { return _posteq; }
85
86 // For a subclass that wants to change the base class array for some reason.
87 void set_bin_groups(int bin_groups[]) {
88 delete [] _bin_groups;
89 _bin_groups = bin_groups; // assume new one is valid
90 }
91
92 int _input_frames, _nargs, _decimation, _overlap, _half_fftlen;
93 float _fund_anal_freq, _nyquist;
94
95 // size of the "control tables" (e.g., EQ) used with _bin_groups[] below
96
97 int _control_table_size;
98
99 // This pointer gives us direct access to the Offt object's buffer.
100 // It has _fftlen elements, interpreted as described in Offt.h.
101
102 float *_fft_buf;
103
104 // A "bin group" is a range of FFT bins that are controlled by a single
105 // element in one of the "control tables." The <_bin_groups> array has
106 // <_half_fftlen> values, each of which is an index into these control
107 // tables. So <_bin_groups> tells us which control table value to use for
108 // a given FFT bin. <_bin_groups> is updated whenever minfreq or maxfreq
109 // changes. The base class _bin_groups array is used by a subclass in any
110 // way it wants (typically for EQ). A subclass may have additional bin
111 // groups, but these are handled entirely within the subclass. The subclass
112 // can call update_bin_groups to initialize these extra bin groups.
113
114 int *_bin_groups;
115
116 // Used for specifying bin groups.
117 int *_binmaptable;
118
119 private:
120 int make_windows();
121 void prepare_input(const float buf[]);
122 void prepare_output();
123 static void process_wrapper(const float buf[], const int len, void *obj);
124 void process(const float *buf, const int len);
125 inline void increment_out_read_index();
126 inline void increment_out_write_index();
127
128 bool _print_stats, _reading_input, _posteq;
129 int _fftlen, _window_len, _prev_bg_ignorevals;
130 int _out_read_index, _out_write_index, _outframes;
131 int _window_len_minus_decimation;
132 float _srate, _minfreq, _maxfreq;
133 float *_anal_window, *_synth_window, *_input, *_output, *_outbuf;
134 unsigned long _cursamp; // good for about 27 hours on a 32bit machine
135 Offt *_fft;
136 Obucket *_bucket;
137};
138
139
140// Return the index of the FFT bin whose center frequency is closest to <freq>.
141inline int SpectacleBase::closest_bin(const float freq)
142{
143 return int((freq / _fund_anal_freq) + 0.5f);
144}
145
146
147inline void SpectacleBase::increment_out_read_index()
148{
149 if (++_out_read_index == _outframes)
150 _out_read_index = 0;
151 // assert(_out_read_index != _out_write_index);
152}
153
154inline void SpectacleBase::increment_out_write_index()
155{
156 if (++_out_write_index == _outframes)
157 _out_write_index = 0;
158 // assert(_out_write_index != _out_read_index);
159}
160
161
Spectacle/fft.c+0−142
@@ -1,142 +0,0 @@
1#include <math.h>
2#include "fft.h"
3
4
5/* Place float array x containing N/2 complex values into bit-reversed order.
6*/
7static void bitreverse(float x[], int N)
8{
9 float rtemp, itemp;
10 int i, j, m;
11
12 for (i = j = 0; i < N; i += 2, j += m) {
13 if (j > i) {
14 rtemp = x[j];
15 itemp = x[j + 1]; /* complex exchange */
16 x[j] = x[i];
17 x[j + 1] = x[i + 1];
18 x[i] = rtemp;
19 x[i + 1] = itemp;
20 }
21 for (m = N >> 1; m >= 2 && j >= m; m >>= 1)
22 j -= m;
23 }
24}
25
26
27/* If forward is true, JGrfft replaces 2*N real data points in x with
28 N complex values representing the positive frequency half of their
29 Fourier spectrum, with x[1] replaced with the real part of the Nyquist
30 frequency value. If forward is false, JGrfft expects x to contain a
31 positive frequency spectrum arranged as before, and replaces it with
32 2*N real values. N MUST be a power of 2.
33*/
34void JGrfft(float x[], int N, int forward)
35{
36 float c1, c2, h1r, h1i, h2r, h2i, wr, wi, wpr, wpi, temp, theta;
37 float xr, xi;
38 int i, i1, i2, i3, i4, N2p1;
39
40 theta = M_PI / N;
41 wr = 1.0;
42 wi = 0.0;
43 c1 = 0.5;
44 if (forward) {
45 c2 = -0.5;
46 JGcfft(x, N, forward);
47 xr = x[0];
48 xi = x[1];
49 }
50 else {
51 c2 = 0.5;
52 theta = -theta;
53 xr = x[1];
54 xi = 0.0;
55 x[1] = 0.0;
56 }
57 wpr = -2.0 * pow(sin(0.5 * theta), 2.0);
58 wpi = sin(theta);
59 N2p1 = (N << 1) + 1;
60 for (i = 0; i <= N >> 1; i++) {
61 i1 = i << 1;
62 i2 = i1 + 1;
63 i3 = N2p1 - i2;
64 i4 = i3 + 1;
65 if (i == 0) {
66 h1r = c1 * (x[i1] + xr);
67 h1i = c1 * (x[i2] - xi);
68 h2r = -c2 * (x[i2] + xi);
69 h2i = c2 * (x[i1] - xr);
70 x[i1] = h1r + wr * h2r - wi * h2i;
71 x[i2] = h1i + wr * h2i + wi * h2r;
72 xr = h1r - wr * h2r + wi * h2i;
73 xi = -h1i + wr * h2i + wi * h2r;
74 }
75 else {
76 h1r = c1 * (x[i1] + x[i3]);
77 h1i = c1 * (x[i2] - x[i4]);
78 h2r = -c2 * (x[i2] + x[i4]);
79 h2i = c2 * (x[i1] - x[i3]);
80 x[i1] = h1r + wr * h2r - wi * h2i;
81 x[i2] = h1i + wr * h2i + wi * h2r;
82 x[i3] = h1r - wr * h2r + wi * h2i;
83 x[i4] = -h1i + wr * h2i + wi * h2r;
84 }
85 temp = wr;
86 wr = wr * wpr - wi * wpi + wr;
87 wi = wi * wpr + temp * wpi + wi;
88 }
89 if (forward)
90 x[1] = xr;
91 else
92 JGcfft(x, N, forward);
93}
94
95
96/* JGcfft replaces float array x containing NC complex values
97 (2*NC float values alternating real, imagininary, etc.)
98 by its Fourier transform if forward is true, or by its
99 inverse Fourier transform if forward is false, using a
100 recursive Fast Fourier transform method due to Danielson
101 and Lanczos. NC MUST be a power of 2.
102*/
103void JGcfft(float x[], int NC, int forward)
104{
105 float wr, wi, wpr, wpi, theta, scale;
106 int mmax, ND, m, i, j, delta;
107
108 ND = NC << 1;
109 bitreverse(x, ND);
110 for (mmax = 2; mmax < ND; mmax = delta) {
111 delta = mmax << 1;
112 theta = TWO_PI / (forward ? mmax : -mmax);
113 wpr = -2.0 * pow(sin(0.5 * theta), 2.0);
114 wpi = sin(theta);
115 wr = 1.0;
116 wi = 0.0;
117 for (m = 0; m < mmax; m += 2) {
118 float rtemp, itemp;
119 for (i = m; i < ND; i += delta) {
120 j = i + mmax;
121 rtemp = wr * x[j] - wi * x[j + 1];
122 itemp = wr * x[j + 1] + wi * x[j];
123 x[j] = x[i] - rtemp;
124 x[j + 1] = x[i + 1] - itemp;
125 x[i] += rtemp;
126 x[i + 1] += itemp;
127 }
128 rtemp = wr;
129 wr = wr * wpr - wi * wpi + wr;
130 wi = wi * wpr + rtemp * wpi + wi;
131 }
132 }
133
134 /* scale output */
135 scale = forward ? 1.0 / ND : 2.0;
136 {
137 float *xi = x, *xe = x + ND;
138 while (xi < xe)
139 *xi++ *= scale;
140 }
141}
142
Spectacle/fft.h+0−24
@@ -1,24 +0,0 @@
1#define FORWARD 1
2#define INVERSE 0
3
4#if (!defined(M_PI))
5 #define M_PI 3.14159265358979323846264338327
6#endif
7#if (!defined(PI))
8 #define PI M_PI
9#endif
10#if (!defined(TWO_PI))
11 #define TWO_PI (2.0 * M_PI)
12#endif
13
14#ifdef __cplusplus
15extern "C" {
16#endif /* __cplusplus */
17
18void JGrfft(float x[], int N, int forward);
19void JGcfft(float x[], int NC, int forward);
20
21#ifdef __cplusplus
22} /* extern "C" */
23#endif
24
Spectacle/genlib/FFTReal.cpp+615−0
@@ -0,0 +1,615 @@
1/*****************************************************************************
2* *
3* DIGITAL SIGNAL PROCESSING TOOLS *
4* Version 1.03, 2001/06/15 *
5* (c) 1999 - Laurent de Soras *
6* *
7* FFTReal.cpp *
8* Fourier transformation of real number arrays. *
9* Portable ISO C++ *
10* *
11* Tab = 3 *
12*****************************************************************************/
13
14
15
16/*\\\ INCLUDE FILES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
17
18#include "FFTReal.h"
19
20#include <cassert>
21#include <cmath>
22
23
24
25#if defined (_MSC_VER)
26#pragma pack (push, 8)
27#endif // _MSC_VER
28
29
30
31/*\\\ PUBLIC MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
32
33
34
35/*==========================================================================*/
36/* Name: Constructor */
37/* Input parameters: */
38/* - length: length of the array on which we want to do a FFT. */
39/* Range: power of 2 only, > 0. */
40/* Throws: std::bad_alloc, anything */
41/*==========================================================================*/
42
43FFTReal::FFTReal (const long length)
44: _length (length)
45, _nbr_bits (int (floor (log (length) / log (2) + 0.5)))
46, _bit_rev_lut (int (floor (log (length) / log (2) + 0.5)))
47, _trigo_lut (int (floor (log (length) / log (2) + 0.5)))
48, _sqrt2_2 (flt_t (sqrt (2) * 0.5))
49{
50 assert ((1L << _nbr_bits) == length);
51
52 _buffer_ptr = 0;
53 if (_nbr_bits > 2)
54 {
55 _buffer_ptr = new flt_t [_length];
56 }
57}
58
59
60
61/*==========================================================================*/
62/* Name: Destructor */
63/*==========================================================================*/
64
65FFTReal::~FFTReal (void)
66{
67 delete [] _buffer_ptr;
68 _buffer_ptr = 0;
69}
70
71
72
73/*==========================================================================*/
74/* Name: do_fft */
75/* Description: Compute the FFT of the array. */
76/* Input parameters: */
77/* - x: pointer on the source array (time). */
78/* Output parameters: */
79/* - f: pointer on the destination array (frequencies). */
80/* f [0...length(x)/2] = real values, */
81/* f [length(x)/2+1...length(x)-1] = imaginary values of */
82/* coefficents 1...length(x)/2-1. */
83/* Throws: Nothing */
84/*==========================================================================*/
85
86void FFTReal::do_fft (flt_t f [], const flt_t x []) const
87{
88
89/*______________________________________________
90 *
91 * General case
92 *______________________________________________
93 */
94
95 if (_nbr_bits > 2)
96 {
97 flt_t * sf;
98 flt_t * df;
99
100 if (_nbr_bits & 1)
101 {
102 df = _buffer_ptr;
103 sf = f;
104 }
105 else
106 {
107 df = f;
108 sf = _buffer_ptr;
109 }
110
111 /* Do the transformation in several pass */
112 {
113 int pass;
114 long nbr_coef;
115 long h_nbr_coef;
116 long d_nbr_coef;
117 long coef_index;
118
119 /* First and second pass at once */
120 {
121 const long * const bit_rev_lut_ptr = _bit_rev_lut.get_ptr ();
122 coef_index = 0;
123 do
124 {
125 const long rev_index_0 = bit_rev_lut_ptr [coef_index];
126 const long rev_index_1 = bit_rev_lut_ptr [coef_index + 1];
127 const long rev_index_2 = bit_rev_lut_ptr [coef_index + 2];
128 const long rev_index_3 = bit_rev_lut_ptr [coef_index + 3];
129
130 flt_t * const df2 = df + coef_index;
131 df2 [1] = x [rev_index_0] - x [rev_index_1];
132 df2 [3] = x [rev_index_2] - x [rev_index_3];
133
134 const flt_t sf_0 = x [rev_index_0] + x [rev_index_1];
135 const flt_t sf_2 = x [rev_index_2] + x [rev_index_3];
136
137 df2 [0] = sf_0 + sf_2;
138 df2 [2] = sf_0 - sf_2;
139
140 coef_index += 4;
141 }
142 while (coef_index < _length);
143 }
144
145 /* Third pass */
146 {
147 coef_index = 0;
148 const flt_t sqrt2_2 = _sqrt2_2;
149 do
150 {
151 flt_t v;
152
153 sf [coef_index] = df [coef_index] + df [coef_index + 4];
154 sf [coef_index + 4] = df [coef_index] - df [coef_index + 4];
155 sf [coef_index + 2] = df [coef_index + 2];
156 sf [coef_index + 6] = df [coef_index + 6];
157
158 v = (df [coef_index + 5] - df [coef_index + 7]) * sqrt2_2;
159 sf [coef_index + 1] = df [coef_index + 1] + v;
160 sf [coef_index + 3] = df [coef_index + 1] - v;
161
162 v = (df [coef_index + 5] + df [coef_index + 7]) * sqrt2_2;
163 sf [coef_index + 5] = v + df [coef_index + 3];
164 sf [coef_index + 7] = v - df [coef_index + 3];
165
166 coef_index += 8;
167 }
168 while (coef_index < _length);
169 }
170
171 /* Next pass */
172 for (pass = 3; pass < _nbr_bits; ++pass)
173 {
174 coef_index = 0;
175 nbr_coef = 1 << pass;
176 h_nbr_coef = nbr_coef >> 1;
177 d_nbr_coef = nbr_coef << 1;
178 const flt_t * const cos_ptr = _trigo_lut.get_ptr (pass);
179 do
180 {
181 long i;
182 const flt_t * const sf1r = sf + coef_index;
183 const flt_t * const sf2r = sf1r + nbr_coef;
184 flt_t * const dfr = df + coef_index;
185 flt_t * const dfi = dfr + nbr_coef;
186
187 /* Extreme coefficients are always real */
188 dfr [0] = sf1r [0] + sf2r [0];
189 dfi [0] = sf1r [0] - sf2r [0]; // dfr [nbr_coef] =
190 dfr [h_nbr_coef] = sf1r [h_nbr_coef];
191 dfi [h_nbr_coef] = sf2r [h_nbr_coef];
192
193 /* Others are conjugate complex numbers */
194 const flt_t * const sf1i = sf1r + h_nbr_coef;
195 const flt_t * const sf2i = sf1i + nbr_coef;
196 for (i = 1; i < h_nbr_coef; ++ i)
197 {
198 const flt_t c = cos_ptr [i]; // cos (i*PI/nbr_coef);
199 const flt_t s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef);
200 flt_t v;
201
202 v = sf2r [i] * c - sf2i [i] * s;
203 dfr [i] = sf1r [i] + v;
204 dfi [-i] = sf1r [i] - v; // dfr [nbr_coef - i] =
205
206 v = sf2r [i] * s + sf2i [i] * c;
207 dfi [i] = v + sf1i [i];
208 dfi [nbr_coef - i] = v - sf1i [i];
209 }
210
211 coef_index += d_nbr_coef;
212 }
213 while (coef_index < _length);
214
215 /* Prepare to the next pass */
216 {
217 flt_t * const temp_ptr = df;
218 df = sf;
219 sf = temp_ptr;
220 }
221 }
222 }
223 }
224
225/*______________________________________________
226 *
227 * Special cases
228 *______________________________________________
229 */
230
231 /* 4-point FFT */
232 else if (_nbr_bits == 2)
233 {
234 f [1] = x [0] - x [2];
235 f [3] = x [1] - x [3];
236
237 const flt_t b_0 = x [0] + x [2];
238 const flt_t b_2 = x [1] + x [3];
239
240 f [0] = b_0 + b_2;
241 f [2] = b_0 - b_2;
242 }
243
244 /* 2-point FFT */
245 else if (_nbr_bits == 1)
246 {
247 f [0] = x [0] + x [1];
248 f [1] = x [0] - x [1];
249 }
250
251 /* 1-point FFT */
252 else
253 {
254 f [0] = x [0];
255 }
256}
257
258
259
260/*==========================================================================*/
261/* Name: do_ifft */
262/* Description: Compute the inverse FFT of the array. Notice that */
263/* IFFT (FFT (x)) = x * length (x). Data must be */
264/* post-scaled. */
265/* Input parameters: */
266/* - f: pointer on the source array (frequencies). */
267/* f [0...length(x)/2] = real values, */
268/* f [length(x)/2+1...length(x)] = imaginary values of */
269/* coefficents 1...length(x)-1. */
270/* Output parameters: */
271/* - x: pointer on the destination array (time). */
272/* Throws: Nothing */
273/*==========================================================================*/
274
275void FFTReal::do_ifft (const flt_t f [], flt_t x []) const
276{
277
278/*______________________________________________
279 *
280 * General case
281 *______________________________________________
282 */
283
284 if (_nbr_bits > 2)
285 {
286 flt_t * sf = const_cast <flt_t *> (f);
287 flt_t * df;
288 flt_t * df_temp;
289
290 if (_nbr_bits & 1)
291 {
292 df = _buffer_ptr;
293 df_temp = x;
294 }
295 else
296 {
297 df = x;
298 df_temp = _buffer_ptr;
299 }
300
301 /* Do the transformation in several pass */
302 {
303 int pass;
304 long nbr_coef;
305 long h_nbr_coef;
306 long d_nbr_coef;
307 long coef_index;
308
309 /* First pass */
310 for (pass = _nbr_bits - 1; pass >= 3; --pass)
311 {
312 coef_index = 0;
313 nbr_coef = 1 << pass;
314 h_nbr_coef = nbr_coef >> 1;
315 d_nbr_coef = nbr_coef << 1;
316 const flt_t *const cos_ptr = _trigo_lut.get_ptr (pass);
317 do
318 {
319 long i;
320 const flt_t * const sfr = sf + coef_index;
321 const flt_t * const sfi = sfr + nbr_coef;
322 flt_t * const df1r = df + coef_index;
323 flt_t * const df2r = df1r + nbr_coef;
324
325 /* Extreme coefficients are always real */
326 df1r [0] = sfr [0] + sfi [0]; // + sfr [nbr_coef]
327 df2r [0] = sfr [0] - sfi [0]; // - sfr [nbr_coef]
328 df1r [h_nbr_coef] = sfr [h_nbr_coef] * 2;
329 df2r [h_nbr_coef] = sfi [h_nbr_coef] * 2;
330
331 /* Others are conjugate complex numbers */
332 flt_t * const df1i = df1r + h_nbr_coef;
333 flt_t * const df2i = df1i + nbr_coef;
334 for (i = 1; i < h_nbr_coef; ++ i)
335 {
336 df1r [i] = sfr [i] + sfi [-i]; // + sfr [nbr_coef - i]
337 df1i [i] = sfi [i] - sfi [nbr_coef - i];
338
339 const flt_t c = cos_ptr [i]; // cos (i*PI/nbr_coef);
340 const flt_t s = cos_ptr [h_nbr_coef - i]; // sin (i*PI/nbr_coef);
341 const flt_t vr = sfr [i] - sfi [-i]; // - sfr [nbr_coef - i]
342 const flt_t vi = sfi [i] + sfi [nbr_coef - i];
343
344 df2r [i] = vr * c + vi * s;
345 df2i [i] = vi * c - vr * s;
346 }
347
348 coef_index += d_nbr_coef;
349 }
350 while (coef_index < _length);
351
352 /* Prepare to the next pass */
353 if (pass < _nbr_bits - 1)
354 {
355 flt_t * const temp_ptr = df;
356 df = sf;
357 sf = temp_ptr;
358 }
359 else
360 {
361 sf = df;
362 df = df_temp;
363 }
364 }
365
366 /* Antepenultimate pass */
367 {
368 const flt_t sqrt2_2 = _sqrt2_2;
369 coef_index = 0;
370 do
371 {
372 df [coef_index] = sf [coef_index] + sf [coef_index + 4];
373 df [coef_index + 4] = sf [coef_index] - sf [coef_index + 4];
374 df [coef_index + 2] = sf [coef_index + 2] * 2;
375 df [coef_index + 6] = sf [coef_index + 6] * 2;
376
377 df [coef_index + 1] = sf [coef_index + 1] + sf [coef_index + 3];
378 df [coef_index + 3] = sf [coef_index + 5] - sf [coef_index + 7];
379
380 const flt_t vr = sf [coef_index + 1] - sf [coef_index + 3];
381 const flt_t vi = sf [coef_index + 5] + sf [coef_index + 7];
382
383 df [coef_index + 5] = (vr + vi) * sqrt2_2;
384 df [coef_index + 7] = (vi - vr) * sqrt2_2;
385
386 coef_index += 8;
387 }
388 while (coef_index < _length);
389 }
390
391 /* Penultimate and last pass at once */
392 {
393 coef_index = 0;
394 const long * bit_rev_lut_ptr = _bit_rev_lut.get_ptr ();
395 const flt_t * sf2 = df;
396 do
397 {
398 {
399 const flt_t b_0 = sf2 [0] + sf2 [2];
400 const flt_t b_2 = sf2 [0] - sf2 [2];
401 const flt_t b_1 = sf2 [1] * 2;
402 const flt_t b_3 = sf2 [3] * 2;
403
404 x [bit_rev_lut_ptr [0]] = b_0 + b_1;
405 x [bit_rev_lut_ptr [1]] = b_0 - b_1;
406 x [bit_rev_lut_ptr [2]] = b_2 + b_3;
407 x [bit_rev_lut_ptr [3]] = b_2 - b_3;
408 }
409 {
410 const flt_t b_0 = sf2 [4] + sf2 [6];
411 const flt_t b_2 = sf2 [4] - sf2 [6];
412 const flt_t b_1 = sf2 [5] * 2;
413 const flt_t b_3 = sf2 [7] * 2;
414
415 x [bit_rev_lut_ptr [4]] = b_0 + b_1;
416 x [bit_rev_lut_ptr [5]] = b_0 - b_1;
417 x [bit_rev_lut_ptr [6]] = b_2 + b_3;
418 x [bit_rev_lut_ptr [7]] = b_2 - b_3;
419 }
420
421 sf2 += 8;
422 coef_index += 8;
423 bit_rev_lut_ptr += 8;
424 }
425 while (coef_index < _length);
426 }
427 }
428 }
429
430/*______________________________________________
431 *
432 * Special cases
433 *______________________________________________
434 */
435
436 /* 4-point IFFT */
437 else if (_nbr_bits == 2)
438 {
439 const flt_t b_0 = f [0] + f [2];
440 const flt_t b_2 = f [0] - f [2];
441
442 x [0] = b_0 + f [1] * 2;
443 x [2] = b_0 - f [1] * 2;
444 x [1] = b_2 + f [3] * 2;
445 x [3] = b_2 - f [3] * 2;
446 }
447
448 /* 2-point IFFT */
449 else if (_nbr_bits == 1)
450 {
451 x [0] = f [0] + f [1];
452 x [1] = f [0] - f [1];
453 }
454
455 /* 1-point IFFT */
456 else
457 {
458 x [0] = f [0];
459 }
460}
461
462
463
464/*==========================================================================*/
465/* Name: rescale */
466/* Description: Scale an array by divide each element by its length. */
467/* This function should be called after FFT + IFFT. */
468/* Input/Output parameters: */
469/* - x: pointer on array to rescale (time or frequency). */
470/* Throws: Nothing */
471/*==========================================================================*/
472
473void FFTReal::rescale (flt_t x []) const
474{
475 const flt_t mul = flt_t (1.0 / _length);
476 long i = _length - 1;
477
478 do
479 {
480 x [i] *= mul;
481 --i;
482 }
483 while (i >= 0);
484}
485
486
487
488/*\\\ NESTED CLASS MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
489
490
491
492/*==========================================================================*/
493/* Name: Constructor */
494/* Input parameters: */
495/* - nbr_bits: number of bits of the array on which we want to do a */
496/* FFT. Range: > 0 */
497/* Throws: std::bad_alloc */
498/*==========================================================================*/
499
500FFTReal::BitReversedLUT::BitReversedLUT (const int nbr_bits)
501{
502 long length;
503 long cnt;
504 long br_index;
505 long bit;
506
507 length = 1L << nbr_bits;
508 _ptr = new long [length];
509
510 br_index = 0;
511 _ptr [0] = 0;
512 for (cnt = 1; cnt < length; ++cnt)
513 {
514 /* ++br_index (bit reversed) */
515 bit = length >> 1;
516 while (((br_index ^= bit) & bit) == 0)
517 {
518 bit >>= 1;
519 }
520
521 _ptr [cnt] = br_index;
522 }
523}
524
525
526
527/*==========================================================================*/
528/* Name: Destructor */
529/*==========================================================================*/
530
531FFTReal::BitReversedLUT::~BitReversedLUT (void)
532{
533 delete [] _ptr;
534 _ptr = 0;
535}
536
537
538
539/*==========================================================================*/
540/* Name: Constructor */
541/* Input parameters: */
542/* - nbr_bits: number of bits of the array on which we want to do a */
543/* FFT. Range: > 0 */
544/* Throws: std::bad_alloc, anything */
545/*==========================================================================*/
546
547FFTReal::TrigoLUT::TrigoLUT (const int nbr_bits)
548{
549 long total_len;
550
551 _ptr = 0;
552 if (nbr_bits > 3)
553 {
554 total_len = (1L << (nbr_bits - 1)) - 4;
555 _ptr = new flt_t [total_len];
556
557 const double PI = atan (1) * 4;
558 for (int level = 3; level < nbr_bits; ++level)
559 {
560 const long level_len = 1L << (level - 1);
561 flt_t * const level_ptr = const_cast<flt_t *> (get_ptr (level));
562 const double mul = PI / (level_len << 1);
563
564 for (long i = 0; i < level_len; ++ i)
565 {
566 level_ptr [i] = (flt_t) cos (i * mul);
567 }
568 }
569 }
570}
571
572
573
574/*==========================================================================*/
575/* Name: Destructor */
576/*==========================================================================*/
577
578FFTReal::TrigoLUT::~TrigoLUT (void)
579{
580 delete [] _ptr;
581 _ptr = 0;
582}
583
584
585
586#if defined (_MSC_VER)
587#pragma pack (pop)
588#endif // _MSC_VER
589
590
591
592/*****************************************************************************
593
594 LEGAL
595
596 Source code may be freely used for any purpose, including commercial
597 applications. Programs must display in their "About" dialog-box (or
598 documentation) a text telling they use these routines by Laurent de Soras.
599 Modified source code can be distributed, but modifications must be clearly
600 indicated.
601
602 CONTACT
603
604 Laurent de Soras
605 92 avenue Albert 1er
606 92500 Rueil-Malmaison
607 France
608
609 ldesoras@club-internet.fr
610
611*****************************************************************************/
612
613
614
615/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
Spectacle/genlib/FFTReal.h+139−0
@@ -0,0 +1,139 @@
1/*****************************************************************************
2* *
3* DIGITAL SIGNAL PROCESSING TOOLS *
4* Version 1.03, 2001/06/15 *
5* (c) 1999 - Laurent de Soras *
6* *
7* FFTReal.h *
8* Fourier transformation of real number arrays. *
9* Portable ISO C++ *
10* *
11* Tab = 3 *
12*****************************************************************************/
13
14
15
16#if defined (FFTReal_CURRENT_HEADER)
17 #error Recursive inclusion of FFTReal header file.
18#endif
19#define FFTReal_CURRENT_HEADER
20
21#if ! defined (FFTReal_HEADER_INCLUDED)
22#define FFTReal_HEADER_INCLUDED
23
24
25
26#if defined (_MSC_VER)
27#pragma pack (push, 8)
28#endif // _MSC_VER
29
30
31
32class FFTReal
33{
34
35/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
36
37public:
38
39 /* Change this typedef to use a different floating point type in your FFTs
40 (i.e. float, double or long double). */
41 typedef float flt_t;
42
43 explicit FFTReal (const long length);
44 ~FFTReal ();
45 void do_fft (flt_t f [], const flt_t x []) const;
46 void do_ifft (const flt_t f [], flt_t x []) const;
47 void rescale (flt_t x []) const;
48
49
50
51/*\\\ PRIVATE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
52
53private:
54
55 /* Bit-reversed look-up table nested class */
56 class BitReversedLUT
57 {
58 public:
59 explicit BitReversedLUT (const int nbr_bits);
60 ~BitReversedLUT ();
61 const long * get_ptr () const
62 {
63 return (_ptr);
64 }
65 private:
66 long * _ptr;
67 };
68
69 /* Trigonometric look-up table nested class */
70 class TrigoLUT
71 {
72 public:
73 explicit TrigoLUT (const int nbr_bits);
74 ~TrigoLUT ();
75 const flt_t * get_ptr (const int level) const
76 {
77 return (_ptr + (1L << (level - 1)) - 4);
78 };
79 private:
80 flt_t * _ptr;
81 };
82
83 const long _length;
84 const int _nbr_bits;
85 const BitReversedLUT _bit_rev_lut;
86 const TrigoLUT _trigo_lut;
87 const flt_t _sqrt2_2;
88 flt_t * _buffer_ptr;
89
90
91
92/*\\\ FORBIDDEN MEMBER FUNCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
93
94private:
95
96 FFTReal (const FFTReal &other);
97 const FFTReal& operator = (const FFTReal &other);
98 int operator == (const FFTReal &other);
99 int operator != (const FFTReal &other);
100};
101
102
103
104#if defined (_MSC_VER)
105#pragma pack (pop)
106#endif // _MSC_VER
107
108
109
110#endif // FFTReal_HEADER_INCLUDED
111
112#undef FFTReal_CURRENT_HEADER
113
114
115
116/*****************************************************************************
117
118 LEGAL
119
120 Source code may be freely used for any purpose, including commercial
121 applications. Programs must display in their "About" dialog-box (or
122 documentation) a text telling they use these routines by Laurent de Soras.
123 Modified source code can be distributed, but modifications must be clearly
124 indicated.
125
126 CONTACT
127
128 Laurent de Soras
129 92 avenue Albert 1er
130 92500 Rueil-Malmaison
131 France
132
133 ldesoras@club-internet.fr
134
135*****************************************************************************/
136
137
138
139/*\\\ EOF \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
Spectacle/genlib/Obucket.cpp+33−0
@@ -0,0 +1,33 @@
1// RTcmix - Copyright (C) 2005 The RTcmix Development Team
2// See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
3// the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
4
5#include "Obucket.h"
6
7Obucket::Obucket(int len, ProcessFunction callback, void *context)
8 : _len(len), _index(0), _callback(callback), _context(context)
9{
10 _buf = new float [len];
11}
12
13Obucket::~Obucket()
14{
15 delete [] _buf;
16}
17
18void Obucket::flush(float defaultval)
19{
20 if (_index > 0) {
21 for (int i = _index; i < _len; i++)
22 _buf[i] = defaultval;
23 (*_callback)(_buf, _len, _context);
24 _index = 0;
25 }
26}
27
28void Obucket::clear(float defaultval)
29{
30 for (int i = 0; i < _len; i++)
31 _buf[i] = defaultval;
32}
33
Spectacle/genlib/Obucket.h+46−0
@@ -0,0 +1,46 @@
1// RTcmix - Copyright (C) 2005 The RTcmix Development Team
2// See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
3// the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
4
5// Obucket maintains a private buffer of floats. Add to the buffer one float
6// at a time. When Obucket sees that the buffer is full, it invokes a callback
7// function with the buffer, its length and a user-supplied context. This
8// is useful for instruments that must process in blocks whose size has no
9// clear and predictable relationship to the run() buffer size (for example,
10// instruments that do FFT-based processing).
11//
12// JGG, 6/1/05
13
14class Obucket {
15public:
16 typedef void (*ProcessFunction)(const float *buf, const int len,
17 void *context);
18 Obucket(int len, ProcessFunction callback, void *context);
19 ~Obucket();
20 inline bool drop(float item); // call this to drop <item> in bucket
21 void flush(float defaultval = 0.0f);
22 void clear(float defaultval = 0.0f);
23
24private:
25 int _len;
26 int _index;
27 ProcessFunction _callback;
28 void *_context;
29 float *_buf;
30};
31
32
33// Store <item> into the buffer; if full, invoke callback and return true;
34// otherwise return false.
35
36inline bool Obucket::drop(float item)
37{
38 _buf[_index++] = item;
39 if (_index == _len) {
40 (*_callback)(_buf, _len, _context);
41 _index = 0;
42 return true;
43 }
44 return false;
45}
46
Spectacle/genlib/Odelay.cpp+145−0
@@ -0,0 +1,145 @@
1/* RTcmix - Copyright (C) 2004 The RTcmix Development Team
2 See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
3 the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
4*/
5
6#include "Odelay.h"
7#include <stdlib.h>
8#include <string.h>
9#include <assert.h>
10#include <stdio.h>
11
12Odelay::Odelay(long defaultLength) : _dline(NULL), _len(0)
13{
14 if (defaultLength > 0) {
15 _outpoint = 0;
16 resize(defaultLength);
17 clear();
18 _inpoint = _len - 1;
19 }
20 else {
21 printf("****program error: Odelay defaultLength not > 0\n");
22 assert(defaultLength > 0); // doesn't print anything in Max/MSP or console
23 }
24}
25
26Odelay::~Odelay()
27{
28 free(_dline);
29}
30
31void Odelay::clear()
32{
33 for (long i = 0; i < _len; i++)
34 _dline[i] = 0.0;
35 _lastout = 0.0;
36}
37
38void Odelay::fill(double val)
39{
40 for (long i = 0; i < _len; i++)
41 _dline[i] = val;
42 _lastout = val;
43}
44
45void Odelay::putsamp(float samp)
46{
47 _dline[_inpoint++] = samp;
48 if (_inpoint == _len)
49 _inpoint = 0;
50}
51
52float Odelay::getsamp(double lagsamps)
53{
54 _outpoint = _inpoint - (long) lagsamps;
55#ifdef AUTOEXPAND
56 if (lagsamps >= (double) _len)
57 resize((long)(lagsamps + 0.5));
58#endif
59 if (_len > 0) {
60 while (_outpoint < 0)
61 _outpoint += _len;
62 }
63 return _lastout = _dline[_outpoint++];
64}
65
66// Set output pointer <_outpoint>.
67
68void Odelay::setdelay(double lagsamps)
69{
70 _outpoint = _inpoint - (long) lagsamps;
71#ifdef AUTOEXPAND
72 if (lagsamps >= (double) _len)
73 resize((long)(lagsamps + 0.5));
74#endif
75 if (_len > 0) {
76 while (_outpoint < 0)
77 _outpoint += _len;
78 }
79}
80
81float Odelay::next(float input)
82{
83 _dline[_inpoint++] = input;
84 if (_inpoint == _len)
85 _inpoint = 0;
86 _lastout = _dline[_outpoint++];
87 if (_outpoint == _len)
88 _outpoint = 0;
89 return _lastout;
90}
91
92static float *newFloats(float *oldptr, long oldlen, long *newlen)
93{
94 float *ptr = NULL;
95 if (oldptr == NULL) {
96 ptr = (float *) malloc(*newlen * sizeof(float));
97 }
98 else {
99 float *newptr = (float *) realloc(oldptr, *newlen * sizeof(float));
100 if (newptr) {
101 ptr = newptr;
102 // Zero out new portion.
103 for (long n = oldlen; n < *newlen; ++n)
104 ptr[n] = 0.0f;
105 }
106 else {
107 *newlen = oldlen; // notify caller that realloc failed
108 ptr = oldptr;
109 }
110 }
111 return ptr;
112}
113
114int Odelay::resize(long thisLength)
115{
116 const long oldlen = _len;
117
118 if (thisLength == oldlen)
119 return oldlen;
120
121// For Max/MSP we rely on object to manage maxdeltime and don't take advantage
122// of Doug's code to automatically resize delay lines.
123#ifdef AUTOEXPAND
124 // Make a guess at how big the new array should be.
125 long newlen = (thisLength < _len * 2) ? _len * 2 : _len + thisLength;
126#else
127 long newlen = thisLength;
128#endif
129 _dline = ::newFloats(_dline, _len, &newlen);
130 if (_outpoint < 0 && newlen > 0) {
131 while (_outpoint < 0)
132 _outpoint += oldlen;
133 long shift = newlen - oldlen;
134 memcpy(&_dline[_outpoint + shift],
135 &_dline[_outpoint],
136 sizeof(float) * (oldlen - _outpoint));
137 _outpoint += shift;
138 }
139 return _len = newlen;
140}
141
142float Odelay::delay() const
143{
144 return (float) (_len > 0) ? abs((_outpoint - _inpoint) % _len) : 0;
145}
Spectacle/genlib/Odelay.h+85−0
@@ -0,0 +1,85 @@
1/* RTcmix - Copyright (C) 2005 The RTcmix Development Team
2 See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
3 the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
4*/
5#ifndef _ODELAY_H_
6#define _ODELAY_H_ 1
7
8// Delay class, offering two different ways of storing and
9// retrieving values from the delay line. (See API 1 and API 2 below.)
10// It's best not to mix the two ways while working with an Odelay object.
11// Based on cmix delset/dliget and STK DLineL.
12//
13// John Gibson, 7/9/04; overhaul by Doug Scott in late 2004.
14// JGG changed a few things for use with Max/MSP objects (2007).
15
16
17class Odelay
18{
19public:
20 Odelay(long defaultLength);
21 virtual ~Odelay();
22 void clear();
23 void fill(double val);
24
25 // --------------------------------------------------------------------------
26 // API 1: putsamp / getsamp
27 //
28 // Put sample into delay line using putsamp(). Retrieve sample from any
29 // point in delay line, specified by floating-point number of samples, using
30 // getsamp(). Unlike API 2 (below), getsamp does not affect the delay line
31 // input pointer, so you can call getsamp multiple times for every call to
32 // putsamp, letting you implement multiple delay taps. Note that it will
33 // not work to have a block with multiple calls to putsamp followed by a
34 // block with multiple calls to getsamp. Each call to putsamp must be
35 // followed by at least one call to getsamp before another putsamp.
36 //
37 // The code is based on classic cmix genlib delset/dliget, except it takes
38 // the number of samples of delay rather than a delay time in seconds.
39 //
40 // Note that this API does not maintain the correct values for _outpoint and
41 // _frac across calls to getsamp, so if you want to use API 2 after API 1 for
42 // the same Odelay object, then be sure to call setdelay before using API 2.
43
44 // Put sample into delay line, and advance input pointer. Use getsamp() to
45 // retrieve samples from delay line at varying delays from this input
46 // pointer.
47
48 void putsamp(float samp);
49
50 // Get sample from delay line that is <lagsamps> samples behind the most
51 // recent sample to enter the delay line. If <lagsamps> is longer than
52 // length of delay line, the delay is resized to fit.
53
54 float getsamp(double lagsamps);
55
56 // --------------------------------------------------------------------------
57 // API 2: setdelay / next
58 //
59 // Set the delay in samples by calling setdelay(), then call next() to store
60 // a new value into delay line and retreive the oldest value. Does not let
61 // you have more than one delay tap. Based on STK DLineL implementation.
62 // If lagsamps is greater than the length of the delay line, the delay is
63 // resized to fit.
64
65 void setdelay(double lagsamps);
66 float next(float input);
67
68 float last() const { return _lastout; }
69
70 long length() const { return _len; }
71
72 // The current delay in samples
73 float delay() const;
74
75 int resize(long newLen);
76
77protected:
78 float *_dline;
79 long _len;
80 long _inpoint;
81 long _outpoint;
82 float _lastout;
83};
84
85#endif // _ODELAYI_H_
Spectacle/genlib/Offt.cpp+157−0
@@ -0,0 +1,157 @@
1/* RTcmix - Copyright (C) 2005 The RTcmix Development Team
2 See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
3 the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
4*/
5
6#include "Offt.h"
7#ifndef FFTW
8#include "FFTReal.h"
9#endif
10
11
12Offt::Offt(int fftsize, unsigned int flags)
13 : _len(fftsize)
14{
15#ifdef FFTW
16 _plan_r2c = _plan_c2r = NULL;
17 _buf = (float *) fftwf_malloc(sizeof(float) * _len);
18 int csize = sizeof(fftwf_complex) * ((_len / 2) + 1);
19 _cbuf = (fftwf_complex *) fftwf_malloc(csize);
20 if (flags & kRealToComplex)
21 _plan_r2c = fftwf_plan_dft_r2c_1d(_len, _buf, _cbuf, FFTW_ESTIMATE);
22 if (flags & kComplexToReal)
23 _plan_c2r = fftwf_plan_dft_c2r_1d(_len, _cbuf, _buf, FFTW_ESTIMATE);
24#else // !FFTW
25 _buf = new float [_len];
26 _tmp = new float [_len];
27 _fftobj = new FFTReal(_len);
28#endif // !FFTW
29}
30
31Offt::~Offt()
32{
33#ifdef FFTW
34 if (_plan_r2c)
35 fftwf_destroy_plan(_plan_r2c);
36 if (_plan_c2r)
37 fftwf_destroy_plan(_plan_c2r);
38 fftwf_free(_buf);
39 fftwf_free(_cbuf);
40#else // !FFTW
41 delete [] _buf;
42 delete [] _tmp;
43 delete _fftobj;
44#endif // !FFTW
45}
46
47
48#ifdef FFTW
49
50void Offt::r2c()
51{
52 fftwf_execute(_plan_r2c);
53
54 // _cbuf has complex result in real,imaginary pairs from DC to Nyquist;
55 // copy into _buf while reordering to...
56 // re(0), re(len/2), re(1), im(1), re(2), im(2)...re(len/2-1), im(len/2-1)
57 // and normalizing by 1 / fftlen.
58 float scale = 1.0 / _len;
59 int last = _len / 2;
60 _buf[0] = _cbuf[0][0] * scale;
61 _buf[1] = _cbuf[last][0] * scale;
62 for (int i = 1; i < last; i++) {
63 _buf[i + i] = _cbuf[i][0] * scale;
64 _buf[i + i + 1] = _cbuf[i][1] * scale;
65 }
66}
67
68void Offt::c2r()
69{
70 // _buf has complex data; copy into _cbuf while reordering from...
71 // re(0), re(len/2), re(1), im(1), re(2), im(2)...re(len/2-1), im(len/2-1)
72 // to real,imaginary pairs from DC to Nyquist;
73 int last = _len / 2;
74 _cbuf[0][0] = _buf[0];
75 _cbuf[last][0] = _buf[1];
76 for (int i = 1; i < last; i++) {
77 _cbuf[i][0] = _buf[i + i];
78 _cbuf[i][1] = _buf[i + i + 1];
79 }
80
81 fftwf_execute(_plan_c2r);
82}
83
84#else // !FFTW
85
86void Offt::r2c()
87{
88 _fftobj->do_fft(_tmp, _buf);
89
90 // _tmp has complex result; copy into _buf while reordering from...
91 // re(0), re(1), re(2)...re(len/2), im(1), im(2)...im(len/2-1)
92 // to...
93 // re(0), re(len/2), re(1), im(1), re(2), im(2)...re(len/2-1), im(len/2-1)
94 // and normalizing by 1 / fftlen.
95
96 float scale = 1.0 / _len;
97 int j = _len / 2;
98 _buf[0] = _tmp[0] * scale;
99 _buf[1] = _tmp[j] * scale;
100#if 0
101 for (int i = 1; i < j; i++) {
102 _buf[i + i] = _tmp[i] * scale; // real
103 _buf[i + i + 1] = _tmp[j + i] * scale; // imag
104 }
105#else
106 int i = j - 1;
107 float *bp = &_buf[_len - 1];
108 float *tp = &_tmp[j + i];
109 do {
110 *bp-- = *tp-- * scale; // imag
111 *bp-- = _tmp[i--] * scale; // real
112 } while (i > 0);
113#endif
114}
115
116void Offt::c2r()
117{
118 // _buf has complex data; copy into _tmp while reordering from...
119 // re(0), re(len/2), re(1), im(1), re(2), im(2)...re(len/2-1), im(len/2-1)
120 // to...
121 // re(0), re(1), re(2)...re(len/2), im(1), im(2)...im(len/2-1)
122
123 int j = _len / 2;
124 _tmp[0] = _buf[0];
125 _tmp[j] = _buf[1];
126#if 0
127 for (int i = 1; i < j; i++) {
128 _tmp[i] = _buf[i + i]; // real
129 _tmp[j + i] = _buf[i + i + 1]; // imag
130 }
131#else
132 int i = j - 1;
133 float *bp = &_buf[_len - 1];
134 float *tp = &_tmp[j + i];
135 do {
136 *tp-- = *bp--; // imag
137 _tmp[i--] = *bp--; // real
138 } while (i > 0);
139#endif
140
141 _fftobj->do_ifft(_tmp, _buf);
142
143 // _buf now holds real output
144}
145
146#endif // !FFTW
147
148
149#include <stdio.h>
150
151void Offt::printbuf(float *buf, char *msg)
152{
153 printf("\n%s\n", msg);
154 for (int i = 0; i < _len; i++)
155 printf("\t[%d] = %f\n", i, buf[i]);
156}
157
Spectacle/genlib/Offt.h+58−0
@@ -0,0 +1,58 @@
1// RTcmix - Copyright (C) 2005 The RTcmix Development Team
2// See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
3// the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
4
5// Offt provides an interface to two different FFT libraries: FFTW v3
6// <www.fftw.org> and FFTReal by Laurent de Soras <http://ldesoras.free.fr>.
7// This is a compile time choice, made via the configure script. By default
8// you get FFTReal, unless you configure --with-fftw. This code uses only
9// the float version of the fftw library, libfftw3f.
10//
11// To use the Offt object, call the constructor with the FFT size, which must
12// be a power of 2. Then call getbuf() to retrieve a pointer to the buffer
13// you use to pass data back and forth to the object. Load up this buffer
14// with real samples, then call r2c() to convert the buffer to complex FFT data.
15// The format is the same as the old cmix fft routine, with pairs of real and
16// imaginary floats, and buf[1] replaced with the real value of Nyquist.
17// Muck around with the FFT complex data, then call c2r() to turn it back into
18// real-valued samples.
19//
20// Check out Obucket also. This class makes it fairly easy to decouple the
21// FFT length from your instrument's buffer size and to have a fixed latency,
22// regardless of note start time. See insts/jg/SPECTACLE2_BASE.cpp for an
23// example.
24// -John Gibson, 6/4/05
25
26#ifdef FFTW
27#include <fftw3.h>
28#else
29class FFTReal;
30#endif
31
32class Offt {
33public:
34 enum {
35 kRealToComplex = 1,
36 kComplexToReal = 2
37 };
38
39 Offt(int fftsize, unsigned int flags = kRealToComplex | kComplexToReal);
40 ~Offt();
41 float *getbuf() const { return _buf; }
42 void r2c();
43 void c2r();
44
45private:
46 void printbuf(float *buf, char *msg);
47
48 int _len;
49 float *_buf;
50#ifdef FFTW
51 fftwf_complex *_cbuf;
52 fftwf_plan _plan_r2c, _plan_c2r;
53#else
54 float *_tmp;
55 FFTReal *_fftobj;
56#endif
57};
58
Spectacle/genlib/Ooscil.cpp+48−0
@@ -0,0 +1,48 @@
1/* RTcmix - Copyright (C) 2004 The RTcmix Development Team
2 See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
3 the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
4*/
5#include "Ooscil.h"
6
7Ooscil::Ooscil(float srate, float freq, float array[], int len)
8 : _array(array), _length(len)
9{
10 _lendivSR = (double) _length / srate;
11 _si = freq * _lendivSR;
12 _phase = 0.0;
13}
14
15// NB: caller must call setfreq after this
16void Ooscil::setsrate(float srate)
17{
18 _lendivSR = (double) _length / srate;
19}
20
21float Ooscil::next()
22{
23 int i = (int) _phase;
24 float output = _array[i];
25
26 // prepare for next call
27 _phase += _si;
28 while (_phase >= (double) _length)
29 _phase -= (double) _length;
30
31 return output;
32}
33
34float Ooscil::nexti()
35{
36 int i = (int) _phase;
37 int k = (i + 1) % _length;
38 double frac = _phase - (double) i;
39 float output = _array[i] + ((_array[k] - _array[i]) * frac);
40
41 // prepare for next call
42 _phase += _si;
43 while (_phase >= (double) _length)
44 _phase -= (double) _length;
45
46 return output;
47}
48
Spectacle/genlib/Ooscil.h+33−0
@@ -0,0 +1,33 @@
1/* RTcmix - Copyright (C) 2004 The RTcmix Development Team
2 See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
3 the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
4*/
5#ifndef _OOSCIL_H_
6#define _OOSCIL_H_ 1
7
8// Simple non-interpolating oscillator. Does not handle negative frequencies!
9//
10// NB: For the convenience of callers (like LFOPField) who need to decide
11// at runtime whether to interpolate, this class provides an interpolating
12// lookup method, nexti. -JGG
13
14class Ooscil
15{
16 double _si, _phase, _lendivSR;
17 float *_array;
18 int _length;
19
20public:
21 Ooscil(float srate, float freq, float array[], int len);
22
23 float next(); // non-interpolating
24 float nexti(); // linear interpolating
25
26 void setsrate(float srate); // NB: caller must call setfreq after this
27 inline void setfreq(float freq) { _si = freq * _lendivSR; }
28 inline void setphase(double phase) { _phase = phase; }
29 inline double getphase() const { return _phase; }
30 inline int getlength() const { return _length; }
31};
32
33#endif // _OOSCIL_H_
Spectacle/genlib/Ougens.h+30−0
@@ -0,0 +1,30 @@
1/* RTcmix - Copyright (C) 2004 The RTcmix Development Team
2 See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
3 the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
4*/
5#ifndef _OUGENS_H_
6#define _OUGENS_H_ 1
7
8//#include "../genlib/Oallpass.h"
9//#include "../genlib/Oallpassi.h"
10//#include "../genlib/Obalance.h"
11#include "../genlib/Obucket.h"
12//#include "../genlib/Ocomb.h"
13//#include "../genlib/Ocombi.h"
14//#include "../genlib/Odcblock.h"
15#include "../genlib/Odelay.h"
16//#include "../genlib/Odelayi.h"
17//#include "../genlib/Odistort.h"
18//#include "../genlib/Oequalizer.h"
19#include "../genlib/Offt.h"
20//#include "../genlib/Oonepole.h"
21#include "../genlib/Ooscil.h"
22//#include "../genlib/Ooscili.h"
23//#include "../genlib/Orand.h"
24//#include "../genlib/Oreson.h"
25//#include "../genlib/Orms.h"
26//#include "../genlib/Ortgetin.h"
27//#include "../genlib/Ostrum.h"
28#include "../genlib/RandGen.h"
29
30#endif // _OUGENS_H_
Spectacle/genlib/RandGen.cpp+224−0
@@ -0,0 +1,224 @@
1/* RTcmix - Copyright (C) 2004 The RTcmix Development Team
2 See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
3 the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
4*/
5
6#include "RandGen.h"
7#include <math.h>
8#include <assert.h>
9
10// Classes for generating random numbers JGG, 6/22/04
11// Most of this code is based on C functions written by Luke Dubois.
12// One class is based on code written by Mara Helmuth.
13
14inline double dmax(double x, double y) { return (x > y) ? x : y; }
15inline double dmin(double x, double y) { return (x < y) ? x : y; }
16
17
18// Base class -----------------------------------------------------------------
19
20RandGen::~RandGen() {}
21
22// Return a random number in range [0, 1]
23inline double RandGen::rawvalue()
24{
25 _randx = (_randx * 1103515245) + 12345;
26 int k = (_randx >> 16) & 077777;
27 return (double) k / 32768.0;
28}
29
30// Scale <num>, which must be in range [0, 1], to fit range [_min, _max]
31inline double RandGen::fitrange(double num) const
32{
33 assert(num >= 0.0 && num <= 1.0);
34 return _min + (num * (_max - _min));
35}
36
37
38// Linear distribution subclass -----------------------------------------------
39
40LinearRandom::LinearRandom(double min, double max, int seed)
41 : RandGen(min, max, seed) {}
42
43LinearRandom::~LinearRandom() {}
44
45double LinearRandom::value()
46{
47 return fitrange(rawvalue());
48}
49
50
51// Low-weighted linear distribution subclass ----------------------------------
52
53LowLinearRandom::LowLinearRandom(double min, double max, int seed)
54 : RandGen(min, max, seed) {}
55
56LowLinearRandom::~LowLinearRandom() {}
57
58double LowLinearRandom::value()
59{
60 double num1 = rawvalue();
61 double num2 = rawvalue();
62 return fitrange(dmin(num1, num2));
63}
64
65
66// High-weighted linear distribution subclass ---------------------------------
67
68HighLinearRandom::HighLinearRandom(double min, double max, int seed)
69 : RandGen(min, max, seed) {}
70
71HighLinearRandom::~HighLinearRandom() {}
72
73double HighLinearRandom::value()
74{
75 double num1 = rawvalue();
76 double num2 = rawvalue();
77 return fitrange(dmax(num1, num2));
78}
79
80
81// Triangle distribution subclass ---------------------------------------------
82
83TriangleRandom::TriangleRandom(double min, double max, int seed)
84 : RandGen(min, max, seed) {}
85
86TriangleRandom::~TriangleRandom() {}
87
88double TriangleRandom::value()
89{
90 double num1 = rawvalue();
91 double num2 = rawvalue();
92 double tmp = 0.5 * (num1 + num2);
93 return fitrange(tmp);
94}
95
96
97// Gaussian distribution subclass ---------------------------------------------
98
99GaussianRandom::GaussianRandom(double min, double max, int seed)
100 : RandGen(min, max, seed) {}
101
102GaussianRandom::~GaussianRandom() {}
103
104double GaussianRandom::value()
105{
106 const int N = 12;
107 const double halfN = 6.0;
108 const double scale = 1.0;
109 const double mu = 0.5;
110 const double sigma = 0.166666;
111
112 double num;
113 do {
114 num = 0.0;
115 for (int j = 0; j < N; j++)
116 num += rawvalue();
117 num = sigma * scale * (num - halfN) + mu;
118 } while (num < 0.0 || num > 1.0);
119
120 return fitrange(num);
121}
122
123
124// Cauchy distribution subclass -----------------------------------------------
125
126CauchyRandom::CauchyRandom(double min, double max, int seed)
127 : RandGen(min, max, seed) {}
128
129CauchyRandom::~CauchyRandom() {}
130
131double CauchyRandom::value()
132{
133 const double alpha = 0.00628338;
134
135 double num;
136 do {
137 do {
138 num = rawvalue();
139 } while (num == 0.5);
140 num = (alpha * tan(num * M_PI)) + 0.5;
141 } while (num < 0.0 || num > 1.0);
142
143 return fitrange(num);
144}
145
146
147// Probability distribution subclass ------------------------------------------
148// Based on code by Mara Helmuth
149//
150// <min> and <max> set the range within which the random numbers fall.
151//
152// <mid> sets the mid-point of the range, which has an effect whenever
153// <tight> is not 1.
154//
155// <tight> governs the degree to which the random numbers adhere either
156// to the mid-point or to the extremes of the range:
157//
158// tight effect
159// -------------------------------------------------------------
160// 0 only the <min> and <max> values appear
161// 1 even distribution
162// > 1 numbers cluster ever more tightly around <mid>
163// 100 almost all numbers are equal to <mid>
164
165ProbRandom::ProbRandom(double min, double max, double mid, double tight,
166 int seed)
167 : RandGen(min, max, mid, tight, seed) {}
168
169ProbRandom::~ProbRandom() {}
170
171double ProbRandom::value()
172{
173 const double min = getmin();
174 const double max = getmax();
175 const double mid = getmid();
176 const double tight = gettight();
177 double hirange = max - mid;
178 double lowrange = mid - min;
179 double range = dmax(hirange, lowrange);
180
181 double num;
182 do {
183 double sign;
184 num = rawvalue(); // num is [0,1]
185 if (num > 0.5)
186 sign = 1.0;
187 else
188 sign = -1.0;
189 num = mid + (sign * (pow(rawvalue(), tight) * range));
190 } while (num < min || num > max);
191
192 return num;
193}
194
195// RandomOscil
196
197RandomOscil::RandomOscil(RandGen *gen, double srate, double freq)
198 : _gen(gen), _srate(srate)
199{
200 setfreq(freq);
201 _counter = _limit; // trigger first _gen->value()
202}
203
204RandomOscil::~RandomOscil()
205{
206 delete _gen;
207}
208
209void RandomOscil::setfreq(double freq)
210{
211 _limit = (int) (_srate / freq);
212}
213
214double RandomOscil::next()
215{
216 if (_counter >= _limit) {
217 _curval = _gen->value();
218 _counter = 0;
219 }
220 else
221 _counter++;
222 return _curval;
223}
224
Spectacle/genlib/RandGen.h+136−0
@@ -0,0 +1,136 @@
1/* RTcmix - Copyright (C) 2004 The RTcmix Development Team
2 See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
3 the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
4*/
5
6// Classes for generating random numbers JGG, 6/22/04
7// Most of this code is based on C functions written by Luke Dubois.
8// One class is based on code written by Mara Helmuth.
9
10#ifndef _RANDOM_H_
11#define _RANDOM_H_
12
13enum {
14 kLinearRandom = 0,
15 kLowLinearRandom,
16 kHighLinearRandom,
17 kTriangleRandom,
18 kGaussianRandom,
19 kCauchyRandom,
20 kProbRandom
21};
22
23// Base class
24
25class RandGen {
26public:
27 RandGen(double min, double max, int seed)
28 : _min(min), _max(max), _mid(0), _tight(0) { setseed(seed); }
29 RandGen(double min, double max, double mid, double tight, int seed)
30 : _min(min), _max(max), _mid(mid), _tight(tight) { setseed(seed); }
31 virtual ~RandGen();
32 virtual double value() = 0; // NB: not const, because it changes _randx
33 void setseed(const int aseed) { _randx = (long) aseed; }
34 void setmin(const double min) { _min = min; }
35 void setmax(const double max) { _max = max; }
36 void setmid(const double mid) { _mid = mid; }
37 void settight(const double tight) { _tight = tight; }
38protected:
39 double rawvalue();
40 double fitrange(double num) const;
41 double getmin() const { return _min; }
42 double getmax() const { return _max; }
43 double getmid() const { return _mid; }
44 double gettight() const { return _tight; }
45private:
46 long _randx;
47 double _min;
48 double _max;
49 double _mid;
50 double _tight;
51};
52
53
54// Subclasses, one for each distribution type
55
56class LinearRandom : public RandGen {
57public:
58 LinearRandom(double min = 0.0, double max = 1.0, int seed = 1);
59 virtual double value();
60protected:
61 virtual ~LinearRandom();
62};
63
64class LowLinearRandom : public RandGen {
65public:
66 LowLinearRandom(double min = 0.0, double max = 1.0, int seed = 1);
67 virtual double value();
68protected:
69 virtual ~LowLinearRandom();
70};
71
72class HighLinearRandom : public RandGen {
73public:
74 HighLinearRandom(double min = 0.0, double max = 1.0, int seed = 1);
75 virtual double value();
76protected:
77 virtual ~HighLinearRandom();
78};
79
80class TriangleRandom : public RandGen {
81public:
82 TriangleRandom(double min = 0.0, double max = 1.0, int seed = 1);
83 virtual double value();
84protected:
85 virtual ~TriangleRandom();
86};
87
88class GaussianRandom : public RandGen {
89public:
90 GaussianRandom(double min = 0.0, double max = 1.0, int seed = 1);
91 virtual double value();
92protected:
93 virtual ~GaussianRandom();
94};
95
96class CauchyRandom : public RandGen {
97public:
98 CauchyRandom(double min = 0.0, double max = 1.0, int seed = 1);
99 virtual double value();
100protected:
101 virtual ~CauchyRandom();
102};
103
104class ProbRandom : public RandGen {
105public:
106 ProbRandom(double min = 0.0, double max = 1.0, double mid = 0.5,
107 double tight = 1.0, int seed = 1);
108 virtual double value();
109protected:
110 virtual ~ProbRandom();
111};
112
113
114class RandomOscil
115{
116public:
117 // NB: RandomOscil takes ownership of gen, deleting it on destruction.
118 RandomOscil(RandGen *gen, double srate, double freq);
119 ~RandomOscil();
120 void setfreq(double freq);
121 void setseed(const int aseed) { _gen->setseed(aseed); }
122 void setmin(const double min) { _gen->setmin(min); }
123 void setmax(const double max) { _gen->setmax(max); }
124 void setmid(const double mid) { _gen->setmid(mid); }
125 void settight(const double tight) { _gen->settight(tight); }
126 double next();
127private:
128 RandGen *_gen;
129 double _srate;
130 double _curval;
131 int _counter;
132 int _limit;
133};
134
135#endif // _RANDOM_H_
136
Spectacle/genlib/ampdb.h+15−0
@@ -0,0 +1,15 @@
1#ifndef _AMPDB_H_
2#define _AMPDB_H_ 1
3
4#ifdef __cplusplus
5extern "C" {
6#endif
7
8double ampdb(float);
9double dbamp(float);
10
11#ifdef __cplusplus
12} /* extern "C" */
13#endif
14
15#endif /* _AMPDB_H_ */
Spectacle/makefile+3−2
@@ -3,8 +3,9 @@ · 2 unmodified lines
3CHUGIN_NAME=Spectacle3CHUGIN_NAME=Spectacle
4 4
5# all of the c/cpp files that compose this chugin5# all of the c/cpp files that compose this chugin
6C_MODULES=fft.c6CXX_MODULES=genlib/FFTReal.cpp genlib/Obucket.cpp genlib/Odelay.cpp genlib/Offt.cpp genlib/Ooscil.cpp genlib/RandGen.cpp
7CXX_MODULES=JGFilter.cpp DLineN.cpp Spectacle.cpp7CXX_MODULES+=SpectacleBase.cpp SpectEQ.cpp Spectacle-dsp.cpp
8CXX_MODULES+=Spectacle.cpp
8 9
9# where the chuck headers are10# where the chuck headers are
10CK_SRC_PATH?=../chuck/include/11CK_SRC_PATH?=../chuck/include/
Spectacle/objdefs.h+0−28
@@ -1,28 +0,0 @@
1#if !defined(__objdefs_h)
2#define __objdefs_h
3
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <math.h>
8#include <unistd.h>
9#include <errno.h>
10#include <assert.h>
11// #define NDEBUG /* define to disable asserts */
12
13#define DEFAULT_CONTROL_RATE 200
14
15#if (!defined(M_PI))
16 #define M_PI 3.14159265358979323846264338327
17#endif
18#if (!defined(PI))
19 #define PI M_PI
20#endif
21#if (!defined(TWO_PI))
22 #define TWO_PI (2.0 * M_PI)
23#endif
24#define ONE_OVER_TWO_PI (1.0 / TWO_PI)
25#define SQRT_TWO 1.4142135623730950488
26
27#endif
28
Spectacle/spectacle-help.ck+3−2
@@ -1,2 +1,3 @@
1adc => Spectacle spec => dac;1adc => Spectacle spect => dac;
2<<< 0.494 => spec.param >>>;2<<< 0.494 => spect.param >>>;
3minute => now;

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t