MIDI input: program change pass-through option

This commit is contained in:
tildearrow 2023-12-17 15:30:51 -05:00
parent 39481ab571
commit 478f7bb3bd
7 changed files with 46 additions and 14 deletions

View file

@ -3392,6 +3392,10 @@ void DivEngine::setMidiDirect(bool value) {
midiIsDirect=value; midiIsDirect=value;
} }
void DivEngine::setMidiDirectProgram(bool value) {
midiIsDirectProgram=value;
}
void DivEngine::setMidiVolExp(float value) { void DivEngine::setMidiVolExp(float value) {
midiVolExp=value; midiVolExp=value;
} }

View file

@ -176,16 +176,16 @@ struct DivNoteEvent {
signed char channel; signed char channel;
unsigned char ins; unsigned char ins;
signed char note, volume; signed char note, volume;
bool on, nop, pad1, pad2; bool on, nop, insChange, fromMIDI;
DivNoteEvent(int c, int i, int n, int v, bool o): DivNoteEvent(int c, int i, int n, int v, bool o, bool ic=false, bool fm=false):
channel(c), channel(c),
ins(i), ins(i),
note(n), note(n),
volume(v), volume(v),
on(o), on(o),
nop(false), nop(false),
pad1(false), insChange(ic),
pad2(false) {} fromMIDI(fm) {}
DivNoteEvent(): DivNoteEvent():
channel(-1), channel(-1),
ins(0), ins(0),
@ -193,8 +193,8 @@ struct DivNoteEvent {
volume(-1), volume(-1),
on(false), on(false),
nop(true), nop(true),
pad1(false), insChange(false),
pad2(false) {} fromMIDI(false) {}
}; };
struct DivDispatchContainer { struct DivDispatchContainer {
@ -415,6 +415,7 @@ class DivEngine {
bool firstTick; bool firstTick;
bool skipping; bool skipping;
bool midiIsDirect; bool midiIsDirect;
bool midiIsDirectProgram;
bool lowLatency; bool lowLatency;
bool systemsRegistered; bool systemsRegistered;
bool hasLoadedSomething; bool hasLoadedSomething;
@ -1188,6 +1189,9 @@ class DivEngine {
// set MIDI direct channel map // set MIDI direct channel map
void setMidiDirect(bool value); void setMidiDirect(bool value);
// set MIDI direct program change
void setMidiDirectProgram(bool value);
// set MIDI volume curve exponent // set MIDI volume curve exponent
void setMidiVolExp(float value); void setMidiVolExp(float value);
@ -1260,6 +1264,7 @@ class DivEngine {
firstTick(false), firstTick(false),
skipping(false), skipping(false),
midiIsDirect(false), midiIsDirect(false),
midiIsDirectProgram(false),
lowLatency(false), lowLatency(false),
systemsRegistered(false), systemsRegistered(false),
hasLoadedSomething(false), hasLoadedSomething(false),

View file

@ -1370,8 +1370,15 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
pendingNotes.pop_front(); pendingNotes.pop_front();
continue; continue;
} }
if (note.insChange) {
dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,0));
pendingNotes.pop_front();
continue;
}
if (note.on) { if (note.on) {
if (!(midiIsDirect && midiIsDirectProgram && note.fromMIDI)) {
dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1)); dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1));
}
if (note.volume>=0 && !disCont[dispatchOfChan[note.channel]].dispatch->isVolGlobal()) { if (note.volume>=0 && !disCont[dispatchOfChan[note.channel]].dispatch->isVolGlobal()) {
float curvedVol=pow((float)note.volume/127.0f,midiVolExp); float curvedVol=pow((float)note.volume/127.0f,midiVolExp);
int mappedVol=disCont[dispatchOfChan[note.channel]].dispatch->mapVelocity(dispatchChanOfChan[note.channel],curvedVol); 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: { case TA_MIDI_NOTE_OFF: {
if (chan<0 || chan>=chans) break; if (chan<0 || chan>=chans) break;
if (midiIsDirect) { if (midiIsDirect) {
pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false)); pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true));
} else { } else {
autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); 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 (chan<0 || chan>=chans) break;
if (msg.data[1]==0) { if (msg.data[1]==0) {
if (midiIsDirect) { if (midiIsDirect) {
pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false)); pendingNotes.push_back(DivNoteEvent(chan,-1,-1,-1,false,false,true));
} else { } else {
autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]);
} }
} else { } else {
if (midiIsDirect) { 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 { } else {
autoNoteOn(msg.type&15,ins,msg.data[0]-12,msg.data[1]); 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; break;
} }
case TA_MIDI_PROGRAM: { 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; break;
} }
} }

View file

@ -3803,7 +3803,7 @@ bool FurnaceGUI::loop() {
} }
break; break;
case TA_MIDI_PROGRAM: case TA_MIDI_PROGRAM:
if (midiMap.programChange) { if (midiMap.programChange && !(midiMap.directChannel && midiMap.directProgram)) {
curIns=msg.data[0]; curIns=msg.data[0];
if (curIns>=(int)e->song.ins.size()) curIns=e->song.ins.size()-1; if (curIns>=(int)e->song.ins.size()) curIns=e->song.ins.size()-1;
wavePreviewInit=true; wavePreviewInit=true;
@ -7018,6 +7018,7 @@ bool FurnaceGUI::init() {
return -2; return -2;
} }
if (midiMap.directChannel && midiMap.directProgram) return -1;
return curIns; return curIns;
}); });

View file

@ -936,7 +936,7 @@ struct MIDIMap {
int**** map; int**** map;
std::vector<MIDIBind> binds; std::vector<MIDIBind> binds;
bool noteInput, volInput, rawVolume, polyInput, directChannel, programChange, midiClock, midiTimeCode, yamahaFMResponse; bool noteInput, volInput, rawVolume, polyInput, directChannel, programChange, midiClock, midiTimeCode, yamahaFMResponse, directProgram;
// 0: disabled // 0: disabled
// //
// 1: C- C# D- D# E- F- F# G- G# A- A# B- // 1: C- C# D- D# E- F- F# G- G# A- A# B-
@ -999,6 +999,7 @@ struct MIDIMap {
midiClock(false), midiClock(false),
midiTimeCode(false), midiTimeCode(false),
yamahaFMResponse(false), yamahaFMResponse(false),
directProgram(false),
valueInputStyle(1), valueInputStyle(1),
valueInputControlMSB(0), valueInputControlMSB(0),
valueInputControlLSB(0), valueInputControlLSB(0),

View file

@ -139,6 +139,7 @@ bool MIDIMap::read(String path) {
UNDERSTAND_OPTION(midiClock) else UNDERSTAND_OPTION(midiClock) else
UNDERSTAND_OPTION(midiTimeCode) else UNDERSTAND_OPTION(midiTimeCode) else
UNDERSTAND_OPTION(yamahaFMResponse) else UNDERSTAND_OPTION(yamahaFMResponse) else
UNDERSTAND_OPTION(directProgram) else
UNDERSTAND_OPTION(valueInputStyle) else UNDERSTAND_OPTION(valueInputStyle) else
UNDERSTAND_OPTION(valueInputControlMSB) else UNDERSTAND_OPTION(valueInputControlMSB) else
UNDERSTAND_OPTION(valueInputControlLSB) else UNDERSTAND_OPTION(valueInputControlLSB) else
@ -205,6 +206,7 @@ bool MIDIMap::write(String path) {
WRITE_OPTION(midiClock); WRITE_OPTION(midiClock);
WRITE_OPTION(midiTimeCode); WRITE_OPTION(midiTimeCode);
WRITE_OPTION(yamahaFMResponse); WRITE_OPTION(yamahaFMResponse);
WRITE_OPTION(directProgram);
WRITE_OPTION(valueInputStyle); WRITE_OPTION(valueInputStyle);
WRITE_OPTION(valueInputControlMSB); WRITE_OPTION(valueInputControlMSB);
WRITE_OPTION(valueInputControlLSB); WRITE_OPTION(valueInputControlLSB);

View file

@ -1141,10 +1141,19 @@ void FurnaceGUI::drawSettings() {
//ImGui::Checkbox("Polyphonic/chord input",&midiMap.polyInput); //ImGui::Checkbox("Polyphonic/chord input",&midiMap.polyInput);
if (ImGui::Checkbox("Map MIDI channels to direct channels",&midiMap.directChannel)) { if (ImGui::Checkbox("Map MIDI channels to direct channels",&midiMap.directChannel)) {
e->setMidiDirect(midiMap.directChannel); e->setMidiDirect(midiMap.directChannel);
e->setMidiDirectProgram(midiMap.directChannel && midiMap.directProgram);
settingsChanged=true; 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("Map Yamaha FM voice data to instruments",&midiMap.yamahaFMResponse)) settingsChanged=true;
if (!(midiMap.directChannel && midiMap.directProgram)) {
if (ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange)) settingsChanged=true; 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 clock",&midiMap.midiClock);
//ImGui::Checkbox("Listen to MIDI time code",&midiMap.midiTimeCode); //ImGui::Checkbox("Listen to MIDI time code",&midiMap.midiTimeCode);
if (ImGui::Combo("Value input style",&midiMap.valueInputStyle,valueInputStyles,7)) settingsChanged=true; if (ImGui::Combo("Value input style",&midiMap.valueInputStyle,valueInputStyles,7)) settingsChanged=true;
@ -4029,6 +4038,7 @@ void FurnaceGUI::syncSettings() {
midiMap.compile(); midiMap.compile();
e->setMidiDirect(midiMap.directChannel); e->setMidiDirect(midiMap.directChannel);
e->setMidiDirectProgram(midiMap.directChannel && midiMap.directProgram);
e->setMidiVolExp(midiMap.volExp); e->setMidiVolExp(midiMap.volExp);
e->setMetronomeVol(((float)settings.metroVol)/100.0f); e->setMetronomeVol(((float)settings.metroVol)/100.0f);
e->setSamplePreviewVol(((float)settings.sampleVol)/100.0f); e->setSamplePreviewVol(((float)settings.sampleVol)/100.0f);