From 3906fc1dd4f77e1d4d044f74d254e3a95020c229 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 28 Apr 2022 05:04:34 -0500 Subject: [PATCH] better channel allocation strategy issue #376 --- src/engine/engine.cpp | 47 ++++++++++++++++++++++++++++++++++++++++--- src/engine/engine.h | 4 ++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index a95b2e2b6..5abd2d53e 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2138,10 +2138,12 @@ void DivEngine::noteOff(int chan) { } void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { - //if (ch<0 || ch>=chans) return; + bool isViable[DIV_MAX_CHANS]; + bool canPlayAnyway=false; if (midiBaseChan<0) midiBaseChan=0; if (midiBaseChan>=chans) midiBaseChan=chans-1; int finalChan=midiBaseChan; + int finalChanType=getChannelType(finalChan); if (!playing) { reset(); @@ -2149,16 +2151,55 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { playing=true; } + // 1. check which channels are viable for this instrument + DivInstrument* insInst=getIns(ins); + for (int i=0; i=song.insLen || getPreferInsType(i)==insInst->type || getPreferInsSecondType(i)==insInst->type) { + if (insInst->type==DIV_INS_OPL) { + if (insInst->fm.ops==2 || getChannelType(i)==DIV_CH_OP) { + isViable[i]=true; + canPlayAnyway=true; + } else { + isViable[i]=false; + } + } else { + isViable[i]=true; + canPlayAnyway=true; + } + } else { + isViable[i]=false; + } + } + + if (!canPlayAnyway) return; + + // 2. find a free channel do { - if ((ins==-1 || ins>=song.insLen || getPreferInsType(finalChan)==getIns(ins)->type || getPreferInsSecondType(finalChan)==getIns(ins)->type) && chan[finalChan].midiNote==-1) { + if (isViable[finalChan] && chan[finalChan].midiNote==-1 && getChannelType(finalChan)==finalChanType) { chan[finalChan].midiNote=note; + chan[finalChan].midiAge=midiAgeCounter++; pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); - break; + return; } if (++finalChan>=chans) { finalChan=0; } } while (finalChan!=midiBaseChan); + + // 3. find the oldest channel + int candidate=finalChan; + do { + if (isViable[finalChan] && getChannelType(finalChan)==finalChanType && chan[finalChan].midiAge=chans) { + finalChan=0; + } + } while (finalChan!=midiBaseChan); + + chan[candidate].midiNote=note; + chan[candidate].midiAge=midiAgeCounter++; + pendingNotes.push(DivNoteEvent(candidate,ins,note,vol,true)); } void DivEngine::autoNoteOff(int ch, int note, int vol) { diff --git a/src/engine/engine.h b/src/engine/engine.h index ea11f9830..441096a23 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -89,6 +89,7 @@ struct DivChannelState { bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, noteOnInhibit, resetArp; int midiNote, curMidiNote, midiPitch; + size_t midiAge; bool midiAftertouch; DivChannelState(): @@ -135,6 +136,7 @@ struct DivChannelState { midiNote(-1), curMidiNote(-1), midiPitch(-1), + midiAge(0), midiAftertouch(false) {} }; @@ -339,6 +341,7 @@ class DivEngine { int reversePitchTable[4096]; int pitchTable[4096]; int midiBaseChan; + size_t midiAgeCounter; blip_buffer_t* samp_bb; size_t samp_bbInLen; @@ -894,6 +897,7 @@ class DivEngine { audioEngine(DIV_AUDIO_NULL), exportMode(DIV_EXPORT_MODE_ONE), midiBaseChan(0), + midiAgeCounter(0), samp_bb(NULL), samp_bbInLen(0), samp_temp(0),