aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorWohlstand <admin@wohlnet.ru>2025-05-23 21:35:53 +0300
committerWohlstand <admin@wohlnet.ru>2025-05-23 21:35:53 +0300
commit91558f59359eedef8d67dacacb838da498e3c3ef (patch)
tree3780e58dec518c6bf2f33d406a7b064a319e21d6 /src
parent9b4e6de8ca758afa0917ad7472bde41bfc3848c0 (diff)
downloadlibADLMIDI-91558f59359eedef8d67dacacb838da498e3c3ef.tar.gz
libADLMIDI-91558f59359eedef8d67dacacb838da498e3c3ef.tar.bz2
libADLMIDI-91558f59359eedef8d67dacacb838da498e3c3ef.zip
Improve stability of 2-voice instruments on single OPL2
Diffstat (limited to 'src')
-rw-r--r--src/adlmidi_midiplay.cpp54
-rw-r--r--src/adlmidi_midiplay.hpp7
2 files changed, 58 insertions, 3 deletions
diff --git a/src/adlmidi_midiplay.cpp b/src/adlmidi_midiplay.cpp
index b27ed13..2383d26 100644
--- a/src/adlmidi_midiplay.cpp
+++ b/src/adlmidi_midiplay.cpp
@@ -513,6 +513,9 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity)
for(uint32_t ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount)
{
+ int32_t c = -1;
+ int32_t bs = -0x7FFFFFFFl;
+
if(ccount == 1)
{
if(voices[0] == voices[1])
@@ -520,9 +523,18 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity)
if(adlchannel[0] == -1)
break; // No secondary if primary failed
}
-
- int32_t c = -1;
- int32_t bs = -0x7FFFFFFFl;
+ else if(!m_setup.enableAutoArpeggio &&
+ (is_2op || pseudo_4op) &&
+ (ains->flags & OplInstMeta::Mask_RhythmMode) == 0)
+ {
+ if(killSecondVoicesIfOverflow(c))
+ {
+ adlchannel[0] = c;
+ adlchannel[1] = -1;
+ voices[1] = voices[0];
+ break;
+ }
+ }
for(size_t a = 0; a < (size_t)synth.m_numChannels; ++a)
{
@@ -1529,6 +1541,42 @@ int64_t MIDIplay::calculateChipChannelGoodness(size_t c, const MIDIchannel::Note
return s;
}
+bool MIDIplay::killSecondVoicesIfOverflow(int32_t &new_chan)
+{
+ Synth &synth = *m_synth;
+ bool ret = false;
+
+ int free2op = 0;
+
+ for(size_t a = 0; a < (size_t)synth.m_numChannels; ++a)
+ {
+ if(synth.m_channelCategory[a] != OPL3::ChanCat_Regular)
+ continue;
+
+ AdlChannel &chan = m_chipChannels[a];
+
+ if(chan.users.empty())
+ ++free2op;
+ else if(chan.recent_ins.pseudo4op)
+ new_chan = a;
+ }
+
+ if(!free2op && new_chan >= 0)
+ {
+ AdlChannel::users_iterator j = &m_chipChannels[new_chan].users.front();
+ AdlChannel::LocationData &jd = j->value;
+ MIDIchannel::notes_iterator i(m_midiChannels[jd.loc.MidCh].ensure_find_activenote(jd.loc.note));
+
+ if(i->value.chip_channels_count == 2)
+ {
+ m_chipChannels[new_chan].users.erase(j);
+ i->value.chip_channels_count = 1;
+ ret = true;
+ }
+ }
+
+ return ret;
+}
void MIDIplay::prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins)
{
diff --git a/src/adlmidi_midiplay.hpp b/src/adlmidi_midiplay.hpp
index 6ab0915..e5058b9 100644
--- a/src/adlmidi_midiplay.hpp
+++ b/src/adlmidi_midiplay.hpp
@@ -963,6 +963,13 @@ private:
int64_t calculateChipChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins) const;
/**
+ * @brief If no free chip channels, try to kill at least one second voice of pseudo-4-op instruments and steal the released channel
+ * @param new_chan Value of released chip channel to reuse
+ * @return true if any channel was been stolen, or false when nothing happen
+ */
+ bool killSecondVoicesIfOverflow(int32_t &new_chan);
+
+ /**
* @brief A new note will be played on this channel using this instrument.
* @param c Wanted chip channel
* @param ins Instrument wanted to be used in this channel