| @@ -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); | |||