- increased version to 0.5.7 - added patch 'Play It Right' to demo bank git-svn-id: http://moon:8086/svn/software/trunk/projects/JaySynth@241 b431acfa-c32f-4a4a-93f1-934dc6c82436
1466 lines
41 KiB
C++
1466 lines
41 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file was generated by user!
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
|
|
#include "synth/synth_defs.h"
|
|
#include "JaySynthSound.h"
|
|
#include "JaySynthVoice.h"
|
|
#include "JaySynthMonophonicMGR.h"
|
|
#include "JaySynthMidiCC.h"
|
|
#include "JaySynth.h"
|
|
|
|
#ifndef max
|
|
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
|
#endif
|
|
|
|
#ifndef min
|
|
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
|
#endif
|
|
|
|
#define WITH_OLD_HUMANIZE
|
|
|
|
//==============================================================================
|
|
/** A JaySynth that just plays incredible sounds.. */
|
|
JaySynth::JaySynth (int num_voices, String pathToWaves)
|
|
{
|
|
int i, j;
|
|
char paramName[256];
|
|
pMidCC_editBuffer = NULL;
|
|
|
|
memset(synth_common.vco.wt.wave_rawdata, 0, sizeof(synth_common.vco.wt.wave_rawdata));
|
|
if (pathToWaves != String::empty)
|
|
{
|
|
File wavesFile = File(pathToWaves);
|
|
ScopedPointer<FileInputStream>pIn = wavesFile.createInputStream();
|
|
pIn->read(synth_common.vco.wt.wave_rawdata, sizeof(synth_common.vco.wt.wave_rawdata));
|
|
}
|
|
|
|
max_num_voices = num_voices;
|
|
total_num_voices = num_voices;
|
|
|
|
m_pVoices = new voice_t[num_voices];
|
|
for (i = 0; i < num_voices; i++)
|
|
addVoice (new JaySynthVoice(&m_pVoices[i], i, &synth_common)); // These voices will play our sounds..
|
|
|
|
addSound (new JaySynthSound());
|
|
|
|
lastPitchWheelValue = 8192;
|
|
lastmidiChannel = 0;
|
|
|
|
paramInfoInit(&pb_range[0], 0, NULL);
|
|
paramInfoInit(&pb_range[1], 1, NULL);
|
|
|
|
paramInfoSet(&pb_range[0], 1, 1, 0.5, -12, 0, +12, 0);
|
|
paramInfoSet(&pb_range[1], 1, 1, 0.5, -12, 0, +12, 0);
|
|
|
|
pPer_voice_controls = new per_voice_control_t[max_num_voices];
|
|
memset(pPer_voice_controls, 0, max_num_voices*sizeof(per_voice_control_t));
|
|
|
|
pCurrNoteInfos = new midi_note_info_t[max_num_voices];
|
|
memset(pCurrNoteInfos, 0, max_num_voices*sizeof(midi_note_info_t));
|
|
|
|
ClearControls();
|
|
|
|
memset(params, 0, SYNTH_NUM_PARAMS*sizeof(synth_float_t));
|
|
memset(last_midiCC_info, 0, NUM_MIDI_CONTROLLERS*sizeof(midiCC_info_t));
|
|
memset(&last_midi_note_info, 0, sizeof(last_midi_note_info));
|
|
|
|
clearKeysPressed();
|
|
|
|
for (i=0; i < NUM_MIDI_CONTROLLERS; i++)
|
|
last_midiCC_info[i].ID = i;
|
|
|
|
// Init humanize parameter from global parameter constraints
|
|
humanize_voice_param = new param_info_t*[SYNTH_NUM_PARAMS];
|
|
|
|
for (i=0; i < SYNTH_NUM_PARAMS; i++)
|
|
{
|
|
humanize_voice_param[i] = new param_info_t[max_num_voices];
|
|
for (j=0; j < max_num_voices; j++)
|
|
{
|
|
sprintf(paramName, "%s Voice# %03d", getParamInfo(i)->pName, j);
|
|
paramInfoInit(&humanize_voice_param[i][j], getParamInfo(i)->id, paramName);
|
|
paramInfoCopy(&humanize_voice_param[i][j], getParamInfo(i));
|
|
}
|
|
}
|
|
updateParameters();
|
|
|
|
next_possible_voice = 0;
|
|
oldest_voice = 0;
|
|
doHumanize = 0;
|
|
isHumanize = 0;
|
|
m_doMonophon = 0;
|
|
m_isMonophon = 0;
|
|
m_doUnison = 0;
|
|
m_isUnison = 0;
|
|
m_unisonGain = 1;
|
|
m_doMonophonRetrigger = true;
|
|
|
|
m_num_audiothreads = SystemStats::getNumCpus();
|
|
m_ppAudioThread = new JaySynthThread*[m_num_audiothreads];
|
|
m_pEventAudioThreadRdy = new WaitableEvent[m_num_audiothreads];
|
|
|
|
for (i=0; i < m_num_audiothreads; i++)
|
|
m_ppAudioThread[i] = new JaySynthThread(&m_pEventAudioThreadRdy[i], m_pVoices, num_voices, i, m_num_audiothreads);
|
|
|
|
}
|
|
|
|
JaySynth::~JaySynth()
|
|
{
|
|
int i, j;
|
|
|
|
paramInfoFree(&pb_range[0]);
|
|
paramInfoFree(&pb_range[1]);
|
|
|
|
for (i=0; i < SYNTH_NUM_PARAMS; i++)
|
|
{
|
|
for (j=0; j < max_num_voices; j++)
|
|
{
|
|
paramInfoFree(&humanize_voice_param[i][j]);
|
|
}
|
|
delete (humanize_voice_param[i]);
|
|
}
|
|
delete (humanize_voice_param);
|
|
|
|
for (i = max_num_voices; --i >= 0;)
|
|
removeVoice(i);
|
|
|
|
delete (pPer_voice_controls);
|
|
delete (pCurrNoteInfos);
|
|
delete (m_pVoices);
|
|
|
|
for (i=0; i < m_num_audiothreads; i++)
|
|
delete(m_ppAudioThread[i]);
|
|
|
|
delete(m_ppAudioThread);
|
|
delete(m_pEventAudioThreadRdy);
|
|
|
|
|
|
}
|
|
|
|
synth_float_t JaySynth::getVolume(void)
|
|
{
|
|
return master_volume*m_unisonGain;
|
|
}
|
|
|
|
void JaySynth::humanizeParam (int paramID, bool useRandom, UINT32 random_seed, synth_float_t spread_min, synth_float_t variance)
|
|
{
|
|
#ifdef WITH_OLD_HUMANIZE
|
|
int j;
|
|
synth_float_t spread;
|
|
|
|
noise_gen_t noise;
|
|
|
|
Noise_Init(&noise, random_seed);
|
|
|
|
for (j=0; j < max_num_voices; j++)
|
|
{
|
|
if (useRandom)
|
|
do
|
|
{
|
|
spread = Noise_Gaussian(&noise, variance*sqrt(1.f/12), 0.0);
|
|
} while (fabs(spread) < spread_min);
|
|
|
|
else
|
|
spread = variance*((synth_float_t)j/max_num_voices -0.5);
|
|
|
|
paramInfoSet(&humanize_voice_param[paramID][j],
|
|
/*base*/getParamInfo(paramID)->base,
|
|
/*kexp*/getParamInfo(paramID)->kexp,
|
|
/*scenter*/getParamInfo(paramID)->scenter,
|
|
/*pmin*/getParamInfo(paramID)->pmin ,
|
|
/*pcenter*/-1E6/*getParamInfo(i)->pcenter*/,
|
|
/*pmax*/getParamInfo(paramID)->pmax * (1+spread),
|
|
/*pinterval*/0);
|
|
}
|
|
Noise_Free(&noise);
|
|
#else
|
|
int j;
|
|
synth_float_t spread, spread_dir, value_lo, value_hi;
|
|
|
|
noise_gen_t noise;
|
|
|
|
Noise_Init(&noise, random_seed);
|
|
|
|
spread_dir = 1.0;
|
|
for (j=0; j < GET_NUM_VOICES; j++)
|
|
{
|
|
if (useRandom)
|
|
{
|
|
do
|
|
{
|
|
spread = Noise_Gaussian(&noise, variance*sqrt(1.f/12), 0.0);
|
|
} while (fabs(spread) < spread_min);
|
|
spread = fabs(spread)*spread_dir;
|
|
}
|
|
else
|
|
spread = variance*((synth_float_t)j/GET_NUM_VOICES -0.5);
|
|
|
|
|
|
value_lo = clampParam(getParamInfo(paramID), getParamInfo(paramID)->pmin * (1 + spread));
|
|
value_hi = clampParam(getParamInfo(paramID), getParamInfo(paramID)->pmax * (1 + spread));
|
|
paramInfoSet(&humanize_voice_param[paramID][j],
|
|
/*base*/getParamInfo(paramID)->base,
|
|
/*kexp*/getParamInfo(paramID)->kexp,
|
|
/*scenter*/getParamInfo(paramID)->scenter,
|
|
/*pmin*/ value_lo,
|
|
/*pcenter*/-1E6/*getParamInfo(i)->pcenter*/,
|
|
/*pmax*/value_hi,
|
|
/*pinterval*/0);
|
|
|
|
spread_dir = -spread_dir;
|
|
}
|
|
Noise_Free(&noise);
|
|
#endif
|
|
}
|
|
|
|
void JaySynth::setSampleRate (synth_float_t sampleRate)
|
|
{
|
|
int i;
|
|
Synthesiser::setCurrentPlaybackSampleRate (sampleRate);
|
|
for (i=0; i < max_num_voices; i++)
|
|
((JaySynthVoice*)getVoice(i))->setSampleRate(sampleRate);
|
|
|
|
}
|
|
|
|
void JaySynth::setBufsize (int /*bufsize*/)
|
|
{
|
|
}
|
|
|
|
void JaySynth::humanizeModeChanged(void)
|
|
{
|
|
if (doHumanize ^ isHumanize)
|
|
{
|
|
isHumanize = doHumanize;
|
|
humanizeVarianceChanged_VCO();
|
|
humanizeVarianceChanged_VCF();
|
|
humanizeVarianceChanged_ENV();
|
|
updateParameters();
|
|
}
|
|
}
|
|
|
|
void JaySynth::humanizeVarianceChanged_VCO(void)
|
|
{
|
|
#ifdef WITH_OLD_HUMANIZE
|
|
// humanizeParam(SYNTH_PARAM_TUNE, true, 0x1234561, 0, 10*humanize_var_vco);
|
|
humanizeParam(SYNTH_PARAM_OSC_0_DETUNE, true, 0x293857, 0, 10*humanize_var_vco);
|
|
humanizeParam(SYNTH_PARAM_OSC_1_DETUNE, true, 0x137573, 0, 10*humanize_var_vco);
|
|
|
|
// Re-Read parameter
|
|
// setParameter(SYNTH_PARAM_TUNE, params[SYNTH_PARAM_TUNE]);
|
|
setParameter(SYNTH_PARAM_OSC_0_DETUNE, params[SYNTH_PARAM_OSC_0_DETUNE]);
|
|
setParameter(SYNTH_PARAM_OSC_1_DETUNE, params[SYNTH_PARAM_OSC_1_DETUNE]);
|
|
#else
|
|
int i;
|
|
synth_float_t f_ref;
|
|
noise_gen_t noise;
|
|
|
|
Noise_Init(&noise, 0x1234561);
|
|
|
|
for (i=0; i < GET_NUM_VOICES; i++)
|
|
{
|
|
f_ref = Noise_Gaussian(&noise, 100*humanize_var_vco, 440.0);
|
|
|
|
if (isHumanize)
|
|
((JaySynthVoice*)getVoice(i))->setParameter(VOICE_PARAM_MASTER_FREF, f_ref);
|
|
else
|
|
((JaySynthVoice*)getVoice(i))->setParameter(VOICE_PARAM_MASTER_FREF, 440.0);
|
|
|
|
}
|
|
Noise_Free(&noise);
|
|
#endif
|
|
}
|
|
|
|
void JaySynth::humanizeVarianceChanged_VCF(void)
|
|
{
|
|
humanizeParam(SYNTH_PARAM_VCF_F, true, 0x1234561, 0, humanize_var_vcf);
|
|
humanizeParam(SYNTH_PARAM_VCF_Q, true, 0x1234561, 0, 0.1*humanize_var_vcf);
|
|
|
|
// Re-Read parameter
|
|
setParameter(SYNTH_PARAM_VCF_F, params[SYNTH_PARAM_VCF_F]);
|
|
setParameter(SYNTH_PARAM_VCF_Q, params[SYNTH_PARAM_VCF_Q]);
|
|
}
|
|
|
|
void JaySynth::humanizeVarianceChanged_ENV(void)
|
|
{
|
|
humanizeParam(SYNTH_PARAM_VCA_ENV_A, true, 0x1234561, 0, humanize_var_env);
|
|
humanizeParam(SYNTH_PARAM_VCA_ENV_D, true, 0x1234561, 0, humanize_var_env);
|
|
humanizeParam(SYNTH_PARAM_VCA_ENV_R, true, 0x1234561, 0, humanize_var_env);
|
|
|
|
humanizeParam(SYNTH_PARAM_VCF_ENV_A, true, 0x1234561, 0, humanize_var_env);
|
|
humanizeParam(SYNTH_PARAM_VCF_ENV_D, true, 0x1234561, 0, humanize_var_env);
|
|
humanizeParam(SYNTH_PARAM_VCF_ENV_R, true, 0x1234561, 0, humanize_var_env);
|
|
|
|
humanizeParam(SYNTH_PARAM_ENV_2_A, true, 0x1234561, 0, humanize_var_env);
|
|
humanizeParam(SYNTH_PARAM_ENV_2_D, true, 0x1234561, 0, humanize_var_env);
|
|
humanizeParam(SYNTH_PARAM_ENV_2_R, true, 0x1234561, 0, humanize_var_env);
|
|
|
|
humanizeParam(SYNTH_PARAM_ENV_3_A, true, 0x1234561, 0, humanize_var_env);
|
|
humanizeParam(SYNTH_PARAM_ENV_3_D, true, 0x1234561, 0, humanize_var_env);
|
|
humanizeParam(SYNTH_PARAM_ENV_3_R, true, 0x1234561, 0, humanize_var_env);
|
|
|
|
// Re-Read parameter
|
|
setParameter(SYNTH_PARAM_VCA_ENV_A, params[SYNTH_PARAM_VCA_ENV_A]);
|
|
setParameter(SYNTH_PARAM_VCA_ENV_D, params[SYNTH_PARAM_VCA_ENV_D]);
|
|
setParameter(SYNTH_PARAM_VCA_ENV_R, params[SYNTH_PARAM_VCA_ENV_R]);
|
|
|
|
setParameter(SYNTH_PARAM_VCF_ENV_A, params[SYNTH_PARAM_VCF_ENV_A]);
|
|
setParameter(SYNTH_PARAM_VCF_ENV_D, params[SYNTH_PARAM_VCF_ENV_D]);
|
|
setParameter(SYNTH_PARAM_VCF_ENV_R, params[SYNTH_PARAM_VCF_ENV_R]);
|
|
|
|
setParameter(SYNTH_PARAM_ENV_2_A, params[SYNTH_PARAM_ENV_2_A]);
|
|
setParameter(SYNTH_PARAM_ENV_2_D, params[SYNTH_PARAM_ENV_2_D]);
|
|
setParameter(SYNTH_PARAM_ENV_2_R, params[SYNTH_PARAM_ENV_2_R]);
|
|
|
|
setParameter(SYNTH_PARAM_ENV_3_A, params[SYNTH_PARAM_ENV_3_A]);
|
|
setParameter(SYNTH_PARAM_ENV_3_D, params[SYNTH_PARAM_ENV_3_D]);
|
|
setParameter(SYNTH_PARAM_ENV_3_R, params[SYNTH_PARAM_ENV_3_R]);
|
|
}
|
|
|
|
//==============================================================================
|
|
// Parameter stuff
|
|
//==============================================================================
|
|
void JaySynth::ClearControls(void)
|
|
{
|
|
memset(controls, 0, SYNTH_NUM_PARAMS*sizeof(synth_float_t));
|
|
}
|
|
|
|
void JaySynth::updateParameters(void)
|
|
{
|
|
int paramID, voice;
|
|
|
|
for (paramID=0; paramID < SYNTH_NUM_PARAMS; paramID++)
|
|
{
|
|
if (pMidCC_editBuffer)
|
|
{
|
|
for (voice=0; voice < GET_NUM_VOICES; voice++)
|
|
{
|
|
setPerVoiceControl(voice, SYNTH_PER_VOICE_CONTROL_VELKEY, paramID, pMidCC_editBuffer->getVelKeyControl(paramID, pCurrNoteInfos[voice].velocity, pCurrNoteInfos[voice].note));
|
|
}
|
|
}
|
|
|
|
setParam(paramID, -1);
|
|
}
|
|
}
|
|
|
|
void JaySynth::updateParameter(int paramID)
|
|
{
|
|
int voice;
|
|
|
|
if (paramID >= SYNTH_NUM_PARAMS)
|
|
return;
|
|
|
|
if (pMidCC_editBuffer)
|
|
{
|
|
for (voice=0; voice < GET_NUM_VOICES; voice++)
|
|
{
|
|
setPerVoiceControl(voice, SYNTH_PER_VOICE_CONTROL_VELKEY, paramID, pMidCC_editBuffer->getVelKeyControl(paramID, pCurrNoteInfos[voice].velocity, pCurrNoteInfos[voice].note));
|
|
}
|
|
}
|
|
|
|
setParam(paramID, -1);
|
|
}
|
|
|
|
synth_float_t JaySynth::getParameter(int paramID)
|
|
{
|
|
if (paramID < SYNTH_NUM_PARAMS)
|
|
return params[paramID];
|
|
|
|
return 0;
|
|
}
|
|
|
|
void JaySynth::setParameter(int paramID, synth_float_t param)
|
|
{
|
|
params[paramID] = param;
|
|
setParam(paramID, -1);
|
|
|
|
paramID_changed = paramID;
|
|
listeners.call (&JaySynthListener::synthChanged, SYNTH_CHANGED_PARAM, ¶mID_changed);
|
|
}
|
|
|
|
void JaySynth::setControl(int paramID, synth_float_t param)
|
|
{
|
|
controls[paramID] = param;
|
|
setParam(paramID, -1);
|
|
|
|
paramID_changed = paramID;
|
|
listeners.call (&JaySynthListener::synthChanged, SYNTH_CHANGED_PARAM, ¶mID_changed);
|
|
}
|
|
|
|
void JaySynth::setPerVoiceControl(int voice, int channel, int paramID, synth_float_t param)
|
|
{
|
|
pPer_voice_controls[voice].ctrl[channel][paramID] = param;
|
|
setParam(paramID, voice);
|
|
}
|
|
|
|
void JaySynth::setParam(int paramID, int voice)
|
|
{
|
|
int i, j, voice_param_type, voice_min, voice_max;
|
|
synth_float_t param;
|
|
|
|
param_info_t *pParamInfo = getParamInfo(paramID);
|
|
|
|
if (!pParamInfo)
|
|
return;
|
|
|
|
voice_param_type = -1;
|
|
param = clampParam(pParamInfo, params[paramID] + controls[paramID]);
|
|
switch (paramID)
|
|
{
|
|
|
|
case SYNTH_PARAM_VOLUME:
|
|
master_volume = param/100;
|
|
break;
|
|
|
|
case SYNTH_PARAM_TUNE:
|
|
voice_param_type = VOICE_PARAM_MASTER_TUNE;
|
|
break;
|
|
|
|
case SYNTH_PARAM_TOTAL_NUM_VOICES:
|
|
total_num_voices = (int)param;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VOICE_PAN_SPREAD:
|
|
voice_param_type = VOICE_PARAM_VOICE_PAN;
|
|
break;
|
|
|
|
case SYNTH_PARAM_HUMANIZE_ENABLE:
|
|
doHumanize = param >= 0.5;
|
|
break;
|
|
|
|
case SYNTH_PARAM_HUMANIZE_VAR_VCO:
|
|
humanize_var_vco = param/100;
|
|
break;
|
|
|
|
case SYNTH_PARAM_HUMANIZE_VAR_VCF:
|
|
humanize_var_vcf = param/100;
|
|
break;
|
|
|
|
case SYNTH_PARAM_HUMANIZE_VAR_ENV:
|
|
humanize_var_env = param/100;
|
|
break;
|
|
|
|
case SYNTH_PARAM_MONOPHONIC_ENABLE:
|
|
m_doMonophon = param >= 0.5;
|
|
break;
|
|
|
|
case SYNTH_PARAM_MONOPHONIC_RETRIGGER_ENABLE:
|
|
m_doMonophonRetrigger = param >= 0.5;
|
|
break;
|
|
|
|
case SYNTH_PARAM_UNISONO_ENABLE:
|
|
m_doUnison = param >= 0.5;
|
|
break;
|
|
|
|
case SYNTH_PARAM_UNISONO_NUM_VOICES:
|
|
unison_num_voices = (int)param;
|
|
break;
|
|
|
|
case SYNTH_PARAM_PORTAMENTO_TIME:
|
|
voice_param_type = VOICE_PARAM_PORTAMENTO_TIME;
|
|
break;
|
|
|
|
case SYNTH_PARAM_NOISE_ENABLE:
|
|
voice_param_type = VOICE_PARAM_NOISE_ENABLE;
|
|
break;
|
|
|
|
case SYNTH_PARAM_NOISE_LEVEL:
|
|
voice_param_type = VOICE_PARAM_NOISE_LEVEL;
|
|
break;
|
|
|
|
case SYNTH_PARAM_NOISE_SHAPE:
|
|
voice_param_type = VOICE_PARAM_NOISE_SHAPE;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCF_TYPE:
|
|
voice_param_type = VOICE_PARAM_VCF_TYPE;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCF_ORDER:
|
|
voice_param_type = VOICE_PARAM_VCF_ORDER;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCF_F:
|
|
voice_param_type = VOICE_PARAM_VCF_F;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCF_Q:
|
|
voice_param_type = VOICE_PARAM_VCF_Q;
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_ENABLE:
|
|
case SYNTH_PARAM_OSC_1_ENABLE:
|
|
voice_param_type = VOICE_PARAM_OSC_0_ENABLE + (paramID - SYNTH_PARAM_OSC_0_ENABLE);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_RESTART_ON_TRIGGER:
|
|
case SYNTH_PARAM_OSC_1_RESTART_ON_TRIGGER:
|
|
voice_param_type = VOICE_PARAM_OSC_0_RESTART_ON_TRIGGER + (paramID - SYNTH_PARAM_OSC_0_RESTART_ON_TRIGGER);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_LEVEL:
|
|
case SYNTH_PARAM_OSC_1_LEVEL:
|
|
voice_param_type = VOICE_PARAM_OSC_0_LEVEL + (paramID - SYNTH_PARAM_OSC_0_LEVEL);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_DUTYCYCLE:
|
|
case SYNTH_PARAM_OSC_1_DUTYCYCLE:
|
|
voice_param_type = VOICE_PARAM_OSC_0_DUTYCYCLE + (paramID - SYNTH_PARAM_OSC_0_DUTYCYCLE);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_DETUNE:
|
|
case SYNTH_PARAM_OSC_1_DETUNE:
|
|
voice_param_type = VOICE_PARAM_OSC_0_DETUNE + (paramID - SYNTH_PARAM_OSC_0_DETUNE);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_TRANSPOSE:
|
|
case SYNTH_PARAM_OSC_1_TRANSPOSE:
|
|
voice_param_type = VOICE_PARAM_OSC_0_TRANSPOSE + (paramID - SYNTH_PARAM_OSC_0_TRANSPOSE);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_PB_RANGE_MIN:
|
|
case SYNTH_PARAM_OSC_1_PB_RANGE_MIN:
|
|
paramInfoSetPMIN(&pb_range[(paramID - SYNTH_PARAM_OSC_0_PB_RANGE_MIN)], param);
|
|
handlePitchWheel(lastmidiChannel, lastPitchWheelValue);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_PB_RANGE_MAX:
|
|
case SYNTH_PARAM_OSC_1_PB_RANGE_MAX:
|
|
paramInfoSetPMAX(&pb_range[(paramID - SYNTH_PARAM_OSC_0_PB_RANGE_MAX)], param);
|
|
handlePitchWheel(lastmidiChannel, lastPitchWheelValue);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_WAVEFORM:
|
|
case SYNTH_PARAM_OSC_1_WAVEFORM:
|
|
voice_param_type = VOICE_PARAM_OSC_0_WAVEFORM + (paramID - SYNTH_PARAM_OSC_0_WAVEFORM);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_FM_AMT_0:
|
|
case SYNTH_PARAM_OSC_1_FM_AMT_0:
|
|
voice_param_type = VOICE_PARAM_OSC_0_FM_AMT_0 + (paramID - SYNTH_PARAM_OSC_0_FM_AMT_0);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_FM_AMT_1:
|
|
case SYNTH_PARAM_OSC_1_FM_AMT_1:
|
|
voice_param_type = VOICE_PARAM_OSC_0_FM_AMT_1 + (paramID - SYNTH_PARAM_OSC_0_FM_AMT_1);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_FM_SRC_0:
|
|
case SYNTH_PARAM_OSC_1_FM_SRC_0:
|
|
voice_param_type = VOICE_PARAM_OSC_0_FM_SRC_0 + (paramID - SYNTH_PARAM_OSC_0_FM_SRC_0);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_FM_SRC_1:
|
|
case SYNTH_PARAM_OSC_1_FM_SRC_1:
|
|
voice_param_type = VOICE_PARAM_OSC_0_FM_SRC_1 + (paramID - SYNTH_PARAM_OSC_0_FM_SRC_1);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_FM_SRC_OP:
|
|
case SYNTH_PARAM_OSC_1_FM_SRC_OP:
|
|
voice_param_type = VOICE_PARAM_OSC_0_FM_SRC_OP + (paramID - SYNTH_PARAM_OSC_0_FM_SRC_OP);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_AM_AMT_0:
|
|
case SYNTH_PARAM_OSC_1_AM_AMT_0:
|
|
voice_param_type = VOICE_PARAM_OSC_0_AM_AMT_0 + (paramID - SYNTH_PARAM_OSC_0_AM_AMT_0);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_AM_AMT_1:
|
|
case SYNTH_PARAM_OSC_1_AM_AMT_1:
|
|
voice_param_type = VOICE_PARAM_OSC_0_AM_AMT_1 + (paramID - SYNTH_PARAM_OSC_0_AM_AMT_1);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_AM_SRC_0:
|
|
case SYNTH_PARAM_OSC_1_AM_SRC_0:
|
|
voice_param_type = VOICE_PARAM_OSC_0_AM_SRC_0 + (paramID - SYNTH_PARAM_OSC_0_AM_SRC_0);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_AM_SRC_1:
|
|
case SYNTH_PARAM_OSC_1_AM_SRC_1:
|
|
voice_param_type = VOICE_PARAM_OSC_0_AM_SRC_1 + (paramID - SYNTH_PARAM_OSC_0_AM_SRC_1);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_AM_SRC_OP:
|
|
case SYNTH_PARAM_OSC_1_AM_SRC_OP:
|
|
voice_param_type = VOICE_PARAM_OSC_0_AM_SRC_OP + (paramID - SYNTH_PARAM_OSC_0_AM_SRC_OP);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_PWM_AMT_0:
|
|
case SYNTH_PARAM_OSC_1_PWM_AMT_0:
|
|
voice_param_type = VOICE_PARAM_OSC_0_PWM_AMT_0 + (paramID - SYNTH_PARAM_OSC_0_PWM_AMT_0);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_PWM_AMT_1:
|
|
case SYNTH_PARAM_OSC_1_PWM_AMT_1:
|
|
voice_param_type = VOICE_PARAM_OSC_0_PWM_AMT_1 + (paramID - SYNTH_PARAM_OSC_0_PWM_AMT_1);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_PWM_SRC_0:
|
|
case SYNTH_PARAM_OSC_1_PWM_SRC_0:
|
|
voice_param_type = VOICE_PARAM_OSC_0_PWM_SRC_0 + (paramID - SYNTH_PARAM_OSC_0_PWM_SRC_0);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_PWM_SRC_1:
|
|
case SYNTH_PARAM_OSC_1_PWM_SRC_1:
|
|
voice_param_type = VOICE_PARAM_OSC_0_PWM_SRC_1 + (paramID - SYNTH_PARAM_OSC_0_PWM_SRC_1);
|
|
break;
|
|
|
|
case SYNTH_PARAM_OSC_0_PWM_SRC_OP:
|
|
case SYNTH_PARAM_OSC_1_PWM_SRC_OP:
|
|
voice_param_type = VOICE_PARAM_OSC_0_PWM_SRC_OP + (paramID - SYNTH_PARAM_OSC_0_PWM_SRC_OP);
|
|
break;
|
|
|
|
case SYNTH_PARAM_ENV_0_A:
|
|
case SYNTH_PARAM_ENV_1_A:
|
|
case SYNTH_PARAM_ENV_2_A:
|
|
case SYNTH_PARAM_ENV_3_A:
|
|
voice_param_type = VOICE_PARAM_ENV_0_A + (paramID - SYNTH_PARAM_ENV_0_A);
|
|
break;
|
|
|
|
case SYNTH_PARAM_ENV_0_D:
|
|
case SYNTH_PARAM_ENV_1_D:
|
|
case SYNTH_PARAM_ENV_2_D:
|
|
case SYNTH_PARAM_ENV_3_D:
|
|
voice_param_type = VOICE_PARAM_ENV_0_D + (paramID - SYNTH_PARAM_ENV_0_D);
|
|
break;
|
|
|
|
case SYNTH_PARAM_ENV_0_S:
|
|
case SYNTH_PARAM_ENV_1_S:
|
|
case SYNTH_PARAM_ENV_2_S:
|
|
case SYNTH_PARAM_ENV_3_S:
|
|
voice_param_type = VOICE_PARAM_ENV_0_S + (paramID - SYNTH_PARAM_ENV_0_S);
|
|
break;
|
|
|
|
case SYNTH_PARAM_ENV_0_R:
|
|
case SYNTH_PARAM_ENV_1_R:
|
|
case SYNTH_PARAM_ENV_2_R:
|
|
case SYNTH_PARAM_ENV_3_R:
|
|
voice_param_type = VOICE_PARAM_ENV_0_R + (paramID - SYNTH_PARAM_ENV_0_R);
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCA_AM_AMT_0:
|
|
voice_param_type = VOICE_PARAM_VCA_AM_AMT_0;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCA_AM_SRC_0:
|
|
voice_param_type = VOICE_PARAM_VCA_AM_SRC_0;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCA_PAN_AMT_0:
|
|
voice_param_type = VOICE_PARAM_VCA_PAN_AMT_0;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCA_PAN_SRC_0:
|
|
voice_param_type = VOICE_PARAM_VCA_PAN_SRC_0;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCA_ENV_AMT:
|
|
voice_param_type = VOICE_PARAM_VCA_ENV_GAIN;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCF_ENV_AMT:
|
|
voice_param_type = VOICE_PARAM_VCF_ENV_GAIN;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCF_F_AMT_0:
|
|
voice_param_type = VOICE_PARAM_VCF_F_AMT_0;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCF_F_AMT_1:
|
|
voice_param_type = VOICE_PARAM_VCF_F_AMT_1;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCF_F_SRC_0:
|
|
voice_param_type = VOICE_PARAM_VCF_F_SRC_0;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCF_F_SRC_1:
|
|
voice_param_type = VOICE_PARAM_VCF_F_SRC_1;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCF_F_SRC_OP:
|
|
voice_param_type = VOICE_PARAM_VCF_F_SRC_OP;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCF_Q_AMT_0:
|
|
voice_param_type = VOICE_PARAM_VCF_Q_AMT_0;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCF_Q_AMT_1:
|
|
voice_param_type = VOICE_PARAM_VCF_Q_AMT_1;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCF_Q_SRC_0:
|
|
voice_param_type = VOICE_PARAM_VCF_Q_SRC_0;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCF_Q_SRC_1:
|
|
voice_param_type = VOICE_PARAM_VCF_Q_SRC_1;
|
|
break;
|
|
|
|
case SYNTH_PARAM_VCF_Q_SRC_OP:
|
|
voice_param_type = VOICE_PARAM_VCF_Q_SRC_OP;
|
|
break;
|
|
|
|
case SYNTH_PARAM_LFO_0_SPEED:
|
|
case SYNTH_PARAM_LFO_1_SPEED:
|
|
case SYNTH_PARAM_LFO_2_SPEED:
|
|
case SYNTH_PARAM_LFO_3_SPEED:
|
|
voice_param_type = VOICE_PARAM_LFO_0_SPEED + (paramID - SYNTH_PARAM_LFO_0_SPEED);
|
|
break;
|
|
|
|
case SYNTH_PARAM_LFO_0_SMOOTH:
|
|
case SYNTH_PARAM_LFO_1_SMOOTH:
|
|
case SYNTH_PARAM_LFO_2_SMOOTH:
|
|
case SYNTH_PARAM_LFO_3_SMOOTH:
|
|
voice_param_type = VOICE_PARAM_LFO_0_SMOOTH + (paramID - SYNTH_PARAM_LFO_0_SMOOTH);
|
|
break;
|
|
|
|
case SYNTH_PARAM_LFO_0_DELAY:
|
|
case SYNTH_PARAM_LFO_1_DELAY:
|
|
case SYNTH_PARAM_LFO_2_DELAY:
|
|
case SYNTH_PARAM_LFO_3_DELAY:
|
|
voice_param_type = VOICE_PARAM_LFO_0_DELAY + (paramID - SYNTH_PARAM_LFO_0_DELAY);
|
|
break;
|
|
|
|
case SYNTH_PARAM_LFO_0_ATTACK:
|
|
case SYNTH_PARAM_LFO_1_ATTACK:
|
|
case SYNTH_PARAM_LFO_2_ATTACK:
|
|
case SYNTH_PARAM_LFO_3_ATTACK:
|
|
voice_param_type = VOICE_PARAM_LFO_0_ATTACK + (paramID - SYNTH_PARAM_LFO_0_ATTACK);
|
|
break;
|
|
|
|
case SYNTH_PARAM_LFO_0_SHAPE:
|
|
case SYNTH_PARAM_LFO_1_SHAPE:
|
|
case SYNTH_PARAM_LFO_2_SHAPE:
|
|
case SYNTH_PARAM_LFO_3_SHAPE:
|
|
voice_param_type = VOICE_PARAM_LFO_0_SHAPE + (paramID - SYNTH_PARAM_LFO_0_SHAPE);
|
|
break;
|
|
|
|
case SYNTH_PARAM_LFO_0_MODE:
|
|
case SYNTH_PARAM_LFO_1_MODE:
|
|
case SYNTH_PARAM_LFO_2_MODE:
|
|
case SYNTH_PARAM_LFO_3_MODE:
|
|
voice_param_type = VOICE_PARAM_LFO_0_MODE + (paramID - SYNTH_PARAM_LFO_0_MODE);
|
|
break;
|
|
|
|
case SYNTH_PARAM_LFO_0_SYNC:
|
|
case SYNTH_PARAM_LFO_1_SYNC:
|
|
case SYNTH_PARAM_LFO_2_SYNC:
|
|
case SYNTH_PARAM_LFO_3_SYNC:
|
|
voice_param_type = VOICE_PARAM_LFO_0_SYNC + (paramID - SYNTH_PARAM_LFO_0_SYNC);
|
|
break;
|
|
|
|
case SYNTH_PARAM_LFO_0_SYMMETRY:
|
|
case SYNTH_PARAM_LFO_1_SYMMETRY:
|
|
case SYNTH_PARAM_LFO_2_SYMMETRY:
|
|
case SYNTH_PARAM_LFO_3_SYMMETRY:
|
|
voice_param_type = VOICE_PARAM_LFO_0_SYMMETRY + (paramID - SYNTH_PARAM_LFO_0_SYMMETRY);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (voice_param_type >= 0)
|
|
{
|
|
voice_min = voice;
|
|
voice_max = voice;
|
|
if (voice < 0)
|
|
{
|
|
voice_min = 0;
|
|
voice_max = GET_NUM_VOICES-1;
|
|
}
|
|
|
|
if (isHumanize)
|
|
{
|
|
param_info_t *pConstraints = getParamInfo(paramID);
|
|
param_info_t *pHumanized;
|
|
for (i=voice_min; i <= voice_max; i++)
|
|
{
|
|
param = params[paramID] + controls[paramID];
|
|
for (j=0; j < SYNTH_PER_VOICE_CONTROL_SIZE; j++)
|
|
param += pPer_voice_controls[i].ctrl[j][paramID];
|
|
|
|
param = clampParam(pParamInfo, param);
|
|
pHumanized = &humanize_voice_param[paramID][i];
|
|
((JaySynthVoice*)getVoice(i))->setParameter(voice_param_type, toParam(pHumanized, toSlider(pConstraints, param)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=voice_min; i <= voice_max; i++)
|
|
{
|
|
param = params[paramID] + controls[paramID];
|
|
for (j=0; j < SYNTH_PER_VOICE_CONTROL_SIZE; j++)
|
|
param += pPer_voice_controls[i].ctrl[j][paramID];
|
|
|
|
param = clampParam(pParamInfo, param);
|
|
((JaySynthVoice*)getVoice(i))->setParameter(voice_param_type, param);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//==============================================================================
|
|
// Midi-Event stuff
|
|
//==============================================================================
|
|
void JaySynth::handlePitchWheel (const int midiChannel, const int wheelValue)
|
|
{
|
|
int i;
|
|
synth_float_t scale, value;
|
|
|
|
lastPitchWheelValue = wheelValue;
|
|
lastmidiChannel = midiChannel;
|
|
value = (synth_float_t)wheelValue/16384; // PB-Range 0 to 1, center at 0.5
|
|
|
|
scale = pow(2, toParam(&pb_range[0], value)/12);
|
|
for (i=0; i < GET_NUM_VOICES; i++)
|
|
((JaySynthVoice*)getVoice(i))->setParameter(VOICE_PARAM_OSC_0_SCALE, scale);
|
|
|
|
scale = pow(2, toParam(&pb_range[1], value)/12);
|
|
for (i=0; i < GET_NUM_VOICES; i++)
|
|
((JaySynthVoice*)getVoice(i))->setParameter(VOICE_PARAM_OSC_1_SCALE, scale);
|
|
|
|
}
|
|
|
|
void JaySynth::handleController (const int midiChannel,
|
|
const int controllerNumber,
|
|
const int controllerValue)
|
|
{
|
|
midiCC_info.channel = midiChannel;
|
|
midiCC_info.ID = controllerNumber;
|
|
midiCC_info.value = (synth_float_t)controllerValue/127;
|
|
|
|
last_midiCC_info[controllerNumber] = midiCC_info;
|
|
listeners.call (&JaySynthListener::synthChanged, SYNTH_CHANGED_MIDICC, &midiCC_info);
|
|
|
|
switch (controllerNumber)
|
|
{
|
|
case 0x40: handleSustainPedal (midiChannel, controllerValue >= 64); break;
|
|
case 0x42: handleSostenutoPedal (midiChannel, controllerValue >= 64); break;
|
|
case 0x43: handleSoftPedal (midiChannel, controllerValue >= 64); break;
|
|
default: break;
|
|
}
|
|
|
|
const ScopedLock sl (lock);
|
|
|
|
for (int i = 0; i < GET_NUM_VOICES; i++)
|
|
{
|
|
JaySynthVoice* const voice = voices.getUnchecked (i);
|
|
|
|
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
|
|
voice->controllerMoved (controllerNumber, controllerValue);
|
|
}
|
|
}
|
|
|
|
void JaySynth::handleSustainPedal (int midiChannel, bool isDown)
|
|
{
|
|
jassert (midiChannel > 0 && midiChannel <= 16);
|
|
const ScopedLock sl (lock);
|
|
|
|
if (isDown)
|
|
{
|
|
sustainPedalsDown.setBit (midiChannel);
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < GET_NUM_VOICES; i++)
|
|
{
|
|
JaySynthVoice* const voice = voices.getUnchecked (i);
|
|
|
|
if (voice->isPlayingChannel (midiChannel) && ! voice->keyIsDown)
|
|
Voicestop (voice, true);
|
|
}
|
|
|
|
sustainPedalsDown.clearBit (midiChannel);
|
|
}
|
|
}
|
|
|
|
void JaySynth::handleSostenutoPedal (int midiChannel, bool isDown)
|
|
{
|
|
jassert (midiChannel > 0 && midiChannel <= 16);
|
|
const ScopedLock sl (lock);
|
|
|
|
for (int i = 0; i < GET_NUM_VOICES; i++)
|
|
{
|
|
JaySynthVoice* const voice = voices.getUnchecked (i);
|
|
|
|
if (voice->isPlayingChannel (midiChannel))
|
|
{
|
|
if (isDown)
|
|
voice->sostenutoPedalDown = true;
|
|
else if (voice->sostenutoPedalDown)
|
|
Voicestop (voice, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void JaySynth::handleSoftPedal (int midiChannel, bool /*isDown*/)
|
|
{
|
|
(void) midiChannel;
|
|
jassert (midiChannel > 0 && midiChannel <= 16);
|
|
}
|
|
|
|
//==============================================================================
|
|
// Audio data rendering stuff
|
|
//==============================================================================
|
|
void JaySynth::renderNextBlock (AudioSampleBuffer& outputBuffer,
|
|
const MidiBuffer& midiData,
|
|
int startSample,
|
|
int numSamples)
|
|
{
|
|
int i;
|
|
int numVoicesPlaying;
|
|
float *buf1, *buf2;
|
|
|
|
// must set the sample rate before using this!
|
|
float sr = Synthesiser::getSampleRate();
|
|
jassert (sr != 0);
|
|
|
|
const ScopedLock sl (lock);
|
|
|
|
MidiBuffer::Iterator midiIterator (midiData);
|
|
midiIterator.setNextSamplePosition (startSample);
|
|
MidiMessage m (0xf4, 0.0);
|
|
|
|
while (numSamples > 0)
|
|
{
|
|
int midiEventPos;
|
|
const bool useEvent = midiIterator.getNextEvent (m, midiEventPos)
|
|
&& midiEventPos < startSample + numSamples;
|
|
|
|
const int numThisTime = useEvent ? midiEventPos - startSample
|
|
: numSamples;
|
|
|
|
if (numThisTime > 0)
|
|
{
|
|
|
|
// Process global voice data
|
|
buf1 = outputBuffer.getWritePointer (0, startSample);
|
|
buf2 = outputBuffer.getWritePointer (1, startSample);
|
|
VoiceProcessDataV_Common(&m_pVoices[0], buf1, buf2, numThisTime);
|
|
|
|
// Start audio threads
|
|
for (i=0; i < m_num_audiothreads; i++)
|
|
{
|
|
m_ppAudioThread[i]->go(numThisTime);
|
|
}
|
|
|
|
// Wait for audio threads finishing and add buffer
|
|
for (i=0; i < m_num_audiothreads; i++)
|
|
{
|
|
m_pEventAudioThreadRdy[i].wait(-1);
|
|
outputBuffer.addFrom(0, startSample, m_ppAudioThread[i]->getBuffer(), 0, 0, numThisTime, 1.0);
|
|
outputBuffer.addFrom(1, startSample, m_ppAudioThread[i]->getBuffer(), 1, 0, numThisTime, 1.0);
|
|
}
|
|
}
|
|
|
|
// Clear note if voice is idle
|
|
for (int i = 0; i < max_num_voices; i++)
|
|
{
|
|
if (m_pVoices[i].state == note_state_idle)
|
|
{
|
|
if (voices.getUnchecked(i)->currentlyPlayingNote >= 0)
|
|
{
|
|
voices.getUnchecked(i)->clearCurrentNote();
|
|
numVoicesPlaying = getNumVoicesPlaying(sounds.getUnchecked(0));
|
|
listeners.call (&JaySynthListener::synthChanged, SYNTH_CHANGED_NUM_VOICES_PLAYING, &numVoicesPlaying);
|
|
}
|
|
}
|
|
}
|
|
if (useEvent)
|
|
handleMidiEvent (m);
|
|
|
|
startSample += numThisTime;
|
|
numSamples -= numThisTime;
|
|
}
|
|
}
|
|
|
|
void JaySynth::handleMidiEvent (const MidiMessage& m)
|
|
{
|
|
if (m.isNoteOn())
|
|
{
|
|
noteOn (m.getChannel(),
|
|
m.getNoteNumber(),
|
|
m.getFloatVelocity());
|
|
}
|
|
else if (m.isNoteOff())
|
|
{
|
|
noteOff (m.getChannel(),
|
|
m.getNoteNumber(),
|
|
true);
|
|
}
|
|
else if (m.isAllNotesOff() || m.isAllSoundOff())
|
|
{
|
|
allNotesOff (m.getChannel(), true);
|
|
|
|
}
|
|
else if (m.isPitchWheel())
|
|
{
|
|
const int channel = m.getChannel();
|
|
const int wheelPos = m.getPitchWheelValue();
|
|
lastPitchWheelValues [channel - 1] = wheelPos;
|
|
|
|
handlePitchWheel (channel, wheelPos);
|
|
}
|
|
else if (m.isController())
|
|
{
|
|
handleController (m.getChannel(),
|
|
m.getControllerNumber(),
|
|
m.getControllerValue());
|
|
}
|
|
else if (m.isMidiStart())
|
|
{
|
|
SynthDebug("MidiStart\n");
|
|
}
|
|
else if (m.isMidiContinue())
|
|
{
|
|
SynthDebug("MidiContinue\n");
|
|
}
|
|
else if (m.isMidiStop())
|
|
{
|
|
SynthDebug("MidiStop\n");
|
|
}
|
|
else if (m.isMidiClock())
|
|
{
|
|
|
|
}
|
|
else if (m.isSongPositionPointer())
|
|
{
|
|
|
|
}
|
|
else if (m.isQuarterFrame())
|
|
{
|
|
|
|
}
|
|
else if (m.isFullFrame())
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
// Voice trigger stuff
|
|
//==============================================================================
|
|
void JaySynth::Voicestart (JaySynthVoice* const voice,
|
|
SynthesiserSound* const sound,
|
|
const int midiChannel,
|
|
const int midiNoteNumber,
|
|
const float velocity, bool doTrigger)
|
|
{
|
|
int numVoicesPlaying;
|
|
|
|
if (voice != nullptr && sound != nullptr)
|
|
{
|
|
// if (voice->getCurrentlyPlayingSound() != nullptr)
|
|
// if (!isMonophon)
|
|
// voice->stopNote (false);
|
|
|
|
if (pMidCC_editBuffer)
|
|
{
|
|
for (int paramID=0; paramID < SYNTH_NUM_PARAMS; paramID++)
|
|
{
|
|
if (pMidCC_editBuffer->isAssignedVelKeyControl(paramID))
|
|
setPerVoiceControl(voice->m_pVoice->id, SYNTH_PER_VOICE_CONTROL_VELKEY, paramID, pMidCC_editBuffer->getVelKeyControl(paramID, velocity, midiNoteNumber));
|
|
}
|
|
}
|
|
|
|
pCurrNoteInfos[voice->m_pVoice->id].note = midiNoteNumber;
|
|
pCurrNoteInfos[voice->m_pVoice->id].velocity = velocity;
|
|
|
|
VoiceParam2Set(voice->m_pVoice, VOICE_PARAM_KEY, midiNoteNumber);
|
|
|
|
// if (!m_isMonophon || (m_isMonophon && doTrigger && (m_num_keys_pressed_last == 0)) || (m_isMonophon && doTrigger && m_doMonophonRetrigger && (m_highest_key_last < midiNoteNumber)))
|
|
if (!m_isMonophon || (m_isMonophon && doTrigger && (m_num_keys_pressed_last == 0)) || (m_isMonophon && m_doMonophonRetrigger && (m_latest_key != m_latest_key_last)))
|
|
{
|
|
voice->startNote (midiNoteNumber, velocity, sound,
|
|
lastPitchWheelValues [midiChannel - 1]);
|
|
}
|
|
|
|
voice->currentlyPlayingNote = midiNoteNumber;
|
|
// voice->noteOnTime = ++lastNoteOnCounter;
|
|
voice->currentlyPlayingSound = sound;
|
|
voice->keyIsDown = true;
|
|
voice->sostenutoPedalDown = false;
|
|
numVoicesPlaying = getNumVoicesPlaying(sounds.getUnchecked(0));
|
|
listeners.call (&JaySynthListener::synthChanged, SYNTH_CHANGED_NUM_VOICES_PLAYING, &numVoicesPlaying);
|
|
|
|
}
|
|
}
|
|
|
|
void JaySynth::Voicestop (JaySynthVoice* voice, const bool allowTailOff)
|
|
{
|
|
jassert (voice != nullptr);
|
|
|
|
voice->stopNote (allowTailOff);
|
|
|
|
// the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()!
|
|
jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0));
|
|
}
|
|
|
|
void JaySynth::allNotesOff (const int midiChannel, const bool allowTailOff)
|
|
{
|
|
int i;
|
|
int numVoicesPlaying;
|
|
const ScopedLock sl (lock);
|
|
|
|
clearKeysPressed();
|
|
|
|
for (i=0; i < max_num_voices; i++)
|
|
{
|
|
JaySynthVoice* const voice = voices.getUnchecked (i);
|
|
|
|
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
|
|
voice->stopNote (allowTailOff);
|
|
}
|
|
sustainPedalsDown.clear();
|
|
next_possible_voice = 0;
|
|
oldest_voice = 0;
|
|
}
|
|
|
|
void JaySynth::noteOn (const int midiChannel, const int midiNoteNumber, const float velocity)
|
|
{
|
|
int i, j, latest_note;
|
|
float latest_vel;
|
|
const ScopedLock sl (lock);
|
|
|
|
last_midi_note_info.note = midiNoteNumber;
|
|
last_midi_note_info.velocity = velocity;
|
|
listeners.call (&JaySynthListener::synthChanged, SYNTH_CHANGED_NOTE_PRESSED, &last_midi_note_info);
|
|
|
|
setKeyPressed(midiNoteNumber, velocity);
|
|
if (m_isMonophon)
|
|
{
|
|
latest_note = m_latest_key;
|
|
latest_vel = m_monoMGR.getVel(m_latest_key);
|
|
for (i=0; i < sounds.size(); i++)
|
|
{
|
|
SynthesiserSound* const sound = sounds.getUnchecked(i);
|
|
if (m_isUnison)
|
|
{
|
|
for (j=0; j < unison_num_voices; j++)
|
|
{
|
|
Voicestart (voices.getUnchecked (j), sound, midiChannel, latest_note, latest_vel, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Voicestart (voices.getUnchecked (0), sound, midiChannel, latest_note, latest_vel, true);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = sounds.size(); --i >= 0;)
|
|
{
|
|
SynthesiserSound* const sound = sounds.getUnchecked(i);
|
|
|
|
if (sound->appliesToNote (midiNoteNumber)
|
|
&& sound->appliesToChannel (midiChannel))
|
|
{
|
|
// If hitting a note that's still ringing, stop it first (it could be
|
|
// still playing because of the sustain or sostenuto pedal).
|
|
// for (j=0; j < max_num_voices; j++)
|
|
// {
|
|
// JaySynthVoice* const voice = voices.getUnchecked (j);
|
|
//
|
|
// if (!m_isMonophon)
|
|
// {
|
|
// if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel))
|
|
// Voicestop (voice, true);
|
|
// }
|
|
// }
|
|
if (m_isUnison)
|
|
{
|
|
for (j=0; j < unison_num_voices; j++)
|
|
{
|
|
Voicestart (getFreeVoice (sound, isNoteStealingEnabled()), sound, midiChannel, midiNoteNumber, velocity, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Voicestart (getFreeVoice (sound, isNoteStealingEnabled()), sound, midiChannel, midiNoteNumber, velocity, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void JaySynth::noteOff (const int midiChannel,
|
|
const int midiNoteNumber,
|
|
const bool allowTailOff)
|
|
{
|
|
int i, j, latest_note;
|
|
float latest_vel;
|
|
const ScopedLock sl (lock);
|
|
|
|
midi_note_info_t midi_note_info;
|
|
midi_note_info.note = midiNoteNumber;
|
|
midi_note_info.velocity = 0;
|
|
listeners.call (&JaySynthListener::synthChanged, SYNTH_CHANGED_NOTE_RELEASED, &midi_note_info);
|
|
|
|
setKeyPressed(midiNoteNumber, 0);
|
|
if (m_isMonophon)
|
|
{
|
|
latest_note = m_latest_key;
|
|
latest_vel = m_monoMGR.getVel(m_latest_key);
|
|
if (latest_note >= 0)
|
|
{
|
|
for (i = sounds.size(); --i >= 0;)
|
|
{
|
|
SynthesiserSound* const sound = sounds.getUnchecked(i);
|
|
if (m_isUnison)
|
|
{
|
|
for (j=0; j < unison_num_voices; j++)
|
|
{
|
|
Voicestart (voices.getUnchecked (j), sound, midiChannel, latest_note, latest_vel, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Voicestart (voices.getUnchecked (0), sound, midiChannel, latest_note, latest_vel, false);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i=0; i < max_num_voices; i++)
|
|
{
|
|
JaySynthVoice* const voice = voices.getUnchecked (i);
|
|
voice->keyIsDown = false;
|
|
if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown))
|
|
{
|
|
Voicestop (voice, allowTailOff);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
for (i=0; i < max_num_voices; i++)
|
|
{
|
|
JaySynthVoice* const voice = voices.getUnchecked (i);
|
|
|
|
if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
|
|
{
|
|
SynthesiserSound* const sound = voice->getCurrentlyPlayingSound();
|
|
|
|
if (sound != nullptr
|
|
&& sound->appliesToNote (midiNoteNumber)
|
|
&& sound->appliesToChannel (midiChannel))
|
|
{
|
|
voice->keyIsDown = false;
|
|
|
|
if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown))
|
|
Voicestop (voice, allowTailOff);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
// Voice management stuff
|
|
//==============================================================================
|
|
void JaySynth::unisonoModeChanged(void)
|
|
{
|
|
int key;
|
|
float vel;
|
|
// if ((m_doMonophon ^ m_isMonophon) || (m_doUnison ^ m_isUnison))
|
|
{
|
|
m_isMonophon = m_doMonophon;
|
|
m_isUnison = m_doUnison;
|
|
allNotesOff(0, false);
|
|
if (m_isMonophon)
|
|
{
|
|
key = m_latest_key;
|
|
vel = m_monoMGR.getVel(m_latest_key);
|
|
}
|
|
if (m_isUnison)
|
|
{
|
|
m_unisonGain = 5.0/(4 + unison_num_voices);
|
|
}
|
|
else
|
|
{
|
|
m_unisonGain = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void JaySynth::advanceNextPossibleVoice(void)
|
|
{
|
|
next_possible_voice++;
|
|
if (next_possible_voice >= total_num_voices)
|
|
next_possible_voice = 0;
|
|
}
|
|
|
|
int JaySynth::getNextPossibleVoice(void)
|
|
{
|
|
return next_possible_voice;
|
|
}
|
|
|
|
void JaySynth::advanceOldestVoice(void)
|
|
{
|
|
oldest_voice++;
|
|
if (oldest_voice >= total_num_voices)
|
|
oldest_voice = 0;
|
|
}
|
|
|
|
int JaySynth::getOldestVoice(void)
|
|
{
|
|
return oldest_voice;
|
|
}
|
|
|
|
int JaySynth::getNumVoicesPlaying (SynthesiserSound* soundToPlay)
|
|
{
|
|
int i, numVoicesFound;
|
|
const ScopedLock sl (lock);
|
|
|
|
numVoicesFound = 0;
|
|
for (i = 0; i < max_num_voices; i++)
|
|
{
|
|
if (voices.getUnchecked (i)->getCurrentlyPlayingNote() >= 0
|
|
&& voices.getUnchecked (i)->canPlaySound (soundToPlay))
|
|
{
|
|
numVoicesFound++;
|
|
}
|
|
}
|
|
return numVoicesFound;
|
|
}
|
|
|
|
JaySynthVoice* JaySynth::getFreeVoice (SynthesiserSound* soundToPlay,
|
|
const bool stealIfNoneAvailable)
|
|
{
|
|
int i, freeVoiceFound;
|
|
const ScopedLock sl (lock);
|
|
|
|
freeVoiceFound = -1;
|
|
for (i = 0; i < total_num_voices; i++)
|
|
{
|
|
if (voices.getUnchecked (getNextPossibleVoice())->getCurrentlyPlayingNote() < 0
|
|
&& voices.getUnchecked (getNextPossibleVoice())->canPlaySound (soundToPlay))
|
|
{
|
|
freeVoiceFound = getNextPossibleVoice();
|
|
advanceNextPossibleVoice();
|
|
break;
|
|
}
|
|
advanceNextPossibleVoice();
|
|
}
|
|
|
|
if (freeVoiceFound >= 0)
|
|
{
|
|
return voices.getUnchecked (freeVoiceFound);
|
|
}
|
|
|
|
if (stealIfNoneAvailable)
|
|
{
|
|
// currently this just steals the one that's been playing the longest, but could be made a bit smarter..
|
|
JaySynthVoice* oldest = voices.getUnchecked (getOldestVoice());
|
|
|
|
for (i = 0; i < total_num_voices; i++)
|
|
{
|
|
advanceOldestVoice();
|
|
if (voices.getUnchecked (getOldestVoice())->getCurrentlyPlayingNote() >= 0)
|
|
break;
|
|
|
|
}
|
|
|
|
jassert (oldest != nullptr);
|
|
return oldest;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void JaySynth::clearKeysPressed(void)
|
|
{
|
|
m_monoMGR.init();
|
|
m_num_keys_pressed = -1;
|
|
m_latest_key = -1;
|
|
m_highest_key = -1;
|
|
m_num_keys_pressed_last = -1;
|
|
m_latest_key_last = -1;
|
|
m_highest_key_last = -1;
|
|
}
|
|
|
|
void JaySynth::setKeyPressed(int key, float vel)
|
|
{
|
|
m_num_keys_pressed_last = m_monoMGR.getNumNotes();
|
|
m_latest_key_last = m_monoMGR.getLatestNote();
|
|
m_highest_key_last = m_monoMGR.getHighestNote();
|
|
m_monoMGR.add(key, vel);
|
|
m_num_keys_pressed = m_monoMGR.getNumNotes();
|
|
m_latest_key = m_monoMGR.getLatestNote();
|
|
m_highest_key = m_monoMGR.getHighestNote();
|
|
}
|
|
|
|
void JaySynthThread::run(void)
|
|
{
|
|
float *buf1, *buf2;
|
|
|
|
while(!threadShouldExit())
|
|
{
|
|
wait(-1);
|
|
if (threadShouldExit())
|
|
break;
|
|
|
|
m_buffer->clear (0, 0, m_sample_len);
|
|
m_buffer->clear (1, 0, m_sample_len);
|
|
|
|
buf1 = m_buffer->getWritePointer (0, 0);
|
|
buf2 = m_buffer->getWritePointer (1, 0);
|
|
|
|
for (int i = m_voice_offset; i < m_num_voices; i += m_voice_step)
|
|
{
|
|
if (m_pVoices[i].state == note_state_idle)
|
|
continue;
|
|
|
|
VoiceProcessDataV(&m_pVoices[i], buf1, buf2, m_sample_len);
|
|
|
|
}
|
|
m_pEventRdy->signal();
|
|
}
|
|
}
|
|
|
|
JaySynthThread::JaySynthThread(WaitableEvent *pEventRdy, voice_t *pVoices, int num_voices, int offset, int step)
|
|
: Thread(L"")
|
|
{
|
|
setCurrentThreadName(L"JaySynthAudioThread_" + String(offset));
|
|
m_pEventRdy = pEventRdy;
|
|
m_num_voices = num_voices;
|
|
m_pVoices = pVoices;
|
|
m_voice_offset = offset;
|
|
m_voice_step = step;
|
|
m_buffer = new AudioSampleBuffer(2, SYNTH_MAX_BUFSIZE);
|
|
startThread (12);
|
|
}
|
|
|
|
void JaySynthThread::go(int len)
|
|
{
|
|
m_sample_len = len;
|
|
notify();
|
|
}
|
|
|
|
AudioSampleBuffer& JaySynthThread::getBuffer(void)
|
|
{
|
|
return *m_buffer;
|
|
}
|
|
|
|
JaySynthThread::~JaySynthThread()
|
|
{
|
|
stopThread (100);
|
|
delete (m_buffer);
|
|
}
|
|
//==============================================================================
|