1167 lines
34 KiB
C++
Executable file
1167 lines
34 KiB
C++
Executable file
/*
|
|
==============================================================================
|
|
|
|
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->isSustainPedalOn())
|
|
{
|
|
synth.sustainOn();
|
|
}
|
|
if(midiMsg->isSustainPedalOff() || midiMsg->isAllNotesOff()||midiMsg->isAllSoundOff())
|
|
{
|
|
synth.sustainOff();
|
|
}
|
|
if(midiMsg->isAllNotesOff())
|
|
{
|
|
synth.allNotesOff();
|
|
}
|
|
if(midiMsg->isAllSoundOff())
|
|
{
|
|
synth.allSoundOff();
|
|
}
|
|
|
|
DBG(" Message: " << midiMsg->getChannel() << " "<<midiMsg->getRawData()[0] << " "<< midiMsg->getRawData()[1] << " "<< midiMsg->getRawData()[2]);
|
|
|
|
if (midiMsg->isProgramChange()){ // xC0
|
|
setCurrentProgram(midiMsg->getProgramChangeNumber());
|
|
|
|
} else
|
|
if (midiMsg->isController()) // xB0
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 && 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::deleteBank() {
|
|
currentBankFile.deleteFile();
|
|
scanAndUpdateBanks();
|
|
if (bankFiles.size() > 0)
|
|
{
|
|
loadFromFXBFile (bankFiles[0]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ObxdAudioProcessor::saveBank() {
|
|
saveFXBFile(currentBankFile);
|
|
}
|
|
|
|
bool ObxdAudioProcessor::loadPreset(const File& fxpFile) {
|
|
loadFromFXBFile(fxpFile);
|
|
currentPreset = fxpFile.getFileName();
|
|
currentPresetFile = fxpFile;
|
|
return true;
|
|
}
|
|
|
|
bool ObxdAudioProcessor::saveFXPFile(const File& fxpFile){
|
|
//auto xml = std::unique_ptr<juce::XmlElement>(new juce::XmlElement(""));
|
|
juce::MemoryBlock m, memoryBlock;
|
|
getCurrentProgramStateInformation(m);
|
|
{
|
|
memoryBlock.reset();
|
|
auto totalLen = sizeof (fxProgramSet) + m.getSize() - 8;
|
|
memoryBlock.setSize (totalLen, true);
|
|
|
|
auto set = static_cast<fxProgramSet*>(memoryBlock.getData());
|
|
set->chunkMagic = fxbName ("CcnK");
|
|
set->byteSize = 0;
|
|
set->fxMagic = fxbName ("FPCh");
|
|
set->version = fxbSwap (fxbVersionNum);
|
|
set->fxID = fxbName ("Obxd");
|
|
set->fxVersion = fxbSwap (fxbVersionNum);
|
|
set->numPrograms = fxbSwap (getNumPrograms());
|
|
programs.currentProgramPtr->name.copyToUTF8(set->name, 28);
|
|
set->chunkSize = fxbSwap (static_cast<int32>(m.getSize()));
|
|
|
|
m.copyTo (set->chunk, 0, m.getSize());
|
|
|
|
fxpFile.replaceWithData(memoryBlock.getData(), memoryBlock.getSize());
|
|
}
|
|
}
|
|
|
|
bool ObxdAudioProcessor::savePreset(const File& fxpFile) {
|
|
saveFXPFile(fxpFile);
|
|
currentPreset = fxpFile.getFileName();
|
|
currentPresetFile = fxpFile;
|
|
}
|
|
|
|
void ObxdAudioProcessor::changePresetName(const String &name){
|
|
programs.currentProgramPtr->name = name;
|
|
//savePreset();
|
|
//saveBank();
|
|
}
|
|
|
|
void ObxdAudioProcessor::deletePreset(){
|
|
programs.currentProgramPtr->setDefaultValues();
|
|
programs.currentProgramPtr->name = "Default";
|
|
sendChangeMessage();
|
|
//saveBank();
|
|
}
|
|
|
|
void ObxdAudioProcessor::newPreset(const String &name) {
|
|
for (int i = 0; i < PROGRAMCOUNT; ++i)
|
|
{
|
|
if (programs.programs[i].name == "Default"){
|
|
setCurrentProgram(i);
|
|
break;
|
|
}
|
|
}
|
|
//savePreset();
|
|
//saveBank();
|
|
}
|
|
|
|
void ObxdAudioProcessor::savePreset() {
|
|
savePreset(currentPresetFile);
|
|
|
|
}
|
|
|
|
bool ObxdAudioProcessor::saveBank(const File& fxbFile){
|
|
saveFXBFile(fxbFile);
|
|
currentBankFile = fxbFile;
|
|
return true;
|
|
}
|
|
|
|
bool ObxdAudioProcessor::saveFXBFile(const File& fxbFile) {
|
|
//auto xml = std::unique_ptr<juce::XmlElement>(new juce::XmlElement(""));
|
|
juce::MemoryBlock m, memoryBlock;
|
|
getStateInformation(m);
|
|
|
|
{
|
|
memoryBlock.reset();
|
|
auto totalLen = sizeof (fxChunkSet) + m.getSize() - 8;
|
|
memoryBlock.setSize (totalLen, true);
|
|
|
|
auto set = static_cast<fxChunkSet*>( memoryBlock.getData());
|
|
set->chunkMagic = fxbName ("CcnK");
|
|
set->byteSize = 0;
|
|
set->fxMagic = fxbName ("FBCh");
|
|
set->version = fxbSwap (fxbVersionNum);
|
|
set->fxID = fxbName ("Obxd");
|
|
set->fxVersion = fxbSwap (fxbVersionNum);
|
|
set->numPrograms = fxbSwap (getNumPrograms());
|
|
set->chunkSize = fxbSwap (static_cast<int32>(m.getSize()));
|
|
|
|
m.copyTo (set->chunk, 0, m.getSize());
|
|
fxbFile.replaceWithData(memoryBlock.getData(), memoryBlock.getSize());
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
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();
|
|
currentBankFile = fxbFile;
|
|
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.clear();
|
|
|
|
DirectoryIterator it (getBanksFolder(), false, "*.fxb", File::findFiles);
|
|
|
|
while (it.next())
|
|
{
|
|
bankFiles.addUsingDefaultSort (it.getFile());
|
|
DBG("Scan Banks: " << it.getFile().getFullPathName());
|
|
}
|
|
}
|
|
|
|
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::getPresetsFolder() const
|
|
{
|
|
return getDocumentFolder().getChildFile("Presets");
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
//DBG("Set Value Parameter: " << getEngineParameterId(index) << " Val: " << 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();
|
|
}
|