diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index f6252cbe7..9ebe26ff6 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2537,6 +2537,45 @@ void DivEngine::noteOff(int chan) { BUSY_END; } +void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { + //if (ch<0 || ch>=chans) return; + if (midiBaseChan<0) midiBaseChan=0; + if (midiBaseChan>=chans) midiBaseChan=chans-1; + int finalChan=midiBaseChan; + + if (!playing) { + reset(); + freelance=true; + playing=true; + } + + do { + if ((ins==-1 || getPreferInsType(finalChan)==getIns(ins)->type) && chan[finalChan].midiNote==-1) { + chan[finalChan].midiNote=note; + pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); + break; + } + if (++finalChan>=chans) { + finalChan=0; + } + } while (finalChan!=midiBaseChan); +} + +void DivEngine::autoNoteOff(int ch, int note, int vol) { + if (!playing) { + reset(); + freelance=true; + playing=true; + } + //if (ch<0 || ch>=chans) return; + for (int i=0; i=chans) chan=0; + midiBaseChan=chan; +} + void DivEngine::setMidiCallback(std::function what) { midiCallback=what; } diff --git a/src/engine/engine.h b/src/engine/engine.h index 52a01291b..7ae5fa7f0 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -86,6 +86,8 @@ struct DivChannelState { bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff; bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, noteOnInhibit, resetArp; + int midiNote; + DivChannelState(): note(-1), oldNote(-1), @@ -126,7 +128,8 @@ struct DivChannelState { scheduledSlideReset(false), shorthandPorta(false), noteOnInhibit(false), - resetArp(false) {} + resetArp(false), + midiNote(-1) {} }; struct DivNoteEvent { @@ -231,6 +234,7 @@ class DivEngine { short vibTable[64]; int reversePitchTable[4096]; int pitchTable[4096]; + int midiBaseChan; blip_buffer_t* samp_bb; size_t samp_bbInLen; @@ -539,6 +543,9 @@ class DivEngine { // stop note void noteOff(int chan); + void autoNoteOn(int chan, int ins, int note, int vol=-1); + void autoNoteOff(int chan, int note, int vol=-1); + // go to order void setOrder(unsigned char order); @@ -638,6 +645,9 @@ class DivEngine { // switch master bool switchMaster(); + // set MIDI base channel + void setMidiBaseChan(int chan); + // set MIDI input callback // if the specified function returns -2, note feedback will be inhibited. void setMidiCallback(std::function what); @@ -726,6 +736,7 @@ class DivEngine { view(DIV_STATUS_NOTHING), haltOn(DIV_HALT_NONE), audioEngine(DIV_AUDIO_NULL), + midiBaseChan(0), samp_bbInLen(0), samp_temp(0), samp_prevSample(0), diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 9d51670c1..570f8a63f 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -430,7 +430,7 @@ struct DivInstrument { DivInstrument(): name(""), mode(false), - type(DIV_INS_STD) { + type(DIV_INS_FM) { } }; #endif diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 0e1922b34..a1acff8cd 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1552,7 +1552,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi switch (msg.type&0xf0) { case TA_MIDI_NOTE_OFF: { if (chan<0 || chan>=chans) break; - pendingNotes.push(DivNoteEvent(msg.type&15,-1,-1,-1,false)); + autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); if (!playing) { reset(); freelance=true; @@ -1563,14 +1563,9 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi case TA_MIDI_NOTE_ON: { if (chan<0 || chan>=chans) break; if (msg.data[1]==0) { - pendingNotes.push(DivNoteEvent(msg.type&15,-1,-1,-1,false)); + autoNoteOff(msg.type&15,msg.data[0]-12,msg.data[1]); } else { - pendingNotes.push(DivNoteEvent(msg.type&15,ins,(int)msg.data[0]-12,msg.data[1],true)); - } - if (!playing) { - reset(); - freelance=true; - playing=true; + autoNoteOn(msg.type&15,ins,msg.data[0]-12,msg.data[1]); } break; } diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp index 6a77ff0bd..421b6d486 100644 --- a/src/gui/cursor.cpp +++ b/src/gui/cursor.cpp @@ -35,6 +35,7 @@ void FurnaceGUI::startSelection(int xCoarse, int xFine, int y) { selEnd.xFine=xFine; selEnd.y=y; selecting=true; + e->setMidiBaseChan(cursor.xCoarse); } void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y) { @@ -88,6 +89,8 @@ void FurnaceGUI::finishSelection() { if (e->song.chanCollapse[selEnd.xCoarse]) { selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; } + + e->setMidiBaseChan(cursor.xCoarse); } void FurnaceGUI::moveCursor(int x, int y, bool select) { @@ -191,6 +194,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { } selEnd=cursor; updateScroll(cursor.y); + e->setMidiBaseChan(cursor.xCoarse); } void FurnaceGUI::moveCursorPrevChannel(bool overflow) { @@ -210,6 +214,7 @@ void FurnaceGUI::moveCursorPrevChannel(bool overflow) { cursor.xCoarse=firstChannel; } } + e->setMidiBaseChan(cursor.xCoarse); selStart=cursor; selEnd=cursor; @@ -233,6 +238,7 @@ void FurnaceGUI::moveCursorNextChannel(bool overflow) { cursor.xCoarse=lastChannel-1; } } + e->setMidiBaseChan(cursor.xCoarse); selStart=cursor; selEnd=cursor; @@ -254,6 +260,7 @@ void FurnaceGUI::moveCursorTop(bool select) { if (!select) { selEnd=cursor; } + e->setMidiBaseChan(cursor.xCoarse); updateScroll(cursor.y); } @@ -273,6 +280,7 @@ void FurnaceGUI::moveCursorBottom(bool select) { selStart=cursor; } selEnd=cursor; + e->setMidiBaseChan(cursor.xCoarse); updateScroll(cursor.y); } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index cbf7f2928..04c9d1461 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -703,7 +703,15 @@ void FurnaceGUI::stop() { activeNotes.clear(); } -void FurnaceGUI::previewNote(int refChan, int note) { +void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) { + if (autoNote) { + e->setMidiBaseChan(refChan); + e->synchronized([this,note]() { + e->autoNoteOn(-1,curIns,note); + }); + return; + } + bool chanBusy[DIV_MAX_CHANS]; memset(chanBusy,0,DIV_MAX_CHANS*sizeof(bool)); for (ActiveNote& i: activeNotes) { @@ -725,8 +733,8 @@ void FurnaceGUI::previewNote(int refChan, int note) { //printf("FAILED TO FIND CHANNEL!\n"); } -void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode) { - if (activeNotes.empty()) return; +void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode, bool autoNote) { + if (activeNotes.empty() && !autoNote) return; try { int key=noteKeys.at(scancode); int num=12*curOctave+key; @@ -737,6 +745,13 @@ void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode) { if (key==101) return; if (key==102) return; + if (autoNote) { + e->synchronized([this,num]() { + e->autoNoteOff(-1,num); + }); + return; + } + for (size_t i=0; inoteOff(activeNotes[i].chan); @@ -1008,7 +1023,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { int key=noteKeys.at(ev.key.keysym.scancode); int num=12*curOctave+key; if (key!=100 && key!=101 && key!=102) { - previewNote(cursor.xCoarse,num); + previewNote(cursor.xCoarse,num,true); } } catch (std::out_of_range& e) { } @@ -1052,7 +1067,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { } void FurnaceGUI::keyUp(SDL_Event& ev) { - stopPreviewNote(ev.key.keysym.scancode); + stopPreviewNote(ev.key.keysym.scancode,curWindow!=GUI_WINDOW_PATTERN); if (wavePreviewOn) { if (ev.key.keysym.scancode==wavePreviewKey) { wavePreviewOn=false; @@ -1923,7 +1938,7 @@ bool FurnaceGUI::loop() { case TA_MIDI_PROGRAM: if (midiMap.programChange) { curIns=msg.data[0]; - 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; } @@ -2693,6 +2708,7 @@ bool FurnaceGUI::init() { midiLock.lock(); midiQueue.push(msg); midiLock.unlock(); + e->setMidiBaseChan(cursor.xCoarse); if (midiMap.at(msg)) return -2; return curIns; }); diff --git a/src/gui/gui.h b/src/gui/gui.h index bb0afefb7..8624080ad 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -988,8 +988,8 @@ class FurnaceGUI { void play(int row=0); void stop(); - void previewNote(int refChan, int note); - void stopPreviewNote(SDL_Scancode scancode); + void previewNote(int refChan, int note, bool autoNote=false); + void stopPreviewNote(SDL_Scancode scancode, bool autoNote=false); void keyDown(SDL_Event& ev); void keyUp(SDL_Event& ev);