text: /*
  ==============================================================================
  
    This file was auto-generated!
  
    It contains the basic startup code for a Juce application.
  
  ==============================================================================
  */
  
  #include "PluginProcessor.h"
  #include "PluginEditor.h"
  
  
  #if JUCE_WINDOWS
  #pragma warning (push)
  #pragma warning (disable: 4458 4996)
  #endif
  
  //==============================================================================
  PitchedDelayAudioProcessor::PitchedDelayAudioProcessor()
  : currentTab(-1),
  	showTooltips(true)
  {
  	for (int i=0; i<NUMDELAYTABS; ++i)
  	{
  		DelayTabDsp* delay = new DelayTabDsp("Tab" + String(i+1));
  		delays.add(delay);
  	}
  
  	params[kMasterVolume] = 0.25119f;
  	params[kDryVolume] = 0.25119f;
  
  	setLatencySamples(delays[0]->getLatencySamples());
  
  }
  
  PitchedDelayAudioProcessor::~PitchedDelayAudioProcessor()
  {
  }
  
  //==============================================================================
  const String PitchedDelayAudioProcessor::getName() const
  {
    return JucePlugin_Name;
  }
  
  int PitchedDelayAudioProcessor::getNumParameters()
  {
  	const int numParams = getNumDelayParameters() + kNumParameters;
  
    return numParams;
  }
  
  float PitchedDelayAudioProcessor::getParameter (int index)
  {
  	const int numDelayParams = getNumDelayParameters();
  
  	if (index < numDelayParams)
  	{
  		const int numDelay = index / delays[0]->getNumParameters();
  		const int paramIdx = index % delays[0]->getNumParameters();
  
  		DelayTabDsp* dsp = delays[numDelay];
  		jassert(dsp != 0);
  
  		return dsp->getParamNormalized(paramIdx);
  	}
  	else
  	{
  		return params[index - numDelayParams];
  	}
  	
  }
  
  void PitchedDelayAudioProcessor::setParameter (int index, float newValue)
  {
  	const int numDelayParams = getNumDelayParameters();
  
  	if (index < numDelayParams)
  	{
  		const int numDelay = index / delays[0]->getNumParameters();
  		const int paramIdx = index % delays[0]->getNumParameters();
  
  		DelayTabDsp* dsp = delays[numDelay];
  		jassert(dsp != 0);
  
  		dsp->setParamNormalized(paramIdx, newValue);	
  	}
  	else
  	{
  		params[index - numDelayParams] = newValue;
  	}
  
  }
  
  const String PitchedDelayAudioProcessor::getParameterName (int index)
  {
  	const int numDelayParams = getNumDelayParameters();
  
  	if (index < numDelayParams)
  	{
  		const int numDelay = index / delays[0]->getNumParameters();
  		const int paramIdx = index % delays[0]->getNumParameters();
  
  		DelayTabDsp* dsp = delays[numDelay];
  		jassert(dsp != 0);
  
  		return dsp->getId() + "-" + dsp->getParamName(paramIdx);
  	}
  	else
  	{
  		switch(index - numDelayParams)
  		{
  		case kDryVolume:
  			return "DryVolume";
  		case kMasterVolume:
  			return "MasterVolume";
  		default:
  			jassertfalse;
  			return "";
  		}
  	}
  
  }
  
  const String PitchedDelayAudioProcessor::getParameterText (int index)
  {
  	const int numDelayParams = getNumDelayParameters();
  
  	if (index < numDelayParams)
  	{
  		const int numDelay = index / delays[0]->getNumParameters();
  		const int paramIdx = index % delays[0]->getNumParameters();
  
  		DelayTabDsp* dsp = delays[numDelay];
  		jassert(dsp != 0);
  
  		return dsp->getParamText(paramIdx) + " " + dsp->getParamUnit(paramIdx);
  	}
  	else
  	{
  		switch(index - numDelayParams)
  		{
  		case kMasterVolume:
  			return String(Decibels::gainToDecibels(params[kMasterVolume]), 2) + " dB";
  		case kDryVolume:
  			return String(Decibels::gainToDecibels(params[kDryVolume]), 2) + " dB";
  		default:
  			jassertfalse;
  			return "";
  		}
  	}
  }
  
  const String PitchedDelayAudioProcessor::getInputChannelName (int channelIndex) const
  {
    return String (channelIndex + 1);
  }
  
  const String PitchedDelayAudioProcessor::getOutputChannelName (int channelIndex) const
  {
    return String (channelIndex + 1);
  }
  
  bool PitchedDelayAudioProcessor::isInputChannelStereoPair (int /*index*/) const
  {
    return true;
  }
  
  bool PitchedDelayAudioProcessor::isOutputChannelStereoPair (int /*index*/) const
  {
    return true;
  }
  
  bool PitchedDelayAudioProcessor::acceptsMidi() const
  {
  #if JucePlugin_WantsMidiInput
    return true;
  #else
    return false;
  #endif
  }
  
  bool PitchedDelayAudioProcessor::producesMidi() const
  {
  #if JucePlugin_ProducesMidiOutput
    return true;
  #else
    return false;
  #endif
  }
  
  bool PitchedDelayAudioProcessor::silenceInProducesSilenceOut() const
  {
  	return false;
  }
  
  int PitchedDelayAudioProcessor::getNumPrograms()
  {
    return kNumPresets+1;
  }
  
  int PitchedDelayAudioProcessor::getCurrentProgram()
  {
    return 0;
  }
  
  void PitchedDelayAudioProcessor::setCurrentProgram (int index)
  {
  	const int sync = index - 1;
  
  	if (sync >= 0)
  	{
  		for (int i=0; i<delays.size(); ++i)
  			delays.getUnchecked(i)->setParam(DelayTabDsp::kSync, (double) sync);
  	}
  }
  
  const String PitchedDelayAudioProcessor::getProgramName (int index)
  {
  	switch(index - 1)
  	{
  	case kPresetAllSeconds:
  		return "All tabs in seconds";
  	case kPresetAll1_2:
  		return "All tabs in 1/2";
  	case kPresetAll1_2T:
  		return "All tabs in 1/2T";
  	case kPresetAll1_4:
  		return "All tabs in 1/4";
  	case kPresetAll1_4T:
  		return "All tabs in 1/4T";
  	case kPresetAll1_8:
  		return "All tabs in 1/8";
  	case kPresetAll1_8T:
  		return "All tabs in 1/8T";
  	case kPresetAll1_16:
  		return "All tabs in 1/16";
  	default:
  		return "";
  	}
  }
  
  void PitchedDelayAudioProcessor::changeProgramName (int /*index*/, const String& /*newName*/)
  {
  }
  
  //==============================================================================
  void PitchedDelayAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
  {
  
  	int dspSR = 44100;
  	int underSampling = 1;
  
  	switch ((int) sampleRate)
  	{
  	case 44100:
  		dspSR = 44100;
  		break;
  	case 88200:
  		dspSR = 44100;
  		underSampling = 2;
  		break;
  	case 176400:
  		dspSR = 44100;
  		underSampling = 4;
  		break;
  	case 48000:
  		dspSR = 48000;
  		break;
  	case 96000:
  		dspSR = 48000;
  		underSampling = 2;
  		break;
  	case 192000:
  		dspSR = 48000;
  		underSampling = 4;
  		break;
  	default:
  		jassertfalse;
  	}
  
  	{
  		int blockSize = 0;
  		downSamplers.clear();
  		upSamplers.clear();
  		Array<int> lengths;
  
  		for (int i=1; i <= underSampling; i = i << 1)
  		{
  			const int curLength = samplesPerBlock / i;
  			blockSize += 2*curLength;
  			lengths.add(curLength);
  
  			if (i > 1)
  			{
  				upSamplers.add(new UpSampler2x(4, false));
  				downSamplers.add(new DownSampler2x(4, false));
  			}
  		}
  
  		overSampleBuffers.allocate(blockSize, true);
  		osBufferL.allocate(lengths.size(), false);
  		osBufferR.allocate(lengths.size(), false);
  
  		int n=0;
  		for (int i=0; i<lengths.size(); ++i)
  		{
  			osBufferL[i] = &(overSampleBuffers[n]);
  			n += lengths[i];
  			osBufferR[i] = &(overSampleBuffers[n]);
  			n += lengths[i];
  		}
  	}
  
  	for (int i=0; i<NUMDELAYTABS; ++i)
  	{
  		DelayTabDsp* dsp = delays[i];
  
  		dsp->prepareToPlay((double) dspSR, samplesPerBlock);
  	}
  
  
  	const int latency = delays[0]->getLatencySamples() / underSampling;
  	setLatencySamples(latency);
  	latencyCompensation.setLength(latency);
  }
  
  void PitchedDelayAudioProcessor::releaseResources()
  {
  	upSamplers.clear();
  	downSamplers.clear();
  }
  
  void PitchedDelayAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& /*midiMessages*/)
  {
  	ScopedSSECSR csr;
  
  	const int numSamples = buffer.getNumSamples();
  	float* chL = buffer.getWritePointer(0);
  	float* chR = buffer.getWritePointer(buffer.getNumChannels()-1);
  
  
  	float* dspProcL = osBufferL[0];
  	float* dspProcR = osBufferR[0];
  
  	{
  		for (int i=0; i< numSamples; ++i)
  		{
  			dspProcL[i] = chL[i];
  			dspProcR[i] = chR[i];
  		}
  
  		int blockSize = numSamples;
  
  		for (int i=0; i<downSamplers.size(); ++i)
  		{
  			blockSize /= 2;
  			DownSampler2x* down = downSamplers[i];
  			jassert(down != nullptr);
  			down->processBlock(dspProcL, dspProcR, osBufferL[i+1], osBufferR[i+1], blockSize);
  			dspProcL = osBufferL[i+1];
  			dspProcR = osBufferR[i+1];
  		}
  
  		for (int i=0; i<NUMDELAYTABS; ++i)
  		{
  			DelayTabDsp* dsp = delays[i];
  			dsp->processBlock(dspProcL, dspProcR, blockSize);
  		}
  
  		for (int n=0; n<blockSize; ++n)
  		{
  			dspProcL[n] = 0;
  			dspProcR[n] = 0;
  		}
  
  		for (int i=0; i<NUMDELAYTABS; ++i)
  		{
  			DelayTabDsp* dsp = delays[i];
  
  			if (! dsp->isEnabled())
  				continue;
  
  			const float* procL = dsp->getLeftData();
  			const float* procR = dsp->getRightData();
  
  			for (int n=0; n<blockSize; ++n)
  			{
  				dspProcL[n] += procL[n];
  				dspProcR[n] += procR[n];
  			}
  		}
  
  		for (int i=upSamplers.size()-1; i >= 0; --i)
  		{
  			UpSampler2x* up = upSamplers[i];
  			jassert(up != nullptr);
  			up->processBlock(dspProcL, dspProcR, osBufferL[i], osBufferR[i], blockSize);
  			blockSize *= 2;
  			dspProcL = osBufferL[i];
  			dspProcR = osBufferR[i];
  		}
  	}
  
  	for (int i=0; i<numSamples; ++i)
  	{
  		chL[i] *= params[kDryVolume] * 3.981f; // params[kDryVolume] == 1 -> +12 dB
  		chR[i] *= params[kDryVolume] * 3.981f; // params[kDryVolume] == 1 -> +12 dB
  	}
  
  	latencyCompensation.processBlock(chL, chR, numSamples);
  
  	for (int i=0; i<numSamples; ++i)
  	{
  		chL[i] = (chL[i] + dspProcL[i]) * params[kMasterVolume] * 3.981f; // params[kMasterVolume] == 1 -> +12 dB
  		chR[i] = (chR[i] + dspProcR[i]) * params[kMasterVolume] * 3.981f; // params[kMasterVolume] == 1 -> +12 dB
  	}
  
  
      for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
      {
          buffer.clear (i, 0, buffer.getNumSamples());
      }
  }
  
  //==============================================================================
  bool PitchedDelayAudioProcessor::hasEditor() const
  {
    return true; // (change this to false if you choose to not supply an editor)
  }
  
  AudioProcessorEditor* PitchedDelayAudioProcessor::createEditor()
  {
    return new PitchedDelayAudioProcessorEditor (this);
  }
  
  //==============================================================================
  void PitchedDelayAudioProcessor::getStateInformation (MemoryBlock& destData)
  {
  	XmlElement xml("PitchedDelay");
  
  	for (int i=0; i<getNumParameters(); ++i)
  		xml.setAttribute(getParameterName(i), getParameter(i));
  
  	xml.setAttribute("currenttab", jmax(0, currentTab));
  	xml.setAttribute("showtooltips", showTooltips ? 1 : 0);
  
  	xml.setAttribute("extended", "1");
  
  	copyXmlToBinary(xml, destData);
  }
  
  void PitchedDelayAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
  {
  	ScopedPointer<XmlElement> xml(getXmlFromBinary(data, sizeInBytes));
  
  	if (xml != 0 && xml->getTagName() == "PitchedDelay")
  	{
  		const bool extendedsync = xml->hasAttribute("extended");
  
  		for (int i=0; i<getNumParameters(); ++i)
  		{
  			const String paramName(getParameterName(i));
  
  			float val = (float) xml->getDoubleAttribute(paramName, -1000);
  
  			if (! extendedsync && paramName.contains("Sync"))
  				val = val*7/9;
  
  			if (! extendedsync && paramName.contains("PitchType"))
  				val = val*5/8;			
  
  			if (val > -1000)
  				setParameterNotifyingHost(i, val);
  		}
  
  		const int curTab = xml->getIntAttribute("currentTab", -1000);
  		showTooltips = xml->getIntAttribute("showtooltips") == 1;
  		currentTab = curTab > -1000 ? curTab : 0;
  	}
  
  
  }
  
  //==============================================================================
  // This creates new instances of the plugin..
  AudioProcessor* JUCE_CALLTYPE createPluginFilter()
  {
    return new PitchedDelayAudioProcessor();
  }
  
  
  #if JUCE_WINDOWS
  #pragma warning (pop)
  #endif