From 478f7bb3bd9afbd1ad435b8b0304b314edd66462 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 17 Dec 2023 15:30:51 -0500 Subject: [PATCH] MIDI input: program change pass-through option --- src/engine/engine.cpp | 4 ++++ src/engine/engine.h | 17 +++++++++++------ src/engine/playback.cpp | 19 ++++++++++++++----- src/gui/gui.cpp | 3 ++- src/gui/gui.h | 3 ++- src/gui/midiMap.cpp | 2 ++ src/gui/settings.cpp | 12 +++++++++++- 7 files changed, 46 insertions(+), 14 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index a7ed96807..b2150ec1b 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -3392,6 +3392,10 @@ void DivEngine::setMidiDirect(bool value) { midiIsDirect=value; } +void DivEngine::setMidiDirectProgram(bool value) { + midiIsDirectProgram=value; +} + void DivEngine::setMidiVolExp(float value) { midiVolExp=value; } diff --git a/src/engine/engine.h b/src/engine/engine.h index c3cd0ff72..4170119e9 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -176,16 +176,16 @@ struct DivNoteEvent { signed char channel; unsigned char ins; signed char note, volume; - bool on, nop, pad1, pad2; - DivNoteEvent(int c, int i, int n, int v, bool o): + bool on, nop, insChange, fromMIDI; + DivNoteEvent(int c, int i, int n, int v, bool o, bool ic=false, bool fm=false): channel(c), ins(i), note(n), volume(v), on(o), nop(false), - pad1(false), - pad2(false) {} + insChange(ic), + fromMIDI(fm) {} DivNoteEvent(): channel(-1), ins(0), @@ -193,8 +193,8 @@ struct DivNoteEvent { volume(-1), on(false), nop(true), - pad1(false), - pad2(false) {} + insChange(false), + fromMIDI(false) {} }; struct DivDispatchContainer { @@ -415,6 +415,7 @@ class DivEngine { bool firstTick; bool skipping; bool midiIsDirect; + bool midiIsDirectProgram; bool lowLatency; bool systemsRegistered; bool hasLoadedSomething; @@ -1188,6 +1189,9 @@ class DivEngine { // set MIDI direct channel map void setMidiDirect(bool value); + // set MIDI direct program change + void setMidiDirectProgram(bool value); + // set MIDI volume curve exponent void setMidiVolExp(float value); @@ -1260,6 +1264,7 @@ class DivEngine { firstTick(false), skipping(false), midiIsDirect(false), + midiIsDirectProgram(false), lowLatency(false), systemsRegistered(false), hasLoadedSomething(false), diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 59c93cf8a..2ab9ead8e 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1370,8 +1370,15 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { pendingNotes.pop_front(); continue; } + if (note.insChange) { + dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,0)); + pendingNotes.pop_front(); + continue; + } if (note.on) { - dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1)); + if (!(midiIsDirect && midiIsDirectProgram && note.fromMIDI)) { + dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1)); + } if (note.volume>=0 && !disCont[dispatchOfChan[note.channel]].dispatch->isVolGlobal()) { float curvedVol=pow((float)note.volume/127.0f,midiVolExp); int mappedVol=disCont[dispatchOfChan[note.channel]].dispatch->mapVelocity(dispatchChanOfChan[note.channel],curvedVol); @@ -1832,7 +1839,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi case TA_MIDI_NOTE_OFF: { if (chan<0 || chan>=chans) break; if (midiIsDirect) { - pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false)); + pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true)); } else { autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); } @@ -1847,13 +1854,13 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi if (chan<0 || chan>=chans) break; if (msg.data[1]==0) { if (midiIsDirect) { - pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false)); + pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true)); } else { autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); } } else { if (midiIsDirect) { - pendingNotes.push_back(DivNoteEvent(chan,ins,msg.data[0]-12,msg.data[1],true)); + pendingNotes.push_back(DivNoteEvent(chan,ins,msg.data[0]-12,msg.data[1],true,false,true)); } else { autoNoteOn(msg.type&15,ins,msg.data[0]-12,msg.data[1]); } @@ -1861,7 +1868,9 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi break; } case TA_MIDI_PROGRAM: { - // TODO: change instrument event thingy + if (midiIsDirect && midiIsDirectProgram) { + pendingNotes.push_back(DivNoteEvent(chan,msg.data[0],0,0,false,true,true)); + } break; } } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 1683c434d..74d615f52 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3803,7 +3803,7 @@ bool FurnaceGUI::loop() { } break; case TA_MIDI_PROGRAM: - if (midiMap.programChange) { + if (midiMap.programChange && !(midiMap.directChannel && midiMap.directProgram)) { curIns=msg.data[0]; if (curIns>=(int)e->song.ins.size()) curIns=e->song.ins.size()-1; wavePreviewInit=true; @@ -7018,6 +7018,7 @@ bool FurnaceGUI::init() { return -2; } + if (midiMap.directChannel && midiMap.directProgram) return -1; return curIns; }); diff --git a/src/gui/gui.h b/src/gui/gui.h index f754a073d..57251fe6d 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -936,7 +936,7 @@ struct MIDIMap { int**** map; std::vector binds; - bool noteInput, volInput, rawVolume, polyInput, directChannel, programChange, midiClock, midiTimeCode, yamahaFMResponse; + bool noteInput, volInput, rawVolume, polyInput, directChannel, programChange, midiClock, midiTimeCode, yamahaFMResponse, directProgram; // 0: disabled // // 1: C- C# D- D# E- F- F# G- G# A- A# B- @@ -999,6 +999,7 @@ struct MIDIMap { midiClock(false), midiTimeCode(false), yamahaFMResponse(false), + directProgram(false), valueInputStyle(1), valueInputControlMSB(0), valueInputControlLSB(0), diff --git a/src/gui/midiMap.cpp b/src/gui/midiMap.cpp index 602347107..fac0399b8 100644 --- a/src/gui/midiMap.cpp +++ b/src/gui/midiMap.cpp @@ -139,6 +139,7 @@ bool MIDIMap::read(String path) { UNDERSTAND_OPTION(midiClock) else UNDERSTAND_OPTION(midiTimeCode) else UNDERSTAND_OPTION(yamahaFMResponse) else + UNDERSTAND_OPTION(directProgram) else UNDERSTAND_OPTION(valueInputStyle) else UNDERSTAND_OPTION(valueInputControlMSB) else UNDERSTAND_OPTION(valueInputControlLSB) else @@ -205,6 +206,7 @@ bool MIDIMap::write(String path) { WRITE_OPTION(midiClock); WRITE_OPTION(midiTimeCode); WRITE_OPTION(yamahaFMResponse); + WRITE_OPTION(directProgram); WRITE_OPTION(valueInputStyle); WRITE_OPTION(valueInputControlMSB); WRITE_OPTION(valueInputControlLSB); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index dbf1a9661..585f49b18 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1141,10 +1141,19 @@ void FurnaceGUI::drawSettings() { //ImGui::Checkbox("Polyphonic/chord input",&midiMap.polyInput); if (ImGui::Checkbox("Map MIDI channels to direct channels",&midiMap.directChannel)) { e->setMidiDirect(midiMap.directChannel); + e->setMidiDirectProgram(midiMap.directChannel && midiMap.directProgram); settingsChanged=true; } + if (midiMap.directChannel) { + if (ImGui::Checkbox("Program change pass-through",&midiMap.directProgram)) { + e->setMidiDirectProgram(midiMap.directChannel && midiMap.directProgram); + settingsChanged=true; + } + } if (ImGui::Checkbox("Map Yamaha FM voice data to instruments",&midiMap.yamahaFMResponse)) settingsChanged=true; - if (ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange)) settingsChanged=true; + if (!(midiMap.directChannel && midiMap.directProgram)) { + if (ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange)) settingsChanged=true; + } //ImGui::Checkbox("Listen to MIDI clock",&midiMap.midiClock); //ImGui::Checkbox("Listen to MIDI time code",&midiMap.midiTimeCode); if (ImGui::Combo("Value input style",&midiMap.valueInputStyle,valueInputStyles,7)) settingsChanged=true; @@ -4029,6 +4038,7 @@ void FurnaceGUI::syncSettings() { midiMap.compile(); e->setMidiDirect(midiMap.directChannel); + e->setMidiDirectProgram(midiMap.directChannel && midiMap.directProgram); e->setMidiVolExp(midiMap.volExp); e->setMetronomeVol(((float)settings.metroVol)/100.0f); e->setSamplePreviewVol(((float)settings.sampleVol)/100.0f);