Showing the changes to Spectacle/Spectacle.cpp (1 of 12 files). Show all
Unified · Split
| @@ -10,6 +10,7 @@ · 9 unmodified lines | ||
| 10 | 10 | // general includes |
| 11 | 11 | #include <stdio.h> |
| 12 | 12 | #include <limits.h> |
| 13 | +#include "DLineN.h" | |
| 13 | 14 | |
| 14 | 15 | // declaration of chugin constructor |
| 15 | 16 | CK_DLL_CTOR(spectacle_ctor); |
| @@ -20,12 +21,23 @@ CK_DLL_DTOR(spectacle_dtor); · 4 unmodified lines | ||
| 20 | 21 | CK_DLL_MFUN(spectacle_setParam); |
| 21 | 22 | CK_DLL_MFUN(spectacle_getParam); |
| 22 | 23 | |
| 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); | |
| 25 | 26 | |
| 26 | 27 | // this is a special offset reserved for Chugin internal data |
| 27 | 28 | t_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 | + | |
| 37 | +typedef enum { | |
| 38 | + Hamming = 0, | |
| 39 | + Rectangle | |
| 40 | +} WindowType; | |
| 29 | 41 | |
| 30 | 42 | // class definition for internal Chugin data |
| 31 | 43 | // (note: this isn't strictly necessary, but serves as example |
| @@ -33,139 +45,500 @@ t_CKINT spectacle_data_offset = 0; · 1 unmodified line | ||
| 33 | 45 | class Spectacle |
| 34 | 46 | { |
| 35 | 47 | 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 | + } | |
| 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 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]; | |
| 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 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; | |
| 54 | 245 | } |
| 246 | + return type; | |
| 247 | + } | |
| 55 | 248 | |
| 56 | − // get parameter example | |
| 57 | − float getParam() { return m_param; } | |
| 249 | + /* ---------------------------------------------------------------- shiftin -- */ | |
| 250 | + void shiftin() | |
| 251 | + { | |
| 252 | + int _cursamp = currentFrame(); | |
| 58 | 253 | |
| 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 | + } | |
| 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 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; | |
| 72 | 349 | |
| 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 | + | |
| 76 | 379 | |
| 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(); | |
| 81 | 384 | |
| 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 | + } | |
| 84 | 407 | |
| 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 | + } | |
| 88 | 414 | |
| 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; | |
| 93 | 434 | |
| 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 | +}; | |
| 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? | |
| 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; | |
| 107 | 480 | } |
| 108 | 481 | |
| 109 | 482 | |
| 110 | 483 | // implementation for the constructor |
| 111 | 484 | CK_DLL_CTOR(spectacle_ctor) |
| 112 | 485 | { |
| 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; | |
| 121 | 494 | } |
| 122 | 495 | |
| 123 | 496 | |
| 124 | 497 | // implementation for the destructor |
| 125 | 498 | CK_DLL_DTOR(spectacle_dtor) |
| 126 | 499 | { |
| 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 ) | |
| 131 | 504 | { |
| 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; | |
| 136 | 509 | } |
| 137 | 510 | } |
| 138 | 511 | |
| 139 | 512 | |
| 140 | 513 | // implementation for tick function |
| 141 | −CK_DLL_TICK(spectacle_tick) | |
| 514 | +CK_DLL_TICKF(spectacle_tick) | |
| 142 | 515 | { |
| 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; | |
| 151 | 524 | } |
| 152 | 525 | |
| 153 | 526 | |
| 154 | 527 | // example implementation for setter |
| 155 | 528 | CK_DLL_MFUN(spectacle_setParam) |
| 156 | 529 | { |
| 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)); | |
| 161 | 534 | } |
| 162 | 535 | |
| 163 | 536 | |
| 164 | 537 | // example implementation for getter |
| 165 | 538 | CK_DLL_MFUN(spectacle_getParam) |
| 166 | 539 | { |
| 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(); | |
| 171 | 544 | } |