History

added SPECTACLE source

Joel Matthys committed 12 years ago

Commit 0ecc7271 parent ca2b47d12 files · +1521 −91Browse files

Showing the changes to Spectacle/Spectacle.cpp (1 of 12 files). Show all

Unified · Split

Spectacle/Spectacle.cpp+462−89
@@ -10,6 +10,7 @@ · 9 unmodified lines
1010 // general includes
1111 #include <stdio.h>
1212 #include <limits.h>
13+#include "DLineN.h"
1314
1415 // declaration of chugin constructor
1516 CK_DLL_CTOR(spectacle_ctor);
@@ -20,12 +21,23 @@ CK_DLL_DTOR(spectacle_dtor); · 4 unmodified lines
2021 CK_DLL_MFUN(spectacle_setParam);
2122 CK_DLL_MFUN(spectacle_getParam);
2223
23−// for Chugins extending UGen, this is mono synthesis function for 1 sample
24−CK_DLL_TICK(spectacle_tick);
24+// for Chugins extending UGen, this is stereo synthesis for 8 samples
25+CK_DLL_TICKF(spectacle_tick);
2526
2627 // this is a special offset reserved for Chugin internal data
2728 t_CKINT spectacle_data_offset = 0;
2829
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;
2941
3042 // class definition for internal Chugin data
3143 // (note: this isn't strictly necessary, but serves as example
@@ -33,139 +45,500 @@ t_CKINT spectacle_data_offset = 0; · 1 unmodified line
3345 class Spectacle
3446 {
3547 public:
36− // constructor
37− Spectacle( t_CKFLOAT fs)
38− {
39− m_param = 0;
48+ // constructor
49+ Spectacle( t_CKFLOAT fs)
50+ {
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;
82+ }
83+
84+ ~Spectacle()
85+ {
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;
94+ }
95+
96+ // 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);
116+ }
117+
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
132+ }
133+
134+ // set parameter example
135+ float setParam( t_CKFLOAT p )
136+ {
137+ m_param = p;
138+ return p;
139+ }
140+
141+ // get parameter example
142+ float getParam() { return m_param; }
143+
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+ }
40179 }
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+ }
41206
42− // for Chugins extending UGen
43− SAMPLE tick( SAMPLE in )
44− {
45− // default: this passes whatever input is patched into Chugin
46− return in;
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];
47214 }
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+ }
48232
49− // set parameter example
50− float setParam( t_CKFLOAT p )
51− {
52− m_param = p;
53− return p;
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;
54245 }
246+ return type;
247+ }
55248
56− // get parameter example
57− float getParam() { return m_param; }
249+ /* ---------------------------------------------------------------- shiftin -- */
250+ void shiftin()
251+ {
252+ int _cursamp = currentFrame();
58253
59−private:
60− // instance data
61− float m_param;
62−};
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+ }
63286
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+ }
64300
65−// query function: chuck calls this when loading the Chugin
66−// NOTE: developer will need to modify this function to
67−// add additional functions to this Chugin
68−CK_DLL_QUERY( Spectacle )
69−{
70− // hmm, don't change this...
71− QUERY->setname(QUERY, "Spectacle");
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;
72349
73− // begin the class definition
74− // can change the second argument to extend a different ChucK class
75− QUERY->begin_class(QUERY, "Spectacle", "UGen");
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+
76379
77− // register the constructor (probably no need to change)
78− QUERY->add_ctor(QUERY, spectacle_ctor);
79− // register the destructor (probably no need to change)
80− QUERY->add_dtor(QUERY, spectacle_dtor);
380+ /* -------------------------------------------------------------- shiftout -- */
381+ void shiftout()
382+ {
383+ int _cursamp = currentFrame();
81384
82− // for UGen's only: add tick function
83− QUERY->add_ugen_func(QUERY, spectacle_tick, NULL, 1, 1);
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+ }
84407
85− // NOTE: if this is to be a UGen with more than 1 channel,
86− // e.g., a multichannel UGen -- will need to use add_ugen_funcf()
87− // and declare a tickf function using CK_DLL_TICKF
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+ }
88414
89− // example of adding setter method
90− QUERY->add_mfun(QUERY, spectacle_setParam, "float", "param");
91− // example of adding argument to the above method
92− QUERY->add_arg(QUERY, "float", "arg");
415+
416+private:
417+ // instance data
418+ 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;
93434
94− // example of adding getter method
95− QUERY->add_mfun(QUERY, spectacle_getParam, "float", "param");
96−
97− // this reserves a variable in the ChucK internal class to store
98− // referene to the c++ class we defined above
99− spectacle_data_offset = QUERY->add_mvar(QUERY, "int", "@s_data", false);
435+};
100436
101− // end the class definition
102− // IMPORTANT: this MUST be called!
103− QUERY->end_class(QUERY);
104437
105− // wasn't that a breeze?
106− return TRUE;
438+// query function: chuck calls this when loading the Chugin
439+// NOTE: developer will need to modify this function to
440+// add additional functions to this Chugin
441+CK_DLL_QUERY( Spectacle )
442+{
443+ // hmm, don't change this...
444+ QUERY->setname(QUERY, "Spectacle");
445+
446+ // begin the class definition
447+ // can change the second argument to extend a different ChucK class
448+ QUERY->begin_class(QUERY, "Spectacle", "UGen");
449+
450+ // register the constructor (probably no need to change)
451+ QUERY->add_ctor(QUERY, spectacle_ctor);
452+ // register the destructor (probably no need to change)
453+ QUERY->add_dtor(QUERY, spectacle_dtor);
454+
455+ // for UGen's only: add tick function
456+ QUERY->add_ugen_funcf(QUERY, spectacle_tick, NULL, 8, 8);
457+
458+ // NOTE: if this is to be a UGen with more than 1 channel,
459+ // e.g., a multichannel UGen -- will need to use add_ugen_funcf()
460+ // and declare a tickf function using CK_DLL_TICKF
461+
462+ // example of adding setter method
463+ QUERY->add_mfun(QUERY, spectacle_setParam, "float", "param");
464+ // example of adding argument to the above method
465+ QUERY->add_arg(QUERY, "float", "arg");
466+
467+ // example of adding getter method
468+ QUERY->add_mfun(QUERY, spectacle_getParam, "float", "param");
469+
470+ // this reserves a variable in the ChucK internal class to store
471+ // referene to the c++ class we defined above
472+ spectacle_data_offset = QUERY->add_mvar(QUERY, "int", "@s_data", false);
473+
474+ // end the class definition
475+ // IMPORTANT: this MUST be called!
476+ QUERY->end_class(QUERY);
477+
478+ // wasn't that a breeze?
479+ return TRUE;
107480 }
108481
109482
110483 // implementation for the constructor
111484 CK_DLL_CTOR(spectacle_ctor)
112485 {
113− // get the offset where we'll store our internal c++ class pointer
114− OBJ_MEMBER_INT(SELF, spectacle_data_offset) = 0;
115−
116− // instantiate our internal c++ class representation
117− Spectacle * bcdata = new Spectacle(API->vm->get_srate());
118−
119− // store the pointer in the ChucK object member
120− OBJ_MEMBER_INT(SELF, spectacle_data_offset) = (t_CKINT) bcdata;
486+ // get the offset where we'll store our internal c++ class pointer
487+ OBJ_MEMBER_INT(SELF, spectacle_data_offset) = 0;
488+
489+ // instantiate our internal c++ class representation
490+ Spectacle * bcdata = new Spectacle(API->vm->get_srate());
491+
492+ // store the pointer in the ChucK object member
493+ OBJ_MEMBER_INT(SELF, spectacle_data_offset) = (t_CKINT) bcdata;
121494 }
122495
123496
124497 // implementation for the destructor
125498 CK_DLL_DTOR(spectacle_dtor)
126499 {
127− // get our c++ class pointer
128− Spectacle * bcdata = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);
129− // check it
130− if( bcdata )
500+ // get our c++ class pointer
501+ Spectacle * bcdata = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);
502+ // check it
503+ if( bcdata )
131504 {
132− // clean up
133− delete bcdata;
134− OBJ_MEMBER_INT(SELF, spectacle_data_offset) = 0;
135− bcdata = NULL;
505+ // clean up
506+ delete bcdata;
507+ OBJ_MEMBER_INT(SELF, spectacle_data_offset) = 0;
508+ bcdata = NULL;
136509 }
137510 }
138511
139512
140513 // implementation for tick function
141−CK_DLL_TICK(spectacle_tick)
514+CK_DLL_TICKF(spectacle_tick)
142515 {
143− // get our c++ class pointer
144− Spectacle * c = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);
145−
146− // invoke our tick function; store in the magical out variable
147− if(c) *out = c->tick(in);
148−
149− // yes
150− return TRUE;
516+ // get our c++ class pointer
517+ Spectacle * c = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);
518+
519+ // invoke our tick function; store in the magical out variable
520+ if(c) c->tick(in,out, nframes);
521+
522+ // yes
523+ return TRUE;
151524 }
152525
153526
154527 // example implementation for setter
155528 CK_DLL_MFUN(spectacle_setParam)
156529 {
157− // get our c++ class pointer
158− Spectacle * bcdata = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);
159− // set the return value
160− RETURN->v_float = bcdata->setParam(GET_NEXT_FLOAT(ARGS));
530+ // get our c++ class pointer
531+ Spectacle * bcdata = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);
532+ // set the return value
533+ RETURN->v_float = bcdata->setParam(GET_NEXT_FLOAT(ARGS));
161534 }
162535
163536
164537 // example implementation for getter
165538 CK_DLL_MFUN(spectacle_getParam)
166539 {
167− // get our c++ class pointer
168− Spectacle * bcdata = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);
169− // set the return value
170− RETURN->v_float = bcdata->getParam();
540+ // get our c++ class pointer
541+ Spectacle * bcdata = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);
542+ // set the return value
543+ RETURN->v_float = bcdata->getParam();
171544 }

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