/**************************************************************************
Copyright (C) 2019 Arnaud Champenois arthelion@free.fr
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/
#include "Synth.h"
#include "SynthVoice.h"
#include "AudioParameters.h"
#include "PluginProcessor.h"
void Synth::handleController(int /*midiChannel*/, int controllerNumber, int controllerValue)
{
const ScopedLock sl(lock);
// In this synth, a voice is meant to only play a single channel,
// and it MUST receive all the events, whatever it is playing or not.
for (auto* voice : voices)
voice->controllerMoved(controllerNumber, controllerValue);
}
void Synth::handleProgramChange(int midiChannel, int programNumber)
{
mSynthParameters.getProcessor().setCurrentProgram(programNumber);
}
void Synth::setCurrentPlaybackSampleRate(double sampleRateIn)
{
Synthesiser::setCurrentPlaybackSampleRate(sampleRateIn);
mChorus.init(float(sampleRateIn));
mDelay.init(float(sampleRateIn));
mReverb.setSampleRate(sampleRateIn);
mMetronome.init(sampleRateIn);
}
void Synth::updateInternalValues() {
const ScopedLock sl(lock);
mChorus.updateInternalValues();
mDelay.updateInternalValues();
mReverbActive = (float(*mSynthParameters.mReverbMix) > 0.0f);
Reverb::Parameters rParam;
rParam.damping = (float(*mSynthParameters.mReverbDamping));
rParam.dryLevel = (1.0f - float(*mSynthParameters.mReverbMix));
rParam.wetLevel = (float(*mSynthParameters.mReverbMix));
rParam.roomSize = (float(*mSynthParameters.mReverbSize));
rParam.width = (float(*mSynthParameters.mReverbWidth));
mGain = (float(*mSynthParameters.mGain));
bool gainAutoset = bool(*mSynthParameters.mGainAutoset);
if (gainAutoset && !mGainAutoset) {
mGain = 1.0;
*mSynthParameters.mGain = float(mGain);
mGainAutoset = true;
}
else
mGainAutoset = gainAutoset;
mReverb.setParameters(rParam);
PolyMode polyMode = PolyMode(int(*mSynthParameters.mPolyMode));
if (polyMode != mPolyMode) {
mPolyMode = polyMode;
allNotesOff(0, true);
mNoteList.clear();
}
mMetronomOn = *mSynthParameters.mMetronomOn;
mMetronome.setTempo(*mSynthParameters.mMetronomTempo);
}
void Synth::lockVoices()
{
for (auto* voice : voices) {
SynthVoice* svoice = static_cast<SynthVoice*>(voice);
svoice->lockVoices();
}
}
void Synth::unlockVoices()
{
for (auto* voice : voices) {
SynthVoice* svoice = static_cast<SynthVoice*>(voice);
svoice->unlockVoices();
}
}
void Synth::renderVoices(AudioBuffer<float>& outputAudio, int startSample, int numSamples)
{
Synthesiser::renderVoices(outputAudio, startSample, numSamples);
const ScopedLock sl(lock);
int samples = numSamples;
int sampleIn = startSample;
while (--samples >= 0) {
double sampleL = outputAudio.getSample(0, sampleIn);
double sampleR = outputAudio.getSample(1, sampleIn);
if (mChorus.isActive()) {
mChorus.next(sampleL, sampleR);
}
if (mDelay.isActive()) {
mDelay.next(sampleL, sampleR);
}
outputAudio.setSample(0, sampleIn, float(sampleL*mGain));
outputAudio.setSample(1, sampleIn, float(sampleR*mGain));
++sampleIn;
}
if (mReverbActive) {
mReverb.processStereo(outputAudio.getWritePointer(0) + startSample,
outputAudio.getWritePointer(1) + startSample, numSamples);
}
if(mMetronomOn)
mMetronome.process(outputAudio, startSample, numSamples);
if (mGainAutoset) {
double max = outputAudio.getMagnitude(startSample, numSamples);
if (max > 1.0) {
mGain *= 0.9 / max;
const MessageManagerLock mmLock;
*mSynthParameters.mGain = float(mGain);
}
}
}
void Synth::noteOn(const int midiChannel,
const int midiNoteNumber,
const float velocity)
{
const ScopedLock sl(lock);
for (auto* sound : sounds)
{
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).
// In mono we alway stop current voice
for (auto* voice : voices) {
// MONO & playing
if ((mPolyMode == MONO && voice->isVoiceActive()) ||
// MONO Legato & playing & note off
(mPolyMode == MONO_LEGATO && voice->isVoiceActive() && !static_cast<SynthVoice*>(mMonoVoice)->isOn()) ||
// playing the same note (all modes)
(mPolyMode != POLY_RINGING && voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel(midiChannel))) {
// We stop and find another mono voice
static_cast<SynthVoice*>(voice)->forceExtinction();
if (isMono()) // repeated note : non legato, start another voice
mMonoVoice = findFreeVoice(sound, midiChannel, midiNoteNumber, isNoteStealingEnabled());
}
}
if (isMono()) {
mNoteList.push_back({ midiNoteNumber, velocity });
if (mPolyMode == MONO_LEGATO && static_cast<SynthVoice*>(mMonoVoice)->isOn()) {
static_cast<SynthVoice*>(mMonoVoice)->changeNote(midiNoteNumber);
}
else
// else we start it
startVoice(mMonoVoice, sound, midiChannel, midiNoteNumber, velocity);
return;
}
}
startVoice(findFreeVoice(sound, midiChannel, midiNoteNumber, isNoteStealingEnabled()),
sound, midiChannel, midiNoteNumber, velocity);
}
}
void Synth::noteOff(int midiChannel, int midiNoteNumber, float velocity, bool allowTailOff)
{
const ScopedLock sl(lock);
if (mPolyMode != MONO_LEGATO) // If no legato, just stops note
Synthesiser::noteOff(midiChannel, midiNoteNumber, velocity, allowTailOff);
if (!mNoteList.empty()) {
// The released note is the last mono one
bool triggerOld = (mNoteList.back().midiNoteNumber == midiNoteNumber);
mNoteList.remove({ midiNoteNumber ,0 });
if (!mNoteList.empty() && triggerOld) {
// We released the last one, so we retrigger the previous if any
NoteDescr&d = mNoteList.back();
noteOn(midiChannel, d.midiNoteNumber, d.velocity);
}
else if (mNoteList.empty() && mPolyMode == MONO_LEGATO) {
// No previous, we have to stop the legato chain
mMonoVoice->stopNote(velocity, true);
}
}
}
SynthesiserVoice* Synth::findFreeVoice(SynthesiserSound* soundToPlay,
int midiChannel, int midiNoteNumber,
const bool stealIfNoneAvailable) const
{
const ScopedLock sl(lock);
for (auto* voice : voices) {
if ((!voice->isVoiceActive()) && voice->canPlaySound(soundToPlay))
return voice;
}
if (stealIfNoneAvailable)
return findVoiceToSteal(soundToPlay, midiChannel, midiNoteNumber);
return nullptr;
}