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
10// general includes10// general includes
11#include <stdio.h>11#include <stdio.h>
12#include <limits.h>12#include <limits.h>
13#include "DLineN.h"
13 14
14// declaration of chugin constructor15// declaration of chugin constructor
15CK_DLL_CTOR(spectacle_ctor);16CK_DLL_CTOR(spectacle_ctor);
@@ -20,12 +21,23 @@ CK_DLL_DTOR(spectacle_dtor); · 4 unmodified lines
20CK_DLL_MFUN(spectacle_setParam);21CK_DLL_MFUN(spectacle_setParam);
21CK_DLL_MFUN(spectacle_getParam);22CK_DLL_MFUN(spectacle_getParam);
22 23
23// for Chugins extending UGen, this is mono synthesis function for 1 sample24// for Chugins extending UGen, this is stereo synthesis for 8 samples
24CK_DLL_TICK(spectacle_tick);25CK_DLL_TICKF(spectacle_tick);
25 26
26// this is a special offset reserved for Chugin internal data27// this is a special offset reserved for Chugin internal data
27t_CKINT spectacle_data_offset = 0;28t_CKINT spectacle_data_offset = 0;
28 29
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
37typedef enum {
38 Hamming = 0,
39 Rectangle
40} WindowType;
29 41
30// class definition for internal Chugin data42// class definition for internal Chugin data
31// (note: this isn't strictly necessary, but serves as example43// (note: this isn't strictly necessary, but serves as example
@@ -33,139 +45,500 @@ t_CKINT spectacle_data_offset = 0; · 1 unmodified line
33class Spectacle45class Spectacle
34{46{
35public:47public:
36 // constructor48 // constructor
37 Spectacle( t_CKFLOAT fs)49 Spectacle( t_CKFLOAT fs)
38 {50 {
39 m_param = 0;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 }
40 }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 }
41 206
42 // for Chugins extending UGen207 double * resample_functable(double *table, int oldsize, int newsize)
43 SAMPLE tick( SAMPLE in )208 {
44 {209 double *newtable = new double [newsize];
45 // default: this passes whatever input is patched into Chugin210
46 return in;211 if (newsize == oldsize) { // straight copy
212 for (int i = 0; i < newsize; i++)
213 newtable[i] = table[i];
47 }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 }
48 232
49 // set parameter example233 /* ------------------------------------------------------------------ init -- */
50 float setParam( t_CKFLOAT p )234 WindowType getWindowType(double pval)
51 {235 {
52 m_param = p;236 int intval = int(pval);
53 return p;237 WindowType type;
238
239 switch (intval) {
240 case 1:
241 type = Rectangle;
242 break;
243 default:
244 type = Hamming;
54 }245 }
246 return type;
247 }
55 248
56 // get parameter example249 /* ---------------------------------------------------------------- shiftin -- */
57 float getParam() { return m_param; }250 void shiftin()
251 {
252 int _cursamp = currentFrame();
58 253
59private:254 /* Shift samples in <input> from right to left by <decimation>. */
60 // instance data255 for (int i = 0; i < window_len_minus_decimation; i++)
61 float m_param;256 input[i] = input[i + decimation];
62};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 }
63 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 }
64 300
65// query function: chuck calls this when loading the Chugin301/* ----------------------------------------------------------- leanconvert -- */
66// NOTE: developer will need to modify this function to302/* <fft_buf> is a spectrum in JGrfft format, i.e. it contains <half_fft_len> * 2
67// add additional functions to this Chugin303 real values, arranged in pairs of real and imaginary values, except for
68CK_DLL_QUERY( Spectacle )304 the first two values, which are the real parts of 0 and Nyquist frequencies.
69{305 Converts these into <half_fft_len> + 1 pairs of magnitude and phase values,
70 // hmm, don't change this...306 and stores them into the output array <anal_chans>.
71 QUERY->setname(QUERY, "Spectacle");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;
72 349
73 // begin the class definition350 for (int i = 0; i <= half_fft_len; i++) {
74 // can change the second argument to extend a different ChucK class351 real_index = amp_index = i << 1;
75 QUERY->begin_class(QUERY, "Spectacle", "UGen");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
76 379
77 // register the constructor (probably no need to change)380 /* -------------------------------------------------------------- shiftout -- */
78 QUERY->add_ctor(QUERY, spectacle_ctor);381 void shiftout()
79 // register the destructor (probably no need to change)382 {
80 QUERY->add_dtor(QUERY, spectacle_dtor);383 int _cursamp = currentFrame();
81 384
82 // for UGen's only: add tick function385 for (int i = 0; i < decimation; i++) {
83 QUERY->add_ugen_func(QUERY, spectacle_tick, NULL, 1, 1);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 }
84 407
85 // NOTE: if this is to be a UGen with more than 1 channel, 408 /* shift samples in <output> from right to left by <decimation> */
86 // e.g., a multichannel UGen -- will need to use add_ugen_funcf()409 for (int i = 0; i < window_len_minus_decimation; i++)
87 // and declare a tickf function using CK_DLL_TICKF410 output[i] = output[i + decimation];
411 for (int i = window_len_minus_decimation; i < window_len; i++)
412 output[i] = 0.0;
413 }
88 414
89 // example of adding setter method415
90 QUERY->add_mfun(QUERY, spectacle_setParam, "float", "param");416private:
91 // example of adding argument to the above method417 // instance data
92 QUERY->add_arg(QUERY, "float", "arg");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;
428protected:
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;
93 434
94 // example of adding getter method435};
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);
100 436
101 // end the class definition
102 // IMPORTANT: this MUST be called!
103 QUERY->end_class(QUERY);
104 437
105 // wasn't that a breeze?438// query function: chuck calls this when loading the Chugin
106 return TRUE;439// NOTE: developer will need to modify this function to
440// add additional functions to this Chugin
441CK_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;
107}480}
108 481
109 482
110// implementation for the constructor483// implementation for the constructor
111CK_DLL_CTOR(spectacle_ctor)484CK_DLL_CTOR(spectacle_ctor)
112{485{
113 // get the offset where we'll store our internal c++ class pointer486 // get the offset where we'll store our internal c++ class pointer
114 OBJ_MEMBER_INT(SELF, spectacle_data_offset) = 0;487 OBJ_MEMBER_INT(SELF, spectacle_data_offset) = 0;
115 488
116 // instantiate our internal c++ class representation489 // instantiate our internal c++ class representation
117 Spectacle * bcdata = new Spectacle(API->vm->get_srate());490 Spectacle * bcdata = new Spectacle(API->vm->get_srate());
118 491
119 // store the pointer in the ChucK object member492 // store the pointer in the ChucK object member
120 OBJ_MEMBER_INT(SELF, spectacle_data_offset) = (t_CKINT) bcdata;493 OBJ_MEMBER_INT(SELF, spectacle_data_offset) = (t_CKINT) bcdata;
121}494}
122 495
123 496
124// implementation for the destructor497// implementation for the destructor
125CK_DLL_DTOR(spectacle_dtor)498CK_DLL_DTOR(spectacle_dtor)
126{499{
127 // get our c++ class pointer500 // get our c++ class pointer
128 Spectacle * bcdata = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);501 Spectacle * bcdata = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);
129 // check it502 // check it
130 if( bcdata )503 if( bcdata )
131 {504 {
132 // clean up505 // clean up
133 delete bcdata;506 delete bcdata;
134 OBJ_MEMBER_INT(SELF, spectacle_data_offset) = 0;507 OBJ_MEMBER_INT(SELF, spectacle_data_offset) = 0;
135 bcdata = NULL;508 bcdata = NULL;
136 }509 }
137}510}
138 511
139 512
140// implementation for tick function513// implementation for tick function
141CK_DLL_TICK(spectacle_tick)514CK_DLL_TICKF(spectacle_tick)
142{515{
143 // get our c++ class pointer516 // get our c++ class pointer
144 Spectacle * c = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);517 Spectacle * c = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);
145 518
146 // invoke our tick function; store in the magical out variable519 // invoke our tick function; store in the magical out variable
147 if(c) *out = c->tick(in);520 if(c) c->tick(in,out, nframes);
148 521
149 // yes522 // yes
150 return TRUE;523 return TRUE;
151}524}
152 525
153 526
154// example implementation for setter527// example implementation for setter
155CK_DLL_MFUN(spectacle_setParam)528CK_DLL_MFUN(spectacle_setParam)
156{529{
157 // get our c++ class pointer530 // get our c++ class pointer
158 Spectacle * bcdata = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);531 Spectacle * bcdata = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);
159 // set the return value532 // set the return value
160 RETURN->v_float = bcdata->setParam(GET_NEXT_FLOAT(ARGS));533 RETURN->v_float = bcdata->setParam(GET_NEXT_FLOAT(ARGS));
161}534}
162 535
163 536
164// example implementation for getter537// example implementation for getter
165CK_DLL_MFUN(spectacle_getParam)538CK_DLL_MFUN(spectacle_getParam)
166{539{
167 // get our c++ class pointer540 // get our c++ class pointer
168 Spectacle * bcdata = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);541 Spectacle * bcdata = (Spectacle *) OBJ_MEMBER_INT(SELF, spectacle_data_offset);
169 // set the return value542 // set the return value
170 RETURN->v_float = bcdata->getParam();543 RETURN->v_float = bcdata->getParam();
171}544}

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