Unified · Split
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | ||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 includes | 12 | // 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 constructor | 16 | // declaration of chugin constructor |
| 16 | CK_DLL_CTOR(spectacle_ctor); | 17 | CK_DLL_CTOR(spectacle_ctor); |
| @@ -21,23 +22,42 @@ CK_DLL_DTOR(spectacle_dtor); · 4 unmodified lines | |||
| 21 | CK_DLL_MFUN(spectacle_setParam); | 22 | CK_DLL_MFUN(spectacle_setParam); |
| 22 | CK_DLL_MFUN(spectacle_getParam); | 23 | CK_DLL_MFUN(spectacle_getParam); |
| 23 | 24 | ||
| 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 | ||
| 25 | CK_DLL_TICKF(spectacle_tick); | 46 | CK_DLL_TICKF(spectacle_tick); |
| 26 | 47 | ||
| 27 | // this is a special offset reserved for Chugin internal data | 48 | // this is a special offset reserved for Chugin internal data |
| 28 | t_CKINT spectacle_data_offset = 0; | 49 | t_CKINT spectacle_data_offset = 0; |
| 29 | 50 | ||
| 30 | #define MAXFFTLEN 4096 | 51 | #define CONFORM_INPUT |
| 31 | #define MAXWINDOWLEN MAXFFTLEN * 8 | 52 | #define CLIP(a, lo, hi) ( (a)>(lo)?( (a)<(hi)?(a):(hi) ):(lo) ) |
| 32 | #define MINOVERLAP 0.25 | 53 | |
| 33 | #define MAXOVERLAP 64.0 | 54 | #define kDefaultFFTLen 1024 |
| 34 | #define MAXDELTIME 20.0 | 55 | #define kDefaultWindowLen 2048 |
| 35 | #define FFTLEN 1024 | 56 | #define kDefaultOverlap 2 |
| 36 | 57 | #define kDefaultMaxDelTime 6.0f | |
| 37 | typedef enum { | 58 | #define kMinMaxDelTime 0.1f |
| 38 | Hamming = 0, | 59 | #define kMaxMaxDelTime 20.0f |
| 39 | Rectangle | 60 | #define kMaxTableLen 512 |
| 40 | } WindowType; | ||
| 41 | 61 | ||
| 42 | // class definition for internal Chugin data | 62 | // class definition for internal Chugin data |
| 43 | // (note: this isn't strictly necessary, but serves as example | 63 | // (note: this isn't strictly necessary, but serves as example |
| @@ -48,87 +68,52 @@ public: · 4 unmodified lines | |||
| 48 | // constructor | 68 | // 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 position | 81 | float *feed = fbtable; |
| 62 | <window_len> frames from right end of input buffer. Set write ptr to | 82 | int *bm = eqbinmap; |
| 63 | beginning of buffer, or, if framesToRun() is not the same as the RTcmix | 83 | int *dbm = delaybinmap; |
| 64 | buffer size, set it so that the next run invocation after this one | 84 | 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_samps | 97 | 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 UGen | 106 | // 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 example | 119 | // set parameter example |
| @@ -141,297 +126,21 @@ public: · 6 unmodified lines | |||
| 141 | // get parameter example | 126 | // 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 | |||
| 416 | private: | 129 | private: |
| 417 | // instance data | 130 | // 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]; |
| 428 | protected: | 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 variable | 228 | // 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 | // yes | 231 | // yes |
| 523 | return TRUE; | 232 | return TRUE; |
| 524 | } | 233 | } |
| @@ -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 | } | ||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | ||
| @@ -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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ | ||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | } | ||
| @@ -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_ | ||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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_ | ||
| @@ -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_ | ||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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_ */ | ||
| @@ -3,8 +3,9 @@ · 2 unmodified lines | |||
| 3 | CHUGIN_NAME=Spectacle | 3 | CHUGIN_NAME=Spectacle |
| 4 | 4 | ||
| 5 | # all of the c/cpp files that compose this chugin | 5 | # all of the c/cpp files that compose this chugin |
| 6 | C_MODULES=fft.c | 6 | CXX_MODULES=genlib/FFTReal.cpp genlib/Obucket.cpp genlib/Odelay.cpp genlib/Offt.cpp genlib/Ooscil.cpp genlib/RandGen.cpp |
| 7 | CXX_MODULES=JGFilter.cpp DLineN.cpp Spectacle.cpp | 7 | CXX_MODULES+=SpectacleBase.cpp SpectEQ.cpp Spectacle-dsp.cpp |
| 8 | CXX_MODULES+=Spectacle.cpp | ||
| 8 | 9 | ||
| 9 | # where the chuck headers are | 10 | # where the chuck headers are |
| 10 | CK_SRC_PATH?=../chuck/include/ | 11 | CK_SRC_PATH?=../chuck/include/ |
| @@ -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 | |||
| @@ -1,2 +1,3 @@ | |||
| 1 | adc => Spectacle spec => dac; | 1 | adc => Spectacle spect => dac; |
| 2 | <<< 0.494 => spec.param >>>; | 2 | <<< 0.494 => spect.param >>>; |
| 3 | minute => now; | ||