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 | 7 | #include "chuck_dl.h" |
| 8 | 8 | #include "chuck_def.h" |
| 9 | 9 | |
| 10 | +#include "Spectacle-dsp.h" | |
| 11 | + | |
| 10 | 12 | // general includes |
| 11 | 13 | #include <stdio.h> |
| 12 | 14 | #include <limits.h> |
| 13 | −#include "DLineN.h" | |
| 14 | 15 | |
| 15 | 16 | // declaration of chugin constructor |
| 16 | 17 | CK_DLL_CTOR(spectacle_ctor); |
| @@ -21,23 +22,42 @@ CK_DLL_DTOR(spectacle_dtor); · 4 unmodified lines | ||
| 21 | 22 | CK_DLL_MFUN(spectacle_setParam); |
| 22 | 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 | 46 | CK_DLL_TICKF(spectacle_tick); |
| 26 | 47 | |
| 27 | 48 | // this is a special offset reserved for Chugin internal data |
| 28 | 49 | t_CKINT spectacle_data_offset = 0; |
| 29 | 50 | |
| 30 | −#define MAXFFTLEN 4096 | |
| 31 | −#define MAXWINDOWLEN MAXFFTLEN * 8 | |
| 32 | −#define MINOVERLAP 0.25 | |
| 33 | −#define MAXOVERLAP 64.0 | |
| 34 | −#define MAXDELTIME 20.0 | |
| 35 | −#define FFTLEN 1024 | |
| 36 | − | |
| 37 | −typedef enum { | |
| 38 | − Hamming = 0, | |
| 39 | − Rectangle | |
| 40 | −} WindowType; | |
| 51 | +#define CONFORM_INPUT | |
| 52 | +#define CLIP(a, lo, hi) ( (a)>(lo)?( (a)<(hi)?(a):(hi) ):(lo) ) | |
| 53 | + | |
| 54 | +#define kDefaultFFTLen 1024 | |
| 55 | +#define kDefaultWindowLen 2048 | |
| 56 | +#define kDefaultOverlap 2 | |
| 57 | +#define kDefaultMaxDelTime 6.0f | |
| 58 | +#define kMinMaxDelTime 0.1f | |
| 59 | +#define kMaxMaxDelTime 20.0f | |
| 60 | +#define kMaxTableLen 512 | |
| 41 | 61 | |
| 42 | 62 | // class definition for internal Chugin data |
| 43 | 63 | // (note: this isn't strictly necessary, but serves as example |
| @@ -48,87 +68,52 @@ public: · 4 unmodified lines | ||
| 48 | 68 | // constructor |
| 49 | 69 | Spectacle( t_CKFLOAT fs) |
| 50 | 70 | { |
| 51 | − /* create segmented input buffer */ | |
| 52 | − int num_segments = (window_len / FFTLEN) + 2; | |
| 53 | − int extrasamps = FFTLEN - framesToRun(); | |
| 54 | − if (extrasamps) | |
| 55 | − num_segments++; | |
| 56 | − int inbuf_samps = num_segments * FFTLEN * inputChannels(); | |
| 57 | − inbuf = new float [inbuf_samps]; | |
| 58 | − for (int i = 0; i < inbuf_samps; i++) | |
| 59 | − inbuf[i] = 0.0; | |
| 60 | − | |
| 61 | − /* Read ptr chases write ptr by <window_len>. Set read ptr to a position | |
| 62 | − <window_len> frames from right end of input buffer. Set write ptr to | |
| 63 | − beginning of buffer, or, if framesToRun() is not the same as the RTcmix | |
| 64 | − buffer size, set it so that the next run invocation after this one | |
| 65 | − will find the write ptr at the start of the second buffer segment. | |
| 66 | − */ | |
| 67 | − inbuf_readptr = inbuf + (inbuf_samps - (window_len * inputChannels())); | |
| 68 | − inbuf_startptr = inbuf + (extrasamps * inputChannels()); | |
| 69 | − inbuf_writeptr = inbuf_startptr; | |
| 70 | − inbuf_endptr = inbuf + inbuf_samps; | |
| 71 | − | |
| 72 | − int outbuf_samps = num_segments * FFTLEN * outputChannels(); | |
| 73 | − outbuf = new float [outbuf_samps]; | |
| 74 | − for (int i = 0; i < outbuf_samps; i++) | |
| 75 | − outbuf[i] = 0.0; | |
| 76 | − | |
| 77 | − outbuf_readptr = outbuf + (outbuf_samps | |
| 78 | − - (framesToRun() * outputChannels())); | |
| 79 | − outbuf_writeptr = outbuf_readptr; | |
| 80 | − outbuf_endptr = outbuf + outbuf_samps; | |
| 81 | − first_time = 0; | |
| 71 | + m_param = 0; | |
| 72 | + fftlen = kDefaultFFTLen; | |
| 73 | + windowlen = kDefaultWindowLen; | |
| 74 | + overlap = kDefaultOverlap; | |
| 75 | + maxdeltime = kDefaultMaxDelTime; | |
| 76 | + maxdeltime = CLIP(maxdeltime, kMinMaxDelTime, kMaxMaxDelTime); | |
| 77 | + srate = fs; | |
| 78 | + spectdelay = NULL; | |
| 79 | + float *del = dttable; | |
| 80 | + float *eq = eqtable; | |
| 81 | + float *feed = fbtable; | |
| 82 | + int *bm = eqbinmap; | |
| 83 | + int *dbm = delaybinmap; | |
| 84 | + for (int i = 0; i < kMaxTableLen; i++) | |
| 85 | + { | |
| 86 | + *del++ = 0.0; | |
| 87 | + *eq++ = 0.0; | |
| 88 | + *feed++ = 0.0; | |
| 89 | + *bm++ = 0; | |
| 90 | + *dbm++ = 0; | |
| 91 | + } | |
| 92 | + dttablen = eqtablen = fbtablen = 0; | |
| 93 | + eqbinmaplen = delaybinmaplen = 0; | |
| 94 | + dminfreq = dmaxfreq = minfreq = maxfreq = 0.0; | |
| 95 | + posteq = hold = false; | |
| 96 | + | |
| 97 | + spectdelay = new Spectacle_dsp(); | |
| 98 | + spectdelay->init(fftlen, windowlen, overlap, srate, maxdeltime); | |
| 82 | 99 | } |
| 83 | 100 | |
| 84 | 101 | ~Spectacle() |
| 85 | 102 | { |
| 86 | − delete [] anal_window; | |
| 87 | − delete [] synth_window; | |
| 88 | − delete [] fft_buf; | |
| 89 | − delete [] anal_chans; | |
| 90 | − delete [] drybuf; | |
| 91 | − delete [] inbuf; | |
| 92 | − delete [] outbuf; | |
| 93 | − delete dry_delay; | |
| 103 | + delete spectdelay; | |
| 94 | 104 | } |
| 95 | − | |
| 105 | + | |
| 96 | 106 | // for Chugins extending UGen |
| 97 | − void tick( SAMPLE *in, SAMPLE *out, int nframes ) | |
| 98 | − { | |
| 99 | − memset(out, 0, sizeof(SAMPLE)*nframes); | |
| 100 | − | |
| 101 | − for (int i=0; i < nframes; i++) | |
| 102 | − { | |
| 103 | − shiftin(); | |
| 104 | − fold(); | |
| 105 | − JGrfft(fft_buf, half_fft_len, FORWARD); | |
| 106 | − leanconvert(); | |
| 107 | − } | |
| 108 | − flush_dry_delay(); | |
| 109 | − modify_analysis(); | |
| 110 | − leanunconvert(); | |
| 111 | − JGrfft(fft_buf, half_fft_len, INVERSE); | |
| 112 | − overlapadd(currentFrame()); | |
| 113 | − shiftout(); | |
| 114 | − | |
| 115 | − increment(decimation); | |
| 107 | + void tick( SAMPLE* in, SAMPLE* out, int nframes) | |
| 108 | + { | |
| 109 | + memset (out, 0, sizeof(SAMPLE)*nframes); | |
| 110 | + // process a block of samples | |
| 111 | + spectdelay->run(in, out, nframes); | |
| 116 | 112 | } |
| 117 | 113 | |
| 118 | − if (currentFrame() < input_end_frame) { | |
| 119 | − inbuf_writeptr += insamps; | |
| 120 | − if (inbuf_writeptr >= inbuf_endptr) | |
| 121 | − inbuf_writeptr = inbuf; | |
| 122 | − } | |
| 123 | − | |
| 124 | − rtbaddout(outbuf_readptr, framesToRun()); | |
| 125 | − outbuf_readptr += framesToRun() * outputChannels(); | |
| 126 | − if (outbuf_readptr >= outbuf_endptr) | |
| 127 | − outbuf_readptr = outbuf; | |
| 128 | − | |
| 129 | − return framesToRun(); | |
| 130 | − | |
| 131 | − // sythesis function here | |
| 114 | + void clear() | |
| 115 | + { | |
| 116 | + spectdelay->clear(); | |
| 132 | 117 | } |
| 133 | 118 | |
| 134 | 119 | // set parameter example |
| @@ -141,297 +126,21 @@ public: · 6 unmodified lines | ||
| 141 | 126 | // get parameter example |
| 142 | 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 | 129 | private: |
| 417 | 130 | // instance data |
| 418 | 131 | float m_param; |
| 419 | − float *anal_window, *synth_window, *input, *output, *fft_buf, *drybuf; | |
| 420 | − float *inbuf, *inbuf_startptr, *inbuf_readptr, *inbuf_writeptr, | |
| 421 | − *inbuf_endptr; | |
| 422 | − float *outbuf, *outbuf_startptr, *outbuf_readptr, *outbuf_writeptr, | |
| 423 | − *outbuf_endptr; | |
| 424 | − float iamptabs[2], oamptabs[2]; | |
| 425 | − double *iamparray, *oamparray; | |
| 426 | − DLineN *dry_delay; | |
| 427 | − WindowType window_type; | |
| 428 | −protected: | |
| 429 | − int inchan, skip, total_insamps, first_time, int_overlap; | |
| 430 | − int fft_len, window_len, decimation, window_len_minus_decimation, | |
| 431 | − half_fft_len, latency, input_end_frame; | |
| 432 | − float wetdry, pctleft, inputdur, ringdur, fund_anal_freq; | |
| 433 | − float *anal_chans; | |
| 434 | − | |
| 132 | + | |
| 133 | + int fftlen, windowlen, overlap; | |
| 134 | + float srate, maxdeltime; | |
| 135 | + Spectacle_dsp *spectdelay; | |
| 136 | + float eqtable[kMaxTableLen]; | |
| 137 | + float dttable[kMaxTableLen]; | |
| 138 | + float fbtable[kMaxTableLen]; | |
| 139 | + int eqbinmap[kMaxTableLen]; | |
| 140 | + int delaybinmap[kMaxTableLen]; | |
| 141 | + int eqtablen, dttablen, fbtablen, eqbinmaplen, delaybinmaplen; | |
| 142 | + float minfreq, maxfreq, dminfreq, dmaxfreq; | |
| 143 | + bool hold, posteq; | |
| 435 | 144 | }; |
| 436 | 145 | |
| 437 | 146 | |
| @@ -518,7 +227,7 @@ CK_DLL_TICKF(spectacle_tick) · 80 unmodified lines | ||
| 518 | 227 | |
| 519 | 228 | // invoke our tick function; store in the magical out variable |
| 520 | 229 | if(c) c->tick(in,out, nframes); |
| 521 | − | |
| 230 | + | |
| 522 | 231 | // yes |
| 523 | 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 | 3 | CHUGIN_NAME=Spectacle |
| 4 | 4 | |
| 5 | 5 | # all of the c/cpp files that compose this chugin |
| 6 | −C_MODULES=fft.c | |
| 7 | −CXX_MODULES=JGFilter.cpp DLineN.cpp Spectacle.cpp | |
| 6 | +CXX_MODULES=genlib/FFTReal.cpp genlib/Obucket.cpp genlib/Odelay.cpp genlib/Offt.cpp genlib/Ooscil.cpp genlib/RandGen.cpp | |
| 7 | +CXX_MODULES+=SpectacleBase.cpp SpectEQ.cpp Spectacle-dsp.cpp | |
| 8 | +CXX_MODULES+=Spectacle.cpp | |
| 8 | 9 | |
| 9 | 10 | # where the chuck headers are |
| 10 | 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; | |
| 2 | −<<< 0.494 => spec.param >>>; | |
| 1 | +adc => Spectacle spect => dac; | |
| 2 | +<<< 0.494 => spect.param >>>; | |
| 3 | +minute => now; | |