GUI: chord input
looks like a horrid hack, but it works I'll improve some of the code at some point
This commit is contained in:
parent
e8aeb45a12
commit
08a27be76f
5 changed files with 165 additions and 18 deletions
|
|
@ -3598,6 +3598,73 @@ void DivEngine::noteOff(int chan) {
|
|||
BUSY_END;
|
||||
}
|
||||
|
||||
int DivEngine::getViableChannel(int chan, int off, int ins) {
|
||||
// if the offset is zero, we don't have to do anything
|
||||
if (off==0) return chan;
|
||||
|
||||
// if there isn't an instrument, just offset chan by off
|
||||
if (ins==-1) {
|
||||
return (chan+off)%chans;
|
||||
}
|
||||
|
||||
bool isViable[DIV_MAX_CHANS];
|
||||
bool isAtLeastOneViable=false;
|
||||
int finalChan=chan;
|
||||
int finalChanType=getChannelType(finalChan);
|
||||
|
||||
// this is a copy of the routine in autoNoteOn...... I am lazy
|
||||
DivInstrument* insInst=getIns(ins);
|
||||
for (int i=0; i<chans; i++) {
|
||||
if (ins==-1 || ins>=song.insLen || getPreferInsType(i)==insInst->type || (getPreferInsType(i)==DIV_INS_NULL && finalChanType==DIV_CH_NOISE) || getPreferInsSecondType(i)==insInst->type) {
|
||||
if (insInst->type==DIV_INS_OPL) {
|
||||
if (insInst->fm.ops==2 || getChannelType(i)==DIV_CH_OP) {
|
||||
isViable[i]=true;
|
||||
isAtLeastOneViable=true;
|
||||
} else {
|
||||
isViable[i]=false;
|
||||
}
|
||||
} else {
|
||||
isViable[i]=true;
|
||||
isAtLeastOneViable=true;
|
||||
}
|
||||
} else {
|
||||
isViable[i]=false;
|
||||
}
|
||||
}
|
||||
|
||||
// screw it if none of the channels are viable
|
||||
if (!isAtLeastOneViable) {
|
||||
return (chan+off)%chans;
|
||||
}
|
||||
|
||||
// now offset (confined to viable channels)
|
||||
int channelsCycled=0;
|
||||
int i=(chan+1)%chans;
|
||||
int attempts=0;
|
||||
while (true) {
|
||||
if (isViable[i]) {
|
||||
channelsCycled++;
|
||||
if (channelsCycled==off) {
|
||||
// we found it
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (++i>=chans) {
|
||||
i=0;
|
||||
}
|
||||
|
||||
// fail-safe
|
||||
if (++attempts>1024) {
|
||||
logE("getViableChannel(): too many attempts!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// fail-safe
|
||||
return (chan+off)%chans;
|
||||
}
|
||||
|
||||
bool DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
|
||||
bool isViable[DIV_MAX_CHANS];
|
||||
bool canPlayAnyway=false;
|
||||
|
|
|
|||
|
|
@ -1202,6 +1202,10 @@ class DivEngine {
|
|||
// set whether autoNoteIn is mono or poly
|
||||
void setAutoNotePoly(bool poly);
|
||||
|
||||
// get next viable channel with an offset
|
||||
// chan is the base channel, off is the offset and ins is the instrument.
|
||||
int getViableChannel(int chan, int off, int ins);
|
||||
|
||||
// go to order
|
||||
void setOrder(unsigned char order);
|
||||
|
||||
|
|
|
|||
|
|
@ -759,8 +759,18 @@ void FurnaceGUI::drawEditControls() {
|
|||
|
||||
ImGui::SameLine();
|
||||
pushToggleColors(noteInputPoly);
|
||||
if (ImGui::Button(noteInputPoly?(_("Poly##PolyInput")):(_("Mono##PolyInput")))) {
|
||||
noteInputPoly=!noteInputPoly;
|
||||
if (ImGui::Button(noteInputPoly?(noteInputChord?(_("Chord##PolyInput")):(_("Poly##PolyInput"))):(_("Mono##PolyInput")))) {
|
||||
if (noteInputPoly) {
|
||||
if (noteInputChord) {
|
||||
noteInputPoly=false;
|
||||
noteInputChord=false;
|
||||
} else {
|
||||
noteInputChord=true;
|
||||
}
|
||||
} else {
|
||||
noteInputPoly=true;
|
||||
noteInputChord=false;
|
||||
}
|
||||
e->setAutoNotePoly(noteInputPoly);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
|
|
@ -889,8 +899,18 @@ void FurnaceGUI::drawEditControls() {
|
|||
|
||||
ImGui::SameLine();
|
||||
pushToggleColors(noteInputPoly);
|
||||
if (ImGui::Button(noteInputPoly?_("Poly##PolyInput"):_("Mono##PolyInput"))) {
|
||||
noteInputPoly=!noteInputPoly;
|
||||
if (ImGui::Button(noteInputPoly?(noteInputChord?(_("Chord##PolyInput")):(_("Poly##PolyInput"))):(_("Mono##PolyInput")))) {
|
||||
if (noteInputPoly) {
|
||||
if (noteInputChord) {
|
||||
noteInputPoly=false;
|
||||
noteInputChord=false;
|
||||
} else {
|
||||
noteInputChord=true;
|
||||
}
|
||||
} else {
|
||||
noteInputPoly=true;
|
||||
noteInputChord=false;
|
||||
}
|
||||
e->setAutoNotePoly(noteInputPoly);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
|
|
@ -1027,8 +1047,18 @@ void FurnaceGUI::drawEditControls() {
|
|||
popToggleColors();
|
||||
|
||||
pushToggleColors(noteInputPoly);
|
||||
if (ImGui::Button(noteInputPoly?_("Poly##PolyInput"):_("Mono##PolyInput"))) {
|
||||
noteInputPoly=!noteInputPoly;
|
||||
if (ImGui::Button(noteInputPoly?(noteInputChord?(_("Chord##PolyInput")):(_("Poly##PolyInput"))):(_("Mono##PolyInput")))) {
|
||||
if (noteInputPoly) {
|
||||
if (noteInputChord) {
|
||||
noteInputPoly=false;
|
||||
noteInputChord=false;
|
||||
} else {
|
||||
noteInputChord=true;
|
||||
}
|
||||
} else {
|
||||
noteInputPoly=true;
|
||||
noteInputChord=false;
|
||||
}
|
||||
e->setAutoNotePoly(noteInputPoly);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
|
|
@ -1127,8 +1157,18 @@ void FurnaceGUI::drawEditControls() {
|
|||
|
||||
ImGui::SameLine();
|
||||
pushToggleColors(noteInputPoly);
|
||||
if (ImGui::Button(noteInputPoly?_("Poly##PolyInput"):_("Mono##PolyInput"))) {
|
||||
noteInputPoly=!noteInputPoly;
|
||||
if (ImGui::Button(noteInputPoly?(noteInputChord?(_("Chord##PolyInput")):(_("Poly##PolyInput"))):(_("Mono##PolyInput")))) {
|
||||
if (noteInputPoly) {
|
||||
if (noteInputChord) {
|
||||
noteInputPoly=false;
|
||||
noteInputChord=false;
|
||||
} else {
|
||||
noteInputChord=true;
|
||||
}
|
||||
} else {
|
||||
noteInputPoly=true;
|
||||
noteInputChord=false;
|
||||
}
|
||||
e->setAutoNotePoly(noteInputPoly);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
|
|
|
|||
|
|
@ -1296,6 +1296,7 @@ void FurnaceGUI::play(int row) {
|
|||
}
|
||||
curNibble=false;
|
||||
orderNibble=false;
|
||||
chordInputOffset=0;
|
||||
activeNotes.clear();
|
||||
}
|
||||
|
||||
|
|
@ -1311,6 +1312,7 @@ void FurnaceGUI::stop() {
|
|||
e->stop();
|
||||
curNibble=false;
|
||||
orderNibble=false;
|
||||
chordInputOffset=0;
|
||||
if (followPattern && wasPlaying) {
|
||||
nextScroll=-1.0f;
|
||||
nextAddScroll=0.0f;
|
||||
|
|
@ -1353,13 +1355,24 @@ void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode, bool autoNote) {
|
|||
}
|
||||
}
|
||||
|
||||
void FurnaceGUI::noteInput(int num, int key, int vol) {
|
||||
void FurnaceGUI::noteInput(int num, int key, int vol, int chanOff) {
|
||||
int ch=cursor.xCoarse;
|
||||
int ord=curOrder;
|
||||
int y=cursor.y;
|
||||
int tick=0;
|
||||
int speed=0;
|
||||
|
||||
if (chanOff>0 && noteInputChord) {
|
||||
ch=e->getViableChannel(ch,chanOff,curIns);
|
||||
if ((!e->isPlaying() || !followPattern)) {
|
||||
y-=editStep;
|
||||
while (y<0) {
|
||||
if (--ord<0) ord=0;
|
||||
y+=e->curSubSong->patLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (e->isPlaying() && !e->isStepping() && followPattern) {
|
||||
e->getPlayPosTick(ord,y,tick,speed);
|
||||
if (tick<=(speed/2)) { // round
|
||||
|
|
@ -1373,12 +1386,12 @@ void FurnaceGUI::noteInput(int num, int key, int vol) {
|
|||
}
|
||||
}
|
||||
|
||||
logV("noteInput: chan %d, %d:%d %d/%d",ch,ord,y,tick,speed);
|
||||
logV("noteInput: chan %d, offset %d, %d:%d %d/%d",ch,chanOff,ord,y,tick,speed);
|
||||
|
||||
DivPattern* pat=e->curPat[ch].getPattern(e->curOrders->ord[ch][ord],true);
|
||||
bool removeIns=false;
|
||||
|
||||
prepareUndo(GUI_UNDO_PATTERN_EDIT);
|
||||
prepareUndo(GUI_UNDO_PATTERN_EDIT,UndoRegion(ord,ch,y,ord,ch,y));
|
||||
|
||||
if (key==GUI_NOTE_OFF) { // note off
|
||||
pat->newData[y][DIV_PAT_NOTE]=DIV_NOTE_OFF;
|
||||
|
|
@ -1416,8 +1429,10 @@ void FurnaceGUI::noteInput(int num, int key, int vol) {
|
|||
pat->newData[y][DIV_PAT_VOL]=-1;
|
||||
}
|
||||
}
|
||||
editAdvance();
|
||||
makeUndo(GUI_UNDO_PATTERN_EDIT);
|
||||
if ((!e->isPlaying() || !followPattern) && (chanOff<1 || !noteInputChord)) {
|
||||
editAdvance();
|
||||
}
|
||||
makeUndo(GUI_UNDO_PATTERN_EDIT,UndoRegion(ord,ch,y,ord,ch,y));
|
||||
curNibble=false;
|
||||
}
|
||||
|
||||
|
|
@ -1736,8 +1751,9 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
|
|||
if (num>119) num=119; // B-9
|
||||
|
||||
if (edit) {
|
||||
noteInput(num,key);
|
||||
noteInput(num,key,-1,chordInputOffset);
|
||||
}
|
||||
chordInputOffset++;
|
||||
}
|
||||
} else if (edit) { // value
|
||||
auto it=valueKeys.find(ev.key.keysym.sym);
|
||||
|
|
@ -1833,7 +1849,10 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
|
|||
}
|
||||
|
||||
void FurnaceGUI::keyUp(SDL_Event& ev) {
|
||||
// nothing for now
|
||||
// this is very, very lazy...
|
||||
if (--chordInputOffset<0) {
|
||||
chordInputOffset=0;
|
||||
}
|
||||
}
|
||||
|
||||
bool dirExists(String s) {
|
||||
|
|
@ -3995,6 +4014,9 @@ bool FurnaceGUI::loop() {
|
|||
break;
|
||||
case SDL_KEYUP:
|
||||
// for now
|
||||
if (!ImGui::GetIO().WantCaptureKeyboard || (newFilePicker->isOpened() && !ImGui::GetIO().WantTextInput)) {
|
||||
keyUp(ev);
|
||||
}
|
||||
insEditMayBeDirty=true;
|
||||
if (introPos<11.0 && introSkip<0.5 && !shortIntro) {
|
||||
introSkipDo=false;
|
||||
|
|
@ -4178,14 +4200,19 @@ bool FurnaceGUI::loop() {
|
|||
if (action!=0) {
|
||||
doAction(action);
|
||||
} else switch (msg.type&0xf0) {
|
||||
case TA_MIDI_NOTE_OFF:
|
||||
if (--chordInputOffset<0) chordInputOffset=0;
|
||||
break;
|
||||
case TA_MIDI_NOTE_ON:
|
||||
if (midiMap.valueInputStyle==0 || midiMap.valueInputStyle>3 || cursor.xFine==0) {
|
||||
if (midiMap.noteInput && edit && msg.data[1]!=0) {
|
||||
noteInput(
|
||||
msg.data[0]-12,
|
||||
0,
|
||||
midiMap.volInput?msg.data[1]:-1
|
||||
midiMap.volInput?msg.data[1]:-1,
|
||||
chordInputOffset
|
||||
);
|
||||
chordInputOffset++;
|
||||
}
|
||||
} else {
|
||||
if (edit && msg.data[1]!=0) {
|
||||
|
|
@ -7423,6 +7450,9 @@ bool FurnaceGUI::loop() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// reset chord count just in case
|
||||
chordInputOffset=0;
|
||||
}
|
||||
|
||||
if (!settings.renderClearPos || renderBackend==GUI_BACKEND_METAL) {
|
||||
|
|
@ -8254,6 +8284,7 @@ void FurnaceGUI::syncState() {
|
|||
followOrders=e->getConfBool("followOrders",true);
|
||||
followPattern=e->getConfBool("followPattern",true);
|
||||
noteInputPoly=e->getConfBool("noteInputPoly",true);
|
||||
noteInputChord=e->getConfBool("noteInputChord",false);
|
||||
filePlayerSync=e->getConfBool("filePlayerSync",true);
|
||||
audioExportOptions.loops=e->getConfInt("exportLoops",0);
|
||||
if (audioExportOptions.loops<0) audioExportOptions.loops=0;
|
||||
|
|
@ -8414,6 +8445,7 @@ void FurnaceGUI::commitState(DivConfig& conf) {
|
|||
conf.set("followPattern",followPattern);
|
||||
conf.set("orderEditMode",orderEditMode);
|
||||
conf.set("noteInputPoly",noteInputPoly);
|
||||
conf.set("noteInputChord",noteInputChord);
|
||||
conf.set("filePlayerSync",filePlayerSync);
|
||||
if (settings.persistFadeOut) {
|
||||
conf.set("exportLoops",audioExportOptions.loops);
|
||||
|
|
@ -8588,6 +8620,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
sysDupCloneChannels(true),
|
||||
sysDupEnd(false),
|
||||
noteInputPoly(true),
|
||||
noteInputChord(false),
|
||||
notifyWaveChange(false),
|
||||
notifySampleChange(false),
|
||||
recalcTimestamps(true),
|
||||
|
|
@ -8623,6 +8656,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
drawHalt(10),
|
||||
macroPointSize(16),
|
||||
waveEditStyle(0),
|
||||
chordInputOffset(0),
|
||||
displayInsTypeListMakeInsSample(-1),
|
||||
makeDrumkitOctave(3),
|
||||
mobileEditPage(0),
|
||||
|
|
|
|||
|
|
@ -1713,7 +1713,8 @@ class FurnaceGUI {
|
|||
bool vgmExportDirectStream, displayInsTypeList, displayWaveSizeList;
|
||||
bool portrait, injectBackUp, mobileMenuOpen, warnColorPushed;
|
||||
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
|
||||
bool displayNew, displayExport, displayPalette, fullScreen, preserveChanPos, sysDupCloneChannels, sysDupEnd, noteInputPoly, notifyWaveChange, notifySampleChange;
|
||||
bool displayNew, displayExport, displayPalette, fullScreen, preserveChanPos, sysDupCloneChannels, sysDupEnd, noteInputPoly, noteInputChord;
|
||||
bool notifyWaveChange, notifySampleChange;
|
||||
bool recalcTimestamps;
|
||||
bool wantScrollListIns, wantScrollListWave, wantScrollListSample;
|
||||
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString;
|
||||
|
|
@ -1737,6 +1738,7 @@ class FurnaceGUI {
|
|||
int drawHalt;
|
||||
int macroPointSize;
|
||||
int waveEditStyle;
|
||||
int chordInputOffset;
|
||||
int displayInsTypeListMakeInsSample;
|
||||
int makeDrumkitOctave;
|
||||
int mobileEditPage;
|
||||
|
|
@ -3080,7 +3082,7 @@ class FurnaceGUI {
|
|||
void doDrag(bool copy=false);
|
||||
void editOptions(bool topMenu);
|
||||
DivSystem systemPicker(bool fullWidth);
|
||||
void noteInput(int num, int key, int vol=-1);
|
||||
void noteInput(int num, int key, int vol=-1, int chanOff=0);
|
||||
void valueInput(int num, bool direct=false, int target=-1);
|
||||
void orderInput(int num);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue