diff --git a/src/audio/abstract.cpp b/src/audio/abstract.cpp index d4973180a..45960fea9 100644 --- a/src/audio/abstract.cpp +++ b/src/audio/abstract.cpp @@ -61,6 +61,10 @@ bool TAMidiIn::gather() { return false; } +bool TAMidiOut::send(const TAMidiMessage& what) { + return false; +} + bool TAMidiIn::isDeviceOpen() { return false; } diff --git a/src/audio/rtmidi.cpp b/src/audio/rtmidi.cpp index a9b2d4b11..fcd992beb 100644 --- a/src/audio/rtmidi.cpp +++ b/src/audio/rtmidi.cpp @@ -19,6 +19,7 @@ #include "rtmidi.h" #include "../ta-log.h" +#include "taAudio.h" // --- IN --- @@ -77,6 +78,7 @@ bool TAMidiInRtMidi::openDevice(String name) { } } isOpen=portOpen; + if (!portOpen) logW("could not find MIDI in device...\n"); return portOpen; } catch (RtMidiError& e) { logW("could not open MIDI in device! %s\n",e.what()); @@ -120,9 +122,40 @@ bool TAMidiInRtMidi::quit() { // --- OUT --- -bool TAMidiOutRtMidi::send(TAMidiMessage& what) { - // TODO - return false; +bool TAMidiOutRtMidi::send(const TAMidiMessage& what) { + if (!isOpen) return false; + if (what.type<0x80) return false; + size_t len=0; + switch (what.type&0xf0) { + case TA_MIDI_NOTE_OFF: + case TA_MIDI_NOTE_ON: + case TA_MIDI_AFTERTOUCH: + case TA_MIDI_CONTROL: + case TA_MIDI_PITCH_BEND: + len=3; + break; + case TA_MIDI_PROGRAM: + case TA_MIDI_CHANNEL_AFTERTOUCH: + len=2; + break; + } + if (len==0) switch (what.type) { + case TA_MIDI_SYSEX: // currently not supported :< + return false; + break; + case TA_MIDI_MTC_FRAME: + case TA_MIDI_SONG_SELECT: + len=2; + break; + case TA_MIDI_POSITION: + len=3; + break; + default: + len=1; + break; + } + port->sendMessage((const unsigned char*)&what.type,len); + return true; } bool TAMidiOutRtMidi::isDeviceOpen() { @@ -143,6 +176,7 @@ bool TAMidiOutRtMidi::openDevice(String name) { } } isOpen=portOpen; + if (!portOpen) logW("could not find MIDI out device...\n"); return portOpen; } catch (RtMidiError& e) { logW("could not open MIDI out device! %s\n",e.what()); diff --git a/src/audio/rtmidi.h b/src/audio/rtmidi.h index 31d7119b1..34ddf73e6 100644 --- a/src/audio/rtmidi.h +++ b/src/audio/rtmidi.h @@ -40,7 +40,7 @@ class TAMidiOutRtMidi: public TAMidiOut { RtMidiOut* port; bool isOpen; public: - bool send(TAMidiMessage& what); + bool send(const TAMidiMessage& what); bool isDeviceOpen(); bool openDevice(String name); bool closeDevice(); diff --git a/src/audio/taAudio.h b/src/audio/taAudio.h index f532e2a7e..c1ef519dd 100644 --- a/src/audio/taAudio.h +++ b/src/audio/taAudio.h @@ -99,6 +99,16 @@ struct TAMidiMessage { void submitSysEx(std::vector data); void done(); + TAMidiMessage(unsigned char t, unsigned char d0, unsigned char d1): + time(0.0), + type(t), + sysExData(NULL), + sysExLen(0) { + memset(&data,0,sizeof(data)); + data[0]=d0; + data[1]=d1; + } + TAMidiMessage(): time(0.0), type(0), @@ -127,7 +137,7 @@ class TAMidiIn { class TAMidiOut { std::queue queue; public: - bool send(TAMidiMessage& what); + virtual bool send(const TAMidiMessage& what); virtual bool isDeviceOpen(); virtual bool openDevice(String name); virtual bool closeDevice(); diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 9ebe26ff6..fe38e8077 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -790,13 +790,20 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) { } speedAB=false; playing=true; + skipping=true; for (int i=0; isetSkipRegisterWrites(true); while (playing && curOrdersetSkipRegisterWrites(false); @@ -816,6 +823,7 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) { if (!preserveDrift) { ticks=1; } + skipping=false; cmdStream.clear(); } @@ -2864,6 +2872,16 @@ bool DivEngine::initAudioBackend() { } } } + if (output->midiOut) { + String outName=getConfString("midiOutDevice",""); + if (!outName.empty()) { + // try opening device + logI("opening MIDI output.\n"); + if (!output->midiOut->openDevice(outName)) { + logW("could not open MIDI output device!\n"); + } + } + } return true; } @@ -2876,6 +2894,12 @@ bool DivEngine::deinitAudioBackend() { output->midiIn->closeDevice(); } } + if (output->midiOut) { + if (output->midiOut->isDeviceOpen()) { + logI("closing MIDI output.\n"); + output->midiOut->closeDevice(); + } + } output->quitMidi(); output->quit(); delete output; diff --git a/src/engine/engine.h b/src/engine/engine.h index 7ae5fa7f0..2f334edeb 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -86,7 +86,7 @@ struct DivChannelState { bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff; bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, noteOnInhibit, resetArp; - int midiNote; + int midiNote, curMidiNote; DivChannelState(): note(-1), @@ -129,7 +129,8 @@ struct DivChannelState { shorthandPorta(false), noteOnInhibit(false), resetArp(false), - midiNote(-1) {} + midiNote(-1), + curMidiNote(-1) {} }; struct DivNoteEvent { @@ -194,6 +195,7 @@ class DivEngine { bool cmdStreamEnabled; bool softLocked; bool firstTick; + bool skipping; int softLockCount; int ticks, curRow, curOrder, remainingLoops, nextSpeed; double divider; @@ -712,6 +714,7 @@ class DivEngine { cmdStreamEnabled(false), softLocked(false), firstTick(false), + skipping(false), softLockCount(0), ticks(0), curRow(0), diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index a1acff8cd..6dddcfcbb 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -172,7 +172,49 @@ int DivEngine::dispatchCmd(DivCommand c) { if (cmdStreamEnabled && cmdStream.size()<2000) { cmdStream.push_back(c); } + + if (!skipping && output->midiOut!=NULL) { + if (output->midiOut->isDeviceOpen()) { + int scaledVol=(chan[c.chan].volume*127)/MAX(1,chan[c.chan].volMax); + if (scaledVol<0) scaledVol=0; + if (scaledVol>127) scaledVol=127; + switch (c.cmd) { + case DIV_CMD_NOTE_ON: + case DIV_CMD_LEGATO: + if (chan[c.chan].curMidiNote>=0) { + output->midiOut->send(TAMidiMessage(0x80|(c.chan&15),chan[c.chan].curMidiNote,scaledVol)); + } + if (c.value!=DIV_NOTE_NULL) chan[c.chan].curMidiNote=c.value+12; + output->midiOut->send(TAMidiMessage(0x90|(c.chan&15),chan[c.chan].curMidiNote,scaledVol)); + break; + case DIV_CMD_NOTE_OFF: + case DIV_CMD_NOTE_OFF_ENV: + if (chan[c.chan].curMidiNote>=0) { + output->midiOut->send(TAMidiMessage(0x80|(c.chan&15),chan[c.chan].curMidiNote,scaledVol)); + } + chan[c.chan].curMidiNote=-1; + break; + case DIV_CMD_INSTRUMENT: + output->midiOut->send(TAMidiMessage(0xc0|(c.chan&15),c.value,0)); + break; + case DIV_CMD_VOLUME: + //output->midiOut->send(TAMidiMessage(0xb0|(c.chan&15),0x07,scaledVol)); + break; + case DIV_CMD_PITCH: { + int pitchBend=8192+(c.value<<5); + if (pitchBend<0) pitchBend=0; + if (pitchBend>16383) pitchBend=16383; + output->midiOut->send(TAMidiMessage(0xe0|(c.chan&15),pitchBend&0x7f,pitchBend>>7)); + break; + } + default: + break; + } + } + } + c.chan=dispatchChanOfChan[c.dis]; + return disCont[dispatchOfChan[c.dis]].dispatch->dispatch(c); }