Skip to content

Commit 08a27be

Browse files
committed
GUI: chord input
looks like a horrid hack, but it works I'll improve some of the code at some point
1 parent e8aeb45 commit 08a27be

File tree

5 files changed

+165
-18
lines changed

5 files changed

+165
-18
lines changed

src/engine/engine.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3598,6 +3598,73 @@ void DivEngine::noteOff(int chan) {
35983598
BUSY_END;
35993599
}
36003600

3601+
int DivEngine::getViableChannel(int chan, int off, int ins) {
3602+
// if the offset is zero, we don't have to do anything
3603+
if (off==0) return chan;
3604+
3605+
// if there isn't an instrument, just offset chan by off
3606+
if (ins==-1) {
3607+
return (chan+off)%chans;
3608+
}
3609+
3610+
bool isViable[DIV_MAX_CHANS];
3611+
bool isAtLeastOneViable=false;
3612+
int finalChan=chan;
3613+
int finalChanType=getChannelType(finalChan);
3614+
3615+
// this is a copy of the routine in autoNoteOn...... I am lazy
3616+
DivInstrument* insInst=getIns(ins);
3617+
for (int i=0; i<chans; i++) {
3618+
if (ins==-1 || ins>=song.insLen || getPreferInsType(i)==insInst->type || (getPreferInsType(i)==DIV_INS_NULL && finalChanType==DIV_CH_NOISE) || getPreferInsSecondType(i)==insInst->type) {
3619+
if (insInst->type==DIV_INS_OPL) {
3620+
if (insInst->fm.ops==2 || getChannelType(i)==DIV_CH_OP) {
3621+
isViable[i]=true;
3622+
isAtLeastOneViable=true;
3623+
} else {
3624+
isViable[i]=false;
3625+
}
3626+
} else {
3627+
isViable[i]=true;
3628+
isAtLeastOneViable=true;
3629+
}
3630+
} else {
3631+
isViable[i]=false;
3632+
}
3633+
}
3634+
3635+
// screw it if none of the channels are viable
3636+
if (!isAtLeastOneViable) {
3637+
return (chan+off)%chans;
3638+
}
3639+
3640+
// now offset (confined to viable channels)
3641+
int channelsCycled=0;
3642+
int i=(chan+1)%chans;
3643+
int attempts=0;
3644+
while (true) {
3645+
if (isViable[i]) {
3646+
channelsCycled++;
3647+
if (channelsCycled==off) {
3648+
// we found it
3649+
return i;
3650+
}
3651+
}
3652+
3653+
if (++i>=chans) {
3654+
i=0;
3655+
}
3656+
3657+
// fail-safe
3658+
if (++attempts>1024) {
3659+
logE("getViableChannel(): too many attempts!");
3660+
break;
3661+
}
3662+
}
3663+
3664+
// fail-safe
3665+
return (chan+off)%chans;
3666+
}
3667+
36013668
bool DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
36023669
bool isViable[DIV_MAX_CHANS];
36033670
bool canPlayAnyway=false;

src/engine/engine.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,6 +1202,10 @@ class DivEngine {
12021202
// set whether autoNoteIn is mono or poly
12031203
void setAutoNotePoly(bool poly);
12041204

1205+
// get next viable channel with an offset
1206+
// chan is the base channel, off is the offset and ins is the instrument.
1207+
int getViableChannel(int chan, int off, int ins);
1208+
12051209
// go to order
12061210
void setOrder(unsigned char order);
12071211

src/gui/editControls.cpp

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -759,8 +759,18 @@ void FurnaceGUI::drawEditControls() {
759759

760760
ImGui::SameLine();
761761
pushToggleColors(noteInputPoly);
762-
if (ImGui::Button(noteInputPoly?(_("Poly##PolyInput")):(_("Mono##PolyInput")))) {
763-
noteInputPoly=!noteInputPoly;
762+
if (ImGui::Button(noteInputPoly?(noteInputChord?(_("Chord##PolyInput")):(_("Poly##PolyInput"))):(_("Mono##PolyInput")))) {
763+
if (noteInputPoly) {
764+
if (noteInputChord) {
765+
noteInputPoly=false;
766+
noteInputChord=false;
767+
} else {
768+
noteInputChord=true;
769+
}
770+
} else {
771+
noteInputPoly=true;
772+
noteInputChord=false;
773+
}
764774
e->setAutoNotePoly(noteInputPoly);
765775
}
766776
if (ImGui::IsItemHovered()) {
@@ -889,8 +899,18 @@ void FurnaceGUI::drawEditControls() {
889899

890900
ImGui::SameLine();
891901
pushToggleColors(noteInputPoly);
892-
if (ImGui::Button(noteInputPoly?_("Poly##PolyInput"):_("Mono##PolyInput"))) {
893-
noteInputPoly=!noteInputPoly;
902+
if (ImGui::Button(noteInputPoly?(noteInputChord?(_("Chord##PolyInput")):(_("Poly##PolyInput"))):(_("Mono##PolyInput")))) {
903+
if (noteInputPoly) {
904+
if (noteInputChord) {
905+
noteInputPoly=false;
906+
noteInputChord=false;
907+
} else {
908+
noteInputChord=true;
909+
}
910+
} else {
911+
noteInputPoly=true;
912+
noteInputChord=false;
913+
}
894914
e->setAutoNotePoly(noteInputPoly);
895915
}
896916
if (ImGui::IsItemHovered()) {
@@ -1027,8 +1047,18 @@ void FurnaceGUI::drawEditControls() {
10271047
popToggleColors();
10281048

10291049
pushToggleColors(noteInputPoly);
1030-
if (ImGui::Button(noteInputPoly?_("Poly##PolyInput"):_("Mono##PolyInput"))) {
1031-
noteInputPoly=!noteInputPoly;
1050+
if (ImGui::Button(noteInputPoly?(noteInputChord?(_("Chord##PolyInput")):(_("Poly##PolyInput"))):(_("Mono##PolyInput")))) {
1051+
if (noteInputPoly) {
1052+
if (noteInputChord) {
1053+
noteInputPoly=false;
1054+
noteInputChord=false;
1055+
} else {
1056+
noteInputChord=true;
1057+
}
1058+
} else {
1059+
noteInputPoly=true;
1060+
noteInputChord=false;
1061+
}
10321062
e->setAutoNotePoly(noteInputPoly);
10331063
}
10341064
if (ImGui::IsItemHovered()) {
@@ -1127,8 +1157,18 @@ void FurnaceGUI::drawEditControls() {
11271157

11281158
ImGui::SameLine();
11291159
pushToggleColors(noteInputPoly);
1130-
if (ImGui::Button(noteInputPoly?_("Poly##PolyInput"):_("Mono##PolyInput"))) {
1131-
noteInputPoly=!noteInputPoly;
1160+
if (ImGui::Button(noteInputPoly?(noteInputChord?(_("Chord##PolyInput")):(_("Poly##PolyInput"))):(_("Mono##PolyInput")))) {
1161+
if (noteInputPoly) {
1162+
if (noteInputChord) {
1163+
noteInputPoly=false;
1164+
noteInputChord=false;
1165+
} else {
1166+
noteInputChord=true;
1167+
}
1168+
} else {
1169+
noteInputPoly=true;
1170+
noteInputChord=false;
1171+
}
11321172
e->setAutoNotePoly(noteInputPoly);
11331173
}
11341174
if (ImGui::IsItemHovered()) {

src/gui/gui.cpp

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,6 +1296,7 @@ void FurnaceGUI::play(int row) {
12961296
}
12971297
curNibble=false;
12981298
orderNibble=false;
1299+
chordInputOffset=0;
12991300
activeNotes.clear();
13001301
}
13011302

@@ -1311,6 +1312,7 @@ void FurnaceGUI::stop() {
13111312
e->stop();
13121313
curNibble=false;
13131314
orderNibble=false;
1315+
chordInputOffset=0;
13141316
if (followPattern && wasPlaying) {
13151317
nextScroll=-1.0f;
13161318
nextAddScroll=0.0f;
@@ -1353,13 +1355,24 @@ void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode, bool autoNote) {
13531355
}
13541356
}
13551357

1356-
void FurnaceGUI::noteInput(int num, int key, int vol) {
1358+
void FurnaceGUI::noteInput(int num, int key, int vol, int chanOff) {
13571359
int ch=cursor.xCoarse;
13581360
int ord=curOrder;
13591361
int y=cursor.y;
13601362
int tick=0;
13611363
int speed=0;
13621364

1365+
if (chanOff>0 && noteInputChord) {
1366+
ch=e->getViableChannel(ch,chanOff,curIns);
1367+
if ((!e->isPlaying() || !followPattern)) {
1368+
y-=editStep;
1369+
while (y<0) {
1370+
if (--ord<0) ord=0;
1371+
y+=e->curSubSong->patLen;
1372+
}
1373+
}
1374+
}
1375+
13631376
if (e->isPlaying() && !e->isStepping() && followPattern) {
13641377
e->getPlayPosTick(ord,y,tick,speed);
13651378
if (tick<=(speed/2)) { // round
@@ -1373,12 +1386,12 @@ void FurnaceGUI::noteInput(int num, int key, int vol) {
13731386
}
13741387
}
13751388

1376-
logV("noteInput: chan %d, %d:%d %d/%d",ch,ord,y,tick,speed);
1389+
logV("noteInput: chan %d, offset %d, %d:%d %d/%d",ch,chanOff,ord,y,tick,speed);
13771390

13781391
DivPattern* pat=e->curPat[ch].getPattern(e->curOrders->ord[ch][ord],true);
13791392
bool removeIns=false;
13801393

1381-
prepareUndo(GUI_UNDO_PATTERN_EDIT);
1394+
prepareUndo(GUI_UNDO_PATTERN_EDIT,UndoRegion(ord,ch,y,ord,ch,y));
13821395

13831396
if (key==GUI_NOTE_OFF) { // note off
13841397
pat->newData[y][DIV_PAT_NOTE]=DIV_NOTE_OFF;
@@ -1416,8 +1429,10 @@ void FurnaceGUI::noteInput(int num, int key, int vol) {
14161429
pat->newData[y][DIV_PAT_VOL]=-1;
14171430
}
14181431
}
1419-
editAdvance();
1420-
makeUndo(GUI_UNDO_PATTERN_EDIT);
1432+
if ((!e->isPlaying() || !followPattern) && (chanOff<1 || !noteInputChord)) {
1433+
editAdvance();
1434+
}
1435+
makeUndo(GUI_UNDO_PATTERN_EDIT,UndoRegion(ord,ch,y,ord,ch,y));
14211436
curNibble=false;
14221437
}
14231438

@@ -1736,8 +1751,9 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
17361751
if (num>119) num=119; // B-9
17371752

17381753
if (edit) {
1739-
noteInput(num,key);
1754+
noteInput(num,key,-1,chordInputOffset);
17401755
}
1756+
chordInputOffset++;
17411757
}
17421758
} else if (edit) { // value
17431759
auto it=valueKeys.find(ev.key.keysym.sym);
@@ -1833,7 +1849,10 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
18331849
}
18341850

18351851
void FurnaceGUI::keyUp(SDL_Event& ev) {
1836-
// nothing for now
1852+
// this is very, very lazy...
1853+
if (--chordInputOffset<0) {
1854+
chordInputOffset=0;
1855+
}
18371856
}
18381857

18391858
bool dirExists(String s) {
@@ -3995,6 +4014,9 @@ bool FurnaceGUI::loop() {
39954014
break;
39964015
case SDL_KEYUP:
39974016
// for now
4017+
if (!ImGui::GetIO().WantCaptureKeyboard || (newFilePicker->isOpened() && !ImGui::GetIO().WantTextInput)) {
4018+
keyUp(ev);
4019+
}
39984020
insEditMayBeDirty=true;
39994021
if (introPos<11.0 && introSkip<0.5 && !shortIntro) {
40004022
introSkipDo=false;
@@ -4178,14 +4200,19 @@ bool FurnaceGUI::loop() {
41784200
if (action!=0) {
41794201
doAction(action);
41804202
} else switch (msg.type&0xf0) {
4203+
case TA_MIDI_NOTE_OFF:
4204+
if (--chordInputOffset<0) chordInputOffset=0;
4205+
break;
41814206
case TA_MIDI_NOTE_ON:
41824207
if (midiMap.valueInputStyle==0 || midiMap.valueInputStyle>3 || cursor.xFine==0) {
41834208
if (midiMap.noteInput && edit && msg.data[1]!=0) {
41844209
noteInput(
41854210
msg.data[0]-12,
41864211
0,
4187-
midiMap.volInput?msg.data[1]:-1
4212+
midiMap.volInput?msg.data[1]:-1,
4213+
chordInputOffset
41884214
);
4215+
chordInputOffset++;
41894216
}
41904217
} else {
41914218
if (edit && msg.data[1]!=0) {
@@ -7423,6 +7450,9 @@ bool FurnaceGUI::loop() {
74237450
break;
74247451
}
74257452
}
7453+
7454+
// reset chord count just in case
7455+
chordInputOffset=0;
74267456
}
74277457

74287458
if (!settings.renderClearPos || renderBackend==GUI_BACKEND_METAL) {
@@ -8254,6 +8284,7 @@ void FurnaceGUI::syncState() {
82548284
followOrders=e->getConfBool("followOrders",true);
82558285
followPattern=e->getConfBool("followPattern",true);
82568286
noteInputPoly=e->getConfBool("noteInputPoly",true);
8287+
noteInputChord=e->getConfBool("noteInputChord",false);
82578288
filePlayerSync=e->getConfBool("filePlayerSync",true);
82588289
audioExportOptions.loops=e->getConfInt("exportLoops",0);
82598290
if (audioExportOptions.loops<0) audioExportOptions.loops=0;
@@ -8414,6 +8445,7 @@ void FurnaceGUI::commitState(DivConfig& conf) {
84148445
conf.set("followPattern",followPattern);
84158446
conf.set("orderEditMode",orderEditMode);
84168447
conf.set("noteInputPoly",noteInputPoly);
8448+
conf.set("noteInputChord",noteInputChord);
84178449
conf.set("filePlayerSync",filePlayerSync);
84188450
if (settings.persistFadeOut) {
84198451
conf.set("exportLoops",audioExportOptions.loops);
@@ -8588,6 +8620,7 @@ FurnaceGUI::FurnaceGUI():
85888620
sysDupCloneChannels(true),
85898621
sysDupEnd(false),
85908622
noteInputPoly(true),
8623+
noteInputChord(false),
85918624
notifyWaveChange(false),
85928625
notifySampleChange(false),
85938626
recalcTimestamps(true),
@@ -8623,6 +8656,7 @@ FurnaceGUI::FurnaceGUI():
86238656
drawHalt(10),
86248657
macroPointSize(16),
86258658
waveEditStyle(0),
8659+
chordInputOffset(0),
86268660
displayInsTypeListMakeInsSample(-1),
86278661
makeDrumkitOctave(3),
86288662
mobileEditPage(0),

src/gui/gui.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1713,7 +1713,8 @@ class FurnaceGUI {
17131713
bool vgmExportDirectStream, displayInsTypeList, displayWaveSizeList;
17141714
bool portrait, injectBackUp, mobileMenuOpen, warnColorPushed;
17151715
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
1716-
bool displayNew, displayExport, displayPalette, fullScreen, preserveChanPos, sysDupCloneChannels, sysDupEnd, noteInputPoly, notifyWaveChange, notifySampleChange;
1716+
bool displayNew, displayExport, displayPalette, fullScreen, preserveChanPos, sysDupCloneChannels, sysDupEnd, noteInputPoly, noteInputChord;
1717+
bool notifyWaveChange, notifySampleChange;
17171718
bool recalcTimestamps;
17181719
bool wantScrollListIns, wantScrollListWave, wantScrollListSample;
17191720
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString;
@@ -1737,6 +1738,7 @@ class FurnaceGUI {
17371738
int drawHalt;
17381739
int macroPointSize;
17391740
int waveEditStyle;
1741+
int chordInputOffset;
17401742
int displayInsTypeListMakeInsSample;
17411743
int makeDrumkitOctave;
17421744
int mobileEditPage;
@@ -3080,7 +3082,7 @@ class FurnaceGUI {
30803082
void doDrag(bool copy=false);
30813083
void editOptions(bool topMenu);
30823084
DivSystem systemPicker(bool fullWidth);
3083-
void noteInput(int num, int key, int vol=-1);
3085+
void noteInput(int num, int key, int vol=-1, int chanOff=0);
30843086
void valueInput(int num, bool direct=false, int target=-1);
30853087
void orderInput(int num);
30863088

0 commit comments

Comments
 (0)