History

implemented basic DSP for Spectacle

Joel Matthys committed 12 years ago

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

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

Unified · Split

Spectacle/Spectacle.cpp+84−375
@@ -7,10 +7,11 @@ · 6 unmodified lines
7#include "chuck_dl.h"7#include "chuck_dl.h"
8#include "chuck_def.h"8#include "chuck_def.h"
9 9
10#include "Spectacle-dsp.h"
11
10// general includes12// general includes
11#include <stdio.h>13#include <stdio.h>
12#include <limits.h>14#include <limits.h>
13#include "DLineN.h"
14 15
15// declaration of chugin constructor16// declaration of chugin constructor
16CK_DLL_CTOR(spectacle_ctor);17CK_DLL_CTOR(spectacle_ctor);
@@ -21,23 +22,42 @@ CK_DLL_DTOR(spectacle_dtor); · 4 unmodified lines
21CK_DLL_MFUN(spectacle_setParam);22CK_DLL_MFUN(spectacle_setParam);
22CK_DLL_MFUN(spectacle_getParam);23CK_DLL_MFUN(spectacle_getParam);
23 24
24// for Chugins extending UGen, this is stereo synthesis for 8 samples25CK_DLL_MFUN(spectacle_setFFTlen);
26CK_DLL_MFUN(spectacle_getFFTlen);
27
28CK_DLL_MFUN(spectacle_setOverlap);
29CK_DLL_MFUN(spectacle_getOverlap);
30
31CK_DLL_MFUN(spectacle_setMaxDelay);
32CK_DLL_MFUN(spectacle_getMaxDelay);
33
34CK_DLL_MFUN(spectacle_clear);
35CK_DLL_MFUN(spectacle_hold);
36CK_DLL_MFUN(spectacle_posteq);
37
38CK_DLL_MFUN(spectacle_setMinFreq);
39CK_DLL_MFUN(spectacle_getMinFreq);
40
41CK_DLL_MFUN(spectacle_setMaxFreq);
42CK_DLL_MFUN(spectacle_getMaxFreq);
43
44
45// for Chugins extending UGen, this is mono synthesis function for 1 sample
25CK_DLL_TICKF(spectacle_tick);46CK_DLL_TICKF(spectacle_tick);
26 47
27// this is a special offset reserved for Chugin internal data48// this is a special offset reserved for Chugin internal data
28t_CKINT spectacle_data_offset = 0;49t_CKINT spectacle_data_offset = 0;
29 50
30#define MAXFFTLEN 409651#define CONFORM_INPUT
31#define MAXWINDOWLEN MAXFFTLEN * 852#define CLIP(a, lo, hi) ( (a)>(lo)?( (a)<(hi)?(a):(hi) ):(lo) )
32#define MINOVERLAP 0.2553
33#define MAXOVERLAP 64.054#define kDefaultFFTLen 1024
34#define MAXDELTIME 20.055#define kDefaultWindowLen 2048
35#define FFTLEN 102456#define kDefaultOverlap 2
36 57#define kDefaultMaxDelTime 6.0f
37typedef enum {58#define kMinMaxDelTime 0.1f
38 Hamming = 0,59#define kMaxMaxDelTime 20.0f
39 Rectangle60#define kMaxTableLen 512
40} WindowType;
41 61
42// class definition for internal Chugin data62// class definition for internal Chugin data
43// (note: this isn't strictly necessary, but serves as example63// (note: this isn't strictly necessary, but serves as example
@@ -48,87 +68,52 @@ public: · 4 unmodified lines
48 // constructor68 // constructor
49 Spectacle( t_CKFLOAT fs)69 Spectacle( t_CKFLOAT fs)
50 {70 {
51 /* create segmented input buffer */71 m_param = 0;
52 int num_segments = (window_len / FFTLEN) + 2;72 fftlen = kDefaultFFTLen;
53 int extrasamps = FFTLEN - framesToRun();73 windowlen = kDefaultWindowLen;
54 if (extrasamps)74 overlap = kDefaultOverlap;
55 num_segments++;75 maxdeltime = kDefaultMaxDelTime;
56 int inbuf_samps = num_segments * FFTLEN * inputChannels();76 maxdeltime = CLIP(maxdeltime, kMinMaxDelTime, kMaxMaxDelTime);
57 inbuf = new float [inbuf_samps];77 srate = fs;
58 for (int i = 0; i < inbuf_samps; i++)78 spectdelay = NULL;
59 inbuf[i] = 0.0;79 float *del = dttable;
60 80 float *eq = eqtable;
61 /* Read ptr chases write ptr by <window_len>. Set read ptr to a position81 float *feed = fbtable;
62 <window_len> frames from right end of input buffer. Set write ptr to82 int *bm = eqbinmap;
63 beginning of buffer, or, if framesToRun() is not the same as the RTcmix83 int *dbm = delaybinmap;
64 buffer size, set it so that the next run invocation after this one84 for (int i = 0; i < kMaxTableLen; i++)
65 will find the write ptr at the start of the second buffer segment.85 {
66 */86 *del++ = 0.0;
67 inbuf_readptr = inbuf + (inbuf_samps - (window_len * inputChannels()));87 *eq++ = 0.0;
68 inbuf_startptr = inbuf + (extrasamps * inputChannels());88 *feed++ = 0.0;
69 inbuf_writeptr = inbuf_startptr;89 *bm++ = 0;
70 inbuf_endptr = inbuf + inbuf_samps;90 *dbm++ = 0;
71 91 }
72 int outbuf_samps = num_segments * FFTLEN * outputChannels();92 dttablen = eqtablen = fbtablen = 0;
73 outbuf = new float [outbuf_samps];93 eqbinmaplen = delaybinmaplen = 0;
74 for (int i = 0; i < outbuf_samps; i++)94 dminfreq = dmaxfreq = minfreq = maxfreq = 0.0;
75 outbuf[i] = 0.0;95 posteq = hold = false;
76 96
77 outbuf_readptr = outbuf + (outbuf_samps97 spectdelay = new Spectacle_dsp();
78 - (framesToRun() * outputChannels()));98 spectdelay->init(fftlen, windowlen, overlap, srate, maxdeltime);
79 outbuf_writeptr = outbuf_readptr;
80 outbuf_endptr = outbuf + outbuf_samps;
81 first_time = 0;
82 }99 }
83 100
84 ~Spectacle()101 ~Spectacle()
85 {102 {
86 delete [] anal_window;103 delete spectdelay;
87 delete [] synth_window;
88 delete [] fft_buf;
89 delete [] anal_chans;
90 delete [] drybuf;
91 delete [] inbuf;
92 delete [] outbuf;
93 delete dry_delay;
94 }104 }
95 105
96 // for Chugins extending UGen106 // for Chugins extending UGen
97 void tick( SAMPLE *in, SAMPLE *out, int nframes )107 void tick( SAMPLE* in, SAMPLE* out, int nframes)
98 { 108 {
99 memset(out, 0, sizeof(SAMPLE)*nframes);109 memset (out, 0, sizeof(SAMPLE)*nframes);
100 110 // process a block of samples
101 for (int i=0; i < nframes; i++)111 spectdelay->run(in, out, nframes);
102 {
103 shiftin();
104 fold();
105 JGrfft(fft_buf, half_fft_len, FORWARD);
106 leanconvert();
107 }
108 flush_dry_delay();
109 modify_analysis();
110 leanunconvert();
111 JGrfft(fft_buf, half_fft_len, INVERSE);
112 overlapadd(currentFrame());
113 shiftout();
114
115 increment(decimation);
116 }112 }
117 113
118 if (currentFrame() < input_end_frame) {114 void clear()
119 inbuf_writeptr += insamps;115 {
120 if (inbuf_writeptr >= inbuf_endptr)116 spectdelay->clear();
121 inbuf_writeptr = inbuf;
122 }
123
124 rtbaddout(outbuf_readptr, framesToRun());
125 outbuf_readptr += framesToRun() * outputChannels();
126 if (outbuf_readptr >= outbuf_endptr)
127 outbuf_readptr = outbuf;
128
129 return framesToRun();
130
131 // sythesis function here
132 }117 }
133 118
134 // set parameter example119 // set parameter example
@@ -141,297 +126,21 @@ public: · 6 unmodified lines
141 // get parameter example126 // get parameter example
142 float getParam() { return m_param; }127 float getParam() { return m_param; }
143 128
144 /* ---------------------------------------------------------- make_windows -- */
145/* Make balanced pair of analysis and synthesis windows.
146*/
147 int make_windows()
148 {
149 switch (window_type) {
150 case Hamming:
151 for (int i = 0; i < window_len; i++)
152 anal_window[i] = synth_window[i] = 0.54 - 0.46
153 * cos(TWO_PI * i / (window_len - 1));
154 break;
155 case Rectangle:
156 //FIXME: is this right?
157 for (int i = 0; i < window_len; i++)
158 anal_window[i] = synth_window[i] = 1.0;
159 break;
160 }
161
162 /* When window_len > fft_len, also apply interpolating (sinc) windows to
163 ensure that window are 0 at increments of fft_len away from the center
164 of the analysis window and of decimation away from the center of the
165 synthesis window.
166 */
167 if (window_len > fft_len) {
168 float x;
169
170 /* Take care to create symmetrical windows. */
171 x = -(window_len - 1) / 2.;
172 for (int i = 0; i < window_len; i++, x += 1.)
173 if (x != 0.) {
174 anal_window[i] *= fft_len * sin(PI * x / fft_len) / (PI * x);
175 if (decimation)
176 synth_window[i] *= decimation * sin(PI * x / decimation)
177 / (PI * x);
178 }
179 }
180
181 /* Normalize windows for unity gain across unmodified
182 analysis-synthesis procedure.
183 */
184 float sum = 0.0;
185 for (int i = 0; i < window_len; i++)
186 sum += anal_window[i];
187
188 for (int i = 0; i < window_len; i++) {
189 float afac = 2. / sum;
190 float sfac = window_len > fft_len ? 1. / afac : afac;
191 anal_window[i] *= afac;
192 synth_window[i] *= sfac;
193 }
194
195 if (window_len <= fft_len && decimation) {
196 sum = 0.0;
197 for (int i = 0; i < window_len; i += decimation)
198 sum += synth_window[i] * synth_window[i];
199 sum = 1.0 / sum;
200 for (int i = 0; i < window_len; i++)
201 synth_window[i] *= sum;
202 }
203
204 return 0;
205 }
206
207 double * resample_functable(double *table, int oldsize, int newsize)
208 {
209 double *newtable = new double [newsize];
210
211 if (newsize == oldsize) { // straight copy
212 for (int i = 0; i < newsize; i++)
213 newtable[i] = table[i];
214 }
215 else {
216 double incr = (double) oldsize / (double) newsize;
217 double f = 0.0;
218 for (int i = 0; i < newsize; i++) {
219 int n = (int) f;
220 double frac = f - (double) n;
221 double diff = 0.0;
222 if (frac) {
223 double next = (n + 1 < oldsize) ? table[n + 1] : table[oldsize - 1];
224 diff = next - table[n];
225 }
226 newtable[i] = table[n] + (diff * frac);
227 f += incr;
228 }
229 }
230 return newtable;
231 }
232
233 /* ------------------------------------------------------------------ init -- */
234 WindowType getWindowType(double pval)
235 {
236 int intval = int(pval);
237 WindowType type;
238
239 switch (intval) {
240 case 1:
241 type = Rectangle;
242 break;
243 default:
244 type = Hamming;
245 }
246 return type;
247 }
248
249 /* ---------------------------------------------------------------- shiftin -- */
250 void shiftin()
251 {
252 int _cursamp = currentFrame();
253
254 /* Shift samples in <input> from right to left by <decimation>. */
255 for (int i = 0; i < window_len_minus_decimation; i++)
256 input[i] = input[i + decimation];
257
258 /* Copy <decimation> samples from <inbuf> to right end of <input> and
259 to left end of <drybuf>.
260 */
261 int j = 0;
262 for (int i = window_len_minus_decimation; i < window_len; i++, j++)
263 {
264 if (--iamp_branch <= 0)
265 {
266 /* Since inbuf_readptr trails inbuf_writeptr, we delay the start of
267 the envelope until we reach actual input rather than zero padding.
268 */
269 if (iamparray && _cursamp >= window_len)
270 iamp = tablei(_cursamp - window_len, iamparray, iamptabs);
271 iamp_branch = skip;
272 }
273 float sig = inbuf_readptr[inchan] * iamp;
274 drybuf[j] = dry_delay->tick(sig);
275 input[i] = sig;
276 inbuf_readptr += inputChannels();
277 if (inbuf_readptr >= inbuf_endptr)
278 {
279 inbuf_readptr = inbuf_startptr;
280 /* <inbuf_startptr> might not == <inbuf> in first call to run(). */
281 inbuf_startptr = inbuf;
282 }
283 _cursamp++;
284 }
285 }
286
287 void fold(int n)
288 {
289 for (int i = 0; i < fft_len; i++)
290 fft_buf[i] = 0.0;
291
292 n %= fft_len;
293
294 for (int i = 0; i < window_len; i++) {
295 fft_buf[n] += input[i] * anal_window[i];
296 if (++n == fft_len)
297 n = 0;
298 }
299 }
300
301/* ----------------------------------------------------------- leanconvert -- */
302/* <fft_buf> is a spectrum in JGrfft format, i.e. it contains <half_fft_len> * 2
303 real values, arranged in pairs of real and imaginary values, except for
304 the first two values, which are the real parts of 0 and Nyquist frequencies.
305 Converts these into <half_fft_len> + 1 pairs of magnitude and phase values,
306 and stores them into the output array <anal_chans>.
307*/
308 void leanconvert()
309 {
310 int real_index, imag_index, amp_index, phase_index;
311 float a, b;
312
313 for (int i = 0; i <= half_fft_len; i++) {
314 real_index = amp_index = i << 1;
315 imag_index = phase_index = real_index + 1;
316 if (i == half_fft_len) {
317 a = fft_buf[1];
318 b = 0.0;
319 }
320 else {
321 a = fft_buf[real_index];
322 b = (i == 0) ? 0.0 : fft_buf[imag_index];
323 }
324 anal_chans[amp_index] = hypot(a, b);
325 anal_chans[phase_index] = -atan2(b, a);
326 }
327 }
328
329 /* ------------------------------------------------------- flush_dry_delay -- */
330 void flush_dry_delay()
331 {
332 if (currentFrame() < input_end_frame + window_len_minus_decimation) {
333 for (int i = 0; i < decimation; i++)
334 drybuf[i] = dry_delay->tick(0.0);
335 }
336 }
337
338
339 /* --------------------------------------------------------- leanunconvert -- */
340 /* leanunconvert essentially undoes what leanconvert does, i.e., it turns
341 <half_fft_len> + 1 pairs of amplitude and phase values in <anal_chans>
342 into <half_fft_len> pairs of complex spectrum data (in JGrfft format) in
343 output array <fft_buf>.
344 */
345 void leanunconvert()
346 {
347 int real_index, imag_index, amp_index, phase_index;
348 float mag, phase;
349
350 for (int i = 0; i <= half_fft_len; i++) {
351 real_index = amp_index = i << 1;
352 imag_index = phase_index = real_index + 1;
353 if (i == half_fft_len)
354 real_index = 1;
355 mag = anal_chans[amp_index];
356 phase = anal_chans[phase_index];
357 fft_buf[real_index] = mag * cos(phase);
358 if (i != half_fft_len)
359 fft_buf[imag_index] = -mag * sin(phase);
360 }
361 }
362
363
364 /* ------------------------------------------------------------ overlapadd -- */
365 /* <fft_buf> is a folded spectrum of length <fft_len>. <output> and
366 <synth_window> are of length <window_len>. Overlap-add windowed,
367 unrotated, unfolded <fft_buf> data into <output>.
368 */
369 void overlapadd(int n)
370 {
371 n %= fft_len;
372 for (int i = 0; i < window_len; i++) {
373 output[i] += fft_buf[n] * synth_window[i];
374 if (++n == fft_len)
375 n = 0;
376 }
377 }
378
379
380 /* -------------------------------------------------------------- shiftout -- */
381 void shiftout()
382 {
383 int _cursamp = currentFrame();
384
385 for (int i = 0; i < decimation; i++) {
386 if (--oamp_branch <= 0) {
387 if (oamparray && (_cursamp >= latency))
388 oamp = tablei(_cursamp - latency, oamparray, oamptabs) * amp;
389 oamp_branch = skip;
390 }
391 float sig;
392 if (_cursamp < input_end_frame)
393 sig = ((output[i] * wetdry) + ((1.0 - wetdry) * drybuf[i])) * oamp;
394 else
395 sig = output[i] * wetdry * oamp;
396 if (outputChannels() == 2) {
397 *outbuf_writeptr++ = sig * pctleft;
398 *outbuf_writeptr++ = sig * (1.0 - pctleft);
399 }
400 else
401 *outbuf_writeptr++ = sig;
402 if (outbuf_writeptr >= outbuf_endptr) {
403 outbuf_writeptr = outbuf;
404 }
405 _cursamp++;
406 }
407
408 /* shift samples in <output> from right to left by <decimation> */
409 for (int i = 0; i < window_len_minus_decimation; i++)
410 output[i] = output[i + decimation];
411 for (int i = window_len_minus_decimation; i < window_len; i++)
412 output[i] = 0.0;
413 }
414
415
416private:129private:
417 // instance data130 // instance data
418 float m_param;131 float m_param;
419 float *anal_window, *synth_window, *input, *output, *fft_buf, *drybuf;132
420 float *inbuf, *inbuf_startptr, *inbuf_readptr, *inbuf_writeptr,133 int fftlen, windowlen, overlap;
421 *inbuf_endptr;134 float srate, maxdeltime;
422 float *outbuf, *outbuf_startptr, *outbuf_readptr, *outbuf_writeptr,135 Spectacle_dsp *spectdelay;
423 *outbuf_endptr;136 float eqtable[kMaxTableLen];
424 float iamptabs[2], oamptabs[2];137 float dttable[kMaxTableLen];
425 double *iamparray, *oamparray;138 float fbtable[kMaxTableLen];
426 DLineN *dry_delay;139 int eqbinmap[kMaxTableLen];
427 WindowType window_type;140 int delaybinmap[kMaxTableLen];
428protected:141 int eqtablen, dttablen, fbtablen, eqbinmaplen, delaybinmaplen;
429 int inchan, skip, total_insamps, first_time, int_overlap;142 float minfreq, maxfreq, dminfreq, dmaxfreq;
430 int fft_len, window_len, decimation, window_len_minus_decimation,143 bool hold, posteq;
431 half_fft_len, latency, input_end_frame;
432 float wetdry, pctleft, inputdur, ringdur, fund_anal_freq;
433 float *anal_chans;
434
435};144};
436 145
437 146
@@ -518,7 +227,7 @@ CK_DLL_TICKF(spectacle_tick) · 80 unmodified lines
518 227
519 // invoke our tick function; store in the magical out variable228 // invoke our tick function; store in the magical out variable
520 if(c) c->tick(in,out, nframes);229 if(c) c->tick(in,out, nframes);
521 230
522 // yes231 // yes
523 return TRUE;232 return TRUE;
524}233}

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