view AddressPlugin/PluginProcessor.cpp @ 0:2cd427e000b0

initial commit
author Yannick JACOB <y.jacob@se12.qmul.ac.uk>
date Tue, 03 Sep 2013 12:53:16 +0100
parents
children
line wrap: on
line source
/*
  ==============================================================================

    This file was auto-generated!

    It contains the basic startup code for a Juce application.

  ==============================================================================
*/

#include "PluginProcessor.h"
#include "PluginEditor.h"


//==============================================================================

#define 	M_PI   3.14159265358979323846	/* pi */

ADRessAudioProcessor::ADRessAudioProcessor() : inputBuffer_(2, 1), outputBuffer_(2, 1)
{
    // Set default values:
    fftSelectedSize_ = 2048;
    hopSelectedSize_ = kHopSize1_8Window;
    windowType_ = kWindowHann;

    fftInitialised_ = false;
    fftActualTransformSize_ = 0;
    inputBufferLength_ = 1;
    outputBufferLength_ = 1;
    inputBufferWritePosition_ = outputBufferWritePosition_ = outputBufferReadPosition_ = 0;
    samplesSinceLastFFT_ = 0;
    windowBuffer_ = 0;
    windowBufferLength_ = 0;
    preparedToPlay_ = false;
    fftScaleFactor_ = 0.0;
	beta_ = 5;
	width_ = 0;
	azimuth_ = beta_;


	indMinL_ = beta_;
	indMaxL_ = beta_;
	indMinR_ = beta_;
	indMaxR_ = beta_;
	computeR_ = true;
	computeL_ = true;
	invGain_ = 2;
	invBeta_ = 1/(float)beta_;
	i1 = std::complex<double>(0,1);

    
    lastUIWidth_ = 360;
    lastUIHeight_ = 400;
}

ADRessAudioProcessor::~ADRessAudioProcessor()
{
    // Release FFT resources if allocated. This should be handled by
    // releaseResources() but in the event it doesn't happen, this avoids
    // a leak. Harmless to call it twice.
    deinitFFT();
    deinitWindow();
}

//==============================================================================
const String ADRessAudioProcessor::getName() const
{
    return JucePlugin_Name;
}

int ADRessAudioProcessor::getNumParameters()
{
    return kNumParameters;
}

float ADRessAudioProcessor::getParameter (int index)
{
    // This method will be called by the host, probably on the audio thread, so
    // it's absolutely time-critical. Don't use critical sections or anything
    // UI-related, or anything at all that may block in any way!
    switch (index)
    {
        case kFFTSizeParam:    return (float)fftSelectedSize_;
        case kHopSizeParam:    return (float)hopSelectedSize_;
        case kWindowTypeParam: return (float)windowType_;
		case kWidthParam:     return (float)width_;
		case kAzimuthParam:		return (float)azimuth_;

        default:               return 0.0f;
    }
}

void ADRessAudioProcessor::setParameter (int index, float newValue)
{
    // This method will be called by the host, probably on the audio thread, so
    // it's absolutely time-critical. Don't use critical sections or anything
    // UI-related, or anything at all that may block in any way!
    switch (index)
    {
        case kFFTSizeParam:
            if((int)newValue != fftSelectedSize_)
            {
                fftSelectedSize_ = (int)newValue;
                if(preparedToPlay_)
                {
                    // Update settings if currently playing, else wait until prepareToPlay() called
                    initFFT(fftSelectedSize_);
                    initWindow(fftSelectedSize_, windowType_);
                }
            }
            break;
        case kHopSizeParam:
            hopSelectedSize_ = (int)newValue;
            if(preparedToPlay_)
                updateHopSize();
            break;
        case kWindowTypeParam:
            // Recalculate window if needed
            if((int)newValue != windowType_)
            {
                windowType_ = (int)newValue;
                if(preparedToPlay_)
                    initWindow(fftActualTransformSize_, (int)newValue);
            }
            break;

		case kWidthParam:
            width_ = (int)newValue;
			changeParams();
			break;

        case kAzimuthParam:
            azimuth_ = (int)newValue;
			changeParams();
			break;

        default:
            break;
    }
}

void ADRessAudioProcessor::changeParams()
{
	int maxA = std::min(azimuth_ + width_,2*beta_);
	int minA = std::max(azimuth_ - width_,0);
	if (maxA >= beta_)
	{
		computeR_ = true;
		indMinR_ = 2*beta_ - maxA;
		if (minA >= beta_)
		{
			indMaxR_ = 2*beta_ - minA;
		} else {
			indMaxR_ = beta_;
		}
	} else {
		computeR_ = false;
	}

	if (minA <= beta_)
	{
		computeL_ = true;
		indMinL_ = minA;
		if (maxA <= beta_)
		{
			indMaxL_ = maxA;
		} else {
			indMaxL_ = beta_;			
		}
	} else {
		computeL_ = false;
	}

	invGain_ = (indMaxR_ - indMinR_ + indMaxL_ - indMinL_);
	if (computeR_)
		invGain_++;
	if (computeL_)
		invGain_++;
	invGain_ = 1;//invGain_;

}


const String ADRessAudioProcessor::getParameterName (int index)
{
    switch (index)
    {
        case kFFTSizeParam:    return "FFT size";
        case kHopSizeParam:    return "hop size";
        case kWindowTypeParam: return "window type";
		case kWidthParam:     return "volume";
		case kAzimuthParam:     return "azimuth";

        default:               break;
    }
    
    return String::empty;
}

const String ADRessAudioProcessor::getParameterText (int index)
{
    return String (getParameter (index), 2);
}

const String ADRessAudioProcessor::getInputChannelName (int channelIndex) const
{
    return String (channelIndex + 1);
}

const String ADRessAudioProcessor::getOutputChannelName (int channelIndex) const
{
    return String (channelIndex + 1);
}

bool ADRessAudioProcessor::isInputChannelStereoPair (int index) const
{
    return true;
}

bool ADRessAudioProcessor::isOutputChannelStereoPair (int index) const
{
    return true;
}

bool ADRessAudioProcessor::silenceInProducesSilenceOut() const
{
#if JucePlugin_SilenceInProducesSilenceOut
    return true;
#else
    return false;
#endif
}

bool ADRessAudioProcessor::acceptsMidi() const
{
#if JucePlugin_WantsMidiInput
    return true;
#else
    return false;
#endif
}

bool ADRessAudioProcessor::producesMidi() const
{
#if JucePlugin_ProducesMidiOutput
    return true;
#else
    return false;
#endif
}

double ADRessAudioProcessor::getTailLengthSeconds() const
{
    return 0;
}

int ADRessAudioProcessor::getNumPrograms()
{
    return 0;
}

int ADRessAudioProcessor::getCurrentProgram()
{
    return 0;
}

void ADRessAudioProcessor::setCurrentProgram (int index)
{
}

const String ADRessAudioProcessor::getProgramName (int index)
{
    return String::empty;
}

void ADRessAudioProcessor::changeProgramName (int index, const String& newName)
{
}

//==============================================================================
void ADRessAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    // Use this method as the place to do any pre-playback
    // initialisation that you need..
    
    initFFT(fftSelectedSize_);
    initWindow(fftSelectedSize_, windowType_);
    preparedToPlay_ = true;
}

void ADRessAudioProcessor::releaseResources()
{
    // When playback stops, you can use this as an opportunity to free up any
    // spare memory, etc.
    
    deinitFFT();
    deinitWindow();
    preparedToPlay_ = false;
}





void ADRessAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    // Helpful information about this block of samples:
    const int numInputChannels = getNumInputChannels();     // How many input channels for our effect?
    const int numOutputChannels = getNumOutputChannels();   // How many output channels for our effect?
    const int numSamples = buffer.getNumSamples();          // How many samples in the buffer for this block?
    
    int channel, inwritepos, sampsincefft;
    int outreadpos, outwritepos;
    
    // Grab the lock that prevents the FFT settings from changing
    fftSpinLock_.enter();
    
    // Check that we're initialised and ready to go. If not, set output to 0
    if(!fftInitialised_)
    {
        for (channel = 0; channel < numOutputChannels; ++channel)
        {
            buffer.clear (channel, 0, buffer.getNumSamples());
        }
        
        fftSpinLock_.exit();
        return;
    }



    // Go through each channel of audio that's passed in. Collect the samples in the input
    // buffer. When we've reached the next hop interval, calculate the FFT.

    // channelDataL is an array of length numSamples which contains the audio for one channel
	float* channelDataL = buffer.getSampleData(0);
	float* channelDataR = buffer.getSampleData(1);
	
	// inputBufferDataL is the circular buffer for collecting input samples for the FFT
	float* inputBufferDataL = inputBuffer_.getSampleData(0);
	float* inputBufferDataR = inputBuffer_.getSampleData(1);
	
	float* outputBufferDataL = outputBuffer_.getSampleData(0);
	float* outputBufferDataR = outputBuffer_.getSampleData(1);

	
	// State variables need to be temporarily cached for each channel. We don't want the
	// operations on one channel to affect the identical behaviour of the next channel
	inwritepos = inputBufferWritePosition_;
	outwritepos = outputBufferWritePosition_;
	outreadpos = outputBufferReadPosition_;
	sampsincefft = samplesSinceLastFFT_;
	
	for (int i = 0; i < numSamples; ++i)
	{
		const float inL = channelDataL[i];
		const float inR = channelDataR[i];
		
		// Store the next buffered sample in the output. Do this first before anything
		// changes the output buffer-- we will have at least one FFT size worth of data
		// stored and ready to go. Set the result to 0 when finished in preparation for the
		// next overlap/add procedure.

		channelDataL[i] = outputBufferDataL[outreadpos];
		channelDataR[i] = outputBufferDataR[outreadpos];
		outputBufferDataL[outreadpos] = 0.0;
		outputBufferDataR[outreadpos] = 0.0;
			
		if(++outreadpos >= outputBufferLength_)
			outreadpos = 0;

		// Store the current sample in the input buffer, incrementing the write pointer. Also
		// increment how many samples we've stored since the last transform. If it reaches the
		// hop size, perform an FFT and any frequency-domain processing.
		inputBufferDataL[inwritepos] = inL;
		inputBufferDataR[inwritepos] = inR;
		if (++inwritepos >= inputBufferLength_)
			inwritepos = 0;
		if (++sampsincefft >= hopActualSize_)
		{
			sampsincefft = 0;
			
			// Find the index of the starting sample in the buffer. When the buffer length
			// is equal to the transform size, this will be the current write position but
			// this code is more general for larger buffers.
			int inputBufferStartPosition = (inwritepos + inputBufferLength_
											- fftActualTransformSize_) % inputBufferLength_;
			
			// Window the buffer and copy it into the FFT input
			int inputBufferIndex = inputBufferStartPosition;
			for(int fftBufferIndex = 0; fftBufferIndex < fftActualTransformSize_; fftBufferIndex++)
			{
				// Set real part to windowed signal; imaginary part to 0.
				fftTimeDomain_[fftBufferIndex][1] = 0.0;
				if(fftBufferIndex >= windowBufferLength_) // Safety check, in case window isn't ready
					fftTimeDomain_[fftBufferIndex][0] = 0.0;
				else
					fftTimeDomain_[fftBufferIndex][0] = windowBuffer_[fftBufferIndex]
															* inputBufferDataL[inputBufferIndex];
				inputBufferIndex++;
				if(inputBufferIndex >= inputBufferLength_)
					inputBufferIndex = 0;
			}

			// Perform the FFT on the windowed data, going into the frequency domain.Result will be in fftFrequencyDomain_
			fftw_execute(fftForwardPlan_);
			
			// ********** PHASE VOCODER PROCESSING GOES HERE **************
			// This is the place where frequency-domain calculations are made on the transformed signal. 
			//Put the result back into fftFrequencyDomain_ before transforming back.

			for (int ii = 0; ii < fftActualTransformSize_/2+1; ii++)
			{
				realL_[ii] = fftFrequencyDomain_[ii][0];
				imagL_[ii] = fftFrequencyDomain_[ii][1];
			}
			
			inputBufferIndex = inputBufferStartPosition;
			for(int fftBufferIndex = 0; fftBufferIndex < fftActualTransformSize_; fftBufferIndex++)
			{
				if(fftBufferIndex >= windowBufferLength_) // Safety check, in case window isn't ready
					fftTimeDomain_[fftBufferIndex][0] = 0.0;
				else
					fftTimeDomain_[fftBufferIndex][0] = windowBuffer_[fftBufferIndex]
															* inputBufferDataR[inputBufferIndex];
				inputBufferIndex++;
				if(inputBufferIndex >= inputBufferLength_)
					inputBufferIndex = 0;
			}
			fftw_execute(fftForwardPlan_);


			for (int ii = 0; ii < fftActualTransformSize_/2+1; ii++)
			{
				realR_[ii] = fftFrequencyDomain_[ii][0];
				imagR_[ii] = fftFrequencyDomain_[ii][1];
			}


				
			double minV, maxV,newVal;
			std::complex<double> fftTemp;
			int indMin;
			for (int ii = 0; ii <fftActualTransformSize_/2+1; ii++)
			{
				if (computeR_)
				{
					//Right plane
					minV = realL_[ii]*realL_[ii] + imagL_[ii]*imagL_[ii];
					maxV  = minV;
					azr_[ii][0] = 0;
					indMin = 0;
					for (int j = 1; j <= beta_; j++)
					{
						newVal = (realL_[ii] - j*invBeta_*realR_[ii])*(realL_[ii] - j*invBeta_*realR_[ii]) + (imagL_[ii] - j*invBeta_*imagR_[ii])*(imagL_[ii] - j*invBeta_*imagR_[ii]);
						azr_[ii][j] = 0;

						if (newVal>maxV)
						{
							maxV = newVal; 
						} 
						else if (newVal<minV)
						{
							minV = newVal; 
							indMin = j;
						}
					}
					azr_[ii][indMin] = sqrt(maxV) - sqrt(minV);
				}

				if(computeL_)
				{
					////Left plane
					minV = realR_[ii]*realR_[ii] + imagR_[ii]*imagR_[ii];
					maxV  = minV;
					indMin = 0;
					azl_[ii][0] = 0;
					for (int j = 1; j <= beta_; j++)
					{
						newVal = (realR_[ii] - j*invBeta_*realL_[ii])*(realR_[ii] - j*invBeta_*realL_[ii]) + (imagR_[ii] - j*invBeta_*imagL_[ii])*(imagR_[ii] - j*invBeta_*imagL_[ii]);
						azr_[ii][j] = 0;
						if (newVal>maxV)
						{
							maxV = newVal; 
						} 
						else if (newVal<minV)
						{
							minV = newVal; 
							indMin = j;
						}
					}
					azl_[ii][indMin] = sqrt(maxV) - sqrt(minV);
				}
			}


			for (int ii = 0; ii < fftActualTransformSize_/2+1; ii++)
			{
				fftTemp = 0;
				for ( int j = indMinR_; j <= indMaxR_; j++)
				{
					fftTemp += azr_[ii][j]*exp(i1*imagR_[ii]);
				} 
				for ( int j = indMinL_; j <= indMaxL_; j++)
				{
					fftTemp += azl_[ii][j]*exp(i1*imagL_[ii]);
				}
//				fftTemp *= invGain_;	

				fftFrequencyDomain_[ii][0] = real(fftTemp);
				fftFrequencyDomain_[ii][1] = imag(fftTemp);
			}

			for (int ii = 1; ii < fftActualTransformSize_/2; ii++)
			{
				fftFrequencyDomain_[fftActualTransformSize_-ii][0] = fftFrequencyDomain_[ii][0];
				fftFrequencyDomain_[fftActualTransformSize_-ii][1] = -fftFrequencyDomain_[ii][1];
			}
			fftw_execute(fftBackwardPlan_);

			float audioTemp;
			int outputBufferIndex = outwritepos;
			for(int fftBufferIndex = 0; fftBufferIndex < fftActualTransformSize_; fftBufferIndex++)
			{
				audioTemp = (float)fftTimeDomain_[fftBufferIndex][0] * fftScaleFactor_;
				outputBufferDataR[outputBufferIndex] += audioTemp;
				outputBufferDataL[outputBufferIndex] += audioTemp;
				if(++outputBufferIndex >= outputBufferLength_)
					outputBufferIndex = 0;
			}



			// Add the result to the output buffer, starting at the current write position
			// (Output buffer will have been zeroed after reading the last time around)
			// Output needs to be scaled by the transform size to get back to original amplitude:
			// this is a property of how fftw is implemented. Scaling will also need to be adjusted
			// based on hop size to get the same output level (smaller hop size produces more overlap
			// and hence higher signal level)




			// Advance the write position within the buffer by the hop size
			outwritepos = (outwritepos + hopActualSize_) % outputBufferLength_;
		}
	}
    
    
    // Having made a local copy of the state variables for each channel, now transfer the result
    // back to the main state variable so they will be preserved for the next call of processBlock()
    inputBufferWritePosition_ = inwritepos;
    outputBufferWritePosition_ = outwritepos;
    outputBufferReadPosition_ = outreadpos;
    samplesSinceLastFFT_ = sampsincefft;
    
    // In case we have more outputs than inputs, we'll clear any output
    // channels that didn't contain input data, (because these aren't
    // guaranteed to be empty - they may contain garbage).
    for (int i = numInputChannels; i < numOutputChannels; ++i)
    {
        buffer.clear (i, 0, buffer.getNumSamples());
    }
    
    fftSpinLock_.exit();
}

//==============================================================================
bool ADRessAudioProcessor::hasEditor() const
{
    return true; // (change this to false if you choose to not supply an editor)
}

AudioProcessorEditor* ADRessAudioProcessor::createEditor()
{
    return new ADRessAudioProcessorEditor (this);
}

//==============================================================================
void ADRessAudioProcessor::getStateInformation (MemoryBlock& destData)
{
    // You should use this method to store your parameters in the memory block.
    // You could do that either as raw data, or use the XML or ValueTree classes
    // as intermediaries to make it easy to save and load complex data.
    
    // Create an outer XML element..
    XmlElement xml("C4DMPLUGINSETTINGS");
    
    // add some attributes to it..
    xml.setAttribute("uiWidth", lastUIWidth_);
    xml.setAttribute("uiHeight", lastUIHeight_);
    xml.setAttribute("fftSize", fftSelectedSize_);
    xml.setAttribute("hopSize", hopSelectedSize_);
    xml.setAttribute("windowType", windowType_);
	xml.setAttribute("volume", width_);
	xml.setAttribute("azimuth", azimuth_);
    
    // then use this helper function to stuff it into the binary blob and return it..
    copyXmlToBinary(xml, destData);
}

void ADRessAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
    // You should use this method to restore your parameters from this memory block,
    // whose contents will have been created by the getStateInformation() call.
    
    // This getXmlFromBinary() helper function retrieves our XML from the binary blob..
    ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
    
    if(xmlState != 0)
    {
        // make sure that it's actually our type of XML object..
        if(xmlState->hasTagName("C4DMPLUGINSETTINGS"))
        {
            // ok, now pull out our parameters..
            lastUIWidth_  = xmlState->getIntAttribute("uiWidth", lastUIWidth_);
            lastUIHeight_ = xmlState->getIntAttribute("uiHeight", lastUIHeight_);
            
            fftSelectedSize_  = (int)xmlState->getDoubleAttribute("fftSize", fftSelectedSize_);
            hopSelectedSize_  = (int)xmlState->getDoubleAttribute("hopSize", hopSelectedSize_);
            windowType_  = (int)xmlState->getDoubleAttribute("windowType", windowType_);
			width_  = (int)xmlState->getDoubleAttribute("volume", width_);
			azimuth_  = (int)xmlState->getDoubleAttribute("azimuth", azimuth_);

            
            if(preparedToPlay_)
            {
                // Update settings if currently playing, else wait until prepareToPlay() called
                initFFT(fftSelectedSize_);
                initWindow(fftSelectedSize_, windowType_);
            }
        }
    }
}

//==============================================================================
// Initialise the FFT data structures for a given length transform
void ADRessAudioProcessor::initFFT(int length)
{
    if(fftInitialised_)
        deinitFFT();
    
    // Save the current length so we know how big our results are later
    fftActualTransformSize_ = length;
    
    // Here we allocate the complex-number buffers for the FFT. This uses
    // a convenient wrapper on the more general fftw_malloc()
    fftTimeDomain_ = fftw_alloc_complex(length);
    fftFrequencyDomain_ = fftw_alloc_complex(length);
    
    // FFTW_ESTIMATE doesn't necessarily produce the fastest executing code (FFTW_MEASURE
    // will get closer) but it carries a minimum startup cost. FFTW_MEASURE might stall for
    // several seconds which would be annoying in an audio plug-in context.
    fftForwardPlan_ = fftw_plan_dft_1d(fftActualTransformSize_, fftTimeDomain_,
                                       fftFrequencyDomain_, FFTW_FORWARD, FFTW_ESTIMATE);
    fftBackwardPlan_ = fftw_plan_dft_1d(fftActualTransformSize_, fftFrequencyDomain_,
                                       fftTimeDomain_, FFTW_BACKWARD, FFTW_ESTIMATE);
    
    // Allocate the buffer that the samples will be collected in
    inputBufferLength_ = fftActualTransformSize_;
    inputBuffer_.setSize(2, inputBufferLength_);
    inputBuffer_.clear();
    inputBufferWritePosition_ = 0;
    samplesSinceLastFFT_ = 0;
    
    // Allocate the output buffer to be twice the size of the FFT
    // This will be enough for all hop size cases
    outputBufferLength_ = 2*fftActualTransformSize_;
    outputBuffer_.setSize(2, outputBufferLength_);
    outputBuffer_.clear();
    outputBufferReadPosition_ = 0;
    
    updateHopSize();
    
    fftInitialised_ = true;
}

// Free the FFT data structures
void ADRessAudioProcessor::deinitFFT()
{
    if(!fftInitialised_)
        return;
    
    // Prevent this variable from changing while an audio callback is running.
    // Once it has changed, the next audio callback will find that it's not
    // initialised and will return silence instead of attempting to work with the
    // (invalid) FFT structures. This produces an audible glitch but no crash,
    // and is the simplest way to handle parameter changes in this example code.
    fftSpinLock_.enter();
    fftInitialised_ = false;
    fftSpinLock_.exit();
    
    fftw_destroy_plan(fftForwardPlan_);
    fftw_destroy_plan(fftBackwardPlan_);
    fftw_free(fftTimeDomain_);
    fftw_free(fftFrequencyDomain_);
    
    // Leave the input buffer in memory until the plugin is released
}

//==============================================================================
// Create a new window of a given length and type
void ADRessAudioProcessor::initWindow(int length, int windowType)
{
    if(windowBuffer_ != 0)
        deinitWindow();
    if(length == 0) // Sanity check
        return;
    
    // Allocate memory for the window
    windowBuffer_ = (double *)malloc(length * sizeof(double));
    
    // Write the length as a double here to simplify the code below (otherwise
    // typecasts would be wise)
    double windowLength = length;
    
    // Set values for the window, depending on its type
    for(int i = 0; i < length; i++)
    {
        // Window functions are typically defined to be symmetrical. This will cause a
        // problem in the overlap-add process: the windows instead need to be periodic
        // when arranged end-to-end. As a result we calculate the window of one sample
        // larger than usual, and drop the last sample. (This works as long as N is even.)
        // See Julius Smith, "Spectral Audio Signal Processing" for details.
        switch(windowType)
        {
            case kWindowBartlett:
                windowBuffer_[i] = (2.0/(windowLength + 2.0))*
                    (0.5*(windowLength + 2.0) - abs((double)i - 0.5*windowLength));
                break;
            case kWindowHann:
                windowBuffer_[i] = 0.5*(1.0 - cos(2.0*M_PI*(double)i/windowLength));
                break;
            case kWindowHamming:
                windowBuffer_[i] = 0.54 - 0.46*cos(2.0*M_PI*(double)i/windowLength);
                break;
            case kWindowRectangular:
            default:
                windowBuffer_[i] = 1.0;
                break;
        }
    }
    
    windowBufferLength_ = length;
    updateScaleFactor();
}

// Free the window buffer
void ADRessAudioProcessor::deinitWindow()
{
    if(windowBuffer_ == 0)
        return;
    
    // Delay clearing the window until the audio thread is not running
    // to avoid a crash if the code tries to access an invalid window
    fftSpinLock_.enter();
    windowBufferLength_ = 0;
    fftSpinLock_.exit();
    
    free(windowBuffer_);
    windowBuffer_ = 0;
}

// Update the actual hop size depending on the window size and hop size settings
// Hop size is expressed as a fraction of a window in the parameters.
void ADRessAudioProcessor::updateHopSize()
{
    switch(hopSelectedSize_)
    {
        case kHopSize1Window:
            hopActualSize_ = fftActualTransformSize_;
            break;
        case kHopSize1_2Window:
            hopActualSize_ = fftActualTransformSize_ / 2;
            break;
        case kHopSize1_4Window:
            hopActualSize_ = fftActualTransformSize_ / 4;
            break;
        case kHopSize1_8Window:
            hopActualSize_ = fftActualTransformSize_ / 8;
            break;
    }
    
    // Update the factor by which samples are scaled to preserve unity gain
    updateScaleFactor();
    
    // Read pointer lags the write pointer to allow for FFT buffers to accumulate and
    // be processed. Total latency is sum of the FFT size and the hop size.
    outputBufferWritePosition_ = hopActualSize_ + fftActualTransformSize_;
}

// Update the factor by which each output sample is scaled. This needs to update
// every time FFT size, hop size, and window type are changed.
void ADRessAudioProcessor::updateScaleFactor()
{
    // The gain needs to be normalised by the sum of the window, which implicitly
    // accounts for the length of the transform and the window type. From there
    // we also update based on hop size: smaller hop means more overlap means the
    // overall gain should be reduced.
    double windowSum = 0.0;
    
    for(int i = 0; i < windowBufferLength_; i++)
    {
        windowSum += windowBuffer_[i];
    }
    
    if(windowSum == 0.0)
        fftScaleFactor_ = 0.0; // Catch invalid cases and mute output
    else
    {
        switch(hopSelectedSize_)
        {
            case kHopSize1Window:   // 0dB
                fftScaleFactor_ = 1.0/(double)windowSum;
                break;
            case kHopSize1_2Window: // -6dB
                fftScaleFactor_ = 0.5/(double)windowSum;
                break;
            case kHopSize1_4Window: // -12dB
                fftScaleFactor_ = 0.25/(double)windowSum;
                break;
            case kHopSize1_8Window: // -18dB
                fftScaleFactor_ = 0.125/(double)windowSum;
                break;
        }
    }
}

//==============================================================================
// This creates new instances of the plugin..
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
    return new ADRessAudioProcessor();
}