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

This file was auto-generated!

It contains the basic startup code for a Juce application.

==============================================================================
*/
#include "PluginProcessor.h"
#include "PluginEditor.h"
#include "Engine/Params.h"

// Compare JUCE_VERSION against this to check for JUCE 5.4.3 and earlier
#define JUCE_543 328707

//only sse2 version on windows
#ifdef _WINDOWS
#define __SSE2__
#define __SSE__
#endif

#ifdef __SSE2__
#include <xmmintrin.h>
#endif

//==============================================================================
#define S(T) (juce::String(T))

//==============================================================================
AudioProcessorValueTreeState::ParameterLayout createParameterLayout()
{
    ObxdParams defaultParams;
    std::vector<std::unique_ptr<AudioParameterFloat>> params;
    
    for (int i = 0; i < PARAM_COUNT; ++i)
    {
        auto id           = ObxdAudioProcessor::getEngineParameterId (i);
        auto name         = TRANS (id);
        auto range        = NormalisableRange<float> {0.0f, 1.0f};
        auto defaultValue = defaultParams.values[i];
        auto parameter    = std::make_unique<AudioParameterFloat> (id,
                                                                   name,
                                                                   range,
                                                                   defaultValue);
        
        params.push_back (std::move (parameter));
    }
    
    return { params.begin(), params.end() };
}

//==============================================================================
ObxdAudioProcessor::ObxdAudioProcessor()
	: bindings()
	, programs()
	, configLock("__" JucePlugin_Name "ConfigLock__")
    , apvtState (*this, &undoManager, "PARAMETERS", createParameterLayout())
{
	isHostAutomatedChange = true;
	midiControlledParamSet = false;
	lastMovedController = 0;
	lastUsedParameter = 0;

	synth.setSampleRate (44100);

	PropertiesFile::Options options;
	options.applicationName = JucePlugin_Name;
	options.storageFormat = PropertiesFile::storeAsXML;
	options.millisecondsBeforeSaving = 2500;
	options.processLock = &configLock;
	config = std::unique_ptr<PropertiesFile> (new PropertiesFile (getDocumentFolder().getChildFile ("Skin.xml"), options));

	currentSkin = config->containsKey("skin") ? config->getValue("skin") : "Ilkka Rosma Dark";
	currentBank = "000 - FMR OB-Xa Patch Book";

	scanAndUpdateBanks();
    scanAndUpdateSkins();
    initAllParams();

	if (bankFiles.size() > 0)
	{
		loadFromFXBFile (bankFiles[0]);
	}
    
    for (int i = 0; i < PARAM_COUNT; ++i)
    {
        apvtState.addParameterListener (getEngineParameterId (i), this);
    }
    
    apvtState.state = ValueTree (JucePlugin_Name);
}

ObxdAudioProcessor::~ObxdAudioProcessor()
{
	config->saveIfNeeded();
	config = nullptr;
}

//==============================================================================
void ObxdAudioProcessor::initAllParams()
{
    for (int i = 0; i < PARAM_COUNT; ++i)
    {
        setEngineParameterValue (i, programs.currentProgramPtr->values[i], true);
    }
}

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

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

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

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

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

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

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

double ObxdAudioProcessor::getTailLengthSeconds() const
{
	return 0.0;
}

//==============================================================================
int ObxdAudioProcessor::getNumPrograms()
{
	return PROGRAMCOUNT;
}

int ObxdAudioProcessor::getCurrentProgram()
{
	return programs.currentProgram;
}

void ObxdAudioProcessor::setCurrentProgram (int index)
{
	programs.currentProgram = index;
	programs.currentProgramPtr = programs.programs + programs.currentProgram;
	isHostAutomatedChange = false;
    
	for (int i = 0; i < PARAM_COUNT; ++i)
		setEngineParameterValue (i, programs.currentProgramPtr->values[i], true);
    
	isHostAutomatedChange = true;
	sendChangeMessage();
	updateHostDisplay();
}

const String ObxdAudioProcessor::getProgramName (int index)
{
	return programs.programs[index].name;
}

void ObxdAudioProcessor::changeProgramName (int index, const String& newName)
{
	 programs.programs[index].name = newName;
}

//==============================================================================
void ObxdAudioProcessor::prepareToPlay (double sampleRate, int /*samplesPerBlock*/)
{
	// Use this method as the place to do any pre-playback
	// initialisation that you need..
	nextMidi = new MidiMessage (0xF0);
	midiMsg  = new MidiMessage (0xF0);
	synth.setSampleRate (sampleRate);
}

void ObxdAudioProcessor::releaseResources()
{
}

inline void ObxdAudioProcessor::processMidiPerSample (MidiBuffer::Iterator* iter, const int samplePos)
{
	while (getNextEvent (iter, samplePos))
	{
		if (midiMsg->isNoteOn())
		{
			synth.procNoteOn (midiMsg->getNoteNumber(), midiMsg->getFloatVelocity());
		}
		if (midiMsg->isNoteOff())
		{
			synth.procNoteOff (midiMsg->getNoteNumber());
		}
		if (midiMsg->isPitchWheel())
		{
			// [0..16383] center = 8192;
			synth.procPitchWheel ((midiMsg->getPitchWheelValue() - 8192) / 8192.0f);
		}
		if (midiMsg->isController() && midiMsg->getControllerNumber() == 1)
        {
			synth.procModWheel (midiMsg->getControllerValue() / 127.0f);
        }
		if (midiMsg->isController())
		{
			lastMovedController = midiMsg->getControllerNumber();
            
            if (programs.currentProgramPtr->values[MIDILEARN] > 0.5f){
                midiControlledParamSet = true;
				bindings[lastMovedController] = lastUsedParameter;
                setEngineParameterValue (MIDILEARN, 0, true);
                lastMovedController = 0;
                lastUsedParameter = 0;
                midiControlledParamSet = false;
            }

			if (bindings[lastMovedController] > 0)
			{
				midiControlledParamSet = true;
				setEngineParameterValue (bindings[lastMovedController],
                                         midiMsg->getControllerValue() / 127.0f, true);
                
				setEngineParameterValue (MIDILEARN, 0, true);
				lastMovedController = 0;
				lastUsedParameter = 0;

				midiControlledParamSet = false;
			}

		}
		if(midiMsg->isSustainPedalOn())
		{
			synth.sustainOn();
		}
		if(midiMsg->isSustainPedalOff() || midiMsg->isAllNotesOff()||midiMsg->isAllSoundOff())
		{
			synth.sustainOff();
		}
		if(midiMsg->isAllNotesOff())
		{
			synth.allNotesOff();
		}
		if(midiMsg->isAllSoundOff())
		{
			synth.allSoundOff();
		}

	}
}

bool ObxdAudioProcessor::getNextEvent (MidiBuffer::Iterator* iter, const int samplePos)
{
	if (hasMidiMessage && midiEventPos <= samplePos)
	{
		*midiMsg = *nextMidi;
		hasMidiMessage = iter->getNextEvent (*nextMidi, midiEventPos);
		return true;
	}
    
	return false;
}

void ObxdAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
	//SSE flags set
#ifdef __SSE__
	_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
#endif
#ifdef __SSE2__
	// _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
#endif

	MidiBuffer::Iterator ppp (midiMessages);
	hasMidiMessage = ppp.getNextEvent (*nextMidi, midiEventPos);

	int samplePos = 0;
	int numSamples = buffer.getNumSamples();
	float* channelData1 = buffer.getWritePointer (0);
	float* channelData2 = buffer.getWritePointer (1);

	AudioPlayHead::CurrentPositionInfo pos;
    
    if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (pos))
    {
		synth.setPlayHead(pos.bpm, pos.ppqPosition);
    }

	while (samplePos < numSamples)
	{
		processMidiPerSample (&ppp, samplePos);
		synth.processSample (channelData1+samplePos, channelData2+samplePos);
		++samplePos;
	}
}

//==============================================================================
bool ObxdAudioProcessor::hasEditor() const
{
	return true;
}

AudioProcessorEditor* ObxdAudioProcessor::createEditor()
{
	return new ObxdAudioProcessorEditor (*this);
}

//==============================================================================
void ObxdAudioProcessor::getStateInformation(MemoryBlock& destData)
{
	XmlElement xmlState = XmlElement("discoDSP");
	xmlState.setAttribute(S("currentProgram"), programs.currentProgram);

	XmlElement* xprogs = new XmlElement("programs");
	for (int i = 0; i < PROGRAMCOUNT; ++i)
	{
		XmlElement* xpr = new XmlElement("program");
		xpr->setAttribute(S("programName"), programs.programs[i].name);
		xpr->setAttribute(S("voiceCount"), Motherboard::MAX_VOICES);

		for (int k = 0; k < PARAM_COUNT; ++k)
		{
            xpr->setAttribute("Val_" + String(k), programs.programs[i].values[k]);
		}

		xprogs->addChildElement(xpr);
	}

	xmlState.addChildElement(xprogs);

    bindings.setXml(xmlState);

	copyXmlToBinary(xmlState, destData);
}

void ObxdAudioProcessor::getCurrentProgramStateInformation(MemoryBlock& destData)
{
	XmlElement xmlState = XmlElement("discoDSP");

	for (int k = 0; k < PARAM_COUNT; ++k)
	{
		xmlState.setAttribute("Val_" + String(k), programs.currentProgramPtr->values[k]);
	}

	xmlState.setAttribute(S("voiceCount"), Motherboard::MAX_VOICES);
	xmlState.setAttribute(S("programName"), programs.currentProgramPtr->name);

	copyXmlToBinary(xmlState, destData);
}

void ObxdAudioProcessor::setStateInformation(const void* data, int sizeInBytes)
{
#if JUCE_VERSION <= JUCE_543
	XmlElement * const xmlState = getXmlFromBinary(data, sizeInBytes);
#else
	std::unique_ptr<XmlElement> xmlState = getXmlFromBinary(data, sizeInBytes);
#endif
	if (xmlState)
	{
		XmlElement* xprogs = xmlState->getFirstChildElement();
		if (xprogs->hasTagName(S("programs")))
		{
			int i = 0;
			forEachXmlChildElement(*xprogs, e)
			{
				bool newFormat = e->hasAttribute("voiceCount");
				programs.programs[i].setDefaultValues();

				for (int k = 0; k < PARAM_COUNT; ++k)
				{
                    float value = 0.0;
                    if (e->hasAttribute("Val_" + String(k))){
                        value = float(e->getDoubleAttribute("Val_" + String(k), programs.programs[i].values[k]));
                    } else {
                        value = float(e->getDoubleAttribute(String(k), programs.programs[i].values[k]));
                    }
                    
					if (!newFormat && k == VOICE_COUNT) value *= 0.25f;
					programs.programs[i].values[k] = value;
				}

				programs.programs[i].name = e->getStringAttribute(S("programName"), S("Default"));

				++i;
				}
			}

		bindings.getXml(*xmlState);
#if ! DEMOVERSION
		setCurrentProgram(xmlState->getIntAttribute(S("currentProgram"), 0));

        sendChangeMessage();
#endif
#if JUCE_VERSION <= JUCE_543
		delete xmlState;
#endif
		}
	}

void  ObxdAudioProcessor::setCurrentProgramStateInformation(const void* data, int sizeInBytes)
{
#if JUCE_VERSION <= JUCE_543
	XmlElement * const e = getXmlFromBinary(data, sizeInBytes);
#else
	std::unique_ptr<XmlElement> e = getXmlFromBinary(data, sizeInBytes);
#endif
	if (e)
	{
		programs.currentProgramPtr->setDefaultValues();

		bool newFormat = e->hasAttribute("voiceCount");
		for (int k = 0; k < PARAM_COUNT; ++k)
		{
            float value = 0.0 ;
            if (e->hasAttribute("Val_" + String(k))){
                value = float(e->getDoubleAttribute("Val_" + String(k), programs.currentProgramPtr->values[k]));
            } else {
                value = float(e->getDoubleAttribute(String(k), programs.currentProgramPtr->values[k]));
            }
			 
			if (!newFormat && k == VOICE_COUNT) value *= 0.25f;
			programs.currentProgramPtr->values[k] = value;
		}

		programs.currentProgramPtr->name = e->getStringAttribute(S("programName"), S("Default"));

		setCurrentProgram(programs.currentProgram);
        
        sendChangeMessage();
#if JUCE_VERSION <= JUCE_543
		delete e;
#endif
	}
}

//==============================================================================
bool ObxdAudioProcessor::loadFromFXBFile(const File& fxbFile)
{
	MemoryBlock mb;
	if (! fxbFile.loadFileAsData(mb))
		return false;

	const void* const data = mb.getData();
	const size_t dataSize = mb.getSize();

	if (dataSize < 28)
		return false;

	const fxSet* const set = (const fxSet*) data;

	if ((! compareMagic (set->chunkMagic, "CcnK")) || fxbSwap (set->version) > fxbVersionNum)
		return false;

	if (compareMagic (set->fxMagic, "FxBk"))
	{
		// bank of programs
		if (fxbSwap (set->numPrograms) >= 0)
		{
			const int oldProg = getCurrentProgram();
			const int numParams = fxbSwap (((const fxProgram*) (set->programs))->numParams);
			const int progLen = (int) sizeof (fxProgram) + (numParams - 1) * (int) sizeof (float);

			for (int i = 0; i < fxbSwap (set->numPrograms); ++i)
			{
				if (i != oldProg)
				{
					const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + i * progLen);
					if (((const char*) prog) - ((const char*) set) >= (ssize_t) dataSize)
						return false;

					if (fxbSwap (set->numPrograms) > 0)
						setCurrentProgram (i);

					if (! restoreProgramSettings (prog))
						return false;
				}
			}

			if (fxbSwap (set->numPrograms) > 0)
				setCurrentProgram (oldProg);

			const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + oldProg * progLen);
			if (((const char*) prog) - ((const char*) set) >= (ssize_t) dataSize)
				return false;

			if (! restoreProgramSettings (prog))
				return false;
		}
	}
	else if (compareMagic (set->fxMagic, "FxCk"))
	{
		// single program
		const fxProgram* const prog = (const fxProgram*) data;

		if (! compareMagic (prog->chunkMagic, "CcnK"))
			return false;

		changeProgramName (getCurrentProgram(), prog->prgName);

		for (int i = 0; i < fxbSwap (prog->numParams); ++i)
			setEngineParameterValue (i, fxbSwapFloat (prog->params[i]));
	}
	else if (compareMagic (set->fxMagic, "FBCh"))
	{
		// non-preset chunk
		const fxChunkSet* const cset = (const fxChunkSet*) data;

		if ((size_t) fxbSwap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (size_t) dataSize)
			return false;

		setStateInformation(cset->chunk, fxbSwap (cset->chunkSize));
	}
	else if (compareMagic (set->fxMagic, "FPCh"))
	{
		// preset chunk
		const fxProgramSet* const cset = (const fxProgramSet*) data;

		if ((size_t) fxbSwap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (size_t) dataSize)
			return false;

		setCurrentProgramStateInformation(cset->chunk, fxbSwap (cset->chunkSize));

		changeProgramName (getCurrentProgram(), cset->name);
	}
	else
	{
		return false;
	}

	currentBank = fxbFile.getFileName();

	updateHostDisplay();

	return true;
}

bool ObxdAudioProcessor::restoreProgramSettings(const fxProgram* const prog)
{
	if (compareMagic (prog->chunkMagic, "CcnK")
		&& compareMagic (prog->fxMagic, "FxCk"))
	{
		changeProgramName (getCurrentProgram(), prog->prgName);

		for (int i = 0; i < fxbSwap (prog->numParams); ++i)
			setEngineParameterValue (i, fxbSwapFloat (prog->params[i]));

		return true;
	}

	return false;
}

//==============================================================================
void ObxdAudioProcessor::scanAndUpdateBanks()
{
	bankFiles.clearQuick();

	DirectoryIterator it (getBanksFolder(), false, "*.fxb", File::findFiles);
	
    while (it.next())
	{
		bankFiles.addUsingDefaultSort (it.getFile());
	}
}

void ObxdAudioProcessor::scanAndUpdateSkins()
{
    skinFiles.clearQuick();
    DirectoryIterator it (getSkinFolder(), false, "*", File::findDirectories);
    
    while (it.next())
    {
        skinFiles.addUsingDefaultSort (it.getFile());
    }
    
}

const Array<File>& ObxdAudioProcessor::getBankFiles() const
{
	return bankFiles;
}

const Array<File>& ObxdAudioProcessor::getSkinFiles() const
{
    return skinFiles;
}

File ObxdAudioProcessor::getCurrentBankFile() const
{
	return getBanksFolder().getChildFile(currentBank);
}

//==============================================================================
File ObxdAudioProcessor::getDocumentFolder() const
{
	File folder = File::getSpecialLocation(File::userDocumentsDirectory).getChildFile("discoDSP").getChildFile("OB-Xd");
/*
    if (! folder.exists())
    {
        NativeMessageBox::showMessageBox(AlertWindow::WarningIcon, "Error", "Documents > discoDSP > OB-Xd folder not found.");
    }
 */
	if (folder.isSymbolicLink())
		folder = folder.getLinkedTarget();
	return folder;
}

File ObxdAudioProcessor::getSkinFolder() const
{
	return getDocumentFolder().getChildFile("Themes");
}

File ObxdAudioProcessor::getBanksFolder() const
{
	return getDocumentFolder().getChildFile("Banks");
}

File ObxdAudioProcessor::getCurrentSkinFolder() const
{
	return getSkinFolder().getChildFile(currentSkin);
}

void ObxdAudioProcessor::setCurrentSkinFolder(const String& folderName)
{
	currentSkin = folderName;

	config->setValue("skin", folderName);
	config->setNeedsToBeSaved(true);
}

//==============================================================================
String ObxdAudioProcessor::getEngineParameterId (size_t index)
{
    switch (index)
	{
        case SELF_OSC_PUSH:      return "SelfOscPush";
        case ENV_PITCH_BOTH:     return "EnvPitchBoth";
        case FENV_INVERT:        return "FenvInvert";
		case PW_OSC2_OFS:        return "PwOfs";
        case LEVEL_DIF:          return "LevelDif";
        case PW_ENV_BOTH:        return "PwEnvBoth";
		case PW_ENV:             return "PwEnv";
	    case LFO_SYNC:           return "LfoSync";
        case ECONOMY_MODE:       return "EconomyMode";
	    case UNLEARN:            return "MidiUnlearn";
        case MIDILEARN:          return "MidiLearn";
	    case VAMPENV:            return "VAmpFactor";
        case VFLTENV:            return "VFltFactor";
	    case ASPLAYEDALLOCATION: return "AsPlayedAllocation";
	    case BENDLFORATE:        return "VibratoRate";
	    case FOURPOLE:           return "FourPole";
	    case LEGATOMODE:         return "LegatoMode";
	    case ENVPITCH:           return "EnvelopeToPitch";
	    case OSCQuantize:        return "PitchQuant";
        case VOICE_COUNT:        return "VoiceCount";
	    case BANDPASS:           return "BandpassBlend";
	    case FILTER_WARM:        return "Filter_Warm";
	    case BENDRANGE:          return "BendRange";
        case BENDOSC2:           return "BendOsc2Only";
        case OCTAVE:             return "Octave";
        case TUNE:               return "Tune";
        case BRIGHTNESS:         return "Brightness";
        case NOISEMIX:           return "NoiseMix";
        case OSC1MIX:            return "Osc1Mix";
        case OSC2MIX:            return "Osc2Mix";
        case MULTIMODE:          return "Multimode";
        case LFOSHWAVE:          return "LfoSampleHoldWave";
        case LFOSINWAVE:         return "LfoSineWave";
        case LFOSQUAREWAVE:      return "LfoSquareWave";
        case LFO1AMT:            return "LfoAmount1";
        case LFO2AMT:            return "LfoAmount2";
        case LFOFILTER:          return "LfoFilter";
        case LFOOSC1:            return "LfoOsc1";
        case LFOOSC2:            return "LfoOsc2";
        case LFOFREQ:            return "LfoFrequency";
        case LFOPW1:             return "LfoPw1";
        case LFOPW2:             return "LfoPw2";
        case PORTADER:           return "PortamentoDetune";
        case FILTERDER:          return "FilterDetune";
        case ENVDER:             return "EnvelopeDetune";
        case PAN1:               return "Pan1";
        case PAN2:               return "Pan2";
        case PAN3:               return "Pan3";
        case PAN4:               return "Pan4";
        case PAN5:               return "Pan5";
        case PAN6:               return "Pan6";
        case PAN7:               return "Pan7";
        case PAN8:               return "Pan8";
        case XMOD:               return "Xmod";
        case OSC2HS:             return "Osc2HardSync";
        case OSC1P:              return "Osc1Pitch";
        case OSC2P:              return "Osc2Pitch";
        case PORTAMENTO:         return "Portamento";
        case UNISON:             return "Unison";
        case FLT_KF:             return "FilterKeyFollow";
        case PW:                 return "PulseWidth";
        case OSC2Saw:            return "Osc2Saw";
        case OSC1Saw:            return "Osc1Saw";
        case OSC1Pul:            return "Osc1Pulse";
        case OSC2Pul:            return "Osc2Pulse";
        case VOLUME:             return "Volume";
        case UDET:               return "VoiceDetune";
        case OSC2_DET:           return "Oscillator2detune";
        case CUTOFF:             return "Cutoff";
        case RESONANCE:          return "Resonance";
        case ENVELOPE_AMT:       return "FilterEnvAmount";
        case LATK:               return "Attack";
        case LDEC:               return "Decay";
        case LSUS:               return "Sustain";
        case LREL:               return "Release";
        case FATK:               return "FilterAttack";
        case FDEC:               return "FilterDecay";
        case FSUS:               return "FilterSustain";
        case FREL:               return "FilterRelease";
            
        default:
            break;
    }
    
    return "Undefined";
}

int ObxdAudioProcessor::getParameterIndexFromId (String paramId)
{
    for (size_t i = 0; i < PARAM_COUNT; ++i)
    {
        if (paramId.compare (getEngineParameterId (i)) == 0)
        {
            return int (i);
        }
    }
    
    return -1;
}

void ObxdAudioProcessor::setEngineParameterValue (int index, float newValue, bool notifyToHost)
{
    if (! midiControlledParamSet || index == MIDILEARN || index == UNLEARN)
    {
        lastUsedParameter = index;
    }
    
    programs.currentProgramPtr->values[index] = newValue;
    
    if (notifyToHost){
        apvtState.getParameter(getEngineParameterId(index))->setValueNotifyingHost(newValue);
    } else {
        apvtState.getParameter(getEngineParameterId(index))->setValue(newValue);
    }
    
    switch (index)
    {
        case SELF_OSC_PUSH:
            synth.processSelfOscPush (newValue);
            break;
        case PW_ENV_BOTH:
            synth.processPwEnvBoth (newValue);
            break;
        case PW_OSC2_OFS:
            synth.processPwOfs (newValue);
            break;
        case ENV_PITCH_BOTH:
            synth.processPitchModBoth (newValue);
            break;
        case FENV_INVERT:
            synth.processInvertFenv (newValue);
            break;
        case LEVEL_DIF:
            synth.processLoudnessDetune (newValue);
            break;
        case PW_ENV:
            synth.processPwEnv (newValue);
            break;
        case LFO_SYNC:
            synth.procLfoSync (newValue);
            break;
        case ECONOMY_MODE:
            synth.procEconomyMode (newValue);
            break;
        case VAMPENV:
            synth.procAmpVelocityAmount (newValue);
            break;
        case VFLTENV:
            synth.procFltVelocityAmount (newValue);
            break;
        case ASPLAYEDALLOCATION:
            synth.procAsPlayedAlloc (newValue);
            break;
        case BENDLFORATE:
            synth.procModWheelFrequency (newValue);
            break;
        case FOURPOLE:
            synth.processFourPole (newValue);
            break;
        case LEGATOMODE:
            synth.processLegatoMode (newValue);
            break;
        case ENVPITCH:
            synth.processEnvelopeToPitch (newValue);
            break;
        case OSCQuantize:
            synth.processPitchQuantization (newValue);
            break;
        case VOICE_COUNT:
            synth.setVoiceCount (newValue);
            break;
        case BANDPASS:
            synth.processBandpassSw (newValue);
            break;
        case FILTER_WARM:
            synth.processOversampling (newValue);
            break;
        case BENDOSC2:
            synth.procPitchWheelOsc2Only (newValue);
            break;
        case BENDRANGE:
            synth.procPitchWheelAmount (newValue);
            break;
        case NOISEMIX:
            synth.processNoiseMix (newValue);
            break;
        case OCTAVE:
            synth.processOctave (newValue);
            break;
        case TUNE:
            synth.processTune (newValue);
            break;
        case BRIGHTNESS:
            synth.processBrightness (newValue);
            break;
        case MULTIMODE:
            synth.processMultimode (newValue);
            break;
        case LFOFREQ:
            synth.processLfoFrequency (newValue);
            break;
        case LFO1AMT:
            synth.processLfoAmt1 (newValue);
            break;
        case LFO2AMT:
            synth.processLfoAmt2 (newValue);
            break;
        case LFOSINWAVE:
            synth.processLfoSine (newValue);
            break;
        case LFOSQUAREWAVE:
            synth.processLfoSquare (newValue);
            break;
        case LFOSHWAVE:
            synth.processLfoSH (newValue);
            break;
        case LFOFILTER:
            synth.processLfoFilter (newValue);
            break;
        case LFOOSC1:
            synth.processLfoOsc1 (newValue);
            break;
        case LFOOSC2:
            synth.processLfoOsc2 (newValue);
            break;
        case LFOPW1:
            synth.processLfoPw1 (newValue);
            break;
        case LFOPW2:
            synth.processLfoPw2 (newValue);
            break;
        case PORTADER:
            synth.processPortamentoDetune (newValue);
            break;
        case FILTERDER:
            synth.processFilterDetune (newValue);
            break;
        case ENVDER:
            synth.processEnvelopeDetune (newValue);
            break;
        case XMOD:
            synth.processOsc2Xmod (newValue);
            break;
        case OSC2HS:
            synth.processOsc2HardSync (newValue);
            break;
        case OSC2P:
            synth.processOsc2Pitch (newValue);
            break;
        case OSC1P:
            synth.processOsc1Pitch (newValue);
            break;
        case PORTAMENTO:
            synth.processPortamento (newValue);
            break;
        case UNISON:
            synth.processUnison (newValue);
            break;
        case FLT_KF:
            synth.processFilterKeyFollow (newValue);
            break;
        case OSC1MIX:
            synth.processOsc1Mix (newValue);
            break;
        case OSC2MIX:
            synth.processOsc2Mix (newValue);
            break;
        case PW:
            synth.processPulseWidth (newValue);
            break;
        case OSC1Saw:
            synth.processOsc1Saw (newValue);
            break;
        case OSC2Saw:
            synth.processOsc2Saw (newValue);
            break;
        case OSC1Pul:
            synth.processOsc1Pulse (newValue);
            break;
        case OSC2Pul:
            synth.processOsc2Pulse (newValue);
            break;
        case VOLUME:
            synth.processVolume (newValue);
            break;
        case UDET:
            synth.processDetune (newValue);
            break;
        case OSC2_DET:
            synth.processOsc2Det (newValue);
            break;
        case CUTOFF:
            synth.processCutoff (newValue);
            break;
        case RESONANCE:
            synth.processResonance (newValue);
            break;
        case ENVELOPE_AMT:
            synth.processFilterEnvelopeAmt (newValue);
            break;
        case LATK:
            synth.processLoudnessEnvelopeAttack (newValue);
            break;
        case LDEC:
            synth.processLoudnessEnvelopeDecay (newValue);
            break;
        case LSUS:
            synth.processLoudnessEnvelopeSustain (newValue);
            break;
        case LREL:
            synth.processLoudnessEnvelopeRelease (newValue);
            break;
        case FATK:
            synth.processFilterEnvelopeAttack (newValue);
            break;
        case FDEC:
            synth.processFilterEnvelopeDecay (newValue);
            break;
        case FSUS:
            synth.processFilterEnvelopeSustain (newValue);
            break;
        case FREL:
            synth.processFilterEnvelopeRelease (newValue);
            break;
        case PAN1:
            synth.processPan (newValue,1);
            break;
        case PAN2:
            synth.processPan (newValue,2);
            break;
        case PAN3:
            synth.processPan (newValue,3);
            break;
        case PAN4:
            synth.processPan (newValue,4);
            break;
        case PAN5:
            synth.processPan (newValue,5);
            break;
        case PAN6:
            synth.processPan (newValue,6);
            break;
        case PAN7:
            synth.processPan (newValue,7);
            break;
        case PAN8:
            synth.processPan (newValue,8);
            break;
    }
    
    //DIRTY HACK
    //This should be checked to avoid stalling on gui update
    //It is needed because some hosts do  wierd stuff
    if (isHostAutomatedChange)
        sendChangeMessage();
}

//==============================================================================
void ObxdAudioProcessor::parameterChanged (const String& parameter, float newValue)
{
    int index = getParameterIndexFromId (parameter);
    
    if ( isPositiveAndBelow (index, PARAM_COUNT) )
    {
        isHostAutomatedChange = false;
        setEngineParameterValue (index, newValue);
        isHostAutomatedChange = true;
    }
}

AudioProcessorValueTreeState& ObxdAudioProcessor::getPluginState()
{
    return apvtState;
}

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