mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Workaround for an edge-case involving sustain + sostenuto pedal parsing in the Synthesiser class. Also gave that class a quick spring-clean internally
This commit is contained in:
parent
5e4fd94b8f
commit
aed0e13745
2 changed files with 41 additions and 128 deletions
|
|
@ -24,20 +24,8 @@ SynthesiserSound::SynthesiserSound() {}
|
|||
SynthesiserSound::~SynthesiserSound() {}
|
||||
|
||||
//==============================================================================
|
||||
SynthesiserVoice::SynthesiserVoice()
|
||||
: currentSampleRate (44100.0),
|
||||
currentlyPlayingNote (-1),
|
||||
currentPlayingMidiChannel (0),
|
||||
noteOnTime (0),
|
||||
keyIsDown (false),
|
||||
sustainPedalDown (false),
|
||||
sostenutoPedalDown (false)
|
||||
{
|
||||
}
|
||||
|
||||
SynthesiserVoice::~SynthesiserVoice()
|
||||
{
|
||||
}
|
||||
SynthesiserVoice::SynthesiserVoice() {}
|
||||
SynthesiserVoice::~SynthesiserVoice() {}
|
||||
|
||||
bool SynthesiserVoice::isPlayingChannel (const int midiChannel) const
|
||||
{
|
||||
|
|
@ -83,11 +71,6 @@ void SynthesiserVoice::renderNextBlock (AudioBuffer<double>& outputBuffer,
|
|||
|
||||
//==============================================================================
|
||||
Synthesiser::Synthesiser()
|
||||
: sampleRate (0),
|
||||
lastNoteOnCounter (0),
|
||||
minimumSubBlockSize (32),
|
||||
subBlockSubdivisionIsStrict (false),
|
||||
shouldStealNotes (true)
|
||||
{
|
||||
for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i)
|
||||
lastPitchWheelValues[i] = 0x2000;
|
||||
|
|
@ -159,13 +142,11 @@ void Synthesiser::setCurrentPlaybackSampleRate (const double newRate)
|
|||
if (sampleRate != newRate)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
allNotesOff (0, false);
|
||||
|
||||
sampleRate = newRate;
|
||||
|
||||
for (int i = voices.size(); --i >= 0;)
|
||||
voices.getUnchecked (i)->setCurrentPlaybackSampleRate (newRate);
|
||||
for (auto* voice : voices)
|
||||
voice->setCurrentPlaybackSampleRate (newRate);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -230,25 +211,19 @@ void Synthesiser::processNextBlock (AudioBuffer<floatType>& outputAudio,
|
|||
}
|
||||
|
||||
// explicit template instantiation
|
||||
template void Synthesiser::processNextBlock<float> (AudioBuffer<float>& outputAudio,
|
||||
const MidiBuffer& midiData,
|
||||
int startSample,
|
||||
int numSamples);
|
||||
template void Synthesiser::processNextBlock<double> (AudioBuffer<double>& outputAudio,
|
||||
const MidiBuffer& midiData,
|
||||
int startSample,
|
||||
int numSamples);
|
||||
template void Synthesiser::processNextBlock<float> (AudioBuffer<float>&, const MidiBuffer&, int, int);
|
||||
template void Synthesiser::processNextBlock<double> (AudioBuffer<double>&, const MidiBuffer&, int, int);
|
||||
|
||||
void Synthesiser::renderVoices (AudioBuffer<float>& buffer, int startSample, int numSamples)
|
||||
{
|
||||
for (int i = voices.size(); --i >= 0;)
|
||||
voices.getUnchecked (i)->renderNextBlock (buffer, startSample, numSamples);
|
||||
for (auto* voice : voices)
|
||||
voice->renderNextBlock (buffer, startSample, numSamples);
|
||||
}
|
||||
|
||||
void Synthesiser::renderVoices (AudioBuffer<double>& buffer, int startSample, int numSamples)
|
||||
{
|
||||
for (int i = voices.size(); --i >= 0;)
|
||||
voices.getUnchecked (i)->renderNextBlock (buffer, startSample, numSamples);
|
||||
for (auto* voice : voices)
|
||||
voice->renderNextBlock (buffer, startSample, numSamples);
|
||||
}
|
||||
|
||||
void Synthesiser::handleMidiEvent (const MidiMessage& m)
|
||||
|
|
@ -298,23 +273,15 @@ void Synthesiser::noteOn (const int midiChannel,
|
|||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = sounds.size(); --i >= 0;)
|
||||
for (auto* sound : sounds)
|
||||
{
|
||||
SynthesiserSound* const sound = sounds.getUnchecked(i);
|
||||
|
||||
if (sound->appliesToNote (midiNoteNumber)
|
||||
&& sound->appliesToChannel (midiChannel))
|
||||
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 (int j = voices.size(); --j >= 0;)
|
||||
{
|
||||
SynthesiserVoice* const voice = voices.getUnchecked (j);
|
||||
|
||||
if (voice->getCurrentlyPlayingNote() == midiNoteNumber
|
||||
&& voice->isPlayingChannel (midiChannel))
|
||||
for (auto* voice : voices)
|
||||
if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel))
|
||||
stopVoice (voice, 1.0f, true);
|
||||
}
|
||||
|
||||
startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes),
|
||||
sound, midiChannel, midiNoteNumber, velocity);
|
||||
|
|
@ -363,10 +330,8 @@ void Synthesiser::noteOff (const int midiChannel,
|
|||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = voices.size(); --i >= 0;)
|
||||
for (auto* voice : voices)
|
||||
{
|
||||
SynthesiserVoice* const voice = voices.getUnchecked (i);
|
||||
|
||||
if (voice->getCurrentlyPlayingNote() == midiNoteNumber
|
||||
&& voice->isPlayingChannel (midiChannel))
|
||||
{
|
||||
|
|
@ -391,13 +356,9 @@ void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff)
|
|||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = voices.size(); --i >= 0;)
|
||||
{
|
||||
SynthesiserVoice* const voice = voices.getUnchecked (i);
|
||||
|
||||
for (auto* voice : voices)
|
||||
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
|
||||
voice->stopNote (1.0f, allowTailOff);
|
||||
}
|
||||
|
||||
sustainPedalsDown.clear();
|
||||
}
|
||||
|
|
@ -406,13 +367,9 @@ void Synthesiser::handlePitchWheel (const int midiChannel, const int wheelValue)
|
|||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = voices.size(); --i >= 0;)
|
||||
{
|
||||
SynthesiserVoice* const voice = voices.getUnchecked (i);
|
||||
|
||||
for (auto* voice : voices)
|
||||
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
|
||||
voice->pitchWheelMoved (wheelValue);
|
||||
}
|
||||
}
|
||||
|
||||
void Synthesiser::handleController (const int midiChannel,
|
||||
|
|
@ -429,40 +386,28 @@ void Synthesiser::handleController (const int midiChannel,
|
|||
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = voices.size(); --i >= 0;)
|
||||
{
|
||||
SynthesiserVoice* const voice = voices.getUnchecked (i);
|
||||
|
||||
for (auto* voice : voices)
|
||||
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
|
||||
voice->controllerMoved (controllerNumber, controllerValue);
|
||||
}
|
||||
}
|
||||
|
||||
void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = voices.size(); --i >= 0;)
|
||||
{
|
||||
SynthesiserVoice* const voice = voices.getUnchecked (i);
|
||||
|
||||
for (auto* voice : voices)
|
||||
if (voice->getCurrentlyPlayingNote() == midiNoteNumber
|
||||
&& (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)))
|
||||
voice->aftertouchChanged (aftertouchValue);
|
||||
}
|
||||
}
|
||||
|
||||
void Synthesiser::handleChannelPressure (int midiChannel, int channelPressureValue)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = voices.size(); --i >= 0;)
|
||||
{
|
||||
SynthesiserVoice* const voice = voices.getUnchecked (i);
|
||||
|
||||
for (auto* voice : voices)
|
||||
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
|
||||
voice->channelPressureChanged (channelPressureValue);
|
||||
}
|
||||
}
|
||||
|
||||
void Synthesiser::handleSustainPedal (int midiChannel, bool isDown)
|
||||
|
|
@ -474,25 +419,19 @@ void Synthesiser::handleSustainPedal (int midiChannel, bool isDown)
|
|||
{
|
||||
sustainPedalsDown.setBit (midiChannel);
|
||||
|
||||
for (int i = voices.size(); --i >= 0;)
|
||||
{
|
||||
SynthesiserVoice* const voice = voices.getUnchecked (i);
|
||||
|
||||
for (auto* voice : voices)
|
||||
if (voice->isPlayingChannel (midiChannel) && voice->isKeyDown())
|
||||
voice->sustainPedalDown = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = voices.size(); --i >= 0;)
|
||||
for (auto* voice : voices)
|
||||
{
|
||||
SynthesiserVoice* const voice = voices.getUnchecked (i);
|
||||
|
||||
if (voice->isPlayingChannel (midiChannel))
|
||||
{
|
||||
voice->sustainPedalDown = false;
|
||||
|
||||
if (! voice->isKeyDown())
|
||||
if (! (voice->isKeyDown() || voice->isSostenutoPedalDown()))
|
||||
stopVoice (voice, 1.0f, true);
|
||||
}
|
||||
}
|
||||
|
|
@ -506,10 +445,8 @@ void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown)
|
|||
jassert (midiChannel > 0 && midiChannel <= 16);
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = voices.size(); --i >= 0;)
|
||||
for (auto* voice : voices)
|
||||
{
|
||||
SynthesiserVoice* const voice = voices.getUnchecked (i);
|
||||
|
||||
if (voice->isPlayingChannel (midiChannel))
|
||||
{
|
||||
if (isDown)
|
||||
|
|
@ -539,13 +476,9 @@ SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay,
|
|||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
for (int i = 0; i < voices.size(); ++i)
|
||||
{
|
||||
SynthesiserVoice* const voice = voices.getUnchecked (i);
|
||||
|
||||
for (auto* voice : voices)
|
||||
if ((! voice->isVoiceActive()) && voice->canPlaySound (soundToPlay))
|
||||
return voice;
|
||||
}
|
||||
|
||||
if (stealIfNoneAvailable)
|
||||
return findVoiceToSteal (soundToPlay, midiChannel, midiNoteNumber);
|
||||
|
|
@ -569,7 +502,7 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
|
|||
// - Protect the lowest & topmost notes, even if sustained, but not if they've been released.
|
||||
|
||||
// apparently you are trying to render audio without having any voices...
|
||||
jassert (voices.size() > 0);
|
||||
jassert (! voices.isEmpty());
|
||||
|
||||
// These are the voices we want to protect (ie: only steal if unavoidable)
|
||||
SynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase
|
||||
|
|
@ -579,10 +512,8 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
|
|||
Array<SynthesiserVoice*> usableVoices;
|
||||
usableVoices.ensureStorageAllocated (voices.size());
|
||||
|
||||
for (int i = 0; i < voices.size(); ++i)
|
||||
for (auto* voice : voices)
|
||||
{
|
||||
SynthesiserVoice* const voice = voices.getUnchecked (i);
|
||||
|
||||
if (voice->canPlaySound (soundToPlay))
|
||||
{
|
||||
jassert (voice->isVoiceActive()); // We wouldn't be here otherwise
|
||||
|
|
@ -592,7 +523,7 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
|
|||
|
||||
if (! voice->isPlayingButReleased()) // Don't protect released notes
|
||||
{
|
||||
const int note = voice->getCurrentlyPlayingNote();
|
||||
auto note = voice->getCurrentlyPlayingNote();
|
||||
|
||||
if (low == nullptr || note < low->getCurrentlyPlayingNote())
|
||||
low = voice;
|
||||
|
|
@ -607,43 +538,25 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
|
|||
if (top == low)
|
||||
top = nullptr;
|
||||
|
||||
const int numUsableVoices = usableVoices.size();
|
||||
|
||||
// The oldest note that's playing with the target pitch is ideal..
|
||||
for (int i = 0; i < numUsableVoices; ++i)
|
||||
{
|
||||
SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
|
||||
|
||||
for (auto* voice : usableVoices)
|
||||
if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
|
||||
return voice;
|
||||
}
|
||||
|
||||
// Oldest voice that has been released (no finger on it and not held by sustain pedal)
|
||||
for (int i = 0; i < numUsableVoices; ++i)
|
||||
{
|
||||
SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
|
||||
|
||||
for (auto* voice : usableVoices)
|
||||
if (voice != low && voice != top && voice->isPlayingButReleased())
|
||||
return voice;
|
||||
}
|
||||
|
||||
// Oldest voice that doesn't have a finger on it:
|
||||
for (int i = 0; i < numUsableVoices; ++i)
|
||||
{
|
||||
SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
|
||||
|
||||
for (auto* voice : usableVoices)
|
||||
if (voice != low && voice != top && ! voice->isKeyDown())
|
||||
return voice;
|
||||
}
|
||||
|
||||
// Oldest voice that isn't protected
|
||||
for (int i = 0; i < numUsableVoices; ++i)
|
||||
{
|
||||
SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
|
||||
|
||||
for (auto* voice : usableVoices)
|
||||
if (voice != low && voice != top)
|
||||
return voice;
|
||||
}
|
||||
|
||||
// We've only got "protected" voices now: lowest note takes priority
|
||||
jassert (low != nullptr);
|
||||
|
|
|
|||
|
|
@ -249,11 +249,11 @@ private:
|
|||
//==============================================================================
|
||||
friend class Synthesiser;
|
||||
|
||||
double currentSampleRate;
|
||||
int currentlyPlayingNote, currentPlayingMidiChannel;
|
||||
uint32 noteOnTime;
|
||||
double currentSampleRate = 44100.0;
|
||||
int currentlyPlayingNote = -1, currentPlayingMidiChannel = 0;
|
||||
uint32 noteOnTime = 0;
|
||||
SynthesiserSound::Ptr currentlyPlayingSound;
|
||||
bool keyIsDown, sustainPedalDown, sostenutoPedalDown;
|
||||
bool keyIsDown = false, sustainPedalDown = false, sostenutoPedalDown = false;
|
||||
|
||||
AudioBuffer<float> tempBuffer;
|
||||
|
||||
|
|
@ -615,11 +615,11 @@ private:
|
|||
int startSample,
|
||||
int numSamples);
|
||||
//==============================================================================
|
||||
double sampleRate;
|
||||
uint32 lastNoteOnCounter;
|
||||
int minimumSubBlockSize;
|
||||
bool subBlockSubdivisionIsStrict;
|
||||
bool shouldStealNotes;
|
||||
double sampleRate = 0;
|
||||
uint32 lastNoteOnCounter = 0;
|
||||
int minimumSubBlockSize = 32;
|
||||
bool subBlockSubdivisionIsStrict = false;
|
||||
bool shouldStealNotes = true;
|
||||
BigInteger sustainPedalsDown;
|
||||
|
||||
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue