y@0: /* y@0: ============================================================================== y@0: y@0: This file was auto-generated! y@0: y@0: It contains the basic startup code for a Juce application. y@0: y@0: ============================================================================== y@0: */ y@0: y@0: #include "PluginProcessor.h" y@0: #include "PluginEditor.h" y@0: y@0: y@0: //============================================================================== y@0: y@0: #define M_PI 3.14159265358979323846 /* pi */ y@0: y@0: ADRessAudioProcessor::ADRessAudioProcessor() : inputBuffer_(2, 1), outputBuffer_(2, 1) y@0: { y@0: // Set default values: y@0: fftSelectedSize_ = 2048; y@0: hopSelectedSize_ = kHopSize1_8Window; y@0: windowType_ = kWindowHann; y@0: y@0: fftInitialised_ = false; y@0: fftActualTransformSize_ = 0; y@0: inputBufferLength_ = 1; y@0: outputBufferLength_ = 1; y@0: inputBufferWritePosition_ = outputBufferWritePosition_ = outputBufferReadPosition_ = 0; y@0: samplesSinceLastFFT_ = 0; y@0: windowBuffer_ = 0; y@0: windowBufferLength_ = 0; y@0: preparedToPlay_ = false; y@0: fftScaleFactor_ = 0.0; y@0: beta_ = 5; y@0: width_ = 0; y@0: azimuth_ = beta_; y@0: y@0: y@0: indMinL_ = beta_; y@0: indMaxL_ = beta_; y@0: indMinR_ = beta_; y@0: indMaxR_ = beta_; y@0: computeR_ = true; y@0: computeL_ = true; y@0: invGain_ = 2; y@0: invBeta_ = 1/(float)beta_; y@0: i1 = std::complex(0,1); y@0: y@0: y@0: lastUIWidth_ = 360; y@0: lastUIHeight_ = 400; y@0: } y@0: y@0: ADRessAudioProcessor::~ADRessAudioProcessor() y@0: { y@0: // Release FFT resources if allocated. This should be handled by y@0: // releaseResources() but in the event it doesn't happen, this avoids y@0: // a leak. Harmless to call it twice. y@0: deinitFFT(); y@0: deinitWindow(); y@0: } y@0: y@0: //============================================================================== y@0: const String ADRessAudioProcessor::getName() const y@0: { y@0: return JucePlugin_Name; y@0: } y@0: y@0: int ADRessAudioProcessor::getNumParameters() y@0: { y@0: return kNumParameters; y@0: } y@0: y@0: float ADRessAudioProcessor::getParameter (int index) y@0: { y@0: // This method will be called by the host, probably on the audio thread, so y@0: // it's absolutely time-critical. Don't use critical sections or anything y@0: // UI-related, or anything at all that may block in any way! y@0: switch (index) y@0: { y@0: case kFFTSizeParam: return (float)fftSelectedSize_; y@0: case kHopSizeParam: return (float)hopSelectedSize_; y@0: case kWindowTypeParam: return (float)windowType_; y@0: case kWidthParam: return (float)width_; y@0: case kAzimuthParam: return (float)azimuth_; y@0: y@0: default: return 0.0f; y@0: } y@0: } y@0: y@0: void ADRessAudioProcessor::setParameter (int index, float newValue) y@0: { y@0: // This method will be called by the host, probably on the audio thread, so y@0: // it's absolutely time-critical. Don't use critical sections or anything y@0: // UI-related, or anything at all that may block in any way! y@0: switch (index) y@0: { y@0: case kFFTSizeParam: y@0: if((int)newValue != fftSelectedSize_) y@0: { y@0: fftSelectedSize_ = (int)newValue; y@0: if(preparedToPlay_) y@0: { y@0: // Update settings if currently playing, else wait until prepareToPlay() called y@0: initFFT(fftSelectedSize_); y@0: initWindow(fftSelectedSize_, windowType_); y@0: } y@0: } y@0: break; y@0: case kHopSizeParam: y@0: hopSelectedSize_ = (int)newValue; y@0: if(preparedToPlay_) y@0: updateHopSize(); y@0: break; y@0: case kWindowTypeParam: y@0: // Recalculate window if needed y@0: if((int)newValue != windowType_) y@0: { y@0: windowType_ = (int)newValue; y@0: if(preparedToPlay_) y@0: initWindow(fftActualTransformSize_, (int)newValue); y@0: } y@0: break; y@0: y@0: case kWidthParam: y@0: width_ = (int)newValue; y@0: changeParams(); y@0: break; y@0: y@0: case kAzimuthParam: y@0: azimuth_ = (int)newValue; y@0: changeParams(); y@0: break; y@0: y@0: default: y@0: break; y@0: } y@0: } y@0: y@0: void ADRessAudioProcessor::changeParams() y@0: { y@0: int maxA = std::min(azimuth_ + width_,2*beta_); y@0: int minA = std::max(azimuth_ - width_,0); y@0: if (maxA >= beta_) y@0: { y@0: computeR_ = true; y@0: indMinR_ = 2*beta_ - maxA; y@0: if (minA >= beta_) y@0: { y@0: indMaxR_ = 2*beta_ - minA; y@0: } else { y@0: indMaxR_ = beta_; y@0: } y@0: } else { y@0: computeR_ = false; y@0: } y@0: y@0: if (minA <= beta_) y@0: { y@0: computeL_ = true; y@0: indMinL_ = minA; y@0: if (maxA <= beta_) y@0: { y@0: indMaxL_ = maxA; y@0: } else { y@0: indMaxL_ = beta_; y@0: } y@0: } else { y@0: computeL_ = false; y@0: } y@0: y@0: invGain_ = (indMaxR_ - indMinR_ + indMaxL_ - indMinL_); y@0: if (computeR_) y@0: invGain_++; y@0: if (computeL_) y@0: invGain_++; y@0: invGain_ = 1;//invGain_; y@0: y@0: } y@0: y@0: y@0: const String ADRessAudioProcessor::getParameterName (int index) y@0: { y@0: switch (index) y@0: { y@0: case kFFTSizeParam: return "FFT size"; y@0: case kHopSizeParam: return "hop size"; y@0: case kWindowTypeParam: return "window type"; y@0: case kWidthParam: return "volume"; y@0: case kAzimuthParam: return "azimuth"; y@0: y@0: default: break; y@0: } y@0: y@0: return String::empty; y@0: } y@0: y@0: const String ADRessAudioProcessor::getParameterText (int index) y@0: { y@0: return String (getParameter (index), 2); y@0: } y@0: y@0: const String ADRessAudioProcessor::getInputChannelName (int channelIndex) const y@0: { y@0: return String (channelIndex + 1); y@0: } y@0: y@0: const String ADRessAudioProcessor::getOutputChannelName (int channelIndex) const y@0: { y@0: return String (channelIndex + 1); y@0: } y@0: y@0: bool ADRessAudioProcessor::isInputChannelStereoPair (int index) const y@0: { y@0: return true; y@0: } y@0: y@0: bool ADRessAudioProcessor::isOutputChannelStereoPair (int index) const y@0: { y@0: return true; y@0: } y@0: y@0: bool ADRessAudioProcessor::silenceInProducesSilenceOut() const y@0: { y@0: #if JucePlugin_SilenceInProducesSilenceOut y@0: return true; y@0: #else y@0: return false; y@0: #endif y@0: } y@0: y@0: bool ADRessAudioProcessor::acceptsMidi() const y@0: { y@0: #if JucePlugin_WantsMidiInput y@0: return true; y@0: #else y@0: return false; y@0: #endif y@0: } y@0: y@0: bool ADRessAudioProcessor::producesMidi() const y@0: { y@0: #if JucePlugin_ProducesMidiOutput y@0: return true; y@0: #else y@0: return false; y@0: #endif y@0: } y@0: y@0: double ADRessAudioProcessor::getTailLengthSeconds() const y@0: { y@0: return 0; y@0: } y@0: y@0: int ADRessAudioProcessor::getNumPrograms() y@0: { y@0: return 0; y@0: } y@0: y@0: int ADRessAudioProcessor::getCurrentProgram() y@0: { y@0: return 0; y@0: } y@0: y@0: void ADRessAudioProcessor::setCurrentProgram (int index) y@0: { y@0: } y@0: y@0: const String ADRessAudioProcessor::getProgramName (int index) y@0: { y@0: return String::empty; y@0: } y@0: y@0: void ADRessAudioProcessor::changeProgramName (int index, const String& newName) y@0: { y@0: } y@0: y@0: //============================================================================== y@0: void ADRessAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) y@0: { y@0: // Use this method as the place to do any pre-playback y@0: // initialisation that you need.. y@0: y@0: initFFT(fftSelectedSize_); y@0: initWindow(fftSelectedSize_, windowType_); y@0: preparedToPlay_ = true; y@0: } y@0: y@0: void ADRessAudioProcessor::releaseResources() y@0: { y@0: // When playback stops, you can use this as an opportunity to free up any y@0: // spare memory, etc. y@0: y@0: deinitFFT(); y@0: deinitWindow(); y@0: preparedToPlay_ = false; y@0: } y@0: y@0: y@0: y@0: y@0: y@0: void ADRessAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) y@0: { y@0: // Helpful information about this block of samples: y@0: const int numInputChannels = getNumInputChannels(); // How many input channels for our effect? y@0: const int numOutputChannels = getNumOutputChannels(); // How many output channels for our effect? y@0: const int numSamples = buffer.getNumSamples(); // How many samples in the buffer for this block? y@0: y@0: int channel, inwritepos, sampsincefft; y@0: int outreadpos, outwritepos; y@0: y@0: // Grab the lock that prevents the FFT settings from changing y@0: fftSpinLock_.enter(); y@0: y@0: // Check that we're initialised and ready to go. If not, set output to 0 y@0: if(!fftInitialised_) y@0: { y@0: for (channel = 0; channel < numOutputChannels; ++channel) y@0: { y@0: buffer.clear (channel, 0, buffer.getNumSamples()); y@0: } y@0: y@0: fftSpinLock_.exit(); y@0: return; y@0: } y@0: y@0: y@0: y@0: // Go through each channel of audio that's passed in. Collect the samples in the input y@0: // buffer. When we've reached the next hop interval, calculate the FFT. y@0: y@0: // channelDataL is an array of length numSamples which contains the audio for one channel y@0: float* channelDataL = buffer.getSampleData(0); y@0: float* channelDataR = buffer.getSampleData(1); y@0: y@0: // inputBufferDataL is the circular buffer for collecting input samples for the FFT y@0: float* inputBufferDataL = inputBuffer_.getSampleData(0); y@0: float* inputBufferDataR = inputBuffer_.getSampleData(1); y@0: y@0: float* outputBufferDataL = outputBuffer_.getSampleData(0); y@0: float* outputBufferDataR = outputBuffer_.getSampleData(1); y@0: y@0: y@0: // State variables need to be temporarily cached for each channel. We don't want the y@0: // operations on one channel to affect the identical behaviour of the next channel y@0: inwritepos = inputBufferWritePosition_; y@0: outwritepos = outputBufferWritePosition_; y@0: outreadpos = outputBufferReadPosition_; y@0: sampsincefft = samplesSinceLastFFT_; y@0: y@0: for (int i = 0; i < numSamples; ++i) y@0: { y@0: const float inL = channelDataL[i]; y@0: const float inR = channelDataR[i]; y@0: y@0: // Store the next buffered sample in the output. Do this first before anything y@0: // changes the output buffer-- we will have at least one FFT size worth of data y@0: // stored and ready to go. Set the result to 0 when finished in preparation for the y@0: // next overlap/add procedure. y@0: y@0: channelDataL[i] = outputBufferDataL[outreadpos]; y@0: channelDataR[i] = outputBufferDataR[outreadpos]; y@0: outputBufferDataL[outreadpos] = 0.0; y@0: outputBufferDataR[outreadpos] = 0.0; y@0: y@0: if(++outreadpos >= outputBufferLength_) y@0: outreadpos = 0; y@0: y@0: // Store the current sample in the input buffer, incrementing the write pointer. Also y@0: // increment how many samples we've stored since the last transform. If it reaches the y@0: // hop size, perform an FFT and any frequency-domain processing. y@0: inputBufferDataL[inwritepos] = inL; y@0: inputBufferDataR[inwritepos] = inR; y@0: if (++inwritepos >= inputBufferLength_) y@0: inwritepos = 0; y@0: if (++sampsincefft >= hopActualSize_) y@0: { y@0: sampsincefft = 0; y@0: y@0: // Find the index of the starting sample in the buffer. When the buffer length y@0: // is equal to the transform size, this will be the current write position but y@0: // this code is more general for larger buffers. y@0: int inputBufferStartPosition = (inwritepos + inputBufferLength_ y@0: - fftActualTransformSize_) % inputBufferLength_; y@0: y@0: // Window the buffer and copy it into the FFT input y@0: int inputBufferIndex = inputBufferStartPosition; y@0: for(int fftBufferIndex = 0; fftBufferIndex < fftActualTransformSize_; fftBufferIndex++) y@0: { y@0: // Set real part to windowed signal; imaginary part to 0. y@0: fftTimeDomain_[fftBufferIndex][1] = 0.0; y@0: if(fftBufferIndex >= windowBufferLength_) // Safety check, in case window isn't ready y@0: fftTimeDomain_[fftBufferIndex][0] = 0.0; y@0: else y@0: fftTimeDomain_[fftBufferIndex][0] = windowBuffer_[fftBufferIndex] y@0: * inputBufferDataL[inputBufferIndex]; y@0: inputBufferIndex++; y@0: if(inputBufferIndex >= inputBufferLength_) y@0: inputBufferIndex = 0; y@0: } y@0: y@0: // Perform the FFT on the windowed data, going into the frequency domain.Result will be in fftFrequencyDomain_ y@0: fftw_execute(fftForwardPlan_); y@0: y@0: // ********** PHASE VOCODER PROCESSING GOES HERE ************** y@0: // This is the place where frequency-domain calculations are made on the transformed signal. y@0: //Put the result back into fftFrequencyDomain_ before transforming back. y@0: y@0: for (int ii = 0; ii < fftActualTransformSize_/2+1; ii++) y@0: { y@0: realL_[ii] = fftFrequencyDomain_[ii][0]; y@0: imagL_[ii] = fftFrequencyDomain_[ii][1]; y@0: } y@0: y@0: inputBufferIndex = inputBufferStartPosition; y@0: for(int fftBufferIndex = 0; fftBufferIndex < fftActualTransformSize_; fftBufferIndex++) y@0: { y@0: if(fftBufferIndex >= windowBufferLength_) // Safety check, in case window isn't ready y@0: fftTimeDomain_[fftBufferIndex][0] = 0.0; y@0: else y@0: fftTimeDomain_[fftBufferIndex][0] = windowBuffer_[fftBufferIndex] y@0: * inputBufferDataR[inputBufferIndex]; y@0: inputBufferIndex++; y@0: if(inputBufferIndex >= inputBufferLength_) y@0: inputBufferIndex = 0; y@0: } y@0: fftw_execute(fftForwardPlan_); y@0: y@0: y@0: for (int ii = 0; ii < fftActualTransformSize_/2+1; ii++) y@0: { y@0: realR_[ii] = fftFrequencyDomain_[ii][0]; y@0: imagR_[ii] = fftFrequencyDomain_[ii][1]; y@0: } y@0: y@0: y@0: y@0: double minV, maxV,newVal; y@0: std::complex fftTemp; y@0: int indMin; y@0: for (int ii = 0; ii maxV) y@0: { y@0: maxV = newVal; y@0: } y@0: else if (newValmaxV) y@0: { y@0: maxV = newVal; y@0: } y@0: else if (newVal= outputBufferLength_) y@0: outputBufferIndex = 0; y@0: } y@0: y@0: y@0: y@0: // Add the result to the output buffer, starting at the current write position y@0: // (Output buffer will have been zeroed after reading the last time around) y@0: // Output needs to be scaled by the transform size to get back to original amplitude: y@0: // this is a property of how fftw is implemented. Scaling will also need to be adjusted y@0: // based on hop size to get the same output level (smaller hop size produces more overlap y@0: // and hence higher signal level) y@0: y@0: y@0: y@0: y@0: // Advance the write position within the buffer by the hop size y@0: outwritepos = (outwritepos + hopActualSize_) % outputBufferLength_; y@0: } y@0: } y@0: y@0: y@0: // Having made a local copy of the state variables for each channel, now transfer the result y@0: // back to the main state variable so they will be preserved for the next call of processBlock() y@0: inputBufferWritePosition_ = inwritepos; y@0: outputBufferWritePosition_ = outwritepos; y@0: outputBufferReadPosition_ = outreadpos; y@0: samplesSinceLastFFT_ = sampsincefft; y@0: y@0: // In case we have more outputs than inputs, we'll clear any output y@0: // channels that didn't contain input data, (because these aren't y@0: // guaranteed to be empty - they may contain garbage). y@0: for (int i = numInputChannels; i < numOutputChannels; ++i) y@0: { y@0: buffer.clear (i, 0, buffer.getNumSamples()); y@0: } y@0: y@0: fftSpinLock_.exit(); y@0: } y@0: y@0: //============================================================================== y@0: bool ADRessAudioProcessor::hasEditor() const y@0: { y@0: return true; // (change this to false if you choose to not supply an editor) y@0: } y@0: y@0: AudioProcessorEditor* ADRessAudioProcessor::createEditor() y@0: { y@0: return new ADRessAudioProcessorEditor (this); y@0: } y@0: y@0: //============================================================================== y@0: void ADRessAudioProcessor::getStateInformation (MemoryBlock& destData) y@0: { y@0: // You should use this method to store your parameters in the memory block. y@0: // You could do that either as raw data, or use the XML or ValueTree classes y@0: // as intermediaries to make it easy to save and load complex data. y@0: y@0: // Create an outer XML element.. y@0: XmlElement xml("C4DMPLUGINSETTINGS"); y@0: y@0: // add some attributes to it.. y@0: xml.setAttribute("uiWidth", lastUIWidth_); y@0: xml.setAttribute("uiHeight", lastUIHeight_); y@0: xml.setAttribute("fftSize", fftSelectedSize_); y@0: xml.setAttribute("hopSize", hopSelectedSize_); y@0: xml.setAttribute("windowType", windowType_); y@0: xml.setAttribute("volume", width_); y@0: xml.setAttribute("azimuth", azimuth_); y@0: y@0: // then use this helper function to stuff it into the binary blob and return it.. y@0: copyXmlToBinary(xml, destData); y@0: } y@0: y@0: void ADRessAudioProcessor::setStateInformation (const void* data, int sizeInBytes) y@0: { y@0: // You should use this method to restore your parameters from this memory block, y@0: // whose contents will have been created by the getStateInformation() call. y@0: y@0: // This getXmlFromBinary() helper function retrieves our XML from the binary blob.. y@0: ScopedPointer xmlState (getXmlFromBinary (data, sizeInBytes)); y@0: y@0: if(xmlState != 0) y@0: { y@0: // make sure that it's actually our type of XML object.. y@0: if(xmlState->hasTagName("C4DMPLUGINSETTINGS")) y@0: { y@0: // ok, now pull out our parameters.. y@0: lastUIWidth_ = xmlState->getIntAttribute("uiWidth", lastUIWidth_); y@0: lastUIHeight_ = xmlState->getIntAttribute("uiHeight", lastUIHeight_); y@0: y@0: fftSelectedSize_ = (int)xmlState->getDoubleAttribute("fftSize", fftSelectedSize_); y@0: hopSelectedSize_ = (int)xmlState->getDoubleAttribute("hopSize", hopSelectedSize_); y@0: windowType_ = (int)xmlState->getDoubleAttribute("windowType", windowType_); y@0: width_ = (int)xmlState->getDoubleAttribute("volume", width_); y@0: azimuth_ = (int)xmlState->getDoubleAttribute("azimuth", azimuth_); y@0: y@0: y@0: if(preparedToPlay_) y@0: { y@0: // Update settings if currently playing, else wait until prepareToPlay() called y@0: initFFT(fftSelectedSize_); y@0: initWindow(fftSelectedSize_, windowType_); y@0: } y@0: } y@0: } y@0: } y@0: y@0: //============================================================================== y@0: // Initialise the FFT data structures for a given length transform y@0: void ADRessAudioProcessor::initFFT(int length) y@0: { y@0: if(fftInitialised_) y@0: deinitFFT(); y@0: y@0: // Save the current length so we know how big our results are later y@0: fftActualTransformSize_ = length; y@0: y@0: // Here we allocate the complex-number buffers for the FFT. This uses y@0: // a convenient wrapper on the more general fftw_malloc() y@0: fftTimeDomain_ = fftw_alloc_complex(length); y@0: fftFrequencyDomain_ = fftw_alloc_complex(length); y@0: y@0: // FFTW_ESTIMATE doesn't necessarily produce the fastest executing code (FFTW_MEASURE y@0: // will get closer) but it carries a minimum startup cost. FFTW_MEASURE might stall for y@0: // several seconds which would be annoying in an audio plug-in context. y@0: fftForwardPlan_ = fftw_plan_dft_1d(fftActualTransformSize_, fftTimeDomain_, y@0: fftFrequencyDomain_, FFTW_FORWARD, FFTW_ESTIMATE); y@0: fftBackwardPlan_ = fftw_plan_dft_1d(fftActualTransformSize_, fftFrequencyDomain_, y@0: fftTimeDomain_, FFTW_BACKWARD, FFTW_ESTIMATE); y@0: y@0: // Allocate the buffer that the samples will be collected in y@0: inputBufferLength_ = fftActualTransformSize_; y@0: inputBuffer_.setSize(2, inputBufferLength_); y@0: inputBuffer_.clear(); y@0: inputBufferWritePosition_ = 0; y@0: samplesSinceLastFFT_ = 0; y@0: y@0: // Allocate the output buffer to be twice the size of the FFT y@0: // This will be enough for all hop size cases y@0: outputBufferLength_ = 2*fftActualTransformSize_; y@0: outputBuffer_.setSize(2, outputBufferLength_); y@0: outputBuffer_.clear(); y@0: outputBufferReadPosition_ = 0; y@0: y@0: updateHopSize(); y@0: y@0: fftInitialised_ = true; y@0: } y@0: y@0: // Free the FFT data structures y@0: void ADRessAudioProcessor::deinitFFT() y@0: { y@0: if(!fftInitialised_) y@0: return; y@0: y@0: // Prevent this variable from changing while an audio callback is running. y@0: // Once it has changed, the next audio callback will find that it's not y@0: // initialised and will return silence instead of attempting to work with the y@0: // (invalid) FFT structures. This produces an audible glitch but no crash, y@0: // and is the simplest way to handle parameter changes in this example code. y@0: fftSpinLock_.enter(); y@0: fftInitialised_ = false; y@0: fftSpinLock_.exit(); y@0: y@0: fftw_destroy_plan(fftForwardPlan_); y@0: fftw_destroy_plan(fftBackwardPlan_); y@0: fftw_free(fftTimeDomain_); y@0: fftw_free(fftFrequencyDomain_); y@0: y@0: // Leave the input buffer in memory until the plugin is released y@0: } y@0: y@0: //============================================================================== y@0: // Create a new window of a given length and type y@0: void ADRessAudioProcessor::initWindow(int length, int windowType) y@0: { y@0: if(windowBuffer_ != 0) y@0: deinitWindow(); y@0: if(length == 0) // Sanity check y@0: return; y@0: y@0: // Allocate memory for the window y@0: windowBuffer_ = (double *)malloc(length * sizeof(double)); y@0: y@0: // Write the length as a double here to simplify the code below (otherwise y@0: // typecasts would be wise) y@0: double windowLength = length; y@0: y@0: // Set values for the window, depending on its type y@0: for(int i = 0; i < length; i++) y@0: { y@0: // Window functions are typically defined to be symmetrical. This will cause a y@0: // problem in the overlap-add process: the windows instead need to be periodic y@0: // when arranged end-to-end. As a result we calculate the window of one sample y@0: // larger than usual, and drop the last sample. (This works as long as N is even.) y@0: // See Julius Smith, "Spectral Audio Signal Processing" for details. y@0: switch(windowType) y@0: { y@0: case kWindowBartlett: y@0: windowBuffer_[i] = (2.0/(windowLength + 2.0))* y@0: (0.5*(windowLength + 2.0) - abs((double)i - 0.5*windowLength)); y@0: break; y@0: case kWindowHann: y@0: windowBuffer_[i] = 0.5*(1.0 - cos(2.0*M_PI*(double)i/windowLength)); y@0: break; y@0: case kWindowHamming: y@0: windowBuffer_[i] = 0.54 - 0.46*cos(2.0*M_PI*(double)i/windowLength); y@0: break; y@0: case kWindowRectangular: y@0: default: y@0: windowBuffer_[i] = 1.0; y@0: break; y@0: } y@0: } y@0: y@0: windowBufferLength_ = length; y@0: updateScaleFactor(); y@0: } y@0: y@0: // Free the window buffer y@0: void ADRessAudioProcessor::deinitWindow() y@0: { y@0: if(windowBuffer_ == 0) y@0: return; y@0: y@0: // Delay clearing the window until the audio thread is not running y@0: // to avoid a crash if the code tries to access an invalid window y@0: fftSpinLock_.enter(); y@0: windowBufferLength_ = 0; y@0: fftSpinLock_.exit(); y@0: y@0: free(windowBuffer_); y@0: windowBuffer_ = 0; y@0: } y@0: y@0: // Update the actual hop size depending on the window size and hop size settings y@0: // Hop size is expressed as a fraction of a window in the parameters. y@0: void ADRessAudioProcessor::updateHopSize() y@0: { y@0: switch(hopSelectedSize_) y@0: { y@0: case kHopSize1Window: y@0: hopActualSize_ = fftActualTransformSize_; y@0: break; y@0: case kHopSize1_2Window: y@0: hopActualSize_ = fftActualTransformSize_ / 2; y@0: break; y@0: case kHopSize1_4Window: y@0: hopActualSize_ = fftActualTransformSize_ / 4; y@0: break; y@0: case kHopSize1_8Window: y@0: hopActualSize_ = fftActualTransformSize_ / 8; y@0: break; y@0: } y@0: y@0: // Update the factor by which samples are scaled to preserve unity gain y@0: updateScaleFactor(); y@0: y@0: // Read pointer lags the write pointer to allow for FFT buffers to accumulate and y@0: // be processed. Total latency is sum of the FFT size and the hop size. y@0: outputBufferWritePosition_ = hopActualSize_ + fftActualTransformSize_; y@0: } y@0: y@0: // Update the factor by which each output sample is scaled. This needs to update y@0: // every time FFT size, hop size, and window type are changed. y@0: void ADRessAudioProcessor::updateScaleFactor() y@0: { y@0: // The gain needs to be normalised by the sum of the window, which implicitly y@0: // accounts for the length of the transform and the window type. From there y@0: // we also update based on hop size: smaller hop means more overlap means the y@0: // overall gain should be reduced. y@0: double windowSum = 0.0; y@0: y@0: for(int i = 0; i < windowBufferLength_; i++) y@0: { y@0: windowSum += windowBuffer_[i]; y@0: } y@0: y@0: if(windowSum == 0.0) y@0: fftScaleFactor_ = 0.0; // Catch invalid cases and mute output y@0: else y@0: { y@0: switch(hopSelectedSize_) y@0: { y@0: case kHopSize1Window: // 0dB y@0: fftScaleFactor_ = 1.0/(double)windowSum; y@0: break; y@0: case kHopSize1_2Window: // -6dB y@0: fftScaleFactor_ = 0.5/(double)windowSum; y@0: break; y@0: case kHopSize1_4Window: // -12dB y@0: fftScaleFactor_ = 0.25/(double)windowSum; y@0: break; y@0: case kHopSize1_8Window: // -18dB y@0: fftScaleFactor_ = 0.125/(double)windowSum; y@0: break; y@0: } y@0: } y@0: } y@0: y@0: //============================================================================== y@0: // This creates new instances of the plugin.. y@0: AudioProcessor* JUCE_CALLTYPE createPluginFilter() y@0: { y@0: return new ADRessAudioProcessor(); y@0: }