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−
26−DLineN :: 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−
41−DLineN :: ~DLineN()
42−{
43− delete [] inputs;
44−}
45−
46−
47−void DLineN :: clear()
48−{
49− for (long i = 0; i < length; i++)
50− inputs[i] = 0.0;
51− lastOutput = 0.0;
52−}
53−
54−
55−void 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−*/
67−double 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−*/
82−void 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−*/
96−double 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−
28−class 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−
10−JGFilter :: JGFilter(double srate) : _sr(srate)
11−{
12−}
13−
14−
15−JGFilter :: ~JGFilter()
16−{
17−}
18−
19−
20−double 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−
11−class 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 -- */
11−SPECTACLE_BASE :: SPECTACLE_BASE()
12−{
13− first_time = 1;
14− iamp_branch = 0;
15− oamp_branch = 0;
16−}
17−
18−
19−/* ------------------------------------------------------- ~SPECTACLE_BASE -- */
20−SPECTACLE_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−*/
38−int 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−*/
118−double * 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 -- */
147−WindowType 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−
178−int 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 -- */
315−void 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−*/
358−void 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−*/
380−void 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 -- */
403−void 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−*/
418−void 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−*/
442−void 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 -- */
454−void 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 -- */
491−int 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−
31−typedef enum {
32− Hamming = 0,
33− Hanning,
34− Rectangle,
35− Triangle,
36− Blackman,
37− Kaiser
38−} WindowType;
39−
40−class SPECTACLE_BASE {
41−
42−private:
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;
54−protected:
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−
61−public:
62− SPECTACLE_BASE();
63− virtual ~SPECTACLE_BASE();
64− virtual int init(double p[], int n_args);
65− virtual int run();
66−protected:
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;
80−private:
81− WindowType getWindowType(double pval);
82−};
83−
84−inline 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 --
21+SpectEQ::SpectEQ()
22+ : _eqconst(0.0f), _eqtable(NULL)
23+{
24+}
25+
26+
27+// ----------------------------------------------------------------- ~SpectEQ --
28+SpectEQ::~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+
38+int 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 --
45+void SpectEQ::set_srate(float srate)
46+{
47+ SpectacleBase::set_srate(srate);
48+}
49+
50+
51+// -------------------------------------------------------------- set_eqtable --
52+void 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+
81+void 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 --
93+void 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+
18+class SpectEQ : public SpectacleBase {
19+
20+public:
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+
28+protected:
29+ virtual void modify_analysis(bool reading_input);
30+ virtual const char *instname() { return "specteq~"; }
31+
32+private:
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+
29+const float kMaxDelayTime = 20.0f;
30+#ifdef ANTI_DENORM
31+ const float kAntiDenormConstant = 1e-18f;
32+#endif
33+
34+
35+// ---------------------------------------------------------------- Spectacle_dsp --
36+Spectacle_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 --
49+Spectacle_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+
68+int 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;
99+bad_alloc:
100+ //error("%s: Not enough memory for delay lines.", instname());
101+ return -1;
102+}
103+
104+
105+// -------------------------------------------------------------------- clear --
106+void 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 --
117+void 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.
134+void 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 --
146+void 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 --
171+void 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 --
195+void 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.
215+bool 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+
239+void 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+
255+void 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 --
267+void 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+
22+class Spectacle_dsp : public SpectacleBase {
23+
24+public:
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+
39+protected:
40+ virtual void modify_analysis(bool reading_input);
41+ virtual const char *instname() { return "spectacle~"; }
42+
43+private:
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
77 #include "chuck_dl.h"
88 #include "chuck_def.h"
99
10+#include "Spectacle-dsp.h"
11+
1012 // general includes
1113 #include <stdio.h>
1214 #include <limits.h>
13−#include "DLineN.h"
1415
1516 // declaration of chugin constructor
1617 CK_DLL_CTOR(spectacle_ctor);
@@ -21,23 +22,42 @@ CK_DLL_DTOR(spectacle_dtor); · 4 unmodified lines
2122 CK_DLL_MFUN(spectacle_setParam);
2223 CK_DLL_MFUN(spectacle_getParam);
2324
24−// for Chugins extending UGen, this is stereo synthesis for 8 samples
25+CK_DLL_MFUN(spectacle_setFFTlen);
26+CK_DLL_MFUN(spectacle_getFFTlen);
27+
28+CK_DLL_MFUN(spectacle_setOverlap);
29+CK_DLL_MFUN(spectacle_getOverlap);
30+
31+CK_DLL_MFUN(spectacle_setMaxDelay);
32+CK_DLL_MFUN(spectacle_getMaxDelay);
33+
34+CK_DLL_MFUN(spectacle_clear);
35+CK_DLL_MFUN(spectacle_hold);
36+CK_DLL_MFUN(spectacle_posteq);
37+
38+CK_DLL_MFUN(spectacle_setMinFreq);
39+CK_DLL_MFUN(spectacle_getMinFreq);
40+
41+CK_DLL_MFUN(spectacle_setMaxFreq);
42+CK_DLL_MFUN(spectacle_getMaxFreq);
43+
44+
45+// for Chugins extending UGen, this is mono synthesis function for 1 sample
2546 CK_DLL_TICKF(spectacle_tick);
2647
2748 // this is a special offset reserved for Chugin internal data
2849 t_CKINT spectacle_data_offset = 0;
2950
30−#define MAXFFTLEN 4096
31−#define MAXWINDOWLEN MAXFFTLEN * 8
32−#define MINOVERLAP 0.25
33−#define MAXOVERLAP 64.0
34−#define MAXDELTIME 20.0
35−#define FFTLEN 1024
36−
37−typedef enum {
38− Hamming = 0,
39− Rectangle
40−} WindowType;
51+#define CONFORM_INPUT
52+#define CLIP(a, lo, hi) ( (a)>(lo)?( (a)<(hi)?(a):(hi) ):(lo) )
53+
54+#define kDefaultFFTLen 1024
55+#define kDefaultWindowLen 2048
56+#define kDefaultOverlap 2
57+#define kDefaultMaxDelTime 6.0f
58+#define kMinMaxDelTime 0.1f
59+#define kMaxMaxDelTime 20.0f
60+#define kMaxTableLen 512
4161
4262 // class definition for internal Chugin data
4363 // (note: this isn't strictly necessary, but serves as example
@@ -48,87 +68,52 @@ public: · 4 unmodified lines
4868 // constructor
4969 Spectacle( t_CKFLOAT fs)
5070 {
51− /* create segmented input buffer */
52− int num_segments = (window_len / FFTLEN) + 2;
53− int extrasamps = FFTLEN - framesToRun();
54− if (extrasamps)
55− num_segments++;
56− int inbuf_samps = num_segments * FFTLEN * inputChannels();
57− inbuf = new float [inbuf_samps];
58− for (int i = 0; i < inbuf_samps; i++)
59− inbuf[i] = 0.0;
60−
61− /* Read ptr chases write ptr by <window_len>. Set read ptr to a position
62− <window_len> frames from right end of input buffer. Set write ptr to
63− beginning of buffer, or, if framesToRun() is not the same as the RTcmix
64− buffer size, set it so that the next run invocation after this one
65− will find the write ptr at the start of the second buffer segment.
66− */
67− inbuf_readptr = inbuf + (inbuf_samps - (window_len * inputChannels()));
68− inbuf_startptr = inbuf + (extrasamps * inputChannels());
69− inbuf_writeptr = inbuf_startptr;
70− inbuf_endptr = inbuf + inbuf_samps;
71−
72− int outbuf_samps = num_segments * FFTLEN * outputChannels();
73− outbuf = new float [outbuf_samps];
74− for (int i = 0; i < outbuf_samps; i++)
75− outbuf[i] = 0.0;
76−
77− outbuf_readptr = outbuf + (outbuf_samps
78− - (framesToRun() * outputChannels()));
79− outbuf_writeptr = outbuf_readptr;
80− outbuf_endptr = outbuf + outbuf_samps;
81− first_time = 0;
71+ m_param = 0;
72+ fftlen = kDefaultFFTLen;
73+ windowlen = kDefaultWindowLen;
74+ overlap = kDefaultOverlap;
75+ maxdeltime = kDefaultMaxDelTime;
76+ maxdeltime = CLIP(maxdeltime, kMinMaxDelTime, kMaxMaxDelTime);
77+ srate = fs;
78+ spectdelay = NULL;
79+ float *del = dttable;
80+ float *eq = eqtable;
81+ float *feed = fbtable;
82+ int *bm = eqbinmap;
83+ int *dbm = delaybinmap;
84+ for (int i = 0; i < kMaxTableLen; i++)
85+ {
86+ *del++ = 0.0;
87+ *eq++ = 0.0;
88+ *feed++ = 0.0;
89+ *bm++ = 0;
90+ *dbm++ = 0;
91+ }
92+ dttablen = eqtablen = fbtablen = 0;
93+ eqbinmaplen = delaybinmaplen = 0;
94+ dminfreq = dmaxfreq = minfreq = maxfreq = 0.0;
95+ posteq = hold = false;
96+
97+ spectdelay = new Spectacle_dsp();
98+ spectdelay->init(fftlen, windowlen, overlap, srate, maxdeltime);
8299 }
83100
84101 ~Spectacle()
85102 {
86− delete [] anal_window;
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;
103+ delete spectdelay;
94104 }
95−
105+
96106 // for Chugins extending UGen
97− void tick( SAMPLE *in, SAMPLE *out, int nframes )
98− {
99− memset(out, 0, sizeof(SAMPLE)*nframes);
100−
101− for (int i=0; i < nframes; i++)
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);
107+ void tick( SAMPLE* in, SAMPLE* out, int nframes)
108+ {
109+ memset (out, 0, sizeof(SAMPLE)*nframes);
110+ // process a block of samples
111+ spectdelay->run(in, out, nframes);
116112 }
117113
118− if (currentFrame() < input_end_frame) {
119− inbuf_writeptr += insamps;
120− if (inbuf_writeptr >= inbuf_endptr)
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
114+ void clear()
115+ {
116+ spectdelay->clear();
132117 }
133118
134119 // set parameter example
@@ -141,297 +126,21 @@ public: · 6 unmodified lines
141126 // get parameter example
142127 float getParam() { return m_param; }
143128
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−
416129 private:
417130 // instance data
418131 float m_param;
419− float *anal_window, *synth_window, *input, *output, *fft_buf, *drybuf;
420− float *inbuf, *inbuf_startptr, *inbuf_readptr, *inbuf_writeptr,
421− *inbuf_endptr;
422− float *outbuf, *outbuf_startptr, *outbuf_readptr, *outbuf_writeptr,
423− *outbuf_endptr;
424− float iamptabs[2], oamptabs[2];
425− double *iamparray, *oamparray;
426− DLineN *dry_delay;
427− WindowType window_type;
428−protected:
429− int inchan, skip, total_insamps, first_time, int_overlap;
430− int fft_len, window_len, decimation, window_len_minus_decimation,
431− half_fft_len, latency, input_end_frame;
432− float wetdry, pctleft, inputdur, ringdur, fund_anal_freq;
433− float *anal_chans;
434−
132+
133+ int fftlen, windowlen, overlap;
134+ float srate, maxdeltime;
135+ Spectacle_dsp *spectdelay;
136+ float eqtable[kMaxTableLen];
137+ float dttable[kMaxTableLen];
138+ float fbtable[kMaxTableLen];
139+ int eqbinmap[kMaxTableLen];
140+ int delaybinmap[kMaxTableLen];
141+ int eqtablen, dttablen, fbtablen, eqbinmaplen, delaybinmaplen;
142+ float minfreq, maxfreq, dminfreq, dmaxfreq;
143+ bool hold, posteq;
435144 };
436145
437146
@@ -518,7 +227,7 @@ CK_DLL_TICKF(spectacle_tick) · 80 unmodified lines
518227
519228 // invoke our tick function; store in the magical out variable
520229 if(c) c->tick(in,out, nframes);
521−
230+
522231 // yes
523232 return TRUE;
524233 }
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+
39+const int kMaxWindowLen = kMaxFFTLen * 8;
40+const int kMinOverlap = 1;
41+const int kMaxOverlap = 64;
42+
43+
44+// ------------------------------------------------------------ SpectacleBase --
45+SpectacleBase::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 --
66+SpectacleBase::~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 --
80+int 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.
132+void 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 --
149+int 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 --
250+void 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+
265+void 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 --
295+void 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+
327+void 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 --
336+void 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+
355+bool 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+
383+void 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[] -----------------------------");
582+for (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
598+void 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 --
628+void 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+
21+const 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+
39+class 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>.
141+inline int SpectacleBase::closest_bin(const float freq)
142+{
143+ return int((freq / _fund_anal_freq) + 0.5f);
144+}
145+
146+
147+inline 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+
154+inline 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−*/
7−static 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−*/
34−void 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−*/
103−void 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
15−extern "C" {
16−#endif /* __cplusplus */
17−
18−void JGrfft(float x[], int N, int forward);
19−void 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+
43+FFTReal::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+
65+FFTReal::~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+
86+void 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+
275+void 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+
473+void 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+
500+FFTReal::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+
531+FFTReal::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+
547+FFTReal::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+
578+FFTReal::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+
32+class FFTReal
33+{
34+
35+/*\\\ PUBLIC \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
36+
37+public:
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+
53+private:
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+
94+private:
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+
7+Obucket::Obucket(int len, ProcessFunction callback, void *context)
8+ : _len(len), _index(0), _callback(callback), _context(context)
9+{
10+ _buf = new float [len];
11+}
12+
13+Obucket::~Obucket()
14+{
15+ delete [] _buf;
16+}
17+
18+void 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+
28+void 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+
14+class Obucket {
15+public:
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+
24+private:
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+
36+inline 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+
12+Odelay::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+
26+Odelay::~Odelay()
27+{
28+ free(_dline);
29+}
30+
31+void Odelay::clear()
32+{
33+ for (long i = 0; i < _len; i++)
34+ _dline[i] = 0.0;
35+ _lastout = 0.0;
36+}
37+
38+void Odelay::fill(double val)
39+{
40+ for (long i = 0; i < _len; i++)
41+ _dline[i] = val;
42+ _lastout = val;
43+}
44+
45+void Odelay::putsamp(float samp)
46+{
47+ _dline[_inpoint++] = samp;
48+ if (_inpoint == _len)
49+ _inpoint = 0;
50+}
51+
52+float 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+
68+void 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+
81+float 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+
92+static 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+
114+int 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+
142+float 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+
17+class Odelay
18+{
19+public:
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+
77+protected:
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+
12+Offt::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+
31+Offt::~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+
50+void 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+
68+void 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+
86+void 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+
116+void 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+
151+void 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
29+class FFTReal;
30+#endif
31+
32+class Offt {
33+public:
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+
45+private:
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+
7+Ooscil::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
16+void Ooscil::setsrate(float srate)
17+{
18+ _lendivSR = (double) _length / srate;
19+}
20+
21+float 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+
34+float 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+
14+class Ooscil
15+{
16+ double _si, _phase, _lendivSR;
17+ float *_array;
18+ int _length;
19+
20+public:
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+
14+inline double dmax(double x, double y) { return (x > y) ? x : y; }
15+inline double dmin(double x, double y) { return (x < y) ? x : y; }
16+
17+
18+// Base class -----------------------------------------------------------------
19+
20+RandGen::~RandGen() {}
21+
22+// Return a random number in range [0, 1]
23+inline 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]
31+inline 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+
40+LinearRandom::LinearRandom(double min, double max, int seed)
41+ : RandGen(min, max, seed) {}
42+
43+LinearRandom::~LinearRandom() {}
44+
45+double LinearRandom::value()
46+{
47+ return fitrange(rawvalue());
48+}
49+
50+
51+// Low-weighted linear distribution subclass ----------------------------------
52+
53+LowLinearRandom::LowLinearRandom(double min, double max, int seed)
54+ : RandGen(min, max, seed) {}
55+
56+LowLinearRandom::~LowLinearRandom() {}
57+
58+double 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+
68+HighLinearRandom::HighLinearRandom(double min, double max, int seed)
69+ : RandGen(min, max, seed) {}
70+
71+HighLinearRandom::~HighLinearRandom() {}
72+
73+double 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+
83+TriangleRandom::TriangleRandom(double min, double max, int seed)
84+ : RandGen(min, max, seed) {}
85+
86+TriangleRandom::~TriangleRandom() {}
87+
88+double 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+
99+GaussianRandom::GaussianRandom(double min, double max, int seed)
100+ : RandGen(min, max, seed) {}
101+
102+GaussianRandom::~GaussianRandom() {}
103+
104+double 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+
126+CauchyRandom::CauchyRandom(double min, double max, int seed)
127+ : RandGen(min, max, seed) {}
128+
129+CauchyRandom::~CauchyRandom() {}
130+
131+double 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+
165+ProbRandom::ProbRandom(double min, double max, double mid, double tight,
166+ int seed)
167+ : RandGen(min, max, mid, tight, seed) {}
168+
169+ProbRandom::~ProbRandom() {}
170+
171+double 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+
197+RandomOscil::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+
204+RandomOscil::~RandomOscil()
205+{
206+ delete _gen;
207+}
208+
209+void RandomOscil::setfreq(double freq)
210+{
211+ _limit = (int) (_srate / freq);
212+}
213+
214+double 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+
13+enum {
14+ kLinearRandom = 0,
15+ kLowLinearRandom,
16+ kHighLinearRandom,
17+ kTriangleRandom,
18+ kGaussianRandom,
19+ kCauchyRandom,
20+ kProbRandom
21+};
22+
23+// Base class
24+
25+class RandGen {
26+public:
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; }
38+protected:
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; }
45+private:
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+
56+class LinearRandom : public RandGen {
57+public:
58+ LinearRandom(double min = 0.0, double max = 1.0, int seed = 1);
59+ virtual double value();
60+protected:
61+ virtual ~LinearRandom();
62+};
63+
64+class LowLinearRandom : public RandGen {
65+public:
66+ LowLinearRandom(double min = 0.0, double max = 1.0, int seed = 1);
67+ virtual double value();
68+protected:
69+ virtual ~LowLinearRandom();
70+};
71+
72+class HighLinearRandom : public RandGen {
73+public:
74+ HighLinearRandom(double min = 0.0, double max = 1.0, int seed = 1);
75+ virtual double value();
76+protected:
77+ virtual ~HighLinearRandom();
78+};
79+
80+class TriangleRandom : public RandGen {
81+public:
82+ TriangleRandom(double min = 0.0, double max = 1.0, int seed = 1);
83+ virtual double value();
84+protected:
85+ virtual ~TriangleRandom();
86+};
87+
88+class GaussianRandom : public RandGen {
89+public:
90+ GaussianRandom(double min = 0.0, double max = 1.0, int seed = 1);
91+ virtual double value();
92+protected:
93+ virtual ~GaussianRandom();
94+};
95+
96+class CauchyRandom : public RandGen {
97+public:
98+ CauchyRandom(double min = 0.0, double max = 1.0, int seed = 1);
99+ virtual double value();
100+protected:
101+ virtual ~CauchyRandom();
102+};
103+
104+class ProbRandom : public RandGen {
105+public:
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();
109+protected:
110+ virtual ~ProbRandom();
111+};
112+
113+
114+class RandomOscil
115+{
116+public:
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();
127+private:
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
5+extern "C" {
6+#endif
7+
8+double ampdb(float);
9+double 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
33 CHUGIN_NAME=Spectacle
44
55 # all of the c/cpp files that compose this chugin
6−C_MODULES=fft.c
7−CXX_MODULES=JGFilter.cpp DLineN.cpp Spectacle.cpp
6+CXX_MODULES=genlib/FFTReal.cpp genlib/Obucket.cpp genlib/Odelay.cpp genlib/Offt.cpp genlib/Ooscil.cpp genlib/RandGen.cpp
7+CXX_MODULES+=SpectacleBase.cpp SpectEQ.cpp Spectacle-dsp.cpp
8+CXX_MODULES+=Spectacle.cpp
89
910 # where the chuck headers are
1011 CK_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 @@
1−adc => Spectacle spec => dac;
2−<<< 0.494 => spec.param >>>;
1+adc => Spectacle spect => dac;
2+<<< 0.494 => spect.param >>>;
3+minute => 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