@@ -15,18 +15,30 @@ struct Message { | |||
uint8_t data1 = 0x00; | |||
uint8_t data2 = 0x00; | |||
uint8_t channel() { | |||
uint8_t getChannel() { | |||
return cmd & 0xf; | |||
} | |||
uint8_t status() { | |||
void setChannel(uint8_t channel) { | |||
cmd = (cmd & 0xf0) | (channel & 0xf); | |||
} | |||
uint8_t getStatus() { | |||
return (cmd >> 4) & 0xf; | |||
} | |||
uint8_t note() { | |||
void setStatus(uint8_t status) { | |||
cmd = (cmd & 0xf) | ((status << 4) & 0xf0); | |||
} | |||
uint8_t getNote() { | |||
return data1 & 0x7f; | |||
} | |||
uint8_t value() { | |||
void setNote(uint8_t note) { | |||
data1 = note & 0x7f; | |||
} | |||
uint8_t getValue() { | |||
return data2 & 0x7f; | |||
} | |||
void setValue(uint8_t value) { | |||
data2 = value & 0x7f; | |||
} | |||
}; | |||
//////////////////// | |||
@@ -102,6 +114,7 @@ struct IO { | |||
virtual std::string getDeviceName(int deviceId) = 0; | |||
virtual void setDeviceId(int deviceId) = 0; | |||
virtual std::vector<int> getChannels() = 0; | |||
std::string getChannelName(int channel); | |||
void setChannel(int channel); | |||
@@ -120,6 +133,8 @@ struct Input : IO { | |||
std::vector<int> getDeviceIds() override; | |||
std::string getDeviceName(int deviceId) override; | |||
void setDeviceId(int deviceId) override; | |||
std::vector<int> getChannels() override; | |||
virtual void onMessage(Message message) {} | |||
}; | |||
@@ -143,6 +158,7 @@ struct Output : IO { | |||
std::vector<int> getDeviceIds() override; | |||
std::string getDeviceName(int deviceId) override; | |||
void setDeviceId(int deviceId) override; | |||
std::vector<int> getChannels() override; | |||
void sendMessage(Message message); | |||
}; | |||
@@ -45,9 +45,9 @@ struct PolyphonicMidiOutput : midi::Output { | |||
for (int note = 0; note <= 127; note++) { | |||
// Note off | |||
midi::Message m; | |||
m.cmd = 0x8; | |||
m.data1 = note; | |||
m.data2 = 0; | |||
m.setStatus(0x8); | |||
m.setNote(note); | |||
m.setValue(0); | |||
sendMessage(m); | |||
} | |||
} | |||
@@ -71,17 +71,17 @@ struct PolyphonicMidiOutput : midi::Output { | |||
if (changedNote || enabledGate) { | |||
// Note on | |||
midi::Message m; | |||
m.cmd = 0x9; | |||
m.data1 = notes[c]; | |||
m.data2 = vels[c]; | |||
m.setStatus(0x9); | |||
m.setNote(notes[c]); | |||
m.setValue(vels[c]); | |||
sendMessage(m); | |||
} | |||
if (changedNote || disabledGate) { | |||
// Note off | |||
midi::Message m; | |||
m.cmd = 0x8; | |||
m.data1 = lastNotes[c]; | |||
m.data2 = vels[c]; | |||
m.setStatus(0x8); | |||
m.setNote(lastNotes[c]); | |||
m.setValue(vels[c]); | |||
sendMessage(m); | |||
} | |||
lastNotes[c] = notes[c]; | |||
@@ -94,9 +94,9 @@ struct PolyphonicMidiOutput : midi::Output { | |||
lastAfts[c] = aft; | |||
// Polyphonic key pressure | |||
midi::Message m; | |||
m.cmd = 0xa; | |||
m.data1 = notes[c]; | |||
m.data2 = aft; | |||
m.setStatus(0xa); | |||
m.setNote(notes[c]); | |||
m.setValue(aft); | |||
sendMessage(m); | |||
} | |||
@@ -106,9 +106,9 @@ struct PolyphonicMidiOutput : midi::Output { | |||
lastPw = pw; | |||
// Pitch wheel | |||
midi::Message m; | |||
m.cmd = 0xe; | |||
m.data1 = pw & 0x7f; | |||
m.data2 = (pw >> 7) & 0x7f; | |||
m.setStatus(0xe); | |||
m.setNote(pw & 0x7f); | |||
m.setValue((pw >> 7) & 0x7f); | |||
sendMessage(m); | |||
} | |||
@@ -118,9 +118,9 @@ struct PolyphonicMidiOutput : midi::Output { | |||
lastMw = mw; | |||
// CC Mod wheel | |||
midi::Message m; | |||
m.cmd = 0xb; | |||
m.data1 = 0x01; | |||
m.data2 = mw; | |||
m.setStatus(0xb); | |||
m.setNote(0x01); | |||
m.setValue(mw); | |||
sendMessage(m); | |||
} | |||
@@ -131,8 +131,8 @@ struct PolyphonicMidiOutput : midi::Output { | |||
if (clk) { | |||
// Timing clock | |||
midi::Message m; | |||
m.cmd = 0xf; | |||
m.data1 = 0x8; | |||
m.setStatus(0xf); | |||
m.setChannel(0x8); | |||
sendMessage(m); | |||
} | |||
} | |||
@@ -143,9 +143,9 @@ struct PolyphonicMidiOutput : midi::Output { | |||
lastVol = vol; | |||
// CC Volume | |||
midi::Message m; | |||
m.cmd = 0xb; | |||
m.data1 = 0x07; | |||
m.data2 = vol; | |||
m.setStatus(0xb); | |||
m.setNote(0x07); | |||
m.setValue(vol); | |||
sendMessage(m); | |||
} | |||
@@ -155,9 +155,9 @@ struct PolyphonicMidiOutput : midi::Output { | |||
lastPan = pan; | |||
// CC Pan | |||
midi::Message m; | |||
m.cmd = 0xb; | |||
m.data1 = 0x0a; | |||
m.data2 = pan; | |||
m.setStatus(0xb); | |||
m.setNote(0x0a); | |||
m.setValue(pan); | |||
sendMessage(m); | |||
} | |||
@@ -168,8 +168,8 @@ struct PolyphonicMidiOutput : midi::Output { | |||
if (start) { | |||
// Start | |||
midi::Message m; | |||
m.cmd = 0xf; | |||
m.data1 = 0xa; | |||
m.setStatus(0xf); | |||
m.setChannel(0xa); | |||
sendMessage(m); | |||
} | |||
} | |||
@@ -181,8 +181,8 @@ struct PolyphonicMidiOutput : midi::Output { | |||
if (stop) { | |||
// Stop | |||
midi::Message m; | |||
m.cmd = 0xf; | |||
m.data1 = 0xb; | |||
m.setStatus(0xf); | |||
m.setChannel(0xb); | |||
sendMessage(m); | |||
} | |||
} | |||
@@ -194,8 +194,8 @@ struct PolyphonicMidiOutput : midi::Output { | |||
if (cont) { | |||
// Continue | |||
midi::Message m; | |||
m.cmd = 0xf; | |||
m.data1 = 0xc; | |||
m.setStatus(0xf); | |||
m.setChannel(0xc); | |||
sendMessage(m); | |||
} | |||
} | |||
@@ -70,10 +70,10 @@ struct MIDICCToCVInterface : Module { | |||
} | |||
void processMessage(midi::Message msg) { | |||
switch (msg.status()) { | |||
switch (msg.getStatus()) { | |||
// cc | |||
case 0xb: { | |||
uint8_t cc = msg.note(); | |||
uint8_t cc = msg.getNote(); | |||
// Learn | |||
if (learningId >= 0 && values[cc] != msg.data2) { | |||
ccs[learningId] = cc; | |||
@@ -166,28 +166,28 @@ struct MIDIToCVInterface : Module { | |||
} | |||
void processMessage(midi::Message msg) { | |||
// DEBUG("MIDI: %01x %01x %02x %02x", msg.status(), msg.channel(), msg.note(), msg.value()); | |||
// DEBUG("MIDI: %01x %01x %02x %02x", msg.getStatus(), msg.channel(), msg.getNote(), msg.getValue()); | |||
switch (msg.status()) { | |||
switch (msg.getStatus()) { | |||
// note off | |||
case 0x8: { | |||
releaseNote(msg.note()); | |||
releaseNote(msg.getNote()); | |||
} break; | |||
// note on | |||
case 0x9: { | |||
if (msg.value() > 0) { | |||
noteData[msg.note()].velocity = msg.value(); | |||
pressNote(msg.note()); | |||
if (msg.getValue() > 0) { | |||
noteData[msg.getNote()].velocity = msg.getValue(); | |||
pressNote(msg.getNote()); | |||
} | |||
else { | |||
// For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released. | |||
releaseNote(msg.note()); | |||
releaseNote(msg.getNote()); | |||
} | |||
} break; | |||
// channel aftertouch | |||
case 0xa: { | |||
uint8_t note = msg.note(); | |||
noteData[note].aftertouch = msg.value(); | |||
uint8_t note = msg.getNote(); | |||
noteData[note].aftertouch = msg.getValue(); | |||
} break; | |||
// cc | |||
case 0xb: { | |||
@@ -195,7 +195,7 @@ struct MIDIToCVInterface : Module { | |||
} break; | |||
// pitch wheel | |||
case 0xe: { | |||
pitch = msg.value() * 128 + msg.note(); | |||
pitch = msg.getValue() * 128 + msg.getNote(); | |||
} break; | |||
case 0xf: { | |||
processSystem(msg); | |||
@@ -205,14 +205,14 @@ struct MIDIToCVInterface : Module { | |||
} | |||
void processCC(midi::Message msg) { | |||
switch (msg.note()) { | |||
switch (msg.getNote()) { | |||
// mod | |||
case 0x01: { | |||
mod = msg.value(); | |||
mod = msg.getValue(); | |||
} break; | |||
// sustain | |||
case 0x40: { | |||
if (msg.value() >= 64) | |||
if (msg.getValue() >= 64) | |||
pressPedal(); | |||
else | |||
releasePedal(); | |||
@@ -222,7 +222,7 @@ struct MIDIToCVInterface : Module { | |||
} | |||
void processSystem(midi::Message msg) { | |||
switch (msg.channel()) { | |||
switch (msg.getChannel()) { | |||
// Timing | |||
case 0x8: { | |||
if (clock % divisions[0] == 0) { | |||
@@ -89,19 +89,19 @@ struct MIDITriggerToCVInterface : Module { | |||
} | |||
void processMessage(midi::Message msg) { | |||
switch (msg.status()) { | |||
switch (msg.getStatus()) { | |||
// note off | |||
case 0x8: { | |||
releaseNote(msg.note()); | |||
releaseNote(msg.getNote()); | |||
} break; | |||
// note on | |||
case 0x9: { | |||
if (msg.value() > 0) { | |||
pressNote(msg.note(), msg.value()); | |||
if (msg.getValue() > 0) { | |||
pressNote(msg.getNote(), msg.getValue()); | |||
} | |||
else { | |||
// Many stupid keyboards send a "note on" command with 0 velocity to mean "note release" | |||
releaseNote(msg.note()); | |||
releaseNote(msg.getNote()); | |||
} | |||
} break; | |||
default: break; | |||
@@ -258,24 +258,24 @@ struct QuadMIDIToCVInterface : Module { | |||
} | |||
void processMessage(midi::Message msg) { | |||
switch (msg.status()) { | |||
switch (msg.getStatus()) { | |||
// note off | |||
case 0x8: { | |||
releaseNote(msg.note()); | |||
releaseNote(msg.getNote()); | |||
} break; | |||
// note on | |||
case 0x9: { | |||
if (msg.value() > 0) { | |||
noteData[msg.note()].velocity = msg.value(); | |||
pressNote(msg.note()); | |||
if (msg.getValue() > 0) { | |||
noteData[msg.getNote()].velocity = msg.getValue(); | |||
pressNote(msg.getNote()); | |||
} | |||
else { | |||
releaseNote(msg.note()); | |||
releaseNote(msg.getNote()); | |||
} | |||
} break; | |||
// channel aftertouch | |||
case 0xa: { | |||
noteData[msg.note()].aftertouch = msg.value(); | |||
noteData[msg.getNote()].aftertouch = msg.getValue(); | |||
} break; | |||
// cc | |||
case 0xb: { | |||
@@ -286,10 +286,10 @@ struct QuadMIDIToCVInterface : Module { | |||
} | |||
void processCC(midi::Message msg) { | |||
switch (msg.note()) { | |||
switch (msg.getNote()) { | |||
// sustain | |||
case 0x40: { | |||
if (msg.value() >= 64) | |||
if (msg.getValue() >= 64) | |||
pressPedal(); | |||
else | |||
releasePedal(); | |||
@@ -112,7 +112,7 @@ struct MidiChannelChoice : LedDisplayChoice { | |||
Menu *menu = createMenu(); | |||
menu->addChild(createMenuLabel("MIDI channel")); | |||
for (int channel = -1; channel < 16; channel++) { | |||
for (int channel : midiWidget->midiIO->getChannels()) { | |||
MidiChannelItem *item = new MidiChannelItem; | |||
item->midiIO = midiWidget->midiIO; | |||
item->channel = channel; | |||
@@ -30,7 +30,7 @@ void InputDevice::unsubscribe(Input *input) { | |||
void InputDevice::onMessage(Message message) { | |||
for (Input *input : subscribed) { | |||
// Filter channel | |||
if (input->channel < 0 || message.status() == 0xf || message.channel() == input->channel) { | |||
if (input->channel < 0 || message.getStatus() == 0xf || message.getChannel() == input->channel) { | |||
input->onMessage(message); | |||
} | |||
} | |||
@@ -127,6 +127,7 @@ void IO::fromJson(json_t *rootJ) { | |||
//////////////////// | |||
Input::Input() { | |||
channel = -1; | |||
// Set first driver as default | |||
if (driverIds.size() >= 1) { | |||
setDriverId(driverIds[0]); | |||
@@ -166,6 +167,14 @@ void Input::setDeviceId(int deviceId) { | |||
} | |||
} | |||
std::vector<int> Input::getChannels() { | |||
std::vector<int> channels; | |||
for (int c = -1; c < 16; c++) { | |||
channels.push_back(c); | |||
} | |||
return channels; | |||
} | |||
void InputQueue::onMessage(Message message) { | |||
// Push to queue | |||
if ((int) queue.size() < queueMaxSize) | |||
@@ -188,6 +197,7 @@ bool InputQueue::shift(Message *message) { | |||
//////////////////// | |||
Output::Output() { | |||
channel = 0; | |||
// Set first driver as default | |||
if (driverIds.size() >= 1) { | |||
setDriverId(driverIds[0]); | |||
@@ -227,7 +237,19 @@ void Output::setDeviceId(int deviceId) { | |||
} | |||
} | |||
std::vector<int> Output::getChannels() { | |||
std::vector<int> channels; | |||
for (int c = 0; c < 16; c++) { | |||
channels.push_back(c); | |||
} | |||
return channels; | |||
} | |||
void Output::sendMessage(Message message) { | |||
// Set channel | |||
if (message.getStatus() != 0xf) { | |||
message.setChannel(channel); | |||
} | |||
// DEBUG("sendMessage %02x %02x %02x", message.cmd, message.data1, message.data2); | |||
if (outputDevice) { | |||
outputDevice->sendMessage(message); | |||