kind of MIDI output

This commit is contained in:
tildearrow 2022-03-31 03:33:05 -05:00
parent 45ce940d66
commit f689409f02
7 changed files with 126 additions and 9 deletions

View file

@ -61,6 +61,10 @@ bool TAMidiIn::gather() {
return false; return false;
} }
bool TAMidiOut::send(const TAMidiMessage& what) {
return false;
}
bool TAMidiIn::isDeviceOpen() { bool TAMidiIn::isDeviceOpen() {
return false; return false;
} }

View file

@ -19,6 +19,7 @@
#include "rtmidi.h" #include "rtmidi.h"
#include "../ta-log.h" #include "../ta-log.h"
#include "taAudio.h"
// --- IN --- // --- IN ---
@ -77,6 +78,7 @@ bool TAMidiInRtMidi::openDevice(String name) {
} }
} }
isOpen=portOpen; isOpen=portOpen;
if (!portOpen) logW("could not find MIDI in device...\n");
return portOpen; return portOpen;
} catch (RtMidiError& e) { } catch (RtMidiError& e) {
logW("could not open MIDI in device! %s\n",e.what()); logW("could not open MIDI in device! %s\n",e.what());
@ -120,9 +122,40 @@ bool TAMidiInRtMidi::quit() {
// --- OUT --- // --- OUT ---
bool TAMidiOutRtMidi::send(TAMidiMessage& what) { bool TAMidiOutRtMidi::send(const TAMidiMessage& what) {
// TODO if (!isOpen) return false;
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() { bool TAMidiOutRtMidi::isDeviceOpen() {
@ -143,6 +176,7 @@ bool TAMidiOutRtMidi::openDevice(String name) {
} }
} }
isOpen=portOpen; isOpen=portOpen;
if (!portOpen) logW("could not find MIDI out device...\n");
return portOpen; return portOpen;
} catch (RtMidiError& e) { } catch (RtMidiError& e) {
logW("could not open MIDI out device! %s\n",e.what()); logW("could not open MIDI out device! %s\n",e.what());

View file

@ -40,7 +40,7 @@ class TAMidiOutRtMidi: public TAMidiOut {
RtMidiOut* port; RtMidiOut* port;
bool isOpen; bool isOpen;
public: public:
bool send(TAMidiMessage& what); bool send(const TAMidiMessage& what);
bool isDeviceOpen(); bool isDeviceOpen();
bool openDevice(String name); bool openDevice(String name);
bool closeDevice(); bool closeDevice();

View file

@ -99,6 +99,16 @@ struct TAMidiMessage {
void submitSysEx(std::vector<unsigned char> data); void submitSysEx(std::vector<unsigned char> data);
void done(); 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(): TAMidiMessage():
time(0.0), time(0.0),
type(0), type(0),
@ -127,7 +137,7 @@ class TAMidiIn {
class TAMidiOut { class TAMidiOut {
std::queue<TAMidiMessage> queue; std::queue<TAMidiMessage> queue;
public: public:
bool send(TAMidiMessage& what); virtual bool send(const TAMidiMessage& what);
virtual bool isDeviceOpen(); virtual bool isDeviceOpen();
virtual bool openDevice(String name); virtual bool openDevice(String name);
virtual bool closeDevice(); virtual bool closeDevice();

View file

@ -790,13 +790,20 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
} }
speedAB=false; speedAB=false;
playing=true; playing=true;
skipping=true;
for (int i=0; i<song.systemLen; i++) disCont[i].dispatch->setSkipRegisterWrites(true); for (int i=0; i<song.systemLen; i++) disCont[i].dispatch->setSkipRegisterWrites(true);
while (playing && curOrder<goal) { while (playing && curOrder<goal) {
if (nextTick(preserveDrift)) return; if (nextTick(preserveDrift)) {
skipping=false;
return;
}
} }
int oldOrder=curOrder; int oldOrder=curOrder;
while (playing && curRow<goalRow) { while (playing && curRow<goalRow) {
if (nextTick(preserveDrift)) return; if (nextTick(preserveDrift)) {
skipping=false;
return;
}
if (oldOrder!=curOrder) break; if (oldOrder!=curOrder) break;
} }
for (int i=0; i<song.systemLen; i++) disCont[i].dispatch->setSkipRegisterWrites(false); for (int i=0; i<song.systemLen; i++) disCont[i].dispatch->setSkipRegisterWrites(false);
@ -816,6 +823,7 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
if (!preserveDrift) { if (!preserveDrift) {
ticks=1; ticks=1;
} }
skipping=false;
cmdStream.clear(); 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; return true;
} }
@ -2876,6 +2894,12 @@ bool DivEngine::deinitAudioBackend() {
output->midiIn->closeDevice(); output->midiIn->closeDevice();
} }
} }
if (output->midiOut) {
if (output->midiOut->isDeviceOpen()) {
logI("closing MIDI output.\n");
output->midiOut->closeDevice();
}
}
output->quitMidi(); output->quitMidi();
output->quit(); output->quit();
delete output; delete output;

View file

@ -86,7 +86,7 @@ struct DivChannelState {
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff; bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, noteOnInhibit, resetArp; bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, noteOnInhibit, resetArp;
int midiNote; int midiNote, curMidiNote;
DivChannelState(): DivChannelState():
note(-1), note(-1),
@ -129,7 +129,8 @@ struct DivChannelState {
shorthandPorta(false), shorthandPorta(false),
noteOnInhibit(false), noteOnInhibit(false),
resetArp(false), resetArp(false),
midiNote(-1) {} midiNote(-1),
curMidiNote(-1) {}
}; };
struct DivNoteEvent { struct DivNoteEvent {
@ -194,6 +195,7 @@ class DivEngine {
bool cmdStreamEnabled; bool cmdStreamEnabled;
bool softLocked; bool softLocked;
bool firstTick; bool firstTick;
bool skipping;
int softLockCount; int softLockCount;
int ticks, curRow, curOrder, remainingLoops, nextSpeed; int ticks, curRow, curOrder, remainingLoops, nextSpeed;
double divider; double divider;
@ -712,6 +714,7 @@ class DivEngine {
cmdStreamEnabled(false), cmdStreamEnabled(false),
softLocked(false), softLocked(false),
firstTick(false), firstTick(false),
skipping(false),
softLockCount(0), softLockCount(0),
ticks(0), ticks(0),
curRow(0), curRow(0),

View file

@ -172,7 +172,49 @@ int DivEngine::dispatchCmd(DivCommand c) {
if (cmdStreamEnabled && cmdStream.size()<2000) { if (cmdStreamEnabled && cmdStream.size()<2000) {
cmdStream.push_back(c); 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]; c.chan=dispatchChanOfChan[c.dis];
return disCont[dispatchOfChan[c.dis]].dispatch->dispatch(c); return disCont[dispatchOfChan[c.dis]].dispatch->dispatch(c);
} }