@@ -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
18351851void  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
18391858bool  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 ),
0 commit comments