From c7a37959f0bb17ae769dcd9aa18bd3ac9ebf5b72 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 29 Mar 2022 17:38:30 -0500 Subject: [PATCH] GUI: early MIDI action UI - COMPLETELY UNTESTED it will crash (or not) --- src/gui/gui.cpp | 4 +- src/gui/gui.h | 10 +- src/gui/midiMap.cpp | 31 +++++-- src/gui/settings.cpp | 213 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 246 insertions(+), 12 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 89bd1a019..256cefce7 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1885,7 +1885,8 @@ bool FurnaceGUI::loop() { // parse message here logD("message is %.2x\n",msg.type); - if (msg.type==0xb0) doAction(GUI_ACTION_PLAY_TOGGLE); + int action=midiMap.at(msg); + if (action!=0) doAction(action); midiLock.lock(); midiQueue.pop(); @@ -2651,6 +2652,7 @@ bool FurnaceGUI::init() { midiLock.lock(); midiQueue.push(msg); midiLock.unlock(); + if (midiMap.at(msg)) return -2; return curIns; }); diff --git a/src/gui/gui.h b/src/gui/gui.h index 22b185a41..05fe6208c 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -497,10 +497,10 @@ struct MIDIBind { int type, channel, data1, data2; int action; MIDIBind(): - type(-1), - channel(-1), - data1(-1), - data2(-1), + type(0), + channel(16), + data1(128), + data2(128), action(0) {} }; @@ -545,7 +545,7 @@ struct MIDIMap { void compile(); void deinit(); - int at(TAMidiMessage& where); + int at(const TAMidiMessage& where); bool read(String path); bool write(String path); MIDIMap(): diff --git a/src/gui/midiMap.cpp b/src/gui/midiMap.cpp index 63337e45e..8d31892be 100644 --- a/src/gui/midiMap.cpp +++ b/src/gui/midiMap.cpp @@ -2,7 +2,7 @@ #include "guiConst.h" #include "../ta-log.h" -int MIDIMap::at(TAMidiMessage& where) { +int MIDIMap::at(const TAMidiMessage& where) { if (map==NULL) return 0; int type=(where.type>>4)-8; int chan=where.type&15; @@ -54,6 +54,14 @@ int MIDIMap::at(TAMidiMessage& where) { return ret; } +#define UNDERSTAND_OPTION(x) if (optionNameS==#x) { \ + x=std::stoi(optionValueS); \ +} + +#define UNDERSTAND_FLOAT_OPTION(x) if (optionNameS==#x) { \ + x=std::stof(optionValueS); \ +} + bool MIDIMap::read(String path) { char line[4096]; int curLine=1; @@ -81,9 +89,18 @@ bool MIDIMap::read(String path) { optionValueS=optionValue; try { - if (optionNameS=="noteInput") { - noteInput=std::stoi(optionValueS); - } else { + UNDERSTAND_OPTION(noteInput) else + UNDERSTAND_OPTION(volInput) else + UNDERSTAND_OPTION(rawVolume) else + UNDERSTAND_OPTION(polyInput) else + UNDERSTAND_OPTION(directChannel) else + UNDERSTAND_OPTION(programChange) else + UNDERSTAND_OPTION(midiClock) else + UNDERSTAND_OPTION(midiTimeCode) else + UNDERSTAND_OPTION(valueInputStyle) else + UNDERSTAND_OPTION(valueInputControlMSB) else + UNDERSTAND_OPTION(valueInputControlLSB) else + UNDERSTAND_FLOAT_OPTION(volExp) else { logW("MIDI map unknown option %s at line %d: %s\n",optionName,curLine,line); } } catch (std::out_of_range& e) { @@ -116,6 +133,8 @@ bool MIDIMap::read(String path) { logW("MIDI map unknown action %s at line %d: %s\n",bindAction,curLine,line); break; } + + binds.push_back(bind); curLine++; } @@ -123,8 +142,8 @@ bool MIDIMap::read(String path) { return true; } -#define WRITE_OPTION(x) fprintf(f,"option " #x "%d\n",x); -#define WRITE_FLOAT_OPTION(x) fprintf(f,"option " #x "%f\n",x); +#define WRITE_OPTION(x) fprintf(f,"option " #x " %d\n",x); +#define WRITE_FLOAT_OPTION(x) fprintf(f,"option " #x " %f\n",x); bool MIDIMap::write(String path) { FILE* f=fopen(path.c_str(),"wb"); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 03c17670c..be9aaf034 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -26,6 +26,7 @@ #include "IconsFontAwesome4.h" #include "misc/cpp/imgui_stdlib.h" #include +#include #ifdef __APPLE__ #define FURKMOD_CMD FURKMOD_META @@ -80,6 +81,38 @@ const char* saaCores[]={ "SAASound" }; +const char* valueInputStyles[]={ + "Disabled/custom", + "Two octaves (0 is C-4, F is D#5)", + "Raw (note number is value)", + "Two octaves alternate (lower keys are 0-9, upper keys are A-F)", + "Use dual control change (one for each nibble)", + "Use 14-bit control change" +}; + +const char* messageTypes[]={ + "--select--", + "???", + "???", + "???", + "???", + "???", + "???", + "???", + "Note Off", + "Note On", + "Aftertouch", + "Control", + "Program", + "ChanPressure", + "Pitch Bend", + "SysEx" +}; + +const char* messageChannels[]={ + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "Any" +}; + #define SAMPLE_RATE_SELECTABLE(x) \ if (ImGui::Selectable(#x,settings.audioRate==x)) { \ settings.audioRate=x; \ @@ -110,6 +143,18 @@ const char* saaCores[]={ } \ if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) actionKeys[what]=0; +String stripName(String what) { + String ret; + for (char& i: what) { + if ((i>='A' && i<='Z') || (i>='a' && i<='z') || (i>='0' && i<='9')) { + ret+=i; + } else { + ret+='-'; + } + } + return ret; +} + void FurnaceGUI::promptKey(int which) { bindSetTarget=which; bindSetActive=true; @@ -316,6 +361,168 @@ void FurnaceGUI::drawSettings() { ImGui::EndCombo(); } + if (ImGui::TreeNode("MIDI input settings")) { + ImGui::Checkbox("Note input",&midiMap.noteInput); + ImGui::Checkbox("Velocity input",&midiMap.volInput); + ImGui::Checkbox("Use raw velocity value (don't map from linear to log)",&midiMap.rawVolume); + ImGui::Checkbox("Polyphonic/chord input",&midiMap.polyInput); + ImGui::Checkbox("Map MIDI channels to direct channels",&midiMap.directChannel); + ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange); + ImGui::Checkbox("Listen to MIDI clock",&midiMap.midiClock); + ImGui::Checkbox("Listen to MIDI time code",&midiMap.midiTimeCode); + ImGui::Combo("Value input style",&midiMap.valueInputStyle,valueInputStyles,6); + if (midiMap.valueInputStyle>3) { + if (ImGui::InputInt((midiMap.valueInputStyle==4)?"CC of upper nibble##valueCC1":"MSB CC##valueCC1",&midiMap.valueInputControlMSB,1,16)) { + if (midiMap.valueInputControlMSB<0) midiMap.valueInputControlMSB=0; + if (midiMap.valueInputControlMSB>127) midiMap.valueInputControlMSB=127; + } + if (ImGui::InputInt((midiMap.valueInputStyle==4)?"CC of lower nibble##valueCC2":"LSB CC##valueCC2",&midiMap.valueInputControlLSB,1,16)) { + if (midiMap.valueInputControlLSB<0) midiMap.valueInputControlLSB=0; + if (midiMap.valueInputControlLSB>127) midiMap.valueInputControlLSB=127; + } + } + if (ImGui::SliderFloat("Volume curve",&midiMap.volExp,0.01,8.0,"%.2f")) { + if (midiMap.volExp<0.01) midiMap.volExp=0.01; + if (midiMap.volExp>8.0) midiMap.volExp=8.0; + } rightClickable + float curve[128]; + for (int i=0; i<128; i++) { + curve[i]=(int)(pow((double)i/127.0,midiMap.volExp)*127.0); + } + ImGui::PlotLines("##VolCurveDisplay",curve,128,0,"Volume curve",0.0,127.0,ImVec2(200.0f*dpiScale,200.0f*dpiScale)); + + ImGui::Text("Actions:"); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_PLUS "##AddAction")) { + midiMap.binds.push_back(MIDIBind()); + } + + if (ImGui::BeginTable("MIDIActions",7)) { + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::Text("Type"); + ImGui::TableNextColumn(); + ImGui::Text("Channel"); + ImGui::TableNextColumn(); + ImGui::Text("Note/Control"); + ImGui::TableNextColumn(); + ImGui::Text("Velocity/Value"); + ImGui::TableNextColumn(); + ImGui::Text("Action"); + ImGui::TableNextColumn(); + ImGui::Text("Detect"); + ImGui::TableNextColumn(); + ImGui::Text("Remove"); + + for (size_t i=0; igetConfString("noteKeys",DEFAULT_NOTE_KEYS)); parseKeybinds(); + + midiMap.read(e->getConfigPath()+DIR_SEPARATOR_STR+"midiIn_"+stripName(settings.midiInDevice)+".cfg"); + midiMap.compile(); } #define PUT_UI_COLOR(source) e->setConf(#source,(int)ImGui::GetColorU32(uiColors[source])); @@ -1598,6 +1808,9 @@ void FurnaceGUI::commitSettings() { e->setConf("noteKeys",encodeKeyMap(noteKeys)); + midiMap.compile(); + midiMap.write(e->getConfigPath()+DIR_SEPARATOR_STR+"midiIn_"+stripName(settings.midiInDevice)+".cfg"); + e->saveConf(); if (!e->switchMaster()) {