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