implement more MIDI stuff
This commit is contained in:
parent
a08f7507fd
commit
052dcb2576
|
@ -251,7 +251,7 @@ class DivEngine {
|
||||||
size_t totalProcessed;
|
size_t totalProcessed;
|
||||||
|
|
||||||
// MIDI stuff
|
// MIDI stuff
|
||||||
std::function<int(const TAMidiMessage&)> midiCallback=[](const TAMidiMessage&) -> int {return -1;};
|
std::function<int(const TAMidiMessage&)> midiCallback=[](const TAMidiMessage&) -> int {return -2;};
|
||||||
|
|
||||||
DivSystem systemFromFile(unsigned char val);
|
DivSystem systemFromFile(unsigned char val);
|
||||||
unsigned char systemToFile(DivSystem val);
|
unsigned char systemToFile(DivSystem val);
|
||||||
|
|
194
src/gui/gui.cpp
194
src/gui/gui.cpp
|
@ -805,6 +805,64 @@ void FurnaceGUI::noteInput(int num, int key, int vol) {
|
||||||
curNibble=false;
|
curNibble=false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FurnaceGUI::valueInput(int num, bool direct, int target) {
|
||||||
|
DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][e->getOrder()],true);
|
||||||
|
prepareUndo(GUI_UNDO_PATTERN_EDIT);
|
||||||
|
if (target==-1) target=cursor.xFine+1;
|
||||||
|
if (direct) {
|
||||||
|
pat->data[cursor.y][target]=num&0xff;
|
||||||
|
} else {
|
||||||
|
if (pat->data[cursor.y][target]==-1) pat->data[cursor.y][target]=0;
|
||||||
|
pat->data[cursor.y][target]=((pat->data[cursor.y][target]<<4)|num)&0xff;
|
||||||
|
}
|
||||||
|
if (cursor.xFine==1) { // instrument
|
||||||
|
if (pat->data[cursor.y][target]>=(int)e->song.ins.size()) {
|
||||||
|
pat->data[cursor.y][target]&=0x0f;
|
||||||
|
if (pat->data[cursor.y][target]>=(int)e->song.ins.size()) {
|
||||||
|
pat->data[cursor.y][target]=(int)e->song.ins.size()-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
makeUndo(GUI_UNDO_PATTERN_EDIT);
|
||||||
|
if (direct) {
|
||||||
|
curNibble=false;
|
||||||
|
} else {
|
||||||
|
if (e->song.ins.size()<16) {
|
||||||
|
curNibble=false;
|
||||||
|
editAdvance();
|
||||||
|
} else {
|
||||||
|
curNibble=!curNibble;
|
||||||
|
if (!curNibble) editAdvance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (cursor.xFine==2) {
|
||||||
|
if (curNibble) {
|
||||||
|
if (pat->data[cursor.y][target]>e->getMaxVolumeChan(cursor.xCoarse)) pat->data[cursor.y][target]=e->getMaxVolumeChan(cursor.xCoarse);
|
||||||
|
} else {
|
||||||
|
pat->data[cursor.y][target]&=15;
|
||||||
|
}
|
||||||
|
makeUndo(GUI_UNDO_PATTERN_EDIT);
|
||||||
|
if (direct) {
|
||||||
|
curNibble=false;
|
||||||
|
} else {
|
||||||
|
if (e->getMaxVolumeChan(cursor.xCoarse)<16) {
|
||||||
|
curNibble=false;
|
||||||
|
editAdvance();
|
||||||
|
} else {
|
||||||
|
curNibble=!curNibble;
|
||||||
|
if (!curNibble) editAdvance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
makeUndo(GUI_UNDO_PATTERN_EDIT);
|
||||||
|
if (direct) {
|
||||||
|
curNibble=false;
|
||||||
|
} else {
|
||||||
|
curNibble=!curNibble;
|
||||||
|
if (!curNibble) editAdvance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FurnaceGUI::keyDown(SDL_Event& ev) {
|
void FurnaceGUI::keyDown(SDL_Event& ev) {
|
||||||
if (ImGuiFileDialog::Instance()->IsOpened()) return;
|
if (ImGuiFileDialog::Instance()->IsOpened()) return;
|
||||||
if (aboutOpen) return;
|
if (aboutOpen) return;
|
||||||
|
@ -879,44 +937,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
|
||||||
} else if (edit) { // value
|
} else if (edit) { // value
|
||||||
try {
|
try {
|
||||||
int num=valueKeys.at(ev.key.keysym.sym);
|
int num=valueKeys.at(ev.key.keysym.sym);
|
||||||
DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][e->getOrder()],true);
|
valueInput(num);
|
||||||
prepareUndo(GUI_UNDO_PATTERN_EDIT);
|
|
||||||
if (pat->data[cursor.y][cursor.xFine+1]==-1) pat->data[cursor.y][cursor.xFine+1]=0;
|
|
||||||
pat->data[cursor.y][cursor.xFine+1]=((pat->data[cursor.y][cursor.xFine+1]<<4)|num)&0xff;
|
|
||||||
if (cursor.xFine==1) { // instrument
|
|
||||||
if (pat->data[cursor.y][cursor.xFine+1]>=(int)e->song.ins.size()) {
|
|
||||||
pat->data[cursor.y][cursor.xFine+1]&=0x0f;
|
|
||||||
if (pat->data[cursor.y][cursor.xFine+1]>=(int)e->song.ins.size()) {
|
|
||||||
pat->data[cursor.y][cursor.xFine+1]=(int)e->song.ins.size()-1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
makeUndo(GUI_UNDO_PATTERN_EDIT);
|
|
||||||
if (e->song.ins.size()<16) {
|
|
||||||
curNibble=false;
|
|
||||||
editAdvance();
|
|
||||||
} else {
|
|
||||||
curNibble=!curNibble;
|
|
||||||
if (!curNibble) editAdvance();
|
|
||||||
}
|
|
||||||
} else if (cursor.xFine==2) {
|
|
||||||
if (curNibble) {
|
|
||||||
if (pat->data[cursor.y][cursor.xFine+1]>e->getMaxVolumeChan(cursor.xCoarse)) pat->data[cursor.y][cursor.xFine+1]=e->getMaxVolumeChan(cursor.xCoarse);
|
|
||||||
} else {
|
|
||||||
pat->data[cursor.y][cursor.xFine+1]&=15;
|
|
||||||
}
|
|
||||||
makeUndo(GUI_UNDO_PATTERN_EDIT);
|
|
||||||
if (e->getMaxVolumeChan(cursor.xCoarse)<16) {
|
|
||||||
curNibble=false;
|
|
||||||
editAdvance();
|
|
||||||
} else {
|
|
||||||
curNibble=!curNibble;
|
|
||||||
if (!curNibble) editAdvance();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
makeUndo(GUI_UNDO_PATTERN_EDIT);
|
|
||||||
curNibble=!curNibble;
|
|
||||||
if (!curNibble) editAdvance();
|
|
||||||
}
|
|
||||||
} catch (std::out_of_range& e) {
|
} catch (std::out_of_range& e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1931,7 +1952,7 @@ bool FurnaceGUI::loop() {
|
||||||
midiMap.binds[learning].type=msg.type>>4;
|
midiMap.binds[learning].type=msg.type>>4;
|
||||||
midiMap.binds[learning].channel=msg.type&15;
|
midiMap.binds[learning].channel=msg.type&15;
|
||||||
midiMap.binds[learning].data1=msg.data[0];
|
midiMap.binds[learning].data1=msg.data[0];
|
||||||
switch (msg.type>>4) {
|
switch (msg.type&0xf0) {
|
||||||
case TA_MIDI_NOTE_OFF:
|
case TA_MIDI_NOTE_OFF:
|
||||||
case TA_MIDI_NOTE_ON:
|
case TA_MIDI_NOTE_ON:
|
||||||
case TA_MIDI_AFTERTOUCH:
|
case TA_MIDI_AFTERTOUCH:
|
||||||
|
@ -1951,12 +1972,35 @@ bool FurnaceGUI::loop() {
|
||||||
doAction(action);
|
doAction(action);
|
||||||
} else switch (msg.type&0xf0) {
|
} else switch (msg.type&0xf0) {
|
||||||
case TA_MIDI_NOTE_ON:
|
case TA_MIDI_NOTE_ON:
|
||||||
if (edit && msg.data[1]!=0) {
|
if (midiMap.valueInputStyle==0 || midiMap.valueInputStyle>3 || cursor.xFine==0) {
|
||||||
noteInput(
|
if (midiMap.noteInput && edit && msg.data[1]!=0) {
|
||||||
msg.data[0]-12,
|
noteInput(
|
||||||
0,
|
msg.data[0]-12,
|
||||||
midiMap.volInput?((int)(pow((double)msg.data[2]/127.0,midiMap.volExp)*127.0)):-1
|
0,
|
||||||
);
|
midiMap.volInput?((int)(pow((double)msg.data[1]/127.0,midiMap.volExp)*127.0)):-1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (edit && msg.data[1]!=0) {
|
||||||
|
switch (midiMap.valueInputStyle) {
|
||||||
|
case 1: {
|
||||||
|
int val=msg.data[0]%24;
|
||||||
|
if (val<16) {
|
||||||
|
valueInput(val);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
valueInput(msg.data[0]&15);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
int val=altValues[msg.data[0]%24];
|
||||||
|
if (val>=0) {
|
||||||
|
valueInput(val);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TA_MIDI_PROGRAM:
|
case TA_MIDI_PROGRAM:
|
||||||
|
@ -1965,6 +2009,64 @@ bool FurnaceGUI::loop() {
|
||||||
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;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TA_MIDI_CONTROL:
|
||||||
|
bool gchanged=false;
|
||||||
|
if (msg.data[0]==midiMap.valueInputControlMSB) {
|
||||||
|
midiMap.valueInputCurMSB=msg.data[1];
|
||||||
|
gchanged=true;
|
||||||
|
}
|
||||||
|
if (msg.data[0]==midiMap.valueInputControlLSB) {
|
||||||
|
midiMap.valueInputCurLSB=msg.data[1];
|
||||||
|
gchanged=true;
|
||||||
|
}
|
||||||
|
if (msg.data[0]==midiMap.valueInputControlSingle) {
|
||||||
|
midiMap.valueInputCurSingle=msg.data[1];
|
||||||
|
gchanged=true;
|
||||||
|
}
|
||||||
|
if (gchanged && cursor.xFine>0) {
|
||||||
|
switch (midiMap.valueInputStyle) {
|
||||||
|
case 4: // dual CC
|
||||||
|
valueInput(((midiMap.valueInputCurMSB>>3)<<4)|(midiMap.valueInputCurLSB>>3),true);
|
||||||
|
break;
|
||||||
|
case 5: // 14-bit
|
||||||
|
valueInput((midiMap.valueInputCurMSB<<1)|(midiMap.valueInputCurLSB>>6),true);
|
||||||
|
break;
|
||||||
|
case 6: // single CC
|
||||||
|
valueInput((midiMap.valueInputCurSingle*255)/127,true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<18; i++) {
|
||||||
|
bool changed=false;
|
||||||
|
if (midiMap.valueInputSpecificStyle[i]!=0) {
|
||||||
|
if (msg.data[0]==midiMap.valueInputSpecificMSB[i]) {
|
||||||
|
changed=true;
|
||||||
|
midiMap.valueInputCurMSBS[i]=msg.data[1];
|
||||||
|
}
|
||||||
|
if (msg.data[0]==midiMap.valueInputSpecificLSB[i]) {
|
||||||
|
changed=true;
|
||||||
|
midiMap.valueInputCurLSBS[i]=msg.data[1];
|
||||||
|
}
|
||||||
|
if (msg.data[0]==midiMap.valueInputSpecificSingle[i]) {
|
||||||
|
changed=true;
|
||||||
|
midiMap.valueInputCurSingleS[i]=msg.data[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) switch (midiMap.valueInputStyle) {
|
||||||
|
case 1: // dual CC
|
||||||
|
valueInput(((midiMap.valueInputCurMSBS[i]>>3)<<4)|(midiMap.valueInputCurLSBS[i]>>3),true,i+2);
|
||||||
|
break;
|
||||||
|
case 2: // 14-bit
|
||||||
|
valueInput((midiMap.valueInputCurMSBS[i]<<1)|(midiMap.valueInputCurLSBS[i]>>6),true,i+2);
|
||||||
|
break;
|
||||||
|
case 3: // single CC
|
||||||
|
valueInput((midiMap.valueInputCurSingleS[i]*255)/127,true,i+2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2734,6 +2836,8 @@ bool FurnaceGUI::init() {
|
||||||
midiQueue.push(msg);
|
midiQueue.push(msg);
|
||||||
midiLock.unlock();
|
midiLock.unlock();
|
||||||
e->setMidiBaseChan(cursor.xCoarse);
|
e->setMidiBaseChan(cursor.xCoarse);
|
||||||
|
if (midiMap.valueInputStyle!=0 && cursor.xFine!=0 && edit) return -2;
|
||||||
|
if (!midiMap.noteInput) return -2;
|
||||||
if (learning!=-1) return -2;
|
if (learning!=-1) return -2;
|
||||||
if (midiMap.at(msg)) return -2;
|
if (midiMap.at(msg)) return -2;
|
||||||
return curIns;
|
return curIns;
|
||||||
|
|
|
@ -539,11 +539,27 @@ struct MIDIMap {
|
||||||
//
|
//
|
||||||
// 4: use dual CC for value input (nibble)
|
// 4: use dual CC for value input (nibble)
|
||||||
// 5: use 14-bit CC for value input (MSB/LSB)
|
// 5: use 14-bit CC for value input (MSB/LSB)
|
||||||
|
// 6: use single CC for value input (may be imprecise)
|
||||||
int valueInputStyle;
|
int valueInputStyle;
|
||||||
int valueInputControlMSB; // on 4
|
int valueInputControlMSB; // on 4
|
||||||
int valueInputControlLSB; // on 4
|
int valueInputControlLSB; // on 4
|
||||||
|
int valueInputControlSingle;
|
||||||
|
|
||||||
|
// 0: disabled
|
||||||
|
// 1: use dual CC (nibble)
|
||||||
|
// 2: use 14-bit CC (MSB/LSB)
|
||||||
|
// 3: use single CC (may be imprecise)
|
||||||
|
int valueInputSpecificStyle[18];
|
||||||
|
int valueInputSpecificMSB[18];
|
||||||
|
int valueInputSpecificLSB[18];
|
||||||
|
int valueInputSpecificSingle[18];
|
||||||
float volExp;
|
float volExp;
|
||||||
|
|
||||||
|
int valueInputCurMSB, valueInputCurLSB, valueInputCurSingle;
|
||||||
|
int valueInputCurMSBS[18];
|
||||||
|
int valueInputCurLSBS[18];
|
||||||
|
int valueInputCurSingleS[18];
|
||||||
|
|
||||||
void compile();
|
void compile();
|
||||||
void deinit();
|
void deinit();
|
||||||
int at(const TAMidiMessage& where);
|
int at(const TAMidiMessage& where);
|
||||||
|
@ -560,7 +576,19 @@ struct MIDIMap {
|
||||||
midiClock(false),
|
midiClock(false),
|
||||||
midiTimeCode(false),
|
midiTimeCode(false),
|
||||||
valueInputStyle(1),
|
valueInputStyle(1),
|
||||||
volExp(1.0f) {}
|
volExp(1.0f),
|
||||||
|
valueInputCurMSB(0),
|
||||||
|
valueInputCurLSB(0),
|
||||||
|
valueInputCurSingle(0) {
|
||||||
|
memset(valueInputSpecificStyle,0,18*sizeof(int));
|
||||||
|
memset(valueInputSpecificMSB,0,18*sizeof(int));
|
||||||
|
memset(valueInputSpecificLSB,0,18*sizeof(int));
|
||||||
|
memset(valueInputSpecificSingle,0,18*sizeof(int));
|
||||||
|
|
||||||
|
memset(valueInputCurMSBS,0,18*sizeof(int));
|
||||||
|
memset(valueInputCurLSBS,0,18*sizeof(int));
|
||||||
|
memset(valueInputCurSingleS,0,18*sizeof(int));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Particle {
|
struct Particle {
|
||||||
|
@ -983,6 +1011,7 @@ class FurnaceGUI {
|
||||||
void doRedo();
|
void doRedo();
|
||||||
void editOptions(bool topMenu);
|
void editOptions(bool topMenu);
|
||||||
void noteInput(int num, int key, int vol=-1);
|
void noteInput(int num, int key, int vol=-1);
|
||||||
|
void valueInput(int num, bool direct=false, int target=-1);
|
||||||
|
|
||||||
void doUndoSample();
|
void doUndoSample();
|
||||||
void doRedoSample();
|
void doRedoSample();
|
||||||
|
|
|
@ -66,6 +66,10 @@ const char* pitchLabel[11]={
|
||||||
"1/6", "1/5", "1/4", "1/3", "1/2", "1x", "2x", "3x", "4x", "5x", "6x"
|
"1/6", "1/5", "1/4", "1/3", "1/2", "1x", "2x", "3x", "4x", "5x", "6x"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const int altValues[24]={
|
||||||
|
0, 10, 1, 11, 2, 3, 12, 4, 13, 5, 14, 6, 7, 15, 8, -1, 9, -1, -1, -1, -1, -1, -1, -1
|
||||||
|
};
|
||||||
|
|
||||||
const char* insTypes[DIV_INS_MAX]={
|
const char* insTypes[DIV_INS_MAX]={
|
||||||
"Standard",
|
"Standard",
|
||||||
"FM (4-operator)",
|
"FM (4-operator)",
|
||||||
|
|
|
@ -28,3 +28,4 @@ extern const char* sampleDepths[17];
|
||||||
extern const char* resampleStrats[];
|
extern const char* resampleStrats[];
|
||||||
extern const int availableSystems[];
|
extern const int availableSystems[];
|
||||||
extern const char* guiActions[][2];
|
extern const char* guiActions[][2];
|
||||||
|
extern const int altValues[24];
|
|
@ -62,6 +62,14 @@ int MIDIMap::at(const TAMidiMessage& where) {
|
||||||
x=std::stof(optionValueS); \
|
x=std::stof(optionValueS); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define UNDERSTAND_ARRAY_OPTION(x,yMax) if (optionNameS==#x) { \
|
||||||
|
if (optionIndex<0 || optionIndex>=yMax) { \
|
||||||
|
logW("MIDI map array option %d out of range (0-%d) at line %d: %s\n",optionIndex,yMax,curLine,line); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
x[optionIndex]=std::stoi(optionValueS); \
|
||||||
|
}
|
||||||
|
|
||||||
bool MIDIMap::read(String path) {
|
bool MIDIMap::read(String path) {
|
||||||
char line[4096];
|
char line[4096];
|
||||||
int curLine=1;
|
int curLine=1;
|
||||||
|
@ -77,6 +85,37 @@ bool MIDIMap::read(String path) {
|
||||||
while (fgets(line,4096,f)) {
|
while (fgets(line,4096,f)) {
|
||||||
char* nlPos=strrchr(line,'\n');
|
char* nlPos=strrchr(line,'\n');
|
||||||
if (nlPos!=NULL) *nlPos=0;
|
if (nlPos!=NULL) *nlPos=0;
|
||||||
|
if (strstr(line,"aOption")==line) {
|
||||||
|
char optionName[256];
|
||||||
|
int optionIndex=-1;
|
||||||
|
char optionValue[256];
|
||||||
|
String optionNameS, optionValueS;
|
||||||
|
|
||||||
|
int result=sscanf(line,"aOption %255s %d %255s",optionName,&optionIndex,optionValue);
|
||||||
|
if (result!=3) {
|
||||||
|
logW("MIDI map garbage data at line %d: %s\n",curLine,line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
optionNameS=optionName;
|
||||||
|
optionValueS=optionValue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
UNDERSTAND_ARRAY_OPTION(valueInputSpecificStyle,18) else
|
||||||
|
UNDERSTAND_ARRAY_OPTION(valueInputSpecificMSB,18) else
|
||||||
|
UNDERSTAND_ARRAY_OPTION(valueInputSpecificLSB,18) else
|
||||||
|
UNDERSTAND_ARRAY_OPTION(valueInputSpecificSingle,18) else {
|
||||||
|
logW("MIDI map unknown array option %s at line %d: %s\n",optionName,curLine,line);
|
||||||
|
}
|
||||||
|
} catch (std::out_of_range& e) {
|
||||||
|
logW("MIDI map invalid value %s for array option %s at line %d: %s\n",optionValue,optionName,curLine,line);
|
||||||
|
} catch (std::invalid_argument& e) {
|
||||||
|
logW("MIDI map invalid value %s for array option %s at line %d: %s\n",optionValue,optionName,curLine,line);
|
||||||
|
}
|
||||||
|
|
||||||
|
curLine++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (strstr(line,"option")==line) {
|
if (strstr(line,"option")==line) {
|
||||||
char optionName[256];
|
char optionName[256];
|
||||||
char optionValue[256];
|
char optionValue[256];
|
||||||
|
@ -102,6 +141,7 @@ bool MIDIMap::read(String path) {
|
||||||
UNDERSTAND_OPTION(valueInputStyle) else
|
UNDERSTAND_OPTION(valueInputStyle) else
|
||||||
UNDERSTAND_OPTION(valueInputControlMSB) else
|
UNDERSTAND_OPTION(valueInputControlMSB) else
|
||||||
UNDERSTAND_OPTION(valueInputControlLSB) else
|
UNDERSTAND_OPTION(valueInputControlLSB) else
|
||||||
|
UNDERSTAND_OPTION(valueInputControlSingle) else
|
||||||
UNDERSTAND_FLOAT_OPTION(volExp) else {
|
UNDERSTAND_FLOAT_OPTION(volExp) else {
|
||||||
logW("MIDI map unknown option %s at line %d: %s\n",optionName,curLine,line);
|
logW("MIDI map unknown option %s at line %d: %s\n",optionName,curLine,line);
|
||||||
}
|
}
|
||||||
|
@ -146,6 +186,7 @@ bool MIDIMap::read(String path) {
|
||||||
|
|
||||||
#define WRITE_OPTION(x) fprintf(f,"option " #x " %d\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);
|
#define WRITE_FLOAT_OPTION(x) fprintf(f,"option " #x " %f\n",x);
|
||||||
|
#define WRITE_ARRAY_OPTION(x,y) fprintf(f,"aOption " #x " %d %d\n",y,x[y]);
|
||||||
|
|
||||||
bool MIDIMap::write(String path) {
|
bool MIDIMap::write(String path) {
|
||||||
FILE* f=fopen(path.c_str(),"wb");
|
FILE* f=fopen(path.c_str(),"wb");
|
||||||
|
@ -165,8 +206,16 @@ bool MIDIMap::write(String path) {
|
||||||
WRITE_OPTION(valueInputStyle);
|
WRITE_OPTION(valueInputStyle);
|
||||||
WRITE_OPTION(valueInputControlMSB);
|
WRITE_OPTION(valueInputControlMSB);
|
||||||
WRITE_OPTION(valueInputControlLSB);
|
WRITE_OPTION(valueInputControlLSB);
|
||||||
|
WRITE_OPTION(valueInputControlSingle);
|
||||||
WRITE_FLOAT_OPTION(volExp);
|
WRITE_FLOAT_OPTION(volExp);
|
||||||
|
|
||||||
|
for (int i=0; i<18; i++) {
|
||||||
|
WRITE_ARRAY_OPTION(valueInputSpecificStyle,i);
|
||||||
|
WRITE_ARRAY_OPTION(valueInputSpecificMSB,i);
|
||||||
|
WRITE_ARRAY_OPTION(valueInputSpecificLSB,i);
|
||||||
|
WRITE_ARRAY_OPTION(valueInputSpecificSingle,i);
|
||||||
|
}
|
||||||
|
|
||||||
for (MIDIBind& i: binds) {
|
for (MIDIBind& i: binds) {
|
||||||
if (fprintf(f,"%d %d %d %d %s\n",i.type,i.channel,i.data1,i.data2,guiActions[i.action][0])<0) {
|
if (fprintf(f,"%d %d %d %d %s\n",i.type,i.channel,i.data1,i.data2,guiActions[i.action][0])<0) {
|
||||||
logW("did not write MIDI mapping entirely! %s\n",strerror(errno));
|
logW("did not write MIDI mapping entirely! %s\n",strerror(errno));
|
||||||
|
|
|
@ -87,7 +87,15 @@ const char* valueInputStyles[]={
|
||||||
"Raw (note number is value)",
|
"Raw (note number is value)",
|
||||||
"Two octaves alternate (lower keys are 0-9, upper keys are A-F)",
|
"Two octaves alternate (lower keys are 0-9, upper keys are A-F)",
|
||||||
"Use dual control change (one for each nibble)",
|
"Use dual control change (one for each nibble)",
|
||||||
"Use 14-bit control change"
|
"Use 14-bit control change",
|
||||||
|
"Use single control change (imprecise)"
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* valueSInputStyles[]={
|
||||||
|
"Disabled/custom",
|
||||||
|
"Use dual control change (one for each nibble)",
|
||||||
|
"Use 14-bit control change",
|
||||||
|
"Use single control change (imprecise)"
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* messageTypes[]={
|
const char* messageTypes[]={
|
||||||
|
@ -113,6 +121,27 @@ const char* messageChannels[]={
|
||||||
"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "Any"
|
"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "Any"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char* specificControls[18]={
|
||||||
|
"Instrument",
|
||||||
|
"Volume",
|
||||||
|
"Effect 1 type",
|
||||||
|
"Effect 1 value",
|
||||||
|
"Effect 2 type",
|
||||||
|
"Effect 2 value",
|
||||||
|
"Effect 3 type",
|
||||||
|
"Effect 3 value",
|
||||||
|
"Effect 4 type",
|
||||||
|
"Effect 4 value",
|
||||||
|
"Effect 5 type",
|
||||||
|
"Effect 5 value",
|
||||||
|
"Effect 6 type",
|
||||||
|
"Effect 6 value",
|
||||||
|
"Effect 7 type",
|
||||||
|
"Effect 7 value",
|
||||||
|
"Effect 8 type",
|
||||||
|
"Effect 8 value"
|
||||||
|
};
|
||||||
|
|
||||||
#define SAMPLE_RATE_SELECTABLE(x) \
|
#define SAMPLE_RATE_SELECTABLE(x) \
|
||||||
if (ImGui::Selectable(#x,settings.audioRate==x)) { \
|
if (ImGui::Selectable(#x,settings.audioRate==x)) { \
|
||||||
settings.audioRate=x; \
|
settings.audioRate=x; \
|
||||||
|
@ -370,16 +399,50 @@ void FurnaceGUI::drawSettings() {
|
||||||
ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange);
|
ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange);
|
||||||
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);
|
||||||
ImGui::Combo("Value input style",&midiMap.valueInputStyle,valueInputStyles,6);
|
ImGui::Combo("Value input style",&midiMap.valueInputStyle,valueInputStyles,7);
|
||||||
if (midiMap.valueInputStyle>3) {
|
if (midiMap.valueInputStyle>3) {
|
||||||
if (ImGui::InputInt((midiMap.valueInputStyle==4)?"CC of upper nibble##valueCC1":"MSB CC##valueCC1",&midiMap.valueInputControlMSB,1,16)) {
|
if (midiMap.valueInputStyle==6) {
|
||||||
if (midiMap.valueInputControlMSB<0) midiMap.valueInputControlMSB=0;
|
if (ImGui::InputInt("Control##valueCCS",&midiMap.valueInputControlSingle,1,16)) {
|
||||||
if (midiMap.valueInputControlMSB>127) midiMap.valueInputControlMSB=127;
|
if (midiMap.valueInputControlSingle<0) midiMap.valueInputControlSingle=0;
|
||||||
|
if (midiMap.valueInputControlSingle>127) midiMap.valueInputControlSingle=127;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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::InputInt((midiMap.valueInputStyle==4)?"CC of lower nibble##valueCC2":"LSB CC##valueCC2",&midiMap.valueInputControlLSB,1,16)) {
|
}
|
||||||
if (midiMap.valueInputControlLSB<0) midiMap.valueInputControlLSB=0;
|
if (ImGui::TreeNode("Per-column control change")) {
|
||||||
if (midiMap.valueInputControlLSB>127) midiMap.valueInputControlLSB=127;
|
for (int i=0; i<18; i++) {
|
||||||
|
ImGui::PushID(i);
|
||||||
|
ImGui::Combo(specificControls[i],&midiMap.valueInputSpecificStyle[i],valueSInputStyles,4);
|
||||||
|
if (midiMap.valueInputSpecificStyle[i]>0) {
|
||||||
|
ImGui::Indent();
|
||||||
|
if (midiMap.valueInputSpecificStyle[i]==3) {
|
||||||
|
if (ImGui::InputInt("Control##valueCCS",&midiMap.valueInputSpecificSingle[i],1,16)) {
|
||||||
|
if (midiMap.valueInputSpecificSingle[i]<0) midiMap.valueInputSpecificSingle[i]=0;
|
||||||
|
if (midiMap.valueInputSpecificSingle[i]>127) midiMap.valueInputSpecificSingle[i]=127;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ImGui::InputInt((midiMap.valueInputSpecificStyle[i]==4)?"CC of upper nibble##valueCC1":"MSB CC##valueCC1",&midiMap.valueInputSpecificMSB[i],1,16)) {
|
||||||
|
if (midiMap.valueInputSpecificMSB[i]<0) midiMap.valueInputSpecificMSB[i]=0;
|
||||||
|
if (midiMap.valueInputSpecificMSB[i]>127) midiMap.valueInputSpecificMSB[i]=127;
|
||||||
|
}
|
||||||
|
if (ImGui::InputInt((midiMap.valueInputSpecificStyle[i]==4)?"CC of lower nibble##valueCC2":"LSB CC##valueCC2",&midiMap.valueInputSpecificLSB[i],1,16)) {
|
||||||
|
if (midiMap.valueInputSpecificLSB[i]<0) midiMap.valueInputSpecificLSB[i]=0;
|
||||||
|
if (midiMap.valueInputSpecificLSB[i]>127) midiMap.valueInputSpecificLSB[i]=127;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::Unindent();
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
if (ImGui::SliderFloat("Volume curve",&midiMap.volExp,0.01,8.0,"%.2f")) {
|
if (ImGui::SliderFloat("Volume curve",&midiMap.volExp,0.01,8.0,"%.2f")) {
|
||||||
if (midiMap.volExp<0.01) midiMap.volExp=0.01;
|
if (midiMap.volExp<0.01) midiMap.volExp=0.01;
|
||||||
|
|
Loading…
Reference in a new issue