- set parameter text for MIDISYNC_MODE git-svn-id: http://moon:8086/svn/software/trunk/projects/JaySynth@736 b431acfa-c32f-4a4a-93f1-934dc6c82436
915 lines
26 KiB
C++
915 lines
26 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file was auto-generated!
|
|
|
|
It contains the basic startup code for a Juce application.
|
|
|
|
==============================================================================
|
|
*/
|
|
#include <math.h>
|
|
|
|
#include "PluginProcessor.h"
|
|
#include "JaySynthAudioProcessorEditor.h"
|
|
#include "synth/vector_utils.h"
|
|
|
|
JaySynthAudioProcessorEditor* getEditor(JaySynthAudioProcessor *pThis)
|
|
{
|
|
return dynamic_cast<JaySynthAudioProcessorEditor*>(pThis->getActiveEditor());
|
|
}
|
|
|
|
//==============================================================================
|
|
JaySynthAudioProcessor::JaySynthAudioProcessor()
|
|
{
|
|
currProgram = 0;
|
|
currpatch = &patches[currProgram];
|
|
the_editor = NULL;
|
|
|
|
m_isWavesAvailable = isWavesAvailable();
|
|
|
|
// Initialise the synth...
|
|
pSynth = new JaySynth(MAX_POLYPHONY, wavesGetPath());
|
|
pSynth->setNoteStealingEnabled(true);
|
|
pSynth->addListener(this);
|
|
|
|
setCurrentProgram(0);
|
|
|
|
m_limiter_env = 0;
|
|
startTimer (10);
|
|
|
|
#ifdef DEBUG
|
|
SynthDebug ("Jay Synth V %s (DEBUG) initialized.\n", JucePlugin_VersionString);
|
|
#else
|
|
SynthDebug ("Jay Synth V %s initialized.\n", JucePlugin_VersionString);
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
JaySynthAudioProcessor::~JaySynthAudioProcessor()
|
|
{
|
|
delete (pSynth);
|
|
}
|
|
|
|
void JaySynthAudioProcessor::timerCallback()
|
|
{
|
|
if (getEditor(this))
|
|
actionListener.callLimiterChanged(m_limiter_active);
|
|
}
|
|
|
|
String JaySynthAudioProcessor::wavesGetPath()
|
|
{
|
|
bool isExistent, isValid;
|
|
|
|
File pluginfile = File::getSpecialLocation (File::currentExecutableFile).getParentDirectory();
|
|
String wavespath = pluginfile.getFullPathName() + String("\\waves.bin");
|
|
File wavesfile = File(wavespath);
|
|
|
|
isExistent = wavesfile.existsAsFile();
|
|
isValid = true;
|
|
|
|
if (isExistent && isValid)
|
|
return wavespath;
|
|
|
|
return String::empty;
|
|
}
|
|
|
|
bool JaySynthAudioProcessor::isWavesAvailable()
|
|
{
|
|
return (wavesGetPath() != String::empty);
|
|
}
|
|
|
|
//==============================================================================
|
|
const String JaySynthAudioProcessor::getName() const
|
|
{
|
|
return JucePlugin_Name;
|
|
}
|
|
|
|
int JaySynthAudioProcessor::getNumParameters()
|
|
{
|
|
return SYNTH_NUM_PARAMS;
|
|
}
|
|
|
|
void JaySynthAudioProcessor::applyCurrentMidiControllers(void)
|
|
{
|
|
JaySynth::midiCC_info_t *pMidiCC_info;
|
|
int i;
|
|
|
|
for (i=0; i < NUM_MIDI_CONTROLLERS; i++)
|
|
{
|
|
pMidiCC_info = pSynth->getLastMidiCC_info(i);
|
|
if (pMidiCC_info)
|
|
applyMidiController(pMidiCC_info, false);
|
|
}
|
|
|
|
}
|
|
|
|
void JaySynthAudioProcessor::applyMidiController(JaySynth::midiCC_info_t *pMidiCC_info, bool forceApply)
|
|
{
|
|
int i, numDst, paramID;
|
|
|
|
midiCC_container_t *pMidi_container;
|
|
|
|
numDst = midCC_editBuffer.getNumDestinations(pMidiCC_info->ID);
|
|
for (i=0; i < numDst; i++)
|
|
{
|
|
pMidi_container = midCC_editBuffer.getDestination(pMidiCC_info->ID, i);
|
|
paramID = midCC_editBuffer.getParamID(pMidi_container);
|
|
if (midCC_editBuffer.doApplyOnPatchInit(pMidi_container) || forceApply)
|
|
{
|
|
if (midCC_editBuffer.doChangeParameter(pMidi_container))
|
|
{
|
|
setParam(paramID, toParam(midCC_editBuffer.getParam(pMidi_container), pMidiCC_info->value), false, false);
|
|
}
|
|
else
|
|
{
|
|
controlParam(midCC_editBuffer.getParamID(pMidi_container), toParam(midCC_editBuffer.getControl(pMidi_container), pMidiCC_info->value));
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void JaySynthAudioProcessor::synthChanged(int type, void *pData)
|
|
{
|
|
JaySynth::midiCC_info_t *pMidiCC_info;
|
|
JaySynth::midi_note_info_t *pMidi_note_info;
|
|
JaySynth::midi_quarter_clock_info_t *pMidi_quarter_info;
|
|
|
|
switch(type)
|
|
{
|
|
case JaySynth::SYNTH_CHANGED_PARAM:
|
|
if (getEditor(this) != NULL)
|
|
actionListener.callParamChanged(*((int*)pData));
|
|
break;
|
|
|
|
case JaySynth::SYNTH_CHANGED_MIDICC:
|
|
pMidiCC_info = (JaySynth::midiCC_info_t*)pData;
|
|
applyMidiController(pMidiCC_info, true);
|
|
if (getEditor(this) != NULL)
|
|
actionListener.callMidiControllerChanged(pMidiCC_info->channel, pMidiCC_info->ID, (float)pMidiCC_info->value);
|
|
break;
|
|
|
|
case JaySynth::SYNTH_CHANGED_NOTE_PRESSED:
|
|
pMidi_note_info = (JaySynth::midi_note_info_t*)pData;
|
|
if (getEditor(this) != NULL)
|
|
actionListener.callMidiNotePressed(pMidi_note_info->note, (float)pMidi_note_info->velocity);
|
|
break;
|
|
|
|
case JaySynth::SYNTH_CHANGED_NOTE_RELEASED:
|
|
pMidi_note_info = (JaySynth::midi_note_info_t*)pData;
|
|
if (getEditor(this) != NULL)
|
|
actionListener.callMidiNoteReleased(pMidi_note_info->note, (float)pMidi_note_info->velocity);
|
|
break;
|
|
|
|
case JaySynth::SYNTH_CHANGED_NUM_VOICES_PLAYING:
|
|
if (getEditor(this) != NULL)
|
|
actionListener.callNumVoicesPlaying(*((int*)pData));
|
|
break;
|
|
|
|
case JaySynth::SYNTH_CHANGED_MIDICLOCK:
|
|
pMidi_quarter_info = (JaySynth::midi_quarter_clock_info_t*)pData;
|
|
if (getEditor(this) != NULL)
|
|
actionListener.callMidiClock(pMidi_quarter_info->bpm, pMidi_quarter_info->type);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
float JaySynthAudioProcessor::getParameter (int index)
|
|
{
|
|
param_info_t *pParamInfo = getParamInfo(index);
|
|
|
|
if (!pParamInfo)
|
|
return 0;
|
|
|
|
return (float)toSlider(pParamInfo, pSynth->getParameter(index));
|
|
}
|
|
|
|
void JaySynthAudioProcessor::setParameter (int index, float newValue)
|
|
{
|
|
param_info_t *pParamInfo = getParamInfo(index);
|
|
|
|
if (!pParamInfo)
|
|
return;
|
|
|
|
setParam(index, toParam(pParamInfo, (synth_float_t)newValue), true, false);
|
|
|
|
}
|
|
|
|
synth_float_t JaySynthAudioProcessor::getParam(int index)
|
|
{
|
|
return (synth_float_t)pSynth->getParameter(index);
|
|
}
|
|
|
|
void JaySynthAudioProcessor::setParam(int index, synth_float_t param, bool doModifyPatch, bool doNotifyHost)
|
|
{
|
|
param_info_t *pParamInfo = getParamInfo(index);
|
|
|
|
if (!pParamInfo)
|
|
return;
|
|
|
|
if (doModifyPatch)
|
|
{
|
|
setPatchModified();
|
|
}
|
|
|
|
if (doNotifyHost)
|
|
sendParamChangeToHost(index, (float)toSlider(pParamInfo, param));
|
|
|
|
pSynth->setParameter(index, clampParam(pParamInfo, param));
|
|
|
|
}
|
|
|
|
void JaySynthAudioProcessor::controlParam(int index, synth_float_t param)
|
|
{
|
|
pSynth->setControl(index, param);
|
|
}
|
|
|
|
const String JaySynthAudioProcessor::getParameterName (int index)
|
|
{
|
|
param_info_t *pParamInfo = getParamInfo(index);
|
|
|
|
if (pParamInfo)
|
|
return pParamInfo->pName;
|
|
|
|
return String::empty;
|
|
}
|
|
|
|
const String JaySynthAudioProcessor::getParameterText (int index)
|
|
{
|
|
param_info_t *pParamInfo = getParamInfo(index);
|
|
|
|
switch (index)
|
|
{
|
|
case SYNTH_PARAM_VCF_TYPE:
|
|
switch ((int)pSynth->getParameter(index))
|
|
{
|
|
case VCF_FILTERTYPE_LOWPASS:
|
|
return "LPF";
|
|
case VCF_FILTERTYPE_HIGHPASS:
|
|
return "HPF";
|
|
case VCF_FILTERTYPE_BANDPASS:
|
|
return "BPF";
|
|
default:
|
|
return "Unknown (" + String((int)pSynth->getParameter(index)) + ")";
|
|
}
|
|
|
|
case SYNTH_PARAM_NOISE_ENABLE:
|
|
case SYNTH_PARAM_OSC_0_ENABLE:
|
|
case SYNTH_PARAM_OSC_1_ENABLE:
|
|
if (!(int)pSynth->getParameter(index))
|
|
return "Off";
|
|
return "On";
|
|
|
|
case SYNTH_PARAM_NOISE_SHAPE:
|
|
switch ((int)pSynth->getParameter(index))
|
|
{
|
|
case VOICE_NOISE_SHAPE_WHITE:
|
|
return "Uniform";
|
|
case VOICE_NOISE_SHAPE_PINK:
|
|
return "Gauss";
|
|
default:
|
|
return "Unknown (" + String((int)pSynth->getParameter(index)) + ")";
|
|
}
|
|
|
|
case SYNTH_PARAM_OSC_0_WAVEFORM:
|
|
case SYNTH_PARAM_OSC_1_WAVEFORM:
|
|
switch ((int)pSynth->getParameter(index))
|
|
{
|
|
case OSC_WAVEFORM_SAWTOOTH:
|
|
return "Saw";
|
|
case OSC_WAVEFORM_SQUARE:
|
|
return "Pulse";
|
|
case OSC_WAVEFORM_TRIANGLE:
|
|
return "Tri";
|
|
case OSC_WAVEFORM_SINE:
|
|
return "Sine";
|
|
default:
|
|
return "Unknown (" + String((int)pSynth->getParameter(index)) + ")";
|
|
}
|
|
|
|
case SYNTH_PARAM_LFO_0_SHAPE:
|
|
case SYNTH_PARAM_LFO_1_SHAPE:
|
|
case SYNTH_PARAM_LFO_2_SHAPE:
|
|
case SYNTH_PARAM_LFO_3_SHAPE:
|
|
switch ((int)pSynth->getParameter(index))
|
|
{
|
|
case LFO_WAVEFORM_SINE:
|
|
return "Sine";
|
|
case LFO_WAVEFORM_COSINE:
|
|
return "Cosine";
|
|
case LFO_WAVEFORM_SAW:
|
|
return "Saw";
|
|
case LFO_WAVEFORM_SQUARE:
|
|
return "Square";
|
|
case LFO_WAVEFORM_TRIANGLE:
|
|
return "Tri";
|
|
case LFO_WAVEFORM_SH_UNI:
|
|
return "S/H 1";
|
|
case LFO_WAVEFORM_SH_GAUSS:
|
|
return "S/H 2";
|
|
default:
|
|
return "Unknown (" + String((int)pSynth->getParameter(index)) + ")";
|
|
}
|
|
|
|
case SYNTH_PARAM_OSC_0_AM_SRC_0:
|
|
case SYNTH_PARAM_OSC_1_AM_SRC_0:
|
|
case SYNTH_PARAM_OSC_0_FM_SRC_0:
|
|
case SYNTH_PARAM_OSC_1_FM_SRC_0:
|
|
case SYNTH_PARAM_OSC_0_PWM_SRC_0:
|
|
case SYNTH_PARAM_OSC_1_PWM_SRC_0:
|
|
case SYNTH_PARAM_VCF_F_SRC_0:
|
|
case SYNTH_PARAM_VCF_Q_SRC_0:
|
|
case SYNTH_PARAM_VCA_AM_SRC_0:
|
|
case SYNTH_PARAM_VCA_PAN_SRC_0:
|
|
case SYNTH_PARAM_OSC_0_AM_SRC_1:
|
|
case SYNTH_PARAM_OSC_1_AM_SRC_1:
|
|
case SYNTH_PARAM_OSC_0_FM_SRC_1:
|
|
case SYNTH_PARAM_OSC_1_FM_SRC_1:
|
|
case SYNTH_PARAM_OSC_0_PWM_SRC_1:
|
|
case SYNTH_PARAM_OSC_1_PWM_SRC_1:
|
|
case SYNTH_PARAM_VCF_F_SRC_1:
|
|
case SYNTH_PARAM_VCF_Q_SRC_1:
|
|
switch ((int)pSynth->getParameter(index))
|
|
{
|
|
case 0:
|
|
return "Off";
|
|
case 1:
|
|
return "LFO 1";
|
|
case 2:
|
|
return "LFO 2";
|
|
case 3:
|
|
return "LFO 3";
|
|
case 4:
|
|
return "LFO 4";
|
|
default:
|
|
return "Unknown (" + String((int)pSynth->getParameter(index)) + ")";
|
|
}
|
|
|
|
case SYNTH_PARAM_LFO_0_MODE:
|
|
case SYNTH_PARAM_LFO_1_MODE:
|
|
case SYNTH_PARAM_LFO_2_MODE:
|
|
case SYNTH_PARAM_LFO_3_MODE:
|
|
switch ((int)pSynth->getParameter(index))
|
|
{
|
|
case VOICE_LFO_MODE_GLOBAL:
|
|
return "Global";
|
|
case VOICE_LFO_MODE_INDIVIDUAL:
|
|
return "Individual";
|
|
default:
|
|
return "Unknown (" + String((int)pSynth->getParameter(index)) + ")";
|
|
}
|
|
|
|
case SYNTH_PARAM_LFO_0_SYNC:
|
|
case SYNTH_PARAM_LFO_1_SYNC:
|
|
case SYNTH_PARAM_LFO_2_SYNC:
|
|
case SYNTH_PARAM_LFO_3_SYNC:
|
|
switch ((int)pSynth->getParameter(index))
|
|
{
|
|
case VOICE_LFO_SYNC_FREE:
|
|
return "Free";
|
|
case VOICE_LFO_SYNC_RESTART:
|
|
return "Restart";
|
|
default:
|
|
return "Unknown (" + String((int)pSynth->getParameter(index)) + ")";
|
|
}
|
|
|
|
case SYNTH_PARAM_LFO_0_MIDISYNC_MODE:
|
|
case SYNTH_PARAM_LFO_1_MIDISYNC_MODE:
|
|
case SYNTH_PARAM_LFO_2_MIDISYNC_MODE:
|
|
case SYNTH_PARAM_LFO_3_MIDISYNC_MODE:
|
|
switch ((int)pSynth->getParameter(index))
|
|
{
|
|
case VOICE_LFO_MIDISYNC_OFF:
|
|
return "Off";
|
|
case VOICE_LFO_MIDISYNC_F:
|
|
return "Freq";
|
|
case VOICE_LFO_MIDISYNC_P:
|
|
return "Phase";
|
|
case VOICE_LFO_MIDISYNC_F_P:
|
|
return "Freq+Phase";
|
|
default:
|
|
return "Unknown (" + String((int)pSynth->getParameter(index)) + ")";
|
|
}
|
|
|
|
default:
|
|
if (pParamInfo->pinterval == 0.0)
|
|
return String (pSynth->getParameter(index));
|
|
else
|
|
return String (pSynth->getParameter(index), (int)(-log(pParamInfo->pinterval)/log(10.0) + 0.5));
|
|
}
|
|
}
|
|
|
|
const String JaySynthAudioProcessor::getInputChannelName (int channelIndex) const
|
|
{
|
|
return String (channelIndex + 1);
|
|
}
|
|
|
|
const String JaySynthAudioProcessor::getOutputChannelName (int channelIndex) const
|
|
{
|
|
return String (channelIndex + 1);
|
|
}
|
|
|
|
bool JaySynthAudioProcessor::isInputChannelStereoPair (int /*index*/) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool JaySynthAudioProcessor::isOutputChannelStereoPair (int /*index*/) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool JaySynthAudioProcessor::acceptsMidi() const
|
|
{
|
|
#if JucePlugin_WantsMidiInput
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool JaySynthAudioProcessor::producesMidi() const
|
|
{
|
|
#if JucePlugin_ProducesMidiOutput
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
int JaySynthAudioProcessor::getNumPrograms()
|
|
{
|
|
return NUM_PROGRAMS;
|
|
}
|
|
|
|
int JaySynthAudioProcessor::getCurrentProgram()
|
|
{
|
|
return currProgram;
|
|
}
|
|
|
|
void JaySynthAudioProcessor::setCurrentProgram (int index)
|
|
{
|
|
int i;
|
|
|
|
param_info_t *pParamInfo;
|
|
|
|
if ((unsigned)index >= NUM_PROGRAMS)
|
|
index = 0;
|
|
|
|
currProgram = index;
|
|
currpatch = &patches[index];
|
|
|
|
// Set SYNTH_PARAM_TOTAL_NUM_VOICES first
|
|
setParam(SYNTH_PARAM_TOTAL_NUM_VOICES, currpatch->getParameter(SYNTH_PARAM_TOTAL_NUM_VOICES), false, false);
|
|
|
|
midCC_editBuffer = *currpatch->getMidiCC();
|
|
|
|
pSynth->setMidiCC_editBuffer(&midCC_editBuffer);
|
|
pSynth->ClearControls();
|
|
for (i=0; i < SYNTH_NUM_PARAMS; i++)
|
|
{
|
|
pParamInfo = getParamInfo(i);
|
|
setParam(i, currpatch->getParameter(i), false, true);
|
|
}
|
|
unisonoModeChanged();
|
|
humanizeModeChanged();
|
|
humanizeVarianceChanged_VCO();
|
|
humanizeVarianceChanged_VCF();
|
|
humanizeVarianceChanged_ENV();
|
|
perVoiceControlsChanged();
|
|
|
|
clearPatchModified();
|
|
|
|
// toDo: do this by synth->setParam via message
|
|
if (getEditor(this))
|
|
{
|
|
actionListener.callProgramChanged();
|
|
for (i=0; i < SYNTH_NUM_PARAMS; i++)
|
|
getEditor(this)->paramUpdate(i, false);
|
|
}
|
|
applyCurrentMidiControllers();
|
|
updateHostDisplay();
|
|
}
|
|
|
|
const String JaySynthAudioProcessor::getProgramName (int index)
|
|
{
|
|
return patches[index].getName();
|
|
}
|
|
|
|
void JaySynthAudioProcessor::changeProgramName (int index, const String& newName)
|
|
{
|
|
patches[index].setName(newName);
|
|
if (getEditor(this))
|
|
{
|
|
actionListener.callProgramChanged();
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
void JaySynthAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
|
|
{
|
|
// Use this method as the place to do any pre-playback
|
|
// initialisation that you need..
|
|
m_fs = sampleRate;
|
|
pSynth->setSampleRate (sampleRate);
|
|
pSynth->setBufsize (samplesPerBlock);
|
|
keyboardState.reset();
|
|
|
|
}
|
|
|
|
void JaySynthAudioProcessor::releaseResources()
|
|
{
|
|
// When playback stops, you can use this as an opportunity to free up any
|
|
// spare memory, etc.
|
|
keyboardState.reset();
|
|
|
|
}
|
|
|
|
void JaySynthAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
|
|
{
|
|
const int numSamples = buffer.getNumSamples();
|
|
int i;
|
|
float *buf1 = buffer.getWritePointer (0, 0);
|
|
float *buf2 = buffer.getWritePointer (1, 0);
|
|
synth_float_t limiter_env[SYNTH_MAX_BUFSIZE];
|
|
synth_float_t limiter_gain[SYNTH_MAX_BUFSIZE];
|
|
synth_float_t buf[2][SYNTH_MAX_BUFSIZE];
|
|
synth_float_t x, limiter_ar, limiter_af, limiter_threshold;
|
|
float synth_gain;
|
|
|
|
AudioPlayHead::CurrentPositionInfo pos;
|
|
getPlayHead()->getCurrentPosition(pos);
|
|
|
|
m_JK_MidiClock.generateMidiclock(pos, &midiMessages, numSamples, getSampleRate());
|
|
|
|
// Now pass any incoming midi messages to our keyboard state object, and let it
|
|
// add messages to the buffer if the user is clicking on the on-screen keys
|
|
keyboardState.processNextMidiBuffer (midiMessages, 0, numSamples, true);
|
|
|
|
// First remove garbage from buffers
|
|
buffer.clear (0, 0, numSamples);
|
|
buffer.clear (1, 0, numSamples);
|
|
|
|
// and now get the synth to process these midi events and generate its output.
|
|
// Render individual voices
|
|
pSynth->renderNextBlock (buffer, midiMessages, 0, numSamples);
|
|
|
|
// apply master volume
|
|
synth_gain = (float)pSynth->getVolume();
|
|
|
|
for (i=0; i <numSamples; i++)
|
|
buf[0][i] = (synth_float_t)buf1[i] * synth_gain;
|
|
|
|
for (i=0; i <numSamples; i++)
|
|
buf[1][i] = (synth_float_t)buf2[i] * synth_gain;
|
|
|
|
// Limiter
|
|
limiter_ar = 5.0/(SYNTH_LIMITER_RISE_TIME*m_fs);
|
|
limiter_af = 5.0/(SYNTH_LIMITER_FALL_TIME*m_fs);
|
|
limiter_threshold = pow((synth_float_t)10.0, (synth_float_t)SYNTH_LIMITER_THRESHOLD/20);
|
|
|
|
for (i=0; i <numSamples; i++)
|
|
{
|
|
x = max(fabs(buf[0][i]), fabs(buf[1][i]));
|
|
if (x > m_limiter_env)
|
|
{
|
|
m_limiter_env = (1.0-limiter_ar)*m_limiter_env + limiter_ar*x;
|
|
}
|
|
else
|
|
{
|
|
m_limiter_env = (1.0-limiter_af)*m_limiter_env;
|
|
}
|
|
|
|
limiter_env[i] = m_limiter_env;
|
|
}
|
|
|
|
m_limiter_active = 0;
|
|
for (i=0; i <numSamples; i++)
|
|
{
|
|
limiter_gain[i] = min(1.0, 1.0/(limiter_env[i] + 1.0e-6));
|
|
if (limiter_gain[i] < 1.0)
|
|
{
|
|
m_limiter_active = 1;
|
|
}
|
|
}
|
|
|
|
for (i=0; i <numSamples; i++)
|
|
buf1[i] = (float)(buf[0][i]*limiter_gain[i]);
|
|
|
|
for (i=0; i <numSamples; i++)
|
|
buf2[i] = (float)(buf[1][i]*limiter_gain[i]);
|
|
|
|
// 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 = getNumInputChannels(); i < getNumOutputChannels(); ++i)
|
|
// {
|
|
// buffer.clear (i, 0, buffer.getNumSamples());
|
|
// }
|
|
}
|
|
|
|
//==============================================================================
|
|
bool JaySynthAudioProcessor::hasEditor() const
|
|
{
|
|
return true; // (change this to false if you choose to not supply an editor)
|
|
}
|
|
|
|
AudioProcessorEditor* JaySynthAudioProcessor::createEditor()
|
|
{
|
|
int i;
|
|
|
|
the_editor = (void*) new JaySynthAudioProcessorEditor (this, m_isWavesAvailable);
|
|
|
|
if (the_editor)
|
|
{
|
|
actionListener.callProgramChanged();
|
|
for (i=0; i < SYNTH_NUM_PARAMS; i++)
|
|
((JaySynthAudioProcessorEditor*)the_editor)->paramUpdate(i, false);
|
|
}
|
|
|
|
return (AudioProcessorEditor*)the_editor;
|
|
}
|
|
|
|
//==============================================================================
|
|
// magic number to identify memory blocks that we've stored as XML
|
|
const uint32 magicXmlNumber = 0x21324356;
|
|
|
|
void JaySynthAudioProcessor::copyXmlToBinary (const XmlElement& xml, juce::MemoryBlock& destData)
|
|
{
|
|
const String xmlString (xml.createDocument (String::empty, true, false));
|
|
const int stringLength = xmlString.getNumBytesAsUTF8();
|
|
|
|
destData.setSize ((size_t) stringLength + 10);
|
|
|
|
char* const d = static_cast<char*> (destData.getData());
|
|
*(uint32*) d = ByteOrder::swapIfBigEndian ((const uint32) magicXmlNumber);
|
|
*(uint32*) (d + 4) = ByteOrder::swapIfBigEndian ((const uint32) stringLength);
|
|
|
|
xmlString.copyToUTF8 (d + 8, stringLength + 1);
|
|
}
|
|
|
|
XmlElement* JaySynthAudioProcessor::getXmlFromBinary (const void* data,
|
|
const int sizeInBytes)
|
|
{
|
|
XmlElement *pResult = nullptr;
|
|
if (sizeInBytes > 8
|
|
&& ByteOrder::littleEndianInt (data) == magicXmlNumber)
|
|
{
|
|
const int stringLength = (int) ByteOrder::littleEndianInt (addBytesToPointer (data, 4));
|
|
XmlDocument doc(String::fromUTF8 ((static_cast<const char*> (data)) + 8, jmin ((sizeInBytes - 8), stringLength)));
|
|
|
|
if (stringLength > 0)
|
|
pResult = doc.getDocumentElement();
|
|
|
|
if (doc.getLastParseError() != String::empty)
|
|
{
|
|
deleteAndZero(pResult);
|
|
}
|
|
}
|
|
return pResult;
|
|
}
|
|
|
|
const String JaySynthAudioProcessor::getParameterNameXML (int index)
|
|
{
|
|
String name = getParameterName(index);
|
|
name = name.removeCharacters (String("."));
|
|
name = name.replaceCharacter (' ','_');
|
|
|
|
return name;
|
|
}
|
|
|
|
void JaySynthAudioProcessor::getStateInformation (MemoryBlock& destData)
|
|
{
|
|
int i, progID;
|
|
synth_float_t value;
|
|
JaySynthSound *pPatch;
|
|
XmlElement *pXML;
|
|
|
|
// Create an outer XML element..
|
|
ScopedPointer<XmlElement>xml = new XmlElement("JSYNTH_BANK");
|
|
|
|
MemoryOutputStream outputStream(destData, false);
|
|
MemoryBlock tmpBlock(8192*1024, /*initialiseToZero*/true);
|
|
GZIPCompressorOutputStream GZIPEnc(&outputStream, /*compressionLevel*/0, /*deleteDestStreamWhenDestroyed*/false, /*windowBits*/0);
|
|
|
|
if (isPatchModified() || midCC_editBuffer.isMidiCCmodified())
|
|
{
|
|
for (i=0; i < SYNTH_NUM_PARAMS; i++)
|
|
{
|
|
value = getParam(i);
|
|
currpatch->setParameter(i, value); // Copy synth param to current program
|
|
}
|
|
currpatch->getMidiCC()->copyMidiCC(&midCC_editBuffer); // Copy MidiCC edit buffer to patch
|
|
}
|
|
|
|
for (progID=0; progID < NUM_PROGRAMS; progID++)
|
|
{
|
|
pXML = xml->createNewChildElement (String("PATCH_") + String(progID));
|
|
pPatch = &patches[progID];
|
|
pXML->setAttribute ("ID", progID);
|
|
pXML->setAttribute ("NAME", pPatch->getName());
|
|
for (i=0; i < SYNTH_NUM_PARAMS; i++)
|
|
{
|
|
value = pPatch->getParameter(i);
|
|
pXML->setAttribute (getParameterNameXML(i), String(value, 10));
|
|
}
|
|
pPatch->getMidiCC()->exportXML(pXML);
|
|
|
|
}
|
|
setCurrentProgram(getCurrentProgram());
|
|
|
|
// then use this helper function to stuff it into the binary blob and return it..
|
|
copyXmlToBinary (*xml, tmpBlock);
|
|
GZIPEnc.write(tmpBlock.getData(), tmpBlock.getSize());
|
|
outputStream.flush();
|
|
|
|
#if 0
|
|
//#ifdef SYNTH_DEBUG
|
|
{
|
|
File file(String("C:\\Users\\jens\\JaySynth\\bank.xml"));
|
|
file.create();
|
|
xml->writeToFile (/*const File &destinationFile*/file, /*const String &dtdToUse*/"JSYNTH_BANK_FILE" /*, const String &encodingType="UTF-8", int lineWrapLength=60*/);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void JaySynthAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
|
|
{
|
|
int i, progID, uncompressedSize;
|
|
synth_float_t value;
|
|
JaySynthSound *pPatch;
|
|
XmlElement *pXML;
|
|
String lastError;
|
|
ScopedPointer<XmlElement> xmlState = NULL;
|
|
void* pData;
|
|
int size;
|
|
|
|
ScopedPointer<char> pUncompressedData;
|
|
pUncompressedData = new char[8192*1024];
|
|
|
|
MemoryInputStream inputStream(data, sizeInBytes, false);
|
|
GZIPDecompressorInputStream GZIPDec(inputStream);
|
|
uncompressedSize = GZIPDec.read(pUncompressedData, 8192*1024);
|
|
|
|
pData = (void *)data;
|
|
size = sizeInBytes;
|
|
if (uncompressedSize)
|
|
{
|
|
pData = (void *)pUncompressedData;
|
|
size = uncompressedSize;
|
|
}
|
|
xmlState = getXmlFromBinary(pData, size);
|
|
|
|
if (xmlState != 0)
|
|
{
|
|
|
|
// make sure that it's actually our type of XML object..
|
|
if (xmlState->hasTagName ("JSYNTH_BANK"))
|
|
{
|
|
pXML = xmlState->getFirstChildElement();
|
|
|
|
while(pXML)
|
|
{
|
|
progID = pXML->getIntAttribute("ID");
|
|
if (progID >= NUM_PROGRAMS)
|
|
break;
|
|
|
|
pPatch = &patches[progID];
|
|
|
|
// ok, now pull out our parameters..
|
|
pPatch->setName(pXML->getStringAttribute("NAME"));
|
|
for (i=0; i < SYNTH_NUM_PARAMS; i++)
|
|
{
|
|
value = (synth_float_t) pXML->getDoubleAttribute (getParameterNameXML(i), pPatch->getParameter(i));
|
|
if (i==SYNTH_PARAM_TUNE)
|
|
{
|
|
if (value > 400.0)
|
|
{
|
|
value = 1200*log(value/440)/log(2.0);
|
|
}
|
|
}
|
|
pPatch->setParameter(i, value);
|
|
}
|
|
pPatch->getMidiCC()->importXML(pXML);
|
|
pXML = pXML->getNextElement();
|
|
|
|
}
|
|
}
|
|
setCurrentProgram(getCurrentProgram());
|
|
}
|
|
}
|
|
|
|
void JaySynthAudioProcessor::getCurrentProgramStateInformation (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.
|
|
// You should use this method to store your parameters in the memory block.
|
|
// Here's an example of how you can use XML to make it easy and more robust:
|
|
|
|
int i;
|
|
synth_float_t value;
|
|
MemoryOutputStream outputStream (destData, false);
|
|
MemoryBlock tmpBlock(2*65536, /*initialiseToZero*/true);
|
|
GZIPCompressorOutputStream GZIPEnc(&outputStream, /*compressionLevel*/0, /*deleteDestStreamWhenDestroyed*/false, /*windowBits*/0);
|
|
|
|
// Create an outer XML element..
|
|
ScopedPointer<XmlElement>xml = new XmlElement("JSYNTH_PATCH");
|
|
|
|
if (isPatchModified() || midCC_editBuffer.isMidiCCmodified())
|
|
{
|
|
for (i=0; i < SYNTH_NUM_PARAMS; i++)
|
|
{
|
|
value = getParam(i);
|
|
currpatch->setParameter(i, value); // Copy synth param to current program
|
|
}
|
|
currpatch->getMidiCC()->copyMidiCC(&midCC_editBuffer); // Copy MidiCC edit buffer to patch
|
|
}
|
|
|
|
xml->setAttribute ("NAME", currpatch->getName());
|
|
for (i=0; i < SYNTH_NUM_PARAMS; i++)
|
|
{
|
|
value = currpatch->getParameter(i);
|
|
xml->setAttribute (getParameterNameXML(i), String(value, 10));
|
|
}
|
|
currpatch->getMidiCC()->exportXML(xml);
|
|
setCurrentProgram(getCurrentProgram());
|
|
|
|
// then use this helper function to stuff it into the binary blob and return it..
|
|
copyXmlToBinary (*xml, tmpBlock);
|
|
GZIPEnc.write(tmpBlock.getData(), tmpBlock.getSize());
|
|
|
|
#if 0
|
|
//#ifdef SYNTH_DEBUG
|
|
{
|
|
File file(String("C:\\Users\\jens\\JaySynth\\") + String(currpatch->getName()) + String(".xml"));
|
|
file.create();
|
|
xml->writeToFile (/*const File &destinationFile*/file, /*const String &dtdToUse*/"JSYNTH_PATCH_FILE" /*, const String &encodingType="UTF-8", int lineWrapLength=60*/);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void JaySynthAudioProcessor::setCurrentProgramStateInformation (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.
|
|
int i, uncompressedSize;
|
|
synth_float_t value;
|
|
ScopedPointer<XmlElement> xmlState = NULL;
|
|
void* pData;
|
|
int size;
|
|
|
|
ScopedPointer<char> pUncompressedData;
|
|
pUncompressedData = new char[2*65536];
|
|
|
|
MemoryInputStream inputStream(data, sizeInBytes, false);
|
|
GZIPDecompressorInputStream GZIPDec(inputStream);
|
|
uncompressedSize = GZIPDec.read(pUncompressedData, 2*65536);
|
|
|
|
pData = (void *)data;
|
|
size = sizeInBytes;
|
|
if (uncompressedSize)
|
|
{
|
|
pData = (void *)pUncompressedData;
|
|
size = uncompressedSize;
|
|
}
|
|
xmlState = getXmlFromBinary(pData, size);
|
|
|
|
if (xmlState != 0)
|
|
{
|
|
// make sure that it's actually our type of XML object..
|
|
if (xmlState->hasTagName ("JSYNTH_PATCH"))
|
|
{
|
|
// ok, now pull out our parameters..
|
|
currpatch->setName(xmlState->getStringAttribute("NAME"));
|
|
for (i=0; i < SYNTH_NUM_PARAMS; i++)
|
|
{
|
|
value = (synth_float_t) xmlState->getDoubleAttribute (getParameterNameXML(i), currpatch->getParameter(i));
|
|
if (i==SYNTH_PARAM_TUNE)
|
|
{
|
|
if (value > 400.0)
|
|
{
|
|
value = 1200*log(value/440)/log(2.0);
|
|
}
|
|
}
|
|
currpatch->setParameter(i, value);
|
|
}
|
|
}
|
|
currpatch->getMidiCC()->importXML(xmlState);
|
|
setCurrentProgram(getCurrentProgram());
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
// This creates new instances of the plugin..
|
|
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
|
|
{
|
|
return new JaySynthAudioProcessor();
|
|
}
|