zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

commit 04c72d6cb90da482d11c9bedba58b950f4255023
parent c784cdb4c99c58d411983a53cb053b303adb0458
Author: Johannes Lorenz <j.git@lorenz-ho.me>
Date:   Sun,  2 Jan 2022 19:53:47 +0100

Remove FFTwrapper buffers for thread-safety

FFTwrapper is made thread-safe by not using buffers inside it. This
works, but requires that the buffers passed to the `freqs2smps` and
`smps2freqs`. This is assured by introducing 2 new classes
`FFTsampleBuffer` and `FFTfreqBuffer`, which are passed to `freqs2smps`
and `smps2freqs`.

Another thing to note is that fftw even modifies its input
buffers, so this commit partially needs to pass "scratch input buffers"
to `freqs2smps` and `smps2freqs`, so that these functions can copy the
input buffers there and only then run fftw on the scratch buffers.

Diffstat:
Msrc/DSP/FFTwrapper.cpp | 51+++++++++++++++++++++++++++++++++------------------
Msrc/DSP/FFTwrapper.h | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/Params/PADnoteParameters.cpp | 12++++++------
Msrc/Synth/OscilGen.cpp | 202++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/Synth/OscilGen.h | 25++++++++++++++-----------
5 files changed, 223 insertions(+), 136 deletions(-)

diff --git a/src/DSP/FFTwrapper.cpp b/src/DSP/FFTwrapper.cpp @@ -21,7 +21,7 @@ namespace zyn { static pthread_mutex_t *mutex = NULL; -FFTwrapper::FFTwrapper(int fftsize_) +FFTwrapper::FFTwrapper(int fftsize_) : m_fftsize(fftsize_) { //first one will spawn the mutex (yeah this may be a race itself) if(!mutex) { @@ -29,8 +29,6 @@ FFTwrapper::FFTwrapper(int fftsize_) pthread_mutex_init(mutex, NULL); } - - m_fftsize = fftsize_; time = new fftwf_real[m_fftsize]; fft = new fftwf_complex[m_fftsize + 1]; pthread_mutex_lock(mutex); @@ -56,32 +54,49 @@ FFTwrapper::~FFTwrapper() delete [] fft; } -void FFTwrapper::smps2freqs(const float *smps, fft_t *freqs) +void FFTwrapper::smps2freqs(const FFTsampleBuffer smps, FFTfreqBuffer freqs, FFTsampleBuffer scratch) const { //Load data - memcpy((void *)time, (const void *)smps, m_fftsize * sizeof(float)); + memcpy((void *)scratch.data, (const void *)smps.data, m_fftsize * sizeof(float)); - //DFT - fftwf_execute(planfftw); + smps2freqs_noconst_input(scratch, freqs); +} - //Grab data - memcpy((void *)freqs, (const void *)fft, m_fftsize * sizeof(float)); +void FFTwrapper::smps2freqs_noconst_input(FFTsampleBuffer smps, FFTfreqBuffer freqs) const +{ + static_assert (sizeof(float) == sizeof(fftwf_real), "sizeof(float) mismatch"); + assert(m_fftsize == freqs.fftsize); + assert(m_fftsize == smps.fftsize); + + //DFT + fftwf_execute_dft_r2c(planfftw, + static_cast<fftwf_real*>(smps.data), + reinterpret_cast<fftwf_complex*>(freqs.data)); } -void FFTwrapper::freqs2smps(const fft_t *freqs, float *smps) + +void FFTwrapper::freqs2smps(const FFTfreqBuffer freqs, FFTsampleBuffer smps, FFTfreqBuffer scratch) const { //Load data - memcpy((void *)fft, (const void *)freqs, m_fftsize * sizeof(float)); + memcpy((void *)scratch.data, (const void *)freqs.data, m_fftsize * sizeof(float)); - //clear unused freq channel - fft[m_fftsize / 2][0] = 0.0f; - fft[m_fftsize / 2][1] = 0.0f; + freqs2smps_noconst_input(scratch, smps); +} - //IDFT - fftwf_execute(planfftw_inv); - //Grab data - memcpy((void*)smps, (const void*)time, m_fftsize * sizeof(float)); +void FFTwrapper::freqs2smps_noconst_input(FFTfreqBuffer freqs, FFTsampleBuffer smps) const +{ + static_assert (sizeof(fft_t) == sizeof(fftwf_complex), "sizeof(complex) mismatch"); + assert(m_fftsize == freqs.fftsize); + assert(m_fftsize == smps.fftsize); + fftwf_complex* freqs_complex = reinterpret_cast<fftwf_complex*>(freqs.data); + + //Clear unused freq channel + freqs_complex[m_fftsize / 2][0] = 0.0f; + freqs_complex[m_fftsize / 2][1] = 0.0f; + + //IDFT + fftwf_execute_dft_c2r(planfftw_inv, freqs_complex, smps.data); } void FFT_cleanup() diff --git a/src/DSP/FFTwrapper.h b/src/DSP/FFTwrapper.h @@ -19,7 +19,52 @@ namespace zyn { -/**A wrapper for the FFTW library (Fast Fourier Transforms)*/ +//! Struct to make sure FFT sizes fit. *Not* an RAII class +struct FFTfreqBuffer +{ + friend class FFTwrapper; + + const int fftsize; + fft_t* data; + + // comfort functions + fft_t& operator[](std::size_t idx) { return data[idx]; } + const fft_t& operator[](std::size_t idx) const { return data[idx]; } + //! Allocation size. For users of this class, `fftsize/2` would be enough, + //! But fftw needs `fftsize+1` freqs to operate on + int allocSize() const { return fftsize + 1; } + +private: + FFTfreqBuffer(int fftsize, fft_t* ptr = nullptr) : // called by FFTwrapper + fftsize(fftsize), + data(ptr ? ptr : new fft_t[allocSize()]) + {} +}; + +//! Struct to make sure FFT sizes fit. *Not* an RAII class +struct FFTsampleBuffer +{ + friend class FFTwrapper; + + const int fftsize; + float* data; + + // comfort functions + float& operator[](std::size_t idx) { return data[idx]; } + const float& operator[](std::size_t idx) const { return data[idx]; } + int allocSize() const { return fftsize; } + +private: + FFTsampleBuffer(int fftsize, float* ptr = nullptr) : // called by FFTwrapper + fftsize(fftsize), + data(ptr ? ptr : new float[allocSize()]) + {} +}; + +/** + A wrapper for the FFTW library (Fast Fourier Transforms) + All methods (except CTOR/DTOR) are static/const. This class is thread-safe. +*/ class FFTwrapper { public: @@ -31,14 +76,26 @@ class FFTwrapper /**Convert Samples to Frequencies using Fourier Transform * @param smps Pointer to Samples to be converted; has length fftsize_ * @param freqs Structure FFTFREQS which stores the frequencies*/ - void smps2freqs(const float *smps, fft_t *freqs); - void freqs2smps(const fft_t *freqs, float *smps); + void smps2freqs(const FFTsampleBuffer smps, FFTfreqBuffer freqs, FFTsampleBuffer scratch) const; + void freqs2smps(const FFTfreqBuffer freqs, FFTsampleBuffer smps, FFTfreqBuffer scratch) const; + void smps2freqs_noconst_input(FFTsampleBuffer smps, FFTfreqBuffer freqs) const; + void freqs2smps_noconst_input(FFTfreqBuffer freqs, FFTsampleBuffer smps) const; + + // Whenever you need one of the FFT*Buffers, you take them from here + // The methods make sure that the FFT*Buffers match the fftsize + FFTfreqBuffer allocFreqBuf(fft_t* ptr = nullptr) const { return FFTfreqBuffer(m_fftsize, ptr); } + FFTsampleBuffer allocSampleBuf(float* ptr = nullptr) const { return FFTsampleBuffer(m_fftsize, ptr); } + // These should only be used exceptionally if you need to alloc those buffers, + // buf never run any FFT/IFFT on them + static FFTfreqBuffer riskAllocFreqBufWithSize(int othersize) { return FFTfreqBuffer(othersize); } + static FFTsampleBuffer riskAllocSampleBufWithSize(int othersize) { return FFTsampleBuffer(othersize); } int fftsize() const { return m_fftsize; } + private: - int m_fftsize; - fftwf_real *time; - fftwf_complex *fft; + const int m_fftsize; + fftwf_real *time; // only used when creating plan + fftwf_complex *fft; // only used when creating plan fftwf_plan planfftw, planfftw_inv; }; diff --git a/src/Params/PADnoteParameters.cpp b/src/Params/PADnoteParameters.cpp @@ -922,9 +922,9 @@ int PADnoteParameters::sampleGenerator(PADnoteParameters::callback cb, unsigned nthreads, unsigned threadno) { //prepare a BIG IFFT - FFTwrapper *fft = new FFTwrapper(samplesize); - fft_t *fftfreqs = new fft_t[samplesize / 2]; - float *spectrum = new float[spectrumsize]; + FFTwrapper *fft = new FFTwrapper(samplesize); + FFTfreqBuffer fftfreqs = fft->allocFreqBuf(); + float *spectrum = new float[spectrumsize]; for(int nsample = 0; nsample < samplemax; ++nsample) if(nsample % nthreads == threadno) @@ -952,12 +952,12 @@ int PADnoteParameters::sampleGenerator(PADnoteParameters::callback cb, newsample.smp = new float[samplesize + extra_samples]; newsample.smp[0] = 0.0f; + fftfreqs[0] = fft_t(0, 0); for(int i = 1; i < spectrumsize; ++i) //randomize the phases fftfreqs[i] = FFTpolar(spectrum[i], (float)RND * 2 * PI); //that's all; here is the only ifft for the whole sample; //no windows are used ;-) - fft->freqs2smps(fftfreqs, newsample.smp); - + fft->freqs2smps_noconst_input(fftfreqs, fft->allocSampleBuf(newsample.smp)); //normalize(rms) float rms = 0.0f; @@ -982,7 +982,7 @@ int PADnoteParameters::sampleGenerator(PADnoteParameters::callback cb, //Cleanup delete (fft); - delete[] fftfreqs; + delete[] fftfreqs.data; delete[] spectrum; }; diff --git a/src/Synth/OscilGen.cpp b/src/Synth/OscilGen.cpp @@ -117,11 +117,11 @@ const rtosc::Ports OscilGen::non_realtime_ports = { char *edit = strrchr(repath, '/')+1; strcpy(edit, "prepare"); OscilGen &o = *((OscilGen*)d.obj); - fft_t *data = new fft_t[o.synth.oscilsize / 2]; - o.prepare(data); + FFTfreqBuffer freqs = o.fft->allocFreqBuf(); + o.prepare(freqs); // fprintf(stderr, "sending '%p' of fft data\n", data); - d.chain(repath, "b", sizeof(fft_t*), &data); - o.pendingfreqs = data; + d.chain(repath, "b", sizeof(fft_t*), &freqs.data); + o.pendingfreqs = freqs.data; d.broadcast(d.loc, "i", phase); } }}, @@ -144,11 +144,11 @@ const rtosc::Ports OscilGen::non_realtime_ports = { char *edit = strrchr(repath, '/')+1; strcpy(edit, "prepare"); OscilGen &o = *((OscilGen*)d.obj); - fft_t *data = new fft_t[o.synth.oscilsize / 2]; - o.prepare(data); + FFTfreqBuffer freqs = o.fft->allocFreqBuf(); + o.prepare(freqs); // fprintf(stderr, "sending '%p' of fft data\n", data); - d.chain(repath, "b", sizeof(fft_t*), &data); - o.pendingfreqs = data; + d.chain(repath, "b", sizeof(fft_t*), &freqs.data); + o.pendingfreqs = freqs.data; d.broadcast(d.loc, "i", mag); } }}, @@ -165,22 +165,22 @@ const rtosc::Ports OscilGen::non_realtime_ports = { {"base-waveform:", rProp(non-realtime) rDoc("Returns base waveshape points"), NULL, [](const char *, rtosc::RtData &d) { OscilGen &o = *((OscilGen*)d.obj); - const unsigned n = o.synth.oscilsize; - float *smps = new float[n]; - memset(smps, 0, 4*n); + FFTsampleBuffer smps = o.fft->allocSampleBuf(); + const unsigned n = smps.allocSize() * sizeof(float); + memset(smps.data, 0, n); ((OscilGen*)d.obj)->getcurrentbasefunction(smps); - d.reply(d.loc, "b", n*sizeof(float), smps); - delete[] smps; + d.reply(d.loc, "b", n, smps.data); + delete[] smps.data; }}, {"prepare:", rProp(non-realtime) rDoc("Performs setup operation to oscillator"), NULL, [](const char *, rtosc::RtData &d) { //fprintf(stderr, "prepare: got a message from '%s'\n", m); OscilGen &o = *(OscilGen*)d.obj; - fft_t *data = new fft_t[o.synth.oscilsize / 2]; - o.prepare(data); + FFTfreqBuffer freqs = o.fft->allocFreqBuf(); + o.prepare(freqs); // fprintf(stderr, "sending '%p' of fft data\n", data); - d.chain(d.loc, "b", sizeof(fft_t*), &data); - o.pendingfreqs = data; + d.chain(d.loc, "b", sizeof(fft_t*), &freqs.data); + o.pendingfreqs = freqs.data; }}, {"convert2sine:", rProp(non-realtime) rDoc("Translates waveform into FS"), NULL, [](const char *, rtosc::RtData &d) { @@ -229,7 +229,7 @@ const rtosc::Ports OscilGen::realtime_ports{ NULL, [](const char *, rtosc::RtData &d) { OscilGen &o = *((OscilGen*)d.obj); const unsigned n = o.synth.oscilsize; - float *smps = new float[n]; + float *smps = new float[n]; // XXXRT memset(smps, 0, 4*n); //printf("%d\n", o->needPrepare()); o.get(smps,-1.0); @@ -241,7 +241,7 @@ const rtosc::Ports OscilGen::realtime_ports{ NULL, [](const char *, rtosc::RtData &d) { OscilGen &o = *((OscilGen*)d.obj); const unsigned n = o.synth.oscilsize / 2; - float *spc = new float[n]; + float *spc = new float[n]; // XXXRT memset(spc, 0, 4*n); ((OscilGen*)d.obj)->getspectrum(n,spc,0); d.reply(d.loc, "b", n*sizeof(float), spc); @@ -252,9 +252,9 @@ const rtosc::Ports OscilGen::realtime_ports{ // fprintf(stderr, "prepare:b got a message from '%s'\n", m); OscilGen &o = *(OscilGen*)d.obj; assert(rtosc_argument(m,0).b.len == sizeof(void*)); - d.reply("/free", "sb", "fft_t", sizeof(void*), &o.oscilFFTfreqs); - assert(o.oscilFFTfreqs !=*(fft_t**)rtosc_argument(m,0).b.data); - o.oscilFFTfreqs = *(fft_t**)rtosc_argument(m,0).b.data; + d.reply("/free", "sb", "fft_t", sizeof(void*), &o.oscilFFTfreqs.data); + assert(o.oscilFFTfreqs.data !=*(fft_t**)rtosc_argument(m,0).b.data); + o.oscilFFTfreqs.data = *(fft_t**)rtosc_argument(m,0).b.data; }}, }; @@ -340,8 +340,27 @@ void rmsNormalize(fft_t *freqs, int oscilsize) #define DIFF(par) (old ## par != P ## par) +FFTfreqBuffer ctorAllocFreqs(FFTwrapper* fft, int oscilsize) { + return fft ? fft->allocFreqBuf() : FFTwrapper::riskAllocFreqBufWithSize(oscilsize); +} + +FFTsampleBuffer ctorAllocSamples(FFTwrapper* fft, int oscilsize) { + return fft ? fft->allocSampleBuf() : FFTwrapper::riskAllocSampleBufWithSize(oscilsize); +} + OscilGen::OscilGen(const SYNTH_T &synth_, FFTwrapper *fft_, Resonance *res_) - :Presets(), synth(synth_) + :Presets(), + // fft_ can be nullptr in case of pasting (TODO: let pasting pass fft ptr?) + oscilFFTfreqs(ctorAllocFreqs(fft_, synth_.oscilsize)), + tmpsmps(ctorAllocSamples(fft_, synth_.oscilsize)), + outoscilFFTfreqs(ctorAllocFreqs(fft_, synth_.oscilsize)), + cachedbasefunc(ctorAllocSamples(fft_, synth_.oscilsize)), + cachedbasevalid(false), + fft(fft_), + basefuncFFTfreqs(ctorAllocFreqs(fft_, synth_.oscilsize)), + scratchFreqs(ctorAllocFreqs(fft_, synth_.oscilsize)), + res(res_), + synth(synth_) { if(fft_) { // FFTwrapper should operate exactly on all "oscilsize" bytes @@ -351,17 +370,8 @@ OscilGen::OscilGen(const SYNTH_T &synth_, FFTwrapper *fft_, Resonance *res_) } setpresettype("Poscilgen"); - fft = fft_; - res = res_; - - tmpsmps = new float[synth.oscilsize]; - outoscilFFTfreqs = new fft_t[synth.oscilsize / 2]; - oscilFFTfreqs = new fft_t[synth.oscilsize / 2]; - basefuncFFTfreqs = new fft_t[synth.oscilsize / 2]; - cachedbasefunc = new float[synth.oscilsize]; - cachedbasevalid = false; - pendingfreqs = oscilFFTfreqs; + pendingfreqs = oscilFFTfreqs.data; randseed = 1; ADvsPAD = false; @@ -371,11 +381,12 @@ OscilGen::OscilGen(const SYNTH_T &synth_, FFTwrapper *fft_, Resonance *res_) OscilGen::~OscilGen() { - delete[] tmpsmps; - delete[] outoscilFFTfreqs; - delete[] basefuncFFTfreqs; - delete[] oscilFFTfreqs; - delete[] cachedbasefunc; + delete[] tmpsmps.data; + delete[] outoscilFFTfreqs.data; + delete[] basefuncFFTfreqs.data; + delete[] oscilFFTfreqs.data; + delete[] cachedbasefunc.data; + delete[] scratchFreqs.data; } @@ -442,8 +453,8 @@ void OscilGen::defaults() Padaptiveharmonicsbasefreq = 128; Padaptiveharmonicspar = 50; - clearAll(oscilFFTfreqs, synth.oscilsize); - clearAll(basefuncFFTfreqs, synth.oscilsize); + clearAll(oscilFFTfreqs.data, synth.oscilsize); + clearAll(basefuncFFTfreqs.data, synth.oscilsize); oscilprepared = 0; oldfilterpars = 0; oldsapars = 0; @@ -453,20 +464,22 @@ void OscilGen::defaults() void OscilGen::convert2sine() { float mag[MAX_AD_HARMONICS], phase[MAX_AD_HARMONICS]; - float oscil[synth.oscilsize]; - fft_t *freqs = new fft_t[synth.oscilsize / 2]; - get(oscil, -1.0f); - FFTwrapper *fft = new FFTwrapper(synth.oscilsize); - fft->smps2freqs(oscil, freqs); - delete (fft); - normalize(freqs, synth.oscilsize); + { + FFTwrapper *fft = new FFTwrapper(synth.oscilsize); + FFTsampleBuffer oscil = fft->allocSampleBuf(); + get(oscil.data, -1.0f); + fft->smps2freqs_noconst_input(oscil, scratchFreqs); + delete (fft); + } + + normalize(scratchFreqs.data, synth.oscilsize); mag[0] = 0; phase[0] = 0; for(int i = 0; i < MAX_AD_HARMONICS; ++i) { - mag[i] = abs(freqs, i + 1); - phase[i] = arg(freqs, i + 1); + mag[i] = abs(scratchFreqs.data, i + 1); + phase[i] = arg(scratchFreqs.data, i + 1); } defaults(); @@ -484,7 +497,6 @@ void OscilGen::convert2sine() if(Phmag[i] == 64) Phphase[i] = 64; } - delete[] freqs; prepare(); } @@ -493,10 +505,10 @@ float OscilGen::userfunc(float x) if (!fft) return 0; if (!cachedbasevalid) { - fft->freqs2smps(basefuncFFTfreqs, cachedbasefunc); + fft->freqs2smps(basefuncFFTfreqs, cachedbasefunc, scratchFreqs); cachedbasevalid = true; } - return cinterpolate(cachedbasefunc, + return cinterpolate(cachedbasefunc.data, synth.oscilsize, synth.oscilsize * (x + 1) - 1); } @@ -504,7 +516,7 @@ float OscilGen::userfunc(float x) /* * Get the base function */ -void OscilGen::getbasefunction(float *smps) +void OscilGen::getbasefunction(FFTsampleBuffer smps) { float par = (Pbasefuncpar + 0.5f) / 128.0f; if(Pbasefuncpar == 64) @@ -590,11 +602,11 @@ void OscilGen::changebasefunction(void) if(Pcurrentbasefunc != 0) { getbasefunction(tmpsmps); if(fft) - fft->smps2freqs(tmpsmps, basefuncFFTfreqs); - clearDC(basefuncFFTfreqs); + fft->smps2freqs_noconst_input(tmpsmps, basefuncFFTfreqs); + clearDC(basefuncFFTfreqs.data); } else //in this case basefuncFFTfreqs are not used - clearAll(basefuncFFTfreqs, synth.oscilsize); + clearAll(basefuncFFTfreqs.data, synth.oscilsize); oscilprepared = 0; oldbasefunc = Pcurrentbasefunc; oldbasepar = Pbasefuncpar; @@ -622,35 +634,35 @@ inline void normalize(float *smps, size_t N) /* * Waveshape */ -void OscilGen::waveshape(fft_t *freqs) +void OscilGen::waveshape(FFTfreqBuffer freqs) { oldwaveshapingfunction = Pwaveshapingfunction; oldwaveshaping = Pwaveshaping; if(Pwaveshapingfunction == 0) return; - clearDC(freqs); + clearDC(freqs.data); //reduce the amplitude of the freqs near the nyquist for(int i = 1; i < synth.oscilsize / 8; ++i) { float gain = i / (synth.oscilsize / 8.0f); freqs[synth.oscilsize / 2 - i] *= gain; } - fft->freqs2smps(freqs, tmpsmps); + fft->freqs2smps_noconst_input(freqs, tmpsmps); //Normalize - normalize(tmpsmps, synth.oscilsize); + normalize(tmpsmps.data, synth.oscilsize); //Do the waveshaping - waveShapeSmps(synth.oscilsize, tmpsmps, Pwaveshapingfunction, Pwaveshaping); + waveShapeSmps(synth.oscilsize, tmpsmps.data, Pwaveshapingfunction, Pwaveshaping); - fft->smps2freqs(tmpsmps, freqs); //perform FFT + fft->smps2freqs_noconst_input(tmpsmps, freqs); //perform FFT } /* * Do the Frequency Modulation of the Oscil */ -void OscilGen::modulation(fft_t *freqs) +void OscilGen::modulation(FFTfreqBuffer freqs) { oldmodulation = Pmodulation; oldmodulationpar1 = Pmodulationpar1; @@ -683,18 +695,18 @@ void OscilGen::modulation(fft_t *freqs) break; } - clearDC(freqs); //remove the DC + clearDC(freqs.data); //remove the DC //reduce the amplitude of the freqs near the nyquist for(int i = 1; i < synth.oscilsize / 8; ++i) { const float tmp = i / (synth.oscilsize / 8.0f); freqs[synth.oscilsize / 2 - i] *= tmp; } - fft->freqs2smps(freqs, tmpsmps); + fft->freqs2smps_noconst_input(freqs, tmpsmps); const int extra_points = 2; float *in = new float[synth.oscilsize + extra_points]; //Normalize - normalize(tmpsmps, synth.oscilsize); + normalize(tmpsmps.data, synth.oscilsize); for(int i = 0; i < synth.oscilsize; ++i) in[i] = tmpsmps[i]; @@ -731,7 +743,7 @@ void OscilGen::modulation(fft_t *freqs) } delete [] in; - fft->smps2freqs(tmpsmps, freqs); //perform FFT + fft->smps2freqs_noconst_input(tmpsmps, freqs); //perform FFT } @@ -825,7 +837,7 @@ void OscilGen::prepare(void) prepare(oscilFFTfreqs); } -void OscilGen::prepare(fft_t *freqs) +void OscilGen::prepare(FFTfreqBuffer freqs) { if((oldbasepar != Pbasefuncpar) || (oldbasefunc != Pcurrentbasefunc) || DIFF(basefuncmodulation) || DIFF(basefuncmodulationpar1) @@ -865,7 +877,7 @@ void OscilGen::prepare(fft_t *freqs) hmag[i] = 0.0f; - clearAll(freqs, synth.oscilsize); + clearAll(freqs.data, synth.oscilsize); if(Pcurrentbasefunc == 0) //the sine case for(int i = 0; i < MAX_AD_HARMONICS - 1; ++i) { freqs[i + 1] = @@ -887,22 +899,22 @@ void OscilGen::prepare(fft_t *freqs) } if(Pharmonicshiftfirst != 0) - shiftharmonics(freqs); + shiftharmonics(freqs.data); if(Pfilterbeforews) { - oscilfilter(freqs); + oscilfilter(freqs.data); waveshape(freqs); } else { waveshape(freqs); - oscilfilter(freqs); + oscilfilter(freqs.data); } modulation(freqs); - spectrumadjust(freqs); + spectrumadjust(freqs.data); if(Pharmonicshiftfirst == 0) - shiftharmonics(freqs); + shiftharmonics(freqs.data); - clearDC(freqs); + clearDC(freqs.data); oldhmagtype = Phmagtype; oldharmonicshift = Pharmonicshift + Pharmonicshiftfirst * 256; @@ -922,7 +934,7 @@ void OscilGen::adaptiveharmonic(fft_t *f, float freq) if(freq < 1.0f) freq = 440.0f; - fft_t *inf = new fft_t[synth.oscilsize / 2]; + fft_t *inf = new fft_t[synth.oscilsize / 2]; // XXXRT for(int i = 0; i < synth.oscilsize / 2; ++i) inf[i] = f[i]; clearAll(f, synth.oscilsize); @@ -968,7 +980,7 @@ void OscilGen::adaptiveharmonicpostprocess(fft_t *f, int size) { if(Padaptiveharmonics <= 1) return; - fft_t *inf = new fft_t[size]; + fft_t *inf = new fft_t[size]; // XXXRT float par = Padaptiveharmonicspar * 0.01f; par = 1.0f - powf((1.0f - par), 1.5f); @@ -1047,12 +1059,12 @@ bool OscilGen::needPrepare(void) /* * Get the oscillator function */ -short int OscilGen::get(float *smps, float freqHz, int resonance) +short int OscilGen::get(float* smps, float freqHz, int resonance) { if(needPrepare()) prepare(); - fft_t *input = freqHz > 0.0f ? oscilFFTfreqs : pendingfreqs; + fft_t *input = freqHz > 0.0f ? oscilFFTfreqs.data : pendingfreqs; unsigned int realrnd = prng(); sprng(randseed); @@ -1063,7 +1075,7 @@ short int OscilGen::get(float *smps, float freqHz, int resonance) outpos = (outpos + 2 * synth.oscilsize) % synth.oscilsize; - clearAll(outoscilFFTfreqs, synth.oscilsize); + clearAll(outoscilFFTfreqs.data, synth.oscilsize); int nyquist = (int)(0.5f * synth.samplerate_f / fabsf(freqHz)) + 2; if(ADvsPAD) @@ -1080,7 +1092,7 @@ short int OscilGen::get(float *smps, float freqHz, int resonance) for(int i = 1; i < nyquist - 1; ++i) outoscilFFTfreqs[i] = input[i]; - adaptiveharmonic(outoscilFFTfreqs, freqHz); + adaptiveharmonic(outoscilFFTfreqs.data, freqHz); adaptiveharmonicpostprocess(&outoscilFFTfreqs[1], synth.oscilsize / 2 - 1); @@ -1124,17 +1136,17 @@ short int OscilGen::get(float *smps, float freqHz, int resonance) } if((freqHz > 0.1f) && (resonance != 0)) - res->applyres(nyquist - 1, outoscilFFTfreqs, freqHz); + res->applyres(nyquist - 1, outoscilFFTfreqs.data, freqHz); - rmsNormalize(outoscilFFTfreqs, synth.oscilsize); + rmsNormalize(outoscilFFTfreqs.data, synth.oscilsize); if((ADvsPAD) && (freqHz > 0.1f)) //in this case the smps will contain the freqs for(int i = 1; i < synth.oscilsize / 2; ++i) - smps[i - 1] = abs(outoscilFFTfreqs, i); + smps[i - 1] = abs(outoscilFFTfreqs.data, i); else { - fft->freqs2smps(outoscilFFTfreqs, smps); + fft->freqs2smps(outoscilFFTfreqs, tmpsmps, scratchFreqs); for(int i = 0; i < synth.oscilsize; ++i) - smps[i] *= 0.25f; //correct the amplitude + smps[i] = tmpsmps[i] * 0.25f; //correct the amplitude } sprng(realrnd + 1); @@ -1186,7 +1198,7 @@ void OscilGen::getspectrum(int n, float *spc, int what) if(Pcurrentbasefunc == 0) spc[i] = ((i == 1) ? (1.0f) : (0.0f)); else - spc[i] = abs(basefuncFFTfreqs, i); + spc[i] = abs(basefuncFFTfreqs.data, i); } } spc[0]=0; @@ -1195,9 +1207,9 @@ void OscilGen::getspectrum(int n, float *spc, int what) for(int i = 0; i < n; ++i) outoscilFFTfreqs[i] = fft_t(spc[i], spc[i]); fft_t zero = 0; - std::fill_n(outoscilFFTfreqs + n, synth.oscilsize / 2 - n, zero); - adaptiveharmonic(outoscilFFTfreqs, 0.0f); - adaptiveharmonicpostprocess(outoscilFFTfreqs, n - 1); + std::fill_n(outoscilFFTfreqs.data + n, synth.oscilsize / 2 - n, zero); + adaptiveharmonic(outoscilFFTfreqs.data, 0.0f); + adaptiveharmonicpostprocess(outoscilFFTfreqs.data, n - 1); for(int i = 0; i < n; ++i) spc[i] = (float)outoscilFFTfreqs[i].imag(); } @@ -1221,10 +1233,10 @@ void OscilGen::useasbase() /* * Get the base function for UI */ -void OscilGen::getcurrentbasefunction(float *smps) +void OscilGen::getcurrentbasefunction(FFTsampleBuffer smps) { if(Pcurrentbasefunc != 0) - fft->freqs2smps(basefuncFFTfreqs, smps); + fft->freqs2smps(basefuncFFTfreqs, smps, scratchFreqs); else getbasefunction(smps); //the sine case } @@ -1330,7 +1342,7 @@ void OscilGen::add2XML(XMLwrapper& xml) xml.endbranch(); if(Pcurrentbasefunc == 127) { - normalize(basefuncFFTfreqs, synth.oscilsize); + normalize(basefuncFFTfreqs.data, synth.oscilsize); xml.beginbranch("BASE_FUNCTION"); for(int i = 1; i < synth.oscilsize / 2; ++i) { @@ -1440,8 +1452,8 @@ void OscilGen::getfromXML(XMLwrapper& xml) } xml.exitbranch(); - clearDC(basefuncFFTfreqs); - normalize(basefuncFFTfreqs, synth.oscilsize); + clearDC(basefuncFFTfreqs.data); + normalize(basefuncFFTfreqs.data, synth.oscilsize); cachedbasevalid = false; }} diff --git a/src/Synth/OscilGen.h b/src/Synth/OscilGen.h @@ -17,6 +17,7 @@ #include "../globals.h" #include <rtosc/ports.h> #include "../Params/Presets.h" +#include "../DSP/FFTwrapper.h" namespace zyn { @@ -29,18 +30,18 @@ class OscilGen:public Presets /**computes the full spectrum of oscil from harmonics,phases and basefunc*/ void prepare(); - void prepare(fft_t *data); + void prepare(FFTfreqBuffer data); /**do the antialiasing(cut off higher freqs.),apply randomness and do a IFFT*/ //returns where should I start getting samples, used in block type randomness short get(float *smps, float freqHz, int resonance = 0); //if freqHz is smaller than 0, return the "un-randomized" sample for UI - void getbasefunction(float *smps); + void getbasefunction(FFTsampleBuffer smps); //called by UI void getspectrum(int n, float *spc, int what); //what=0 pt. oscil,1 pt. basefunc - void getcurrentbasefunction(float *smps); + void getcurrentbasefunction(FFTsampleBuffer smps); /**convert oscil to base function*/ void useasbase(); @@ -113,15 +114,15 @@ class OscilGen:public Presets /* Oscillator Frequencies - * this is different than the harmonics set-up by the user, - * it may contains time-domain data if the antialiasing is turned off*/ - fft_t *oscilFFTfreqs; + * it may contain time-domain data if the antialiasing is turned off*/ + FFTfreqBuffer oscilFFTfreqs; fft_t *pendingfreqs; private: //This array stores some temporary data and it has OSCIL_SIZE elements - float *tmpsmps; - fft_t *outoscilFFTfreqs; - float *cachedbasefunc; + FFTsampleBuffer tmpsmps; + FFTfreqBuffer outoscilFFTfreqs; + FFTsampleBuffer cachedbasefunc; bool cachedbasevalid; float hmag[MAX_AD_HARMONICS], hphase[MAX_AD_HARMONICS]; //the magnituides and the phases of the sine/nonsine harmonics @@ -130,7 +131,7 @@ class OscilGen:public Presets //computes the basefunction and make the FFT; newbasefunc<0 = same basefunc void changebasefunction(void); //Waveshaping - void waveshape(fft_t *freqs); + void waveshape(FFTfreqBuffer freqs); //Filter the oscillator accotding to Pfiltertype and Pfilterpar void oscilfilter(fft_t *freqs); @@ -142,7 +143,7 @@ class OscilGen:public Presets void shiftharmonics(fft_t *freqs); //Do the oscil modulation stuff - void modulation(fft_t *freqs); + void modulation(FFTfreqBuffer freqs); float userfunc(float x); @@ -170,7 +171,9 @@ class OscilGen:public Presets oldmodulationpar3; - fft_t *basefuncFFTfreqs; //Base Function Frequencies + FFTfreqBuffer basefuncFFTfreqs; //Base function frequencies + FFTfreqBuffer scratchFreqs; //Yet another tmp buffer + int oscilprepared; //1 if the oscil is prepared, 0 if it is not prepared and is need to call ::prepare() before ::get() Resonance *res;