diff --git a/src/sound/oplsynth/musicblock.cpp b/src/sound/oplsynth/musicblock.cpp index 32254f670..bbbf6f075 100644 --- a/src/sound/oplsynth/musicblock.cpp +++ b/src/sound/oplsynth/musicblock.cpp @@ -53,6 +53,8 @@ int musicBlock::releaseVoice(uint32_t slot, uint32_t killed) struct OPLVoice *ch = &voices[slot]; io->WriteFrequency(slot, ch->note, ch->pitch, 0); ch->index = ~0u; + ch->sustained = false; + if (!killed) ch->timestamp = ++timeCounter; if (killed) io->MuteChannel(slot); return slot; } @@ -65,15 +67,25 @@ int musicBlock::releaseVoice(uint32_t slot, uint32_t killed) int musicBlock::findFreeVoice() { + // We want to prefer the least recently freed voice, as more recently + // freed voices can still play a tone from their release state. + // Sustained voices are replaced when there are no free voices. + uint32_t min_value = ~0u; + int result = -1; for (uint32_t i = 0; i < io->NumChannels; ++i) { - if (voices[i].index == ~0u) + uint32_t voice_value = voices[i].timestamp + (voices[i].sustained ? (1 << 31) : 0); + if ((voices[i].index == ~0u || voices[i].sustained) && (voice_value < min_value)) { - releaseVoice(i, 1); - return i; + min_value = voice_value; + result = i; } } - return -1; + if (result >= 0) + { + releaseVoice(result, 1); + } + return result; } //---------------------------------------------------------------------------- @@ -94,7 +106,6 @@ int musicBlock::replaceExistingVoice() // Lower numbered MIDI channels implicitly have a higher priority // than higher-numbered channels, eg. MIDI channel 1 is never // discarded for MIDI channel 2. - int result = 0; for (uint32_t i = 0; i < io->NumChannels; ++i) @@ -236,7 +247,11 @@ void musicBlock::noteOff(uint32_t id, uint8_t note) { if (voices[i].index == id && voices[i].key == note) { - if (sustain >= MIN_SUSTAIN) voices[i].sustained = true; + if (sustain >= MIN_SUSTAIN) + { + voices[i].sustained = true; + voices[i].timestamp = ++timeCounter; + } else releaseVoice(i, 0); } } @@ -357,7 +372,11 @@ void musicBlock::notesOff(uint32_t id, int value) { if (voices[i].index == id) { - if (oplchannels[id].Sustain >= MIN_SUSTAIN) voices[i].sustained = true; + if (oplchannels[id].Sustain >= MIN_SUSTAIN) + { + voices[i].sustained = true; + voices[i].timestamp = ++timeCounter; + } else releaseVoice(i, 0); } } @@ -475,5 +494,7 @@ void musicBlock::stopAllVoices() for (uint32_t i = 0; i < io->NumChannels; i++) { if (voices[i].index != ~0u) releaseVoice(i, 1); + voices[i].timestamp = 0; } + timeCounter = 0; } diff --git a/src/sound/oplsynth/musicblock.h b/src/sound/oplsynth/musicblock.h index 815e3d267..f6cf2150c 100644 --- a/src/sound/oplsynth/musicblock.h +++ b/src/sound/oplsynth/musicblock.h @@ -15,6 +15,7 @@ struct OPLVoice bool sustained; int8_t fine_tuning; int pitch; + uint32_t timestamp; }; struct musicBlock { @@ -23,6 +24,7 @@ struct musicBlock { OPLChannel oplchannels[NUM_CHANNELS]; OPLio *io; + uint32_t timeCounter; struct GenMidiInstrument OPLinstruments[GENMIDI_NUM_TOTAL];