mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Synthesiser: Ensured that the voice stealing algorithm does not allocate
This commit is contained in:
parent
4abdb6da3e
commit
87d519759e
4 changed files with 52 additions and 22 deletions
|
|
@ -184,15 +184,19 @@ MPESynthesiserVoice* MPESynthesiser::findVoiceToSteal (MPENote noteToStealVoiceF
|
|||
MPESynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase
|
||||
MPESynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase
|
||||
|
||||
// All major OSes use double-locking so this will be lock- and wait-free as long as stealLock is not
|
||||
// contended. This is always the case if you do not call findVoiceToSteal on multiple threads at
|
||||
// the same time.
|
||||
const ScopedLock sl (stealLock);
|
||||
|
||||
// this is a list of voices we can steal, sorted by how long they've been running
|
||||
Array<MPESynthesiserVoice*> usableVoices;
|
||||
usableVoices.ensureStorageAllocated (voices.size());
|
||||
usableVoicesToStealArray.clear();
|
||||
|
||||
for (auto* voice : voices)
|
||||
{
|
||||
jassert (voice->isActive()); // We wouldn't be here otherwise
|
||||
|
||||
usableVoices.add (voice);
|
||||
usableVoicesToStealArray.add (voice);
|
||||
|
||||
// NB: Using a functor rather than a lambda here due to scare-stories about
|
||||
// compilers generating code containing heap allocations..
|
||||
|
|
@ -201,7 +205,7 @@ MPESynthesiserVoice* MPESynthesiser::findVoiceToSteal (MPENote noteToStealVoiceF
|
|||
bool operator() (const MPESynthesiserVoice* a, const MPESynthesiserVoice* b) const noexcept { return a->noteOnTime < b->noteOnTime; }
|
||||
};
|
||||
|
||||
std::sort (usableVoices.begin(), usableVoices.end(), Sorter());
|
||||
std::sort (usableVoicesToStealArray.begin(), usableVoicesToStealArray.end(), Sorter());
|
||||
|
||||
if (! voice->isPlayingButReleased()) // Don't protect released notes
|
||||
{
|
||||
|
|
@ -222,24 +226,24 @@ MPESynthesiserVoice* MPESynthesiser::findVoiceToSteal (MPENote noteToStealVoiceF
|
|||
// If we want to re-use the voice to trigger a new note,
|
||||
// then The oldest note that's playing the same note number is ideal.
|
||||
if (noteToStealVoiceFor.isValid())
|
||||
for (auto* voice : usableVoices)
|
||||
for (auto* voice : usableVoicesToStealArray)
|
||||
if (voice->getCurrentlyPlayingNote().initialNote == noteToStealVoiceFor.initialNote)
|
||||
return voice;
|
||||
|
||||
// Oldest voice that has been released (no finger on it and not held by sustain pedal)
|
||||
for (auto* voice : usableVoices)
|
||||
for (auto* voice : usableVoicesToStealArray)
|
||||
if (voice != low && voice != top && voice->isPlayingButReleased())
|
||||
return voice;
|
||||
|
||||
// Oldest voice that doesn't have a finger on it:
|
||||
for (auto* voice : usableVoices)
|
||||
for (auto* voice : usableVoicesToStealArray)
|
||||
if (voice != low && voice != top
|
||||
&& voice->getCurrentlyPlayingNote().keyState != MPENote::keyDown
|
||||
&& voice->getCurrentlyPlayingNote().keyState != MPENote::keyDownAndSustained)
|
||||
return voice;
|
||||
|
||||
// Oldest voice that isn't protected
|
||||
for (auto* voice : usableVoices)
|
||||
for (auto* voice : usableVoicesToStealArray)
|
||||
if (voice != low && voice != top)
|
||||
return voice;
|
||||
|
||||
|
|
@ -256,9 +260,16 @@ MPESynthesiserVoice* MPESynthesiser::findVoiceToSteal (MPENote noteToStealVoiceF
|
|||
//==============================================================================
|
||||
void MPESynthesiser::addVoice (MPESynthesiserVoice* const newVoice)
|
||||
{
|
||||
const ScopedLock sl (voicesLock);
|
||||
newVoice->setCurrentSampleRate (getSampleRate());
|
||||
voices.add (newVoice);
|
||||
{
|
||||
const ScopedLock sl (voicesLock);
|
||||
newVoice->setCurrentSampleRate (getSampleRate());
|
||||
voices.add (newVoice);
|
||||
}
|
||||
|
||||
{
|
||||
const ScopedLock sl (stealLock);
|
||||
usableVoicesToStealArray.ensureStorageAllocated (voices.size() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void MPESynthesiser::clearVoices()
|
||||
|
|
|
|||
|
|
@ -304,6 +304,8 @@ private:
|
|||
//==============================================================================
|
||||
std::atomic<bool> shouldStealVoices { false };
|
||||
uint32 lastNoteOnCounter = 0;
|
||||
mutable CriticalSection stealLock;
|
||||
mutable Array<MPESynthesiserVoice*> usableVoicesToStealArray;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiser)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -98,9 +98,20 @@ void Synthesiser::clearVoices()
|
|||
|
||||
SynthesiserVoice* Synthesiser::addVoice (SynthesiserVoice* const newVoice)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
newVoice->setCurrentPlaybackSampleRate (sampleRate);
|
||||
return voices.add (newVoice);
|
||||
SynthesiserVoice* voice;
|
||||
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
newVoice->setCurrentPlaybackSampleRate (sampleRate);
|
||||
voice = voices.add (newVoice);
|
||||
}
|
||||
|
||||
{
|
||||
const ScopedLock sl (stealLock);
|
||||
usableVoicesToStealArray.ensureStorageAllocated (voices.size() + 1);
|
||||
}
|
||||
|
||||
return voice;
|
||||
}
|
||||
|
||||
void Synthesiser::removeVoice (const int index)
|
||||
|
|
@ -514,9 +525,13 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
|
|||
SynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase
|
||||
SynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase
|
||||
|
||||
// All major OSes use double-locking so this will be lock- and wait-free as long as the lock is not
|
||||
// contended. This is always the case if you do not call findVoiceToSteal on multiple threads at
|
||||
// the same time.
|
||||
const ScopedLock sl (stealLock);
|
||||
|
||||
// this is a list of voices we can steal, sorted by how long they've been running
|
||||
Array<SynthesiserVoice*> usableVoices;
|
||||
usableVoices.ensureStorageAllocated (voices.size());
|
||||
usableVoicesToStealArray.clear();
|
||||
|
||||
for (auto* voice : voices)
|
||||
{
|
||||
|
|
@ -524,7 +539,7 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
|
|||
{
|
||||
jassert (voice->isVoiceActive()); // We wouldn't be here otherwise
|
||||
|
||||
usableVoices.add (voice);
|
||||
usableVoicesToStealArray.add (voice);
|
||||
|
||||
// NB: Using a functor rather than a lambda here due to scare-stories about
|
||||
// compilers generating code containing heap allocations..
|
||||
|
|
@ -533,7 +548,7 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
|
|||
bool operator() (const SynthesiserVoice* a, const SynthesiserVoice* b) const noexcept { return a->wasStartedBefore (*b); }
|
||||
};
|
||||
|
||||
std::sort (usableVoices.begin(), usableVoices.end(), Sorter());
|
||||
std::sort (usableVoicesToStealArray.begin(), usableVoicesToStealArray.end(), Sorter());
|
||||
|
||||
if (! voice->isPlayingButReleased()) // Don't protect released notes
|
||||
{
|
||||
|
|
@ -553,22 +568,22 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
|
|||
top = nullptr;
|
||||
|
||||
// The oldest note that's playing with the target pitch is ideal..
|
||||
for (auto* voice : usableVoices)
|
||||
for (auto* voice : usableVoicesToStealArray)
|
||||
if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
|
||||
return voice;
|
||||
|
||||
// Oldest voice that has been released (no finger on it and not held by sustain pedal)
|
||||
for (auto* voice : usableVoices)
|
||||
for (auto* voice : usableVoicesToStealArray)
|
||||
if (voice != low && voice != top && voice->isPlayingButReleased())
|
||||
return voice;
|
||||
|
||||
// Oldest voice that doesn't have a finger on it:
|
||||
for (auto* voice : usableVoices)
|
||||
for (auto* voice : usableVoicesToStealArray)
|
||||
if (voice != low && voice != top && ! voice->isKeyDown())
|
||||
return voice;
|
||||
|
||||
// Oldest voice that isn't protected
|
||||
for (auto* voice : usableVoices)
|
||||
for (auto* voice : usableVoicesToStealArray)
|
||||
if (voice != low && voice != top)
|
||||
return voice;
|
||||
|
||||
|
|
|
|||
|
|
@ -627,6 +627,8 @@ private:
|
|||
bool subBlockSubdivisionIsStrict = false;
|
||||
bool shouldStealNotes = true;
|
||||
BigInteger sustainPedalsDown;
|
||||
mutable CriticalSection stealLock;
|
||||
mutable Array<SynthesiserVoice*> usableVoicesToStealArray;
|
||||
|
||||
template <typename floatType>
|
||||
void processNextBlock (AudioBuffer<floatType>&, const MidiBuffer&, int startSample, int numSamples);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue