GUI: early MIDI action UI - COMPLETELY UNTESTED

it will crash (or not)
This commit is contained in:
tildearrow 2022-03-29 17:38:30 -05:00
parent 2921f88895
commit c7a37959f0
4 changed files with 246 additions and 12 deletions

View File

@ -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;
});

View File

@ -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():

View File

@ -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");

View File

@ -26,6 +26,7 @@
#include "IconsFontAwesome4.h"
#include "misc/cpp/imgui_stdlib.h"
#include <fmt/printf.h>
#include <imgui.h>
#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; i<midiMap.binds.size(); i++) {
MIDIBind& bind=midiMap.binds[i];
char bindID[1024];
ImGui::PushID(i);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("##BType",messageTypes[bind.type])) {
for (int j=8; j<15; j++) {
if (ImGui::Selectable(messageTypes[j],bind.type==j)) {
bind.type=j;
}
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("##BChannel",messageChannels[bind.channel])) {
if (ImGui::Selectable(messageChannels[16],bind.channel==16)) {
bind.channel=16;
}
for (int j=0; j<16; j++) {
if (ImGui::Selectable(messageChannels[j],bind.channel==j)) {
bind.channel=j;
}
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
if (bind.data1==128) {
snprintf(bindID,1024,"Any");
} else {
snprintf(bindID,1024,"%d (0x%.2X, %s)",bind.data1,bind.data1,noteNames[bind.data1+60]);
}
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("##BValue1",bindID)) {
if (ImGui::Selectable("Any",bind.data1==128)) {
bind.data1=128;
}
for (int j=0; j<128; j++) {
snprintf(bindID,1024,"%d (0x%.2X, %s)##BV1_%d",j,j,noteNames[j+60],j);
if (ImGui::Selectable(bindID,bind.data1==j)) {
bind.data1=j;
}
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
if (bind.data2==128) {
snprintf(bindID,1024,"Any");
} else {
snprintf(bindID,1024,"%d (0x%.2X)",bind.data2,bind.data2);
}
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("##BValue2",bindID)) {
if (ImGui::Selectable("Any",bind.data2==128)) {
bind.data2=128;
}
for (int j=0; j<128; j++) {
snprintf(bindID,1024,"%d (0x%.2X)##BV2_%d",j,j,j);
if (ImGui::Selectable(bindID,bind.data2==j)) {
bind.data2=j;
}
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("##BAction",(bind.action==0)?"--none--":guiActions[bind.action][1])) {
if (ImGui::Selectable("--none--",bind.action==0)) {
bind.action=0;
}
for (int j=0; j<GUI_ACTION_MAX; j++) {
if (strcmp(guiActions[j][1],"")==0) continue;
if (strstr(guiActions[j][1],"---")==guiActions[j][1]) {
ImGui::TextUnformatted(guiActions[j][1]);
} else {
snprintf(bindID,1024,"%s##BA_%d",guiActions[j][1],j);
if (ImGui::Selectable(bindID,bind.action==j)) {
bind.action=j;
}
}
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
ImGui::Button(ICON_FA_SQUARE_O "##BLearn");
// TODO!
ImGui::TableNextColumn();
if (ImGui::Button(ICON_FA_TIMES "##BRemove")) {
midiMap.binds.erase(midiMap.binds.begin()+i);
i--;
}
ImGui::PopID();
}
ImGui::EndTable();
}
ImGui::TreePop();
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Emulation")) {
@ -1273,6 +1480,9 @@ void FurnaceGUI::syncSettings() {
decodeKeyMap(noteKeys,e->getConfString("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()) {