From c3d4eda06e3889a4974d326e4340de6303f8e7b2 Mon Sep 17 00:00:00 2001 From: Sapphire Koser Date: Fri, 6 Dec 2024 18:19:42 -0800 Subject: [PATCH 01/10] Move wantsToOutput logic to cables --- src/deluge/io/midi/cable_types/din.cpp | 4 ++++ src/deluge/io/midi/cable_types/din.h | 2 ++ src/deluge/io/midi/cable_types/usb_common.cpp | 9 +++++++++ src/deluge/io/midi/cable_types/usb_common.h | 2 ++ src/deluge/io/midi/midi_device.cpp | 15 ++++++++------- src/deluge/io/midi/midi_device.h | 4 +++- src/deluge/io/midi/midi_engine.cpp | 7 ++----- 7 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/deluge/io/midi/cable_types/din.cpp b/src/deluge/io/midi/cable_types/din.cpp index e311c5ba0d..f4d5b29300 100644 --- a/src/deluge/io/midi/cable_types/din.cpp +++ b/src/deluge/io/midi/cable_types/din.cpp @@ -56,3 +56,7 @@ void MIDICableDINPorts::sendSysex(const uint8_t* data, int32_t len) { bufferMIDIUart(data[i]); } } + +bool MIDICableDINPorts::wantsToOutputMIDIOnChannel(MIDIMessage message, int32_t filter) const { + return message.isSystemMessage() || MIDICable::wantsToOutputMIDIOnChannel(message, filter); +} diff --git a/src/deluge/io/midi/cable_types/din.h b/src/deluge/io/midi/cable_types/din.h index 8ab2ea549f..5c97e60c76 100644 --- a/src/deluge/io/midi/cable_types/din.h +++ b/src/deluge/io/midi/cable_types/din.h @@ -25,6 +25,8 @@ class MIDICableDINPorts final : public MIDICable { connectionFlags = 1; // DIN ports are always connected } + [[nodiscard]] bool wantsToOutputMIDIOnChannel(MIDIMessage message, int32_t filter) const override; + void writeReferenceAttributesToFile(Serializer& writer) override; void writeToFlash(uint8_t* memory) override; char const* getDisplayName() override; diff --git a/src/deluge/io/midi/cable_types/usb_common.cpp b/src/deluge/io/midi/cable_types/usb_common.cpp index 47bfbe06e0..6ac31080e6 100644 --- a/src/deluge/io/midi/cable_types/usb_common.cpp +++ b/src/deluge/io/midi/cable_types/usb_common.cpp @@ -130,3 +130,12 @@ void MIDICableUSB::sendSysex(const uint8_t* data, int32_t len) { connectedDevice->bufferMessage(packed); } } + +bool MIDICableUSB::wantsToOutputMIDIOnChannel(MIDIMessage message, int32_t filter) const { + if (message.isSystemMessage()) { + return sendClock; + } + else { + return MIDICable::wantsToOutputMIDIOnChannel(message, filter); + } +} diff --git a/src/deluge/io/midi/cable_types/usb_common.h b/src/deluge/io/midi/cable_types/usb_common.h index 4f1f003ff8..0a4f0fe75f 100644 --- a/src/deluge/io/midi/cable_types/usb_common.h +++ b/src/deluge/io/midi/cable_types/usb_common.h @@ -26,6 +26,8 @@ class MIDICableUSB : public MIDICable { needsToSendMCMs = 0; } + [[nodiscard]] bool wantsToOutputMIDIOnChannel(MIDIMessage message, int32_t filter) const override; + void sendMessage(MIDIMessage message) override; void sendSysex(const uint8_t* data, int32_t len) override; size_t sendBufferSpace() override; diff --git a/src/deluge/io/midi/midi_device.cpp b/src/deluge/io/midi/midi_device.cpp index f561a745a6..6483caff8c 100644 --- a/src/deluge/io/midi/midi_device.cpp +++ b/src/deluge/io/midi/midi_device.cpp @@ -131,19 +131,20 @@ void MIDICable::dataEntryMessageReceived(ModelStack* modelStack, int32_t channel } } -bool MIDICable::wantsToOutputMIDIOnChannel(int32_t channel, int32_t filter) { +bool MIDICable::wantsToOutputMIDIOnChannel(MIDIMessage message, int32_t filter) const { + auto lowerLastMember = ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeLowerZoneLastMemberChannel; + auto upperLastMember = ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeUpperZoneLastMemberChannel; + switch (filter) { case MIDI_CHANNEL_MPE_LOWER_ZONE: - return (ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeLowerZoneLastMemberChannel != 0); + return lowerLastMember != 0; case MIDI_CHANNEL_MPE_UPPER_ZONE: - return (ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeUpperZoneLastMemberChannel != 15); + return upperLastMember != 15; default: - return (ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeLowerZoneLastMemberChannel == 0 - || ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeLowerZoneLastMemberChannel < channel) - && (ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeUpperZoneLastMemberChannel == 15 - || ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeUpperZoneLastMemberChannel > channel); + return (lowerLastMember == 0 || lowerLastMember < message.channel) + && (upperLastMember == 15 || upperLastMember > message.channel); } } diff --git a/src/deluge/io/midi/midi_device.h b/src/deluge/io/midi/midi_device.h index 7ed0304468..8aada0d5d7 100644 --- a/src/deluge/io/midi/midi_device.h +++ b/src/deluge/io/midi/midi_device.h @@ -98,7 +98,9 @@ class MIDICable { virtual char const* getDisplayName() = 0; void dataEntryMessageReceived(ModelStack* modelStack, int32_t channel, int32_t msb); - bool wantsToOutputMIDIOnChannel(int32_t channel, int32_t filter); + + /// Used to decide if the given message should be output on a channel + [[nodiscard]] virtual bool wantsToOutputMIDIOnChannel(MIDIMessage message, int32_t filter) const; /// @name File IO functions /// @{ diff --git a/src/deluge/io/midi/midi_engine.cpp b/src/deluge/io/midi/midi_engine.cpp index c7e583e637..feb675ae76 100644 --- a/src/deluge/io/midi/midi_engine.cpp +++ b/src/deluge/io/midi/midi_engine.cpp @@ -514,8 +514,7 @@ void MidiEngine::sendMidi(MIDISource source, MIDIMessage message, int32_t filter } // Send serial MIDI - if (message.isSystemMessage() - || MIDIDeviceManager::dinMIDIPorts.wantsToOutputMIDIOnChannel(message.channel, filter)) { + if (MIDIDeviceManager::dinMIDIPorts.wantsToOutputMIDIOnChannel(message, filter)) { sendSerialMidi(message); } @@ -561,9 +560,7 @@ void MidiEngine::sendUsbMidi(MIDIMessage message, int32_t filter) { && connectedDevice->cable[p] != &MIDIDeviceManager::upstreamUSBMIDICable3) { // if it's a clock (or sysex technically but we don't send that to this function) // or if it's a message that this channel wants - if ((isSystemMessage && connectedDevice->cable[p]->sendClock) - || (!isSystemMessage - && connectedDevice->cable[p]->wantsToOutputMIDIOnChannel(message.channel, filter))) { + if (connectedDevice->cable[p]->wantsToOutputMIDIOnChannel(message, filter)) { // Or with the port to add the cable number to the full message. This // is a bit hacky but it works From 0ea7e6b7c1c0b0f7bdc0ed44a04ced199bf8968d Mon Sep 17 00:00:00 2001 From: Sapphire Koser Date: Fri, 6 Dec 2024 18:07:17 -0800 Subject: [PATCH 02/10] make MidiCable::sendBufferSpace const --- src/deluge/io/midi/cable_types/din.cpp | 2 +- src/deluge/io/midi/cable_types/din.h | 2 +- src/deluge/io/midi/cable_types/usb_common.cpp | 2 +- src/deluge/io/midi/cable_types/usb_common.h | 2 +- src/deluge/io/midi/midi_device.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/deluge/io/midi/cable_types/din.cpp b/src/deluge/io/midi/cable_types/din.cpp index f4d5b29300..5e90dd2a8f 100644 --- a/src/deluge/io/midi/cable_types/din.cpp +++ b/src/deluge/io/midi/cable_types/din.cpp @@ -42,7 +42,7 @@ void MIDICableDINPorts::sendMessage(MIDIMessage message) { midiEngine.sendSerialMidi(message); } -size_t MIDICableDINPorts::sendBufferSpace() { +size_t MIDICableDINPorts::sendBufferSpace() const { return uartGetTxBufferSpace(UART_ITEM_MIDI); } diff --git a/src/deluge/io/midi/cable_types/din.h b/src/deluge/io/midi/cable_types/din.h index 5c97e60c76..af41f69aeb 100644 --- a/src/deluge/io/midi/cable_types/din.h +++ b/src/deluge/io/midi/cable_types/din.h @@ -33,5 +33,5 @@ class MIDICableDINPorts final : public MIDICable { void sendMessage(MIDIMessage message) override; void sendSysex(const uint8_t* data, int32_t len) override; - size_t sendBufferSpace() override; + size_t sendBufferSpace() const override; }; diff --git a/src/deluge/io/midi/cable_types/usb_common.cpp b/src/deluge/io/midi/cable_types/usb_common.cpp index 6ac31080e6..1c968f2ba0 100644 --- a/src/deluge/io/midi/cable_types/usb_common.cpp +++ b/src/deluge/io/midi/cable_types/usb_common.cpp @@ -49,7 +49,7 @@ void MIDICableUSB::sendMessage(MIDIMessage message) { } } -size_t MIDICableUSB::sendBufferSpace() { +size_t MIDICableUSB::sendBufferSpace() const { int32_t ip = 0; ConnectedUSBMIDIDevice* connectedDevice = nullptr; diff --git a/src/deluge/io/midi/cable_types/usb_common.h b/src/deluge/io/midi/cable_types/usb_common.h index 0a4f0fe75f..ebeaed9e5e 100644 --- a/src/deluge/io/midi/cable_types/usb_common.h +++ b/src/deluge/io/midi/cable_types/usb_common.h @@ -30,7 +30,7 @@ class MIDICableUSB : public MIDICable { void sendMessage(MIDIMessage message) override; void sendSysex(const uint8_t* data, int32_t len) override; - size_t sendBufferSpace() override; + size_t sendBufferSpace() const override; void connectedNow(int32_t midiDeviceNum); void sendMCMsNowIfNeeded(); diff --git a/src/deluge/io/midi/midi_device.h b/src/deluge/io/midi/midi_device.h index 8aada0d5d7..b59c334dd7 100644 --- a/src/deluge/io/midi/midi_device.h +++ b/src/deluge/io/midi/midi_device.h @@ -124,7 +124,7 @@ class MIDICable { virtual void sendSysex(const uint8_t* data, int32_t len) = 0; /// Get the number of bytes available in the send buffer. - virtual size_t sendBufferSpace() = 0; + [[nodiscard]] virtual size_t sendBufferSpace() const = 0; /// @} /// @name High-level IO functions From 97aac5ff2db5ddab2e36f20fec0cfd2d30a1982e Mon Sep 17 00:00:00 2001 From: Sapphire Koser Date: Sun, 8 Dec 2024 12:21:03 -0800 Subject: [PATCH 03/10] Rename MIDIDeviceUSB* to MIDICableUSB* This was missed in #2934 --- src/deluge/gui/views/arranger_view.cpp | 4 +- src/deluge/gui/views/instrument_clip_view.cpp | 14 ++--- src/deluge/gui/views/session_view.cpp | 10 +-- src/deluge/gui/views/view.cpp | 4 +- .../io/midi/cable_types/usb_device_cable.cpp | 6 +- .../io/midi/cable_types/usb_device_cable.h | 4 +- src/deluge/io/midi/cable_types/usb_hosted.cpp | 8 +-- src/deluge/io/midi/cable_types/usb_hosted.h | 6 +- .../device_specific/midi_device_lumi_keys.h | 2 +- .../device_specific/specific_midi_device.cpp | 16 ++--- .../device_specific/specific_midi_device.h | 8 +-- src/deluge/io/midi/midi_device_manager.cpp | 62 +++++++++---------- src/deluge/io/midi/midi_device_manager.h | 6 +- 13 files changed, 75 insertions(+), 75 deletions(-) diff --git a/src/deluge/gui/views/arranger_view.cpp b/src/deluge/gui/views/arranger_view.cpp index 4a9cff67c8..62adc103b6 100644 --- a/src/deluge/gui/views/arranger_view.cpp +++ b/src/deluge/gui/views/arranger_view.cpp @@ -1905,7 +1905,7 @@ void ArrangerView::transitionToClipView(ClipInstance* clipInstance) { PadLEDs::sendOutSidebarColours(); // They'll have been cleared by the first explode render // Hook point for specificMidiDevice - iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook::HOOK_ON_TRANSITION_TO_CLIP_VIEW); + iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook::HOOK_ON_TRANSITION_TO_CLIP_VIEW); } // Returns false if error @@ -2009,7 +2009,7 @@ bool ArrangerView::transitionToArrangementEditor() { doingAutoScrollNow = false; // May get changed back at new scroll pos soon // Hook point for specificMidiDevice - iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook::HOOK_ON_TRANSITION_TO_ARRANGER_VIEW); + iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook::HOOK_ON_TRANSITION_TO_ARRANGER_VIEW); return true; } diff --git a/src/deluge/gui/views/instrument_clip_view.cpp b/src/deluge/gui/views/instrument_clip_view.cpp index aae38a2459..5df3d038db 100644 --- a/src/deluge/gui/views/instrument_clip_view.cpp +++ b/src/deluge/gui/views/instrument_clip_view.cpp @@ -195,7 +195,7 @@ ActionResult InstrumentClipView::commandLearnUserScale() { recalculateColours(); uiNeedsRendering(this); // Hook point for specificMidiDevice - iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook::HOOK_ON_CHANGE_SCALE); + iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook::HOOK_ON_CHANGE_SCALE); display->popupTextTemporary("USER"); return ActionResult::DEALT_WITH; } @@ -205,7 +205,7 @@ ActionResult InstrumentClipView::commandCycleThroughScales() { recalculateColours(); uiNeedsRendering(this); // Hook point for specificMidiDevice - iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook::HOOK_ON_CHANGE_SCALE); + iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook::HOOK_ON_CHANGE_SCALE); return ActionResult::DEALT_WITH; } @@ -215,7 +215,7 @@ ActionResult InstrumentClipView::commandFlashRootNote() { flashDefaultRootNoteOn = false; flashDefaultRootNote(); // Hook point for specificMidiDevice - iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook::HOOK_ON_CHANGE_SCALE); + iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook::HOOK_ON_CHANGE_SCALE); return ActionResult::DEALT_WITH; } @@ -239,7 +239,7 @@ ActionResult InstrumentClipView::commandChangeRootNote(uint8_t yDisplay) { uiNeedsRendering(this); // Hook point for specificMidiDevice - iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook::HOOK_ON_CHANGE_ROOT_NOTE); + iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook::HOOK_ON_CHANGE_ROOT_NOTE); return ActionResult::DEALT_WITH; } @@ -3766,7 +3766,7 @@ void InstrumentClipView::recalculateColour(uint8_t yDisplay) { rowBlurColour[yDisplay] = rowColour[yDisplay].forBlur(); // Hook point for specificMidiDevice - iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook::HOOK_ON_RECALCULATE_COLOUR); + iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook::HOOK_ON_RECALCULATE_COLOUR); } ActionResult InstrumentClipView::scrollVertical(int32_t scrollAmount, bool inCardRoutine, bool draggingNoteRow) { @@ -5258,7 +5258,7 @@ void InstrumentClipView::enterScaleMode(uint8_t yDisplay) { } // Hook point for specificMidiDevice - iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook::HOOK_ON_ENTER_SCALE_MODE); + iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook::HOOK_ON_ENTER_SCALE_MODE); } int32_t InstrumentClipView::setupForExitingScaleMode() { @@ -5349,7 +5349,7 @@ void InstrumentClipView::exitScaleMode() { } // Hook point for specificMidiDevice - iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook::HOOK_ON_EXIT_SCALE_MODE); + iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook::HOOK_ON_EXIT_SCALE_MODE); } // If called from KeyboardScreen, the newRootNote won't correspond to the yDisplay, and that's ok diff --git a/src/deluge/gui/views/session_view.cpp b/src/deluge/gui/views/session_view.cpp index c99a4f6827..d8edabe2ff 100644 --- a/src/deluge/gui/views/session_view.cpp +++ b/src/deluge/gui/views/session_view.cpp @@ -2744,7 +2744,7 @@ void SessionView::transitionToViewForClip(Clip* clip) { if (clip->type == ClipType::INSTRUMENT) { // Hook point for specificMidiDevice - iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook::HOOK_ON_TRANSITION_TO_SESSION_VIEW); + iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook::HOOK_ON_TRANSITION_TO_SESSION_VIEW); } } @@ -2788,7 +2788,7 @@ void SessionView::transitionToViewForClip(Clip* clip) { PadLEDs::renderClipExpandOrCollapse(); // Hook point for specificMidiDevice - iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook::HOOK_ON_TRANSITION_TO_SESSION_VIEW); + iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook::HOOK_ON_TRANSITION_TO_SESSION_VIEW); } // AudioClips @@ -2898,7 +2898,7 @@ void SessionView::transitionToSessionView() { } // Hook point for specificMidiDevice - iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook::HOOK_ON_TRANSITION_TO_SESSION_VIEW); + iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook::HOOK_ON_TRANSITION_TO_SESSION_VIEW); } // Might be called during card routine! So renders might fail. Not too likely @@ -4393,7 +4393,7 @@ void SessionView::gridTransitionToSessionView() { uiTimerManager.setTimer(TimerName::MATRIX_DRIVER, 35); // Hook point for specificMidiDevice - iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook::HOOK_ON_TRANSITION_TO_SESSION_VIEW); + iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook::HOOK_ON_TRANSITION_TO_SESSION_VIEW); } void SessionView::gridTransitionToViewForClip(Clip* clip) { @@ -4477,7 +4477,7 @@ void SessionView::gridTransitionToViewForClip(Clip* clip) { PadLEDs::sendOutSidebarColours(); // They'll have been cleared by the first explode render // Hook point for specificMidiDevice - iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook::HOOK_ON_TRANSITION_TO_CLIP_VIEW); + iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook::HOOK_ON_TRANSITION_TO_CLIP_VIEW); } const uint32_t SessionView::gridTrackCount() { diff --git a/src/deluge/gui/views/view.cpp b/src/deluge/gui/views/view.cpp index 598c670b00..bff1c5a5b4 100644 --- a/src/deluge/gui/views/view.cpp +++ b/src/deluge/gui/views/view.cpp @@ -541,7 +541,7 @@ void View::drumMidiLearnPadPressed(bool on, Drum* drum, Kit* kit) { learnedThing = &drum->midiInput; drumPressedForMIDILearn = drum; kitPressedForMIDILearn = kit; // Having this makes it possible to search much faster when we call - // grabVelocityToLevelFromMIDIDeviceAndSetupPatchingForAllParamManagersForDrum() + // grabVelocityToLevelFromMIDICableAndSetupPatchingForAllParamManagersForDrum() } else if (thingPressedForMidiLearn == MidiLearn::DRUM_INPUT) { @@ -587,7 +587,7 @@ void View::endMidiLearnPressSession(MidiLearn newThingPressed) { thingPressedForMidiLearn = newThingPressed; // Hook point for specificMidiDevice - iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook::HOOK_ON_MIDI_LEARN); + iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook::HOOK_ON_MIDI_LEARN); } void View::noteOnReceivedForMidiLearn(MIDICable& cable, int32_t channelOrZone, int32_t note, int32_t velocity) { diff --git a/src/deluge/io/midi/cable_types/usb_device_cable.cpp b/src/deluge/io/midi/cable_types/usb_device_cable.cpp index 92cd228913..6407136fb4 100644 --- a/src/deluge/io/midi/cable_types/usb_device_cable.cpp +++ b/src/deluge/io/midi/cable_types/usb_device_cable.cpp @@ -20,17 +20,17 @@ #include "io/debug/log.h" #include "storage/storage_manager.h" -void MIDIDeviceUSBUpstream::writeReferenceAttributesToFile(Serializer& writer) { +void MIDICableUSBUpstream::writeReferenceAttributesToFile(Serializer& writer) { // Same line. Usually the user wouldn't have default velocity sensitivity set for their computer. writer.writeAttribute("port", portNumber ? "upstreamUSB2" : "upstreamUSB", false); } -void MIDIDeviceUSBUpstream::writeToFlash(uint8_t* memory) { +void MIDICableUSBUpstream::writeToFlash(uint8_t* memory) { D_PRINTLN("writing to flash port %d into ", portNumber); *(uint16_t*)memory = portNumber ? VENDOR_ID_UPSTREAM_USB2 : VENDOR_ID_UPSTREAM_USB; } -char const* MIDIDeviceUSBUpstream::getDisplayName() { +char const* MIDICableUSBUpstream::getDisplayName() { switch (portNumber) { case 0: return deluge::l10n::get(deluge::l10n::String::STRING_FOR_UPSTREAM_USB_PORT_1); diff --git a/src/deluge/io/midi/cable_types/usb_device_cable.h b/src/deluge/io/midi/cable_types/usb_device_cable.h index 5c8d0bc734..a5a13e288b 100644 --- a/src/deluge/io/midi/cable_types/usb_device_cable.h +++ b/src/deluge/io/midi/cable_types/usb_device_cable.h @@ -19,9 +19,9 @@ #include "deluge/io/midi/cable_types/usb_common.h" -class MIDIDeviceUSBUpstream final : public MIDICableUSB { +class MIDICableUSBUpstream final : public MIDICableUSB { public: - MIDIDeviceUSBUpstream(uint8_t portNum = 0) : MIDICableUSB(portNum) {} + MIDICableUSBUpstream(uint8_t portNum = 0) : MIDICableUSB(portNum) {} void writeReferenceAttributesToFile(Serializer& writer) override; void writeToFlash(uint8_t* memory) override; char const* getDisplayName() override; diff --git a/src/deluge/io/midi/cable_types/usb_hosted.cpp b/src/deluge/io/midi/cable_types/usb_hosted.cpp index 81711881be..71865a0f3b 100644 --- a/src/deluge/io/midi/cable_types/usb_hosted.cpp +++ b/src/deluge/io/midi/cable_types/usb_hosted.cpp @@ -18,22 +18,22 @@ #include "usb_hosted.h" #include "storage/storage_manager.h" -void MIDIDeviceUSBHosted::writeReferenceAttributesToFile(Serializer& writer) { +void MIDICableUSBHosted::writeReferenceAttributesToFile(Serializer& writer) { writer.writeAttribute("name", name.get()); writer.writeAttributeHex("vendorId", vendorId, 4); writer.writeAttributeHex("productId", productId, 4); } -void MIDIDeviceUSBHosted::writeToFlash(uint8_t* memory) { +void MIDICableUSBHosted::writeToFlash(uint8_t* memory) { *(uint16_t*)memory = vendorId; *(uint16_t*)(memory + 2) = productId; } -char const* MIDIDeviceUSBHosted::getDisplayName() { +char const* MIDICableUSBHosted::getDisplayName() { return name.get(); } -void MIDIDeviceUSBHosted::callHook(Hook hook) { +void MIDICableUSBHosted::callHook(Hook hook) { switch (hook) { case Hook::HOOK_ON_CONNECTED: hookOnConnected(); diff --git a/src/deluge/io/midi/cable_types/usb_hosted.h b/src/deluge/io/midi/cable_types/usb_hosted.h index e22a190b7d..5ae4c7d5d2 100644 --- a/src/deluge/io/midi/cable_types/usb_hosted.h +++ b/src/deluge/io/midi/cable_types/usb_hosted.h @@ -19,10 +19,10 @@ #include "deluge/io/midi/cable_types/usb_common.h" -class MIDIDeviceUSBHosted : public MIDICableUSB { +class MIDICableUSBHosted : public MIDICableUSB { public: - virtual ~MIDIDeviceUSBHosted() = default; - MIDIDeviceUSBHosted() = default; + virtual ~MIDICableUSBHosted() = default; + MIDICableUSBHosted() = default; void writeReferenceAttributesToFile(Serializer& writer) override; void writeToFlash(uint8_t* memory) override; diff --git a/src/deluge/io/midi/device_specific/midi_device_lumi_keys.h b/src/deluge/io/midi/device_specific/midi_device_lumi_keys.h index be0b653254..89795af8d1 100644 --- a/src/deluge/io/midi/device_specific/midi_device_lumi_keys.h +++ b/src/deluge/io/midi/device_specific/midi_device_lumi_keys.h @@ -78,7 +78,7 @@ #define MIDI_DEVICE_LUMI_SCALE_8TONE_SPANISH 0b010101111011 #define MIDI_DEVICE_LUMI_SCALE_CHROMATIC 0b111111111111 -class MIDIDeviceLumiKeys final : public MIDIDeviceUSBHosted { +class MIDIDeviceLumiKeys final : public MIDICableUSBHosted { public: static constexpr uint16_t lumiKeysVendorProductPairs[MIDI_DEVICE_LUMI_KEYS_VP_COUNT][2] = {{0x2af4, 0xe00}}; diff --git a/src/deluge/io/midi/device_specific/specific_midi_device.cpp b/src/deluge/io/midi/device_specific/specific_midi_device.cpp index 6e687d3be8..5550a07c10 100644 --- a/src/deluge/io/midi/device_specific/specific_midi_device.cpp +++ b/src/deluge/io/midi/device_specific/specific_midi_device.cpp @@ -26,35 +26,35 @@ SpecificMidiDeviceType getSpecificMidiDeviceType(uint16_t vendorId, uint16_t pro return SpecificMidiDeviceType::NONE; } -MIDIDeviceUSBHosted* recastSpecificMidiDevice(void* sourceDevice) { - return recastSpecificMidiDevice((MIDIDeviceUSBHosted*)sourceDevice); +MIDICableUSBHosted* recastSpecificMidiDevice(void* sourceDevice) { + return recastSpecificMidiDevice((MIDICableUSBHosted*)sourceDevice); } /// @brief Recasts a MIDIDeviceUSBHosted pointer to a specific child device and back, to take advantage of virtual /// functions /// @param sourceDevice The known MIDIDeviceUSBHosted /// @return A MIDIDeviceUSBHosted pointer, cast from Specific MIDI devices if found. -MIDIDeviceUSBHosted* recastSpecificMidiDevice(MIDIDeviceUSBHosted* sourceDevice) { +MIDICableUSBHosted* recastSpecificMidiDevice(MIDICableUSBHosted* sourceDevice) { if (MIDIDeviceLumiKeys::matchesVendorProduct(sourceDevice->vendorId, sourceDevice->productId)) { MIDIDeviceLumiKeys* targetDevice = (MIDIDeviceLumiKeys*)sourceDevice; return targetDevice; } - MIDIDeviceUSBHosted* targetDevice = sourceDevice; + MIDICableUSBHosted* targetDevice = sourceDevice; return targetDevice; } /// @brief When a MIDIDevice is known, this locates the matching MIDIDeviceUSBHosted based on connectionFlags /// @param sourceDevice The known MIDIDevice /// @return A MIDIDeviceUSBHosted pointer, cast from Specific MIDI devices if found. -MIDIDeviceUSBHosted* getSpecificDeviceFromMIDICable(MIDICable& cable) { +MIDICableUSBHosted* getSpecificDeviceFromMIDICable(MIDICable& cable) { using namespace MIDIDeviceManager; // This depends on the MIDIDevice having been originally cast as something with a name const char* sourceName = cable.getDisplayName(); if (cable.connectionFlags && sourceName) { for (int32_t i = 0; i < hostedMIDIDevices.getNumElements(); i++) { - MIDIDeviceUSBHosted* candidate = recastSpecificMidiDevice(hostedMIDIDevices.getElement(i)); + MIDICableUSBHosted* candidate = recastSpecificMidiDevice(hostedMIDIDevices.getElement(i)); const char* candidateName = candidate->getDisplayName(); @@ -69,11 +69,11 @@ MIDIDeviceUSBHosted* getSpecificDeviceFromMIDICable(MIDICable& cable) { /// @brief Space-saving function to call a specific Hosted USB MIDI device's hook from any entry point /// @param hook -void iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook hook) { +void iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook hook) { using namespace MIDIDeviceManager; for (int32_t i = 0; i < hostedMIDIDevices.getNumElements(); i++) { - MIDIDeviceUSBHosted* specificDevice = recastSpecificMidiDevice(hostedMIDIDevices.getElement(i)); + MIDICableUSBHosted* specificDevice = recastSpecificMidiDevice(hostedMIDIDevices.getElement(i)); specificDevice->callHook(hook); } diff --git a/src/deluge/io/midi/device_specific/specific_midi_device.h b/src/deluge/io/midi/device_specific/specific_midi_device.h index 9091f4b586..fd7a4c189b 100644 --- a/src/deluge/io/midi/device_specific/specific_midi_device.h +++ b/src/deluge/io/midi/device_specific/specific_midi_device.h @@ -24,9 +24,9 @@ enum class SpecificMidiDeviceType { NONE = 0, LUMI_KEYS = 1 }; SpecificMidiDeviceType getSpecificMidiDeviceType(uint16_t vendorId, uint16_t productId); -MIDIDeviceUSBHosted* recastSpecificMidiDevice(void* sourceDevice); -MIDIDeviceUSBHosted* recastSpecificMidiDevice(MIDIDeviceUSBHosted* sourceDevice); +MIDICableUSBHosted* recastSpecificMidiDevice(void* sourceDevice); +MIDICableUSBHosted* recastSpecificMidiDevice(MIDICableUSBHosted* sourceDevice); -MIDIDeviceUSBHosted* getSpecificDeviceFromMIDICable(MIDICable& sourceDevice); +MIDICableUSBHosted* getSpecificDeviceFromMIDICable(MIDICable& sourceDevice); -void iterateAndCallSpecificDeviceHook(MIDIDeviceUSBHosted::Hook hook); +void iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook hook); diff --git a/src/deluge/io/midi/midi_device_manager.cpp b/src/deluge/io/midi/midi_device_manager.cpp index fc053655db..dd1ff7d07c 100644 --- a/src/deluge/io/midi/midi_device_manager.cpp +++ b/src/deluge/io/midi/midi_device_manager.cpp @@ -48,7 +48,7 @@ PLACE_INTERNAL_FRUNK ConnectedUSBMIDIDevice connectedUSBMIDIDevices[USB_NUM_USBI namespace MIDIDeviceManager { -NamedThingVector hostedMIDIDevices{__builtin_offsetof(MIDIDeviceUSBHosted, name)}; +NamedThingVector hostedMIDIDevices{__builtin_offsetof(MIDICableUSBHosted, name)}; bool differentiatingInputsByDevice = true; @@ -61,9 +61,9 @@ std::array usbDeviceCurrentlyBeingSetUp{}; // This class represents a thing you can send midi too, // the virtual cable is an implementation detail -MIDIDeviceUSBUpstream upstreamUSBMIDICable1{0}; -MIDIDeviceUSBUpstream upstreamUSBMIDICable2{1}; -MIDIDeviceUSBUpstream upstreamUSBMIDICable3{2}; +MIDICableUSBUpstream upstreamUSBMIDICable1{0}; +MIDICableUSBUpstream upstreamUSBMIDICable2{1}; +MIDICableUSBUpstream upstreamUSBMIDICable3{2}; MIDICableDINPorts dinMIDIPorts{}; uint8_t lowestLastMemberChannelOfLowerZoneOnConnectedOutput = 15; @@ -78,7 +78,7 @@ void slowRoutine() { // port3 is not used for channel data for (int32_t d = 0; d < hostedMIDIDevices.getNumElements(); d++) { - MIDIDeviceUSBHosted* device = (MIDIDeviceUSBHosted*)hostedMIDIDevices.getElement(d); + MIDICableUSBHosted* device = (MIDICableUSBHosted*)hostedMIDIDevices.getElement(d); device->sendMCMsNowIfNeeded(); // This routine placed here because for whatever reason we can't send sysex from hostedDeviceConfigured @@ -104,7 +104,7 @@ extern "C" void giveDetailsOfDeviceBeingSetUp(int32_t ip, char const* name, uint } // name can be NULL, or an empty String -MIDIDeviceUSBHosted* getOrCreateHostedMIDIDeviceFromDetails(String* name, uint16_t vendorId, uint16_t productId) { +MIDICableUSBHosted* getOrCreateHostedMIDIDeviceFromDetails(String* name, uint16_t vendorId, uint16_t productId) { // Do we know any details about this device already? @@ -118,7 +118,7 @@ MIDIDeviceUSBHosted* getOrCreateHostedMIDIDeviceFromDetails(String* name, uint16 // If we'd already seen it before... if (foundExact) { - MIDIDeviceUSBHosted* device = recastSpecificMidiDevice(hostedMIDIDevices.getElement(i)); + MIDICableUSBHosted* device = recastSpecificMidiDevice(hostedMIDIDevices.getElement(i)); // Update vendor and product id, if we have those if (vendorId) { @@ -132,7 +132,7 @@ MIDIDeviceUSBHosted* getOrCreateHostedMIDIDeviceFromDetails(String* name, uint16 // Ok, try searching by vendor / product id for (int32_t i = 0; i < hostedMIDIDevices.getNumElements(); i++) { - MIDIDeviceUSBHosted* candidate = recastSpecificMidiDevice(hostedMIDIDevices.getElement(i)); + MIDICableUSBHosted* candidate = recastSpecificMidiDevice(hostedMIDIDevices.getElement(i)); if (candidate->vendorId == vendorId && candidate->productId == productId) { // Update its name - if we got one and it's different @@ -148,7 +148,7 @@ MIDIDeviceUSBHosted* getOrCreateHostedMIDIDeviceFromDetails(String* name, uint16 return nullptr; } - MIDIDeviceUSBHosted* device = nullptr; + MIDICableUSBHosted* device = nullptr; SpecificMidiDeviceType devType = getSpecificMidiDeviceType(vendorId, productId); if (devType == SpecificMidiDeviceType::LUMI_KEYS) { @@ -161,12 +161,12 @@ MIDIDeviceUSBHosted* getOrCreateHostedMIDIDeviceFromDetails(String* name, uint16 device = instDevice; } else { - void* memory = GeneralMemoryAllocator::get().allocMaxSpeed(sizeof(MIDIDeviceUSBHosted)); + void* memory = GeneralMemoryAllocator::get().allocMaxSpeed(sizeof(MIDICableUSBHosted)); if (!memory) { return nullptr; } - MIDIDeviceUSBHosted* instDevice = new (memory) MIDIDeviceUSBHosted(); + MIDICableUSBHosted* instDevice = new (memory) MIDICableUSBHosted(); device = instDevice; } @@ -187,25 +187,25 @@ MIDIDeviceUSBHosted* getOrCreateHostedMIDIDeviceFromDetails(String* name, uint16 return device; } -void recountSmallestMPEZonesForDevice(MIDICable* device) { - if (!device->connectionFlags) { +void recountSmallestMPEZonesForCable(MIDICable& cable) { + if (!cable.connectionFlags) { return; } - if (device->ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeLowerZoneLastMemberChannel - && device->ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeLowerZoneLastMemberChannel + if (cable.ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeLowerZoneLastMemberChannel + && cable.ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeLowerZoneLastMemberChannel < lowestLastMemberChannelOfLowerZoneOnConnectedOutput) { lowestLastMemberChannelOfLowerZoneOnConnectedOutput = - device->ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeLowerZoneLastMemberChannel; + cable.ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeLowerZoneLastMemberChannel; } - if (device->ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeUpperZoneLastMemberChannel != 15 - && device->ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeUpperZoneLastMemberChannel + if (cable.ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeUpperZoneLastMemberChannel != 15 + && cable.ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeUpperZoneLastMemberChannel > highestLastMemberChannelOfUpperZoneOnConnectedOutput) { highestLastMemberChannelOfUpperZoneOnConnectedOutput = - device->ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeUpperZoneLastMemberChannel; + cable.ports[MIDI_DIRECTION_OUTPUT_FROM_DELUGE].mpeUpperZoneLastMemberChannel; } } @@ -213,21 +213,21 @@ void recountSmallestMPEZones() { lowestLastMemberChannelOfLowerZoneOnConnectedOutput = 15; highestLastMemberChannelOfUpperZoneOnConnectedOutput = 0; - recountSmallestMPEZonesForDevice(&upstreamUSBMIDICable1); - recountSmallestMPEZonesForDevice(&upstreamUSBMIDICable2); - recountSmallestMPEZonesForDevice(&dinMIDIPorts); + recountSmallestMPEZonesForCable(upstreamUSBMIDICable1); + recountSmallestMPEZonesForCable(upstreamUSBMIDICable2); + recountSmallestMPEZonesForCable(dinMIDIPorts); for (int32_t d = 0; d < hostedMIDIDevices.getNumElements(); d++) { - MIDIDeviceUSBHosted* device = (MIDIDeviceUSBHosted*)hostedMIDIDevices.getElement(d); - recountSmallestMPEZonesForDevice(device); + MIDICableUSBHosted* cable = (MIDICableUSBHosted*)hostedMIDIDevices.getElement(d); + recountSmallestMPEZonesForCable(*cable); } } // Create the midi device configuration and add to the USB midi array extern "C" void hostedDeviceConfigured(int32_t ip, int32_t midiDeviceNum) { - MIDIDeviceUSBHosted* device = getOrCreateHostedMIDIDeviceFromDetails(&usbDeviceCurrentlyBeingSetUp[ip].name, - usbDeviceCurrentlyBeingSetUp[ip].vendorId, - usbDeviceCurrentlyBeingSetUp[ip].productId); + MIDICableUSBHosted* device = getOrCreateHostedMIDIDeviceFromDetails(&usbDeviceCurrentlyBeingSetUp[ip].name, + usbDeviceCurrentlyBeingSetUp[ip].vendorId, + usbDeviceCurrentlyBeingSetUp[ip].productId); usbDeviceCurrentlyBeingSetUp[ip].name.clear(); // Save some memory. Not strictly necessary @@ -462,7 +462,7 @@ void writeDevicesToFile() { } for (int32_t d = 0; d < hostedMIDIDevices.getNumElements(); d++) { - MIDIDeviceUSBHosted* device = (MIDIDeviceUSBHosted*)hostedMIDIDevices.getElement(d); + MIDICableUSBHosted* device = (MIDICableUSBHosted*)hostedMIDIDevices.getElement(d); if (device->worthWritingToFile()) { goto worthIt; } @@ -478,7 +478,7 @@ void writeDevicesToFile() { return; } - MIDIDeviceUSBHosted* specificMIDIDevice = nullptr; +(??) MIDIDeviceUSBHosted* specificMIDIDevice = NULL; Serializer& writer = GetSerializer(); writer.writeOpeningTagBeginning("midiDevices"); writer.writeFirmwareVersion(); @@ -496,7 +496,7 @@ void writeDevicesToFile() { } for (int32_t d = 0; d < hostedMIDIDevices.getNumElements(); d++) { - MIDIDeviceUSBHosted* device = (MIDIDeviceUSBHosted*)hostedMIDIDevices.getElement(d); + MIDICableUSBHosted* device = (MIDICableUSBHosted*)hostedMIDIDevices.getElement(d); if (device->worthWritingToFile()) { device->writeToFile(writer, "hostedUSBDevice"); } @@ -577,7 +577,7 @@ void readDevicesFromFile() { } void readAHostedDeviceFromFile(Deserializer& reader) { - MIDIDeviceUSBHosted* device = nullptr; +(??) MIDIDeviceUSBHosted* device = NULL; String name; uint16_t vendorId; diff --git a/src/deluge/io/midi/midi_device_manager.h b/src/deluge/io/midi/midi_device_manager.h index 78926eb0ae..1d9aa5e45b 100644 --- a/src/deluge/io/midi/midi_device_manager.h +++ b/src/deluge/io/midi/midi_device_manager.h @@ -111,9 +111,9 @@ void writeDevicesToFile(); void readAHostedDeviceFromFile(Deserializer& reader); void readDevicesFromFile(); -extern MIDIDeviceUSBUpstream upstreamUSBMIDICable1; -extern MIDIDeviceUSBUpstream upstreamUSBMIDICable2; -extern MIDIDeviceUSBUpstream upstreamUSBMIDICable3; +extern MIDICableUSBUpstream upstreamUSBMIDICable1; +extern MIDICableUSBUpstream upstreamUSBMIDICable2; +extern MIDICableUSBUpstream upstreamUSBMIDICable3; extern MIDICableDINPorts dinMIDIPorts; extern bool differentiatingInputsByDevice; From 5d8d9cb1781718370479ec715bcf30a29f88b15a Mon Sep 17 00:00:00 2001 From: Sapphire Koser Date: Sat, 21 Dec 2024 16:10:09 -0800 Subject: [PATCH 04/10] refactor: unify global midi and midi follow cable read this logic is mostly identical, just the location the cable is stored changes. --- src/deluge/io/midi/midi_device_manager.cpp | 54 +++++++--------------- 1 file changed, 16 insertions(+), 38 deletions(-) diff --git a/src/deluge/io/midi/midi_device_manager.cpp b/src/deluge/io/midi/midi_device_manager.cpp index dd1ff7d07c..d93f729b31 100644 --- a/src/deluge/io/midi/midi_device_manager.cpp +++ b/src/deluge/io/midi/midi_device_manager.cpp @@ -374,33 +374,36 @@ MIDICable* readDeviceReferenceFromFile(Deserializer& reader) { return nullptr; } -void readDeviceReferenceFromFlash(GlobalMIDICommand whichCommand, uint8_t const* memory) { - +static MIDICable* readCableFromFlash(uint8_t const* memory) { uint16_t vendorId = *(uint16_t const*)memory; - MIDICable* device; + MIDICable* cable; if (vendorId == VENDOR_ID_NONE) { - device = nullptr; + cable = nullptr; } else if (vendorId == VENDOR_ID_UPSTREAM_USB) { - device = &upstreamUSBMIDICable1; + cable = &upstreamUSBMIDICable1; } else if (vendorId == VENDOR_ID_UPSTREAM_USB2) { - device = &upstreamUSBMIDICable2; + cable = &upstreamUSBMIDICable2; } else if (vendorId == VENDOR_ID_UPSTREAM_USB3) { - device = &upstreamUSBMIDICable3; + cable = &upstreamUSBMIDICable3; } else if (vendorId == VENDOR_ID_DIN) { - device = &dinMIDIPorts; + cable = &dinMIDIPorts; } else { uint16_t productId = *(uint16_t const*)(memory + 2); - device = getOrCreateHostedMIDIDeviceFromDetails(nullptr, vendorId, productId); + cable = getOrCreateHostedMIDIDeviceFromDetails(nullptr, vendorId, productId); } - midiEngine.globalMIDICommands[util::to_underlying(whichCommand)].cable = device; + return cable; +} + +void readDeviceReferenceFromFlash(GlobalMIDICommand whichCommand, uint8_t const* memory) { + midiEngine.globalMIDICommands[util::to_underlying(whichCommand)].cable = readCableFromFlash(memory); } void writeDeviceReferenceToFlash(GlobalMIDICommand whichCommand, uint8_t* memory) { @@ -410,32 +413,7 @@ void writeDeviceReferenceToFlash(GlobalMIDICommand whichCommand, uint8_t* memory } void readMidiFollowDeviceReferenceFromFlash(MIDIFollowChannelType whichType, uint8_t const* memory) { - - uint16_t vendorId = *(uint16_t const*)memory; - - MIDICable* device; - - if (vendorId == VENDOR_ID_NONE) { - device = nullptr; - } - else if (vendorId == VENDOR_ID_UPSTREAM_USB) { - device = &upstreamUSBMIDICable1; - } - else if (vendorId == VENDOR_ID_UPSTREAM_USB2) { - device = &upstreamUSBMIDICable2; - } - else if (vendorId == VENDOR_ID_UPSTREAM_USB3) { - device = &upstreamUSBMIDICable3; - } - else if (vendorId == VENDOR_ID_DIN) { - device = &dinMIDIPorts; - } - else { - uint16_t productId = *(uint16_t const*)(memory + 2); - device = getOrCreateHostedMIDIDeviceFromDetails(nullptr, vendorId, productId); - } - - midiEngine.midiFollowChannelType[util::to_underlying(whichType)].cable = device; + midiEngine.midiFollowChannelType[util::to_underlying(whichType)].cable = readCableFromFlash(memory); } void writeMidiFollowDeviceReferenceToFlash(MIDIFollowChannelType whichType, uint8_t* memory) { @@ -478,7 +456,7 @@ void writeDevicesToFile() { return; } -(??) MIDIDeviceUSBHosted* specificMIDIDevice = NULL; + MIDICableUSBHosted* specificMIDIDevice = NULL; Serializer& writer = GetSerializer(); writer.writeOpeningTagBeginning("midiDevices"); writer.writeFirmwareVersion(); @@ -577,7 +555,7 @@ void readDevicesFromFile() { } void readAHostedDeviceFromFile(Deserializer& reader) { -(??) MIDIDeviceUSBHosted* device = NULL; + MIDICableUSBHosted* device = nullptr; String name; uint16_t vendorId; From 48619fe07d4bd97d6d60ed4f457cbcc307ce444f Mon Sep 17 00:00:00 2001 From: Sapphire Koser Date: Sat, 21 Dec 2024 17:25:46 -0800 Subject: [PATCH 05/10] cleanup: do not use goto in writeDevicesToFile --- src/deluge/io/midi/midi_device_manager.cpp | 33 +++++++++------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/deluge/io/midi/midi_device_manager.cpp b/src/deluge/io/midi/midi_device_manager.cpp index d93f729b31..d2134a9244 100644 --- a/src/deluge/io/midi/midi_device_manager.cpp +++ b/src/deluge/io/midi/midi_device_manager.cpp @@ -428,29 +428,24 @@ void writeDevicesToFile() { } anyChangesToSave = false; - // First, see if it's even worth writing anything - if (dinMIDIPorts.worthWritingToFile()) { - goto worthIt; - } - if (upstreamUSBMIDICable1.worthWritingToFile()) { - goto worthIt; - } - if (upstreamUSBMIDICable2.worthWritingToFile()) { - goto worthIt; - } - - for (int32_t d = 0; d < hostedMIDIDevices.getNumElements(); d++) { - MIDICableUSBHosted* device = (MIDICableUSBHosted*)hostedMIDIDevices.getElement(d); - if (device->worthWritingToFile()) { - goto worthIt; + bool anyWorthWritting = dinMIDIPorts.worthWritingToFile() || upstreamUSBMIDICable1.worthWritingToFile() + || upstreamUSBMIDICable2.worthWritingToFile(); + if (!anyWorthWritting) { + for (int32_t d = 0; d < hostedMIDIDevices.getNumElements(); d++) { + MIDICableUSBHosted* device = (MIDICableUSBHosted*)hostedMIDIDevices.getElement(d); + if (device->worthWritingToFile()) { + anyWorthWritting = true; + break; + } } } - // If still here, nothing worth writing. Delete the file if there was one. - f_unlink(MIDI_DEVICES_XML); // May give error, but no real consequence from that. - return; + if (!anyWorthWritting) { + // If still here, nothing worth writing. Delete the file if there was one. + f_unlink(MIDI_DEVICES_XML); // May give error, but no real consequence from that. + return; + } -worthIt: Error error = StorageManager::createXMLFile(MIDI_DEVICES_XML, smSerializer, true); if (error != Error::NONE) { return; From a30d8dc6e7d1186f13aaa40d9af66488d30c8151 Mon Sep 17 00:00:00 2001 From: Sapphire Koser Date: Tue, 24 Dec 2024 09:14:50 -0800 Subject: [PATCH 06/10] MIDICableUSB: fix send logic - it should actually send to the correct port now - it will no longer send to devices that can't be sent to --- src/deluge/io/midi/cable_types/usb_common.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/deluge/io/midi/cable_types/usb_common.cpp b/src/deluge/io/midi/cable_types/usb_common.cpp index 1c968f2ba0..e5be69e171 100644 --- a/src/deluge/io/midi/cable_types/usb_common.cpp +++ b/src/deluge/io/midi/cable_types/usb_common.cpp @@ -44,7 +44,10 @@ void MIDICableUSB::sendMessage(MIDIMessage message) { for (int32_t d = 0; d < MAX_NUM_USB_MIDI_DEVICES; d++) { if (connectionFlags & (1 << d)) { ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][d]; - connectedDevice->bufferMessage(fullMessage); + if (connectedDevice->canHaveMIDISent) { + uint32_t channeledMessage = fullMessage | (portNumber << 4); + connectedDevice->bufferMessage(fullMessage); + } } } } From 575ef19c2c98a72349b6bfd0dece63ecaf9fc738 Mon Sep 17 00:00:00 2001 From: Sapphire Koser Date: Sat, 21 Dec 2024 19:58:27 -0800 Subject: [PATCH 07/10] delete recastSpecificMidiDevice This function should be a noop since the relevant locations call through virtual function calls. --- .../device_specific/specific_midi_device.cpp | 43 +------------------ .../device_specific/specific_midi_device.h | 3 -- src/deluge/io/midi/midi_device_manager.cpp | 12 ++---- 3 files changed, 4 insertions(+), 54 deletions(-) diff --git a/src/deluge/io/midi/device_specific/specific_midi_device.cpp b/src/deluge/io/midi/device_specific/specific_midi_device.cpp index 5550a07c10..f3be4b8698 100644 --- a/src/deluge/io/midi/device_specific/specific_midi_device.cpp +++ b/src/deluge/io/midi/device_specific/specific_midi_device.cpp @@ -26,54 +26,13 @@ SpecificMidiDeviceType getSpecificMidiDeviceType(uint16_t vendorId, uint16_t pro return SpecificMidiDeviceType::NONE; } -MIDICableUSBHosted* recastSpecificMidiDevice(void* sourceDevice) { - return recastSpecificMidiDevice((MIDICableUSBHosted*)sourceDevice); -} - -/// @brief Recasts a MIDIDeviceUSBHosted pointer to a specific child device and back, to take advantage of virtual -/// functions -/// @param sourceDevice The known MIDIDeviceUSBHosted -/// @return A MIDIDeviceUSBHosted pointer, cast from Specific MIDI devices if found. -MIDICableUSBHosted* recastSpecificMidiDevice(MIDICableUSBHosted* sourceDevice) { - if (MIDIDeviceLumiKeys::matchesVendorProduct(sourceDevice->vendorId, sourceDevice->productId)) { - MIDIDeviceLumiKeys* targetDevice = (MIDIDeviceLumiKeys*)sourceDevice; - return targetDevice; - } - - MIDICableUSBHosted* targetDevice = sourceDevice; - return targetDevice; -} - -/// @brief When a MIDIDevice is known, this locates the matching MIDIDeviceUSBHosted based on connectionFlags -/// @param sourceDevice The known MIDIDevice -/// @return A MIDIDeviceUSBHosted pointer, cast from Specific MIDI devices if found. -MIDICableUSBHosted* getSpecificDeviceFromMIDICable(MIDICable& cable) { - using namespace MIDIDeviceManager; - // This depends on the MIDIDevice having been originally cast as something with a name - const char* sourceName = cable.getDisplayName(); - - if (cable.connectionFlags && sourceName) { - for (int32_t i = 0; i < hostedMIDIDevices.getNumElements(); i++) { - MIDICableUSBHosted* candidate = recastSpecificMidiDevice(hostedMIDIDevices.getElement(i)); - - const char* candidateName = candidate->getDisplayName(); - - if (sourceName == candidateName && candidate->connectionFlags == cable.connectionFlags) { - return candidate; - } - } - } - - return nullptr; -} - /// @brief Space-saving function to call a specific Hosted USB MIDI device's hook from any entry point /// @param hook void iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook hook) { using namespace MIDIDeviceManager; for (int32_t i = 0; i < hostedMIDIDevices.getNumElements(); i++) { - MIDICableUSBHosted* specificDevice = recastSpecificMidiDevice(hostedMIDIDevices.getElement(i)); + auto* specificDevice = static_cast(hostedMIDIDevices.getElement(i)); specificDevice->callHook(hook); } diff --git a/src/deluge/io/midi/device_specific/specific_midi_device.h b/src/deluge/io/midi/device_specific/specific_midi_device.h index fd7a4c189b..deb417e0c9 100644 --- a/src/deluge/io/midi/device_specific/specific_midi_device.h +++ b/src/deluge/io/midi/device_specific/specific_midi_device.h @@ -24,9 +24,6 @@ enum class SpecificMidiDeviceType { NONE = 0, LUMI_KEYS = 1 }; SpecificMidiDeviceType getSpecificMidiDeviceType(uint16_t vendorId, uint16_t productId); -MIDICableUSBHosted* recastSpecificMidiDevice(void* sourceDevice); -MIDICableUSBHosted* recastSpecificMidiDevice(MIDICableUSBHosted* sourceDevice); - MIDICableUSBHosted* getSpecificDeviceFromMIDICable(MIDICable& sourceDevice); void iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook hook); diff --git a/src/deluge/io/midi/midi_device_manager.cpp b/src/deluge/io/midi/midi_device_manager.cpp index d2134a9244..af6ee3e6e4 100644 --- a/src/deluge/io/midi/midi_device_manager.cpp +++ b/src/deluge/io/midi/midi_device_manager.cpp @@ -118,7 +118,7 @@ MIDICableUSBHosted* getOrCreateHostedMIDIDeviceFromDetails(String* name, uint16_ // If we'd already seen it before... if (foundExact) { - MIDICableUSBHosted* device = recastSpecificMidiDevice(hostedMIDIDevices.getElement(i)); + auto* device = static_cast(hostedMIDIDevices.getElement(i)); // Update vendor and product id, if we have those if (vendorId) { @@ -132,7 +132,7 @@ MIDICableUSBHosted* getOrCreateHostedMIDIDeviceFromDetails(String* name, uint16_ // Ok, try searching by vendor / product id for (int32_t i = 0; i < hostedMIDIDevices.getNumElements(); i++) { - MIDICableUSBHosted* candidate = recastSpecificMidiDevice(hostedMIDIDevices.getElement(i)); + auto* candidate = static_cast(hostedMIDIDevices.getElement(i)); if (candidate->vendorId == vendorId && candidate->productId == productId) { // Update its name - if we got one and it's different @@ -451,7 +451,6 @@ void writeDevicesToFile() { return; } - MIDICableUSBHosted* specificMIDIDevice = NULL; Serializer& writer = GetSerializer(); writer.writeOpeningTagBeginning("midiDevices"); writer.writeFirmwareVersion(); @@ -474,17 +473,12 @@ void writeDevicesToFile() { device->writeToFile(writer, "hostedUSBDevice"); } // Stow this for the hook point later - specificMIDIDevice = recastSpecificMidiDevice(device); + device->hookOnWriteHostedDeviceToFile(); } writer.writeClosingTag("midiDevices"); writer.closeFileAfterWriting(); - - // Hook point for Hosted USB MIDI Device - if (specificMIDIDevice != nullptr) { - specificMIDIDevice->hookOnWriteHostedDeviceToFile(); - } } bool successfullyReadDevicesFromFile = false; // We'll only do this one time From b0da70887bfbf7d433a504db56d24275455b5b7f Mon Sep 17 00:00:00 2001 From: bobtwinkles Date: Sat, 23 Nov 2024 22:26:27 -0800 Subject: [PATCH 08/10] MIDI: factor DIN midi root complex logic out --- src/definitions_cxx.hpp | 2 + src/deluge/gui/menu_item/midi/devices.cpp | 2 +- src/deluge/io/midi/cable_types/din.cpp | 114 +++++++++++++++- src/deluge/io/midi/cable_types/din.h | 13 +- src/deluge/io/midi/cable_types/usb_common.cpp | 18 ++- src/deluge/io/midi/cable_types/usb_common.h | 6 +- src/deluge/io/midi/midi_device.h | 4 +- src/deluge/io/midi/midi_device_manager.cpp | 18 +-- src/deluge/io/midi/midi_device_manager.h | 5 +- src/deluge/io/midi/midi_engine.cpp | 129 +++--------------- src/deluge/io/midi/midi_engine.h | 5 +- src/deluge/io/midi/midi_root_complex.h | 42 ++++++ src/deluge/io/midi/root_complex/din.cpp | 49 +++++++ src/deluge/io/midi/root_complex/din.h | 35 +++++ src/deluge/playback/playback_handler.cpp | 9 +- src/deluge/testing/hardware_testing.cpp | 3 +- 16 files changed, 307 insertions(+), 147 deletions(-) create mode 100644 src/deluge/io/midi/midi_root_complex.h create mode 100644 src/deluge/io/midi/root_complex/din.cpp create mode 100644 src/deluge/io/midi/root_complex/din.h diff --git a/src/definitions_cxx.hpp b/src/definitions_cxx.hpp index 5aa4301037..8f7d0725c8 100644 --- a/src/definitions_cxx.hpp +++ b/src/definitions_cxx.hpp @@ -489,6 +489,8 @@ enum class Error { INSUFFICIENT_RAM_FOR_FOLDER_CONTENTS_SIZE, SD_CARD_NOT_PRESENT, SD_CARD_NO_FILESYSTEM, + OUT_OF_BUFFER_SPACE, + INVALID_SYSEX_FORMAT, }; enum class SampleRepeatMode { diff --git a/src/deluge/gui/menu_item/midi/devices.cpp b/src/deluge/gui/menu_item/midi/devices.cpp index 0e5412df7e..9b3c826cdf 100644 --- a/src/deluge/gui/menu_item/midi/devices.cpp +++ b/src/deluge/gui/menu_item/midi/devices.cpp @@ -121,7 +121,7 @@ MIDICable* Devices::getCable(int32_t deviceIndex) { } switch (deviceIndex) { case -3: { - return &MIDIDeviceManager::dinMIDIPorts; + return &MIDIDeviceManager::rootDin.cable; } case -2: { return &MIDIDeviceManager::upstreamUSBMIDICable1; diff --git a/src/deluge/io/midi/cable_types/din.cpp b/src/deluge/io/midi/cable_types/din.cpp index 5e90dd2a8f..e6632a5869 100644 --- a/src/deluge/io/midi/cable_types/din.cpp +++ b/src/deluge/io/midi/cable_types/din.cpp @@ -15,6 +15,7 @@ * If not, see . */ +#include "io/debug/log.h" extern "C" { #include "RZA1/uart/sio_char.h" } @@ -38,23 +39,128 @@ char const* MIDICableDINPorts::getDisplayName() { return deluge::l10n::get(deluge::l10n::String::STRING_FOR_DIN_PORTS); } -void MIDICableDINPorts::sendMessage(MIDIMessage message) { - midiEngine.sendSerialMidi(message); +Error MIDICableDINPorts::sendMessage(MIDIMessage message) { + uint8_t statusByte = message.channel | (message.statusType << 4); + int32_t messageLength = bytesPerStatusMessage(statusByte); + + if (messageLength > sendBufferSpace()) { + return Error::OUT_OF_BUFFER_SPACE; + } + + bufferMIDIUart(statusByte); + + if (messageLength >= 2) { + bufferMIDIUart(message.data1); + + if (messageLength == 3) { + bufferMIDIUart(message.data2); + } + } + + return Error::NONE; } size_t MIDICableDINPorts::sendBufferSpace() const { return uartGetTxBufferSpace(UART_ITEM_MIDI); } -void MIDICableDINPorts::sendSysex(const uint8_t* data, int32_t len) { +Error MIDICableDINPorts::sendSysex(const uint8_t* data, int32_t len) { if (len < 3 || data[0] != 0xf0 || data[len - 1] != 0xf7) { - return; + return Error::OUT_OF_BUFFER_SPACE; + } + if (len > sendBufferSpace()) { + return Error::OUT_OF_BUFFER_SPACE; } // NB: beware of MIDI_TX_BUFFER_SIZE for (int32_t i = 0; i < len; i++) { bufferMIDIUart(data[i]); } + + return Error::NONE; +} + +Error MIDICableDINPorts::onReceiveByte(uint32_t timestamp, uint8_t thisSerialByte) { + // D_PRINTLN((uint32_t)thisSerialByte); + // If this is a status byte, then we have to store it as the first byte. + if (thisSerialByte & 0x80) { + + switch (thisSerialByte) { + + // If it's a realtime message, we have to obey it right now, separately from any other message it was + // inserted into the middle of + case 0xF8 ... 0xFF: + midiEngine.midiMessageReceived(*this, thisSerialByte >> 4, thisSerialByte & 0x0F, 0, 0, ×tamp); + return Error::NONE; + + // Or if it's a SysEx start... + case 0xF0: + currentlyReceivingSysex_ = true; + D_PRINTLN("Sysex start"); + incomingSysexBuffer[0] = thisSerialByte; + incomingSysexPos = 1; + // numSerialMidiInput = 0; // This would throw away any running status stuff... + return Error::NONE; + } + + // If we didn't return for any of those, then it's just a regular old status message (or Sysex stop + // message). All these will end any ongoing SysEx + + // If it was a Sysex stop, that's all we need to do + if (thisSerialByte == 0xF7) { + D_PRINTLN("Sysex end"); + if (currentlyReceivingSysex_) { + currentlyReceivingSysex_ = false; + if (incomingSysexPos < sizeof incomingSysexBuffer) { + incomingSysexBuffer[incomingSysexPos++] = thisSerialByte; + midiEngine.midiSysexReceived(*this, incomingSysexBuffer, incomingSysexPos); + } + } + return Error::NONE; + } + + currentlyReceivingSysex_ = false; + currentByte_ = 0; + } + + // If not a status byte... + else { + // If we're currently receiving a SysEx, don't throw it away + if (currentlyReceivingSysex_) { + // TODO: allocate a GMA buffer to some bigger size + if (incomingSysexPos < sizeof incomingSysexBuffer) { + incomingSysexBuffer[incomingSysexPos++] = thisSerialByte; + } + D_PRINTLN("Sysex: %d", thisSerialByte); + return Error::NONE; + } + + // Ensure that we're not writing to the first byte of the buffer + if (currentByte_ == 0) { + return Error::NONE; + } + } + + messageBytes_[currentByte_] = thisSerialByte; + currentByte_++; + + // If we've received the whole MIDI message, deal with it + if (bytesPerStatusMessage(messageBytes_[0]) == currentByte_) { + uint8_t type = (messageBytes_[0]) >> 4; + uint8_t channel = messageBytes_[0] & 0x0F; + + midiEngine.midiMessageReceived(*this, type, channel, messageBytes_[1], messageBytes_[2], ×tamp); + + // If message was more than 1 byte long, and was a voice or mode message, then allow for running status + if (currentByte_ > 1 && type != 0xF) { + currentByte_ = 1; + } + else { + currentByte_ = 0; + } + } + + return Error::NONE; } bool MIDICableDINPorts::wantsToOutputMIDIOnChannel(MIDIMessage message, int32_t filter) const { diff --git a/src/deluge/io/midi/cable_types/din.h b/src/deluge/io/midi/cable_types/din.h index af41f69aeb..abbd693588 100644 --- a/src/deluge/io/midi/cable_types/din.h +++ b/src/deluge/io/midi/cable_types/din.h @@ -20,18 +20,27 @@ #include "deluge/io/midi/midi_device.h" class MIDICableDINPorts final : public MIDICable { +private: + using IntermediateMessageBuffer = std::array; + IntermediateMessageBuffer messageBytes_; + IntermediateMessageBuffer::size_type currentByte_; + bool currentlyReceivingSysex_{false}; + public: MIDICableDINPorts() { connectionFlags = 1; // DIN ports are always connected } + /// Called by the DIN root complex when a byte is received + [[nodiscard]] Error onReceiveByte(uint32_t timestamp, uint8_t byte); + [[nodiscard]] bool wantsToOutputMIDIOnChannel(MIDIMessage message, int32_t filter) const override; void writeReferenceAttributesToFile(Serializer& writer) override; void writeToFlash(uint8_t* memory) override; char const* getDisplayName() override; - void sendMessage(MIDIMessage message) override; - void sendSysex(const uint8_t* data, int32_t len) override; + [[nodiscard]] Error sendMessage(MIDIMessage message) override; + [[nodiscard]] Error sendSysex(const uint8_t* data, int32_t len) override; size_t sendBufferSpace() const override; }; diff --git a/src/deluge/io/midi/cable_types/usb_common.cpp b/src/deluge/io/midi/cable_types/usb_common.cpp index e5be69e171..46f6829c6c 100644 --- a/src/deluge/io/midi/cable_types/usb_common.cpp +++ b/src/deluge/io/midi/cable_types/usb_common.cpp @@ -32,9 +32,9 @@ void MIDICableUSB::sendMCMsNowIfNeeded() { } } -void MIDICableUSB::sendMessage(MIDIMessage message) { +Error MIDICableUSB::sendMessage(MIDIMessage message) { if (!connectionFlags) { - return; + return Error::NONE; } int32_t ip = 0; @@ -50,6 +50,8 @@ void MIDICableUSB::sendMessage(MIDIMessage message) { } } } + + return Error::NONE; } size_t MIDICableUSB::sendBufferSpace() const { @@ -74,9 +76,13 @@ size_t MIDICableUSB::sendBufferSpace() const { extern bool developerSysexCodeReceived; -void MIDICableUSB::sendSysex(const uint8_t* data, int32_t len) { +Error MIDICableUSB::sendSysex(const uint8_t* data, int32_t len) { if (len < 6 || data[0] != 0xf0 || data[len - 1] != 0xf7) { - return; + return Error::INVALID_SYSEX_FORMAT; + } + + if (len > sendBufferSpace()) { + return Error::OUT_OF_BUFFER_SPACE; } int32_t ip = 0; @@ -92,7 +98,7 @@ void MIDICableUSB::sendSysex(const uint8_t* data, int32_t len) { } if (!connectedDevice) { - return; + return Error::NONE; } int32_t pos = 0; @@ -132,6 +138,8 @@ void MIDICableUSB::sendSysex(const uint8_t* data, int32_t len) { uint32_t packed = ((uint32_t)byte2 << 24) | ((uint32_t)byte1 << 16) | ((uint32_t)byte0 << 8) | status; connectedDevice->bufferMessage(packed); } + + return Error::NONE; } bool MIDICableUSB::wantsToOutputMIDIOnChannel(MIDIMessage message, int32_t filter) const { diff --git a/src/deluge/io/midi/cable_types/usb_common.h b/src/deluge/io/midi/cable_types/usb_common.h index ebeaed9e5e..84a6f0668b 100644 --- a/src/deluge/io/midi/cable_types/usb_common.h +++ b/src/deluge/io/midi/cable_types/usb_common.h @@ -28,9 +28,9 @@ class MIDICableUSB : public MIDICable { [[nodiscard]] bool wantsToOutputMIDIOnChannel(MIDIMessage message, int32_t filter) const override; - void sendMessage(MIDIMessage message) override; - void sendSysex(const uint8_t* data, int32_t len) override; - size_t sendBufferSpace() const override; + [[nodiscard]] Error sendMessage(MIDIMessage message) override; + [[nodiscard]] Error sendSysex(const uint8_t* data, int32_t len) override; + [[nodiscard]] size_t sendBufferSpace() const override; void connectedNow(int32_t midiDeviceNum); void sendMCMsNowIfNeeded(); diff --git a/src/deluge/io/midi/midi_device.h b/src/deluge/io/midi/midi_device.h index b59c334dd7..1aba6abaf1 100644 --- a/src/deluge/io/midi/midi_device.h +++ b/src/deluge/io/midi/midi_device.h @@ -115,13 +115,13 @@ class MIDICable { /// @{ /// Send a MIDI message - virtual void sendMessage(MIDIMessage message) = 0; + [[nodiscard]] virtual Error sendMessage(MIDIMessage message) = 0; /// Send a chunk of SYSEX data. /// /// @param data Data to send. Should include the 0xf0 and 0xf7 start/stop bytes. /// @param len Number of bytes in data, including the start/stop bytes. - virtual void sendSysex(const uint8_t* data, int32_t len) = 0; + [[nodiscard]] virtual Error sendSysex(const uint8_t* data, int32_t len) = 0; /// Get the number of bytes available in the send buffer. [[nodiscard]] virtual size_t sendBufferSpace() const = 0; diff --git a/src/deluge/io/midi/midi_device_manager.cpp b/src/deluge/io/midi/midi_device_manager.cpp index af6ee3e6e4..1c2f9916f8 100644 --- a/src/deluge/io/midi/midi_device_manager.cpp +++ b/src/deluge/io/midi/midi_device_manager.cpp @@ -20,7 +20,6 @@ #include "gui/menu_item/mpe/zone_num_member_channels.h" #include "gui/ui/sound_editor.h" #include "hid/display/display.h" -#include "io/midi/cable_types/din.h" #include "io/midi/cable_types/usb_device_cable.h" #include "io/midi/device_specific/specific_midi_device.h" #include "io/midi/midi_device.h" @@ -64,7 +63,8 @@ std::array usbDeviceCurrentlyBeingSetUp{}; MIDICableUSBUpstream upstreamUSBMIDICable1{0}; MIDICableUSBUpstream upstreamUSBMIDICable2{1}; MIDICableUSBUpstream upstreamUSBMIDICable3{2}; -MIDICableDINPorts dinMIDIPorts{}; + +DINRootComplex rootDin{}; uint8_t lowestLastMemberChannelOfLowerZoneOnConnectedOutput = 15; uint8_t highestLastMemberChannelOfUpperZoneOnConnectedOutput = 0; @@ -215,7 +215,7 @@ void recountSmallestMPEZones() { recountSmallestMPEZonesForCable(upstreamUSBMIDICable1); recountSmallestMPEZonesForCable(upstreamUSBMIDICable2); - recountSmallestMPEZonesForCable(dinMIDIPorts); + recountSmallestMPEZonesForCable(rootDin.cable); for (int32_t d = 0; d < hostedMIDIDevices.getNumElements(); d++) { MIDICableUSBHosted* cable = (MIDICableUSBHosted*)hostedMIDIDevices.getElement(d); @@ -355,7 +355,7 @@ MIDICable* readDeviceReferenceFromFile(Deserializer& reader) { device = &upstreamUSBMIDICable3; } else if (!strcmp(port, "din")) { - device = &dinMIDIPorts; + device = &rootDin.cable; } } @@ -392,7 +392,7 @@ static MIDICable* readCableFromFlash(uint8_t const* memory) { cable = &upstreamUSBMIDICable3; } else if (vendorId == VENDOR_ID_DIN) { - cable = &dinMIDIPorts; + cable = &rootDin.cable; } else { uint16_t productId = *(uint16_t const*)(memory + 2); @@ -428,7 +428,7 @@ void writeDevicesToFile() { } anyChangesToSave = false; - bool anyWorthWritting = dinMIDIPorts.worthWritingToFile() || upstreamUSBMIDICable1.worthWritingToFile() + bool anyWorthWritting = rootDin.cable.worthWritingToFile() || upstreamUSBMIDICable1.worthWritingToFile() || upstreamUSBMIDICable2.worthWritingToFile(); if (!anyWorthWritting) { for (int32_t d = 0; d < hostedMIDIDevices.getNumElements(); d++) { @@ -457,8 +457,8 @@ void writeDevicesToFile() { writer.writeEarliestCompatibleFirmwareVersion("4.0.0"); writer.writeOpeningTagEnd(); - if (dinMIDIPorts.worthWritingToFile()) { - dinMIDIPorts.writeToFile(writer, "dinPorts"); + if (rootDin.cable.worthWritingToFile()) { + rootDin.cable.writeToFile(writer, "dinPorts"); } if (upstreamUSBMIDICable1.worthWritingToFile()) { upstreamUSBMIDICable1.writeToFile(writer, "upstreamUSBDevice"); @@ -516,7 +516,7 @@ void readDevicesFromFile() { char const* tagName; while (*(tagName = reader.readNextTagOrAttributeName())) { if (!strcmp(tagName, "dinPorts")) { - dinMIDIPorts.readFromFile(reader); + rootDin.cable.readFromFile(reader); } else if (!strcmp(tagName, "upstreamUSBDevice")) { upstreamUSBMIDICable1.readFromFile(reader); diff --git a/src/deluge/io/midi/midi_device_manager.h b/src/deluge/io/midi/midi_device_manager.h index 1d9aa5e45b..83cfdbf96d 100644 --- a/src/deluge/io/midi/midi_device_manager.h +++ b/src/deluge/io/midi/midi_device_manager.h @@ -18,9 +18,9 @@ #pragma once #ifdef __cplusplus #include "definitions_cxx.hpp" -#include "io/midi/cable_types/din.h" #include "io/midi/cable_types/usb_common.h" #include "io/midi/cable_types/usb_device_cable.h" +#include "io/midi/root_complex/din.h" #include "util/container/vector/named_thing_vector.h" class Serializer; class Deserializer; @@ -114,7 +114,8 @@ void readDevicesFromFile(); extern MIDICableUSBUpstream upstreamUSBMIDICable1; extern MIDICableUSBUpstream upstreamUSBMIDICable2; extern MIDICableUSBUpstream upstreamUSBMIDICable3; -extern MIDICableDINPorts dinMIDIPorts; + +extern DINRootComplex rootDin; extern bool differentiatingInputsByDevice; diff --git a/src/deluge/io/midi/midi_engine.cpp b/src/deluge/io/midi/midi_engine.cpp index feb675ae76..913181f3c4 100644 --- a/src/deluge/io/midi/midi_engine.cpp +++ b/src/deluge/io/midi/midi_engine.cpp @@ -514,8 +514,12 @@ void MidiEngine::sendMidi(MIDISource source, MIDIMessage message, int32_t filter } // Send serial MIDI - if (MIDIDeviceManager::dinMIDIPorts.wantsToOutputMIDIOnChannel(message, filter)) { - sendSerialMidi(message); + auto& dinCable = MIDIDeviceManager::rootDin.cable; + if (dinCable.wantsToOutputMIDIOnChannel(message, filter)) { + auto error = dinCable.sendMessage(message); + if (error != Error::NONE && error != Error::NO_ERROR_BUT_GET_OUT) { + D_PRINTLN("MIDI send error: %d", static_cast(error)); + } } --eventStackTop_; @@ -573,115 +577,26 @@ void MidiEngine::sendUsbMidi(MIDIMessage message, int32_t filter) { } } -// Warning - this will sometimes (not always) be called in an ISR -void MidiEngine::flushMIDI() { - flushUSBMIDIOutput(); - uartFlushIfNotSending(UART_ITEM_MIDI); -} +void MidiEngine::checkIncomingMidi() { + // Check incoming USB MIDI + midiEngine.checkIncomingUsbMidi(); -void MidiEngine::sendSerialMidi(MIDIMessage message) { - - uint8_t statusByte = message.channel | (message.statusType << 4); - int32_t messageLength = bytesPerStatusMessage(statusByte); - bufferMIDIUart(statusByte); - - if (messageLength >= 2) { - bufferMIDIUart(message.data1); - - if (messageLength == 3) { - bufferMIDIUart(message.data2); + // Check incoming Serial MIDI + for (int32_t i = 0; i < 12; i++) { + auto error = MIDIDeviceManager::rootDin.poll(); + if (error == Error::NO_ERROR_BUT_GET_OUT) { + break; + } + else if (error != Error::NONE) { + D_PRINTLN("MIDI poll error: %d\n", static_cast(error)); } } } -bool MidiEngine::checkIncomingSerialMidi() { - - uint8_t thisSerialByte; - uint32_t* timer = uartGetCharWithTiming(TIMING_CAPTURE_ITEM_MIDI, (char*)&thisSerialByte); - if (timer) { - // D_PRINTLN((uint32_t)thisSerialByte); - MIDICable& cable = MIDIDeviceManager::dinMIDIPorts; - - // If this is a status byte, then we have to store it as the first byte. - if (thisSerialByte & 0x80) { - - switch (thisSerialByte) { - - // If it's a realtime message, we have to obey it right now, separately from any other message it was - // inserted into the middle of - case 0xF8 ... 0xFF: - midiMessageReceived(cable, thisSerialByte >> 4, thisSerialByte & 0x0F, 0, 0, timer); - return true; - - // Or if it's a SysEx start... - case 0xF0: - currentlyReceivingSysExSerial = true; - D_PRINTLN("Sysex start"); - cable.incomingSysexBuffer[0] = thisSerialByte; - cable.incomingSysexPos = 1; - // numSerialMidiInput = 0; // This would throw away any running status stuff... - return true; - } - - // If we didn't return for any of those, then it's just a regular old status message (or Sysex stop - // message). All these will end any ongoing SysEx - - // If it was a Sysex stop, that's all we need to do - if (thisSerialByte == 0xF7) { - D_PRINTLN("Sysex end"); - if (currentlyReceivingSysExSerial) { - currentlyReceivingSysExSerial = false; - if (cable.incomingSysexPos < sizeof cable.incomingSysexBuffer) { - cable.incomingSysexBuffer[cable.incomingSysexPos++] = thisSerialByte; - midiSysexReceived(cable, cable.incomingSysexBuffer, cable.incomingSysexPos); - } - } - return true; - } - - currentlyReceivingSysExSerial = false; - numSerialMidiInput = 0; - } - - // If not a status byte... - else { - // If we're currently receiving a SysEx, don't throw it away - if (currentlyReceivingSysExSerial) { - // TODO: allocate a GMA buffer to some bigger size - if (cable.incomingSysexPos < sizeof cable.incomingSysexBuffer) { - cable.incomingSysexBuffer[cable.incomingSysexPos++] = thisSerialByte; - } - D_PRINTLN("Sysex: %d", thisSerialByte); - return true; - } - - // Ensure that we're not writing to the first byte of the buffer - if (numSerialMidiInput == 0) { - return true; - } - } - - serialMidiInput[numSerialMidiInput] = thisSerialByte; - numSerialMidiInput++; - - // If we've received the whole MIDI message, deal with it - if (bytesPerStatusMessage(serialMidiInput[0]) == numSerialMidiInput) { - uint8_t channel = serialMidiInput[0] & 0x0F; - - midiMessageReceived(MIDIDeviceManager::dinMIDIPorts, serialMidiInput[0] >> 4, channel, serialMidiInput[1], - serialMidiInput[2], timer); - - // If message was more than 1 byte long, and was a voice or mode message, then allow for running status - if (numSerialMidiInput > 1 && ((serialMidiInput[0] & 0xF0) != 0xF0)) { - numSerialMidiInput = 1; - } - else { - numSerialMidiInput = 0; - } - } - return true; - } - return false; +// Warning - this will sometimes (not always) be called in an ISR +void MidiEngine::flushMIDI() { + flushUSBMIDIOutput(); + MIDIDeviceManager::rootDin.flush(); } // Lock USB before calling this! @@ -1062,7 +977,7 @@ void MidiEngine::midiMessageReceived(MIDICable& cable, uint8_t statusType, uint8 // other messages, rather than using our special clock-specific system if (shouldDoMidiThruNow) { // Only send out on USB if it didn't originate from USB - bool shouldSendUSB = (&cable == &MIDIDeviceManager::dinMIDIPorts); + bool shouldSendUSB = (&cable == &MIDIDeviceManager::rootDin.cable); // TODO: reconsider interaction with MPE? sendMidi(cable, MIDIMessage{ diff --git a/src/deluge/io/midi/midi_engine.h b/src/deluge/io/midi/midi_engine.h index 404b4d01d3..1cba511677 100644 --- a/src/deluge/io/midi/midi_engine.h +++ b/src/deluge/io/midi/midi_engine.h @@ -64,9 +64,8 @@ class MidiEngine { void sendNote(MIDISource source, bool on, int32_t note, uint8_t velocity, uint8_t channel, int32_t filter); void sendCC(MIDISource source, int32_t channel, int32_t cc, int32_t value, int32_t filter); - bool checkIncomingSerialMidi(); - void checkIncomingUsbMidi(); + void checkIncomingUsbMidi(); void checkIncomingUsbSysex(uint8_t const* message, int32_t ip, int32_t d, int32_t cable); void sendMidi(MIDISource source, MIDIMessage message, int32_t filter = kMIDIOutputFilterNoMPE, bool sendUSB = true); @@ -76,9 +75,9 @@ class MidiEngine { void sendPositionPointer(MIDISource source, uint16_t positionPointer); void sendContinue(MIDISource source); + void checkIncomingMidi(); void flushMIDI(); void sendUsbMidi(MIDIMessage message, int32_t filter); - void sendSerialMidi(MIDIMessage message); void sendPGMChange(MIDISource source, int32_t channel, int32_t pgm, int32_t filter); void sendAllNotesOff(MIDISource source, int32_t channel, int32_t filter); diff --git a/src/deluge/io/midi/midi_root_complex.h b/src/deluge/io/midi/midi_root_complex.h new file mode 100644 index 0000000000..3f061970d5 --- /dev/null +++ b/src/deluge/io/midi/midi_root_complex.h @@ -0,0 +1,42 @@ +/* + * Copyright © 2024 Synthstrom Audible Limited + * + * This file is part of The Synthstrom Audible Deluge Firmware. + * + * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#pragma once + +#include "definitions_cxx.hpp" + +/// Represents a group of cables we can do I/O on. +/// +/// The name is meant to be analogous to a "root complex" in a PCIe device hierarchy, which represents the physical +/// bridge to the PCIe bus by the bus controller. Similarly, this class represents the interface from the Deluge +/// software to a MIDI device connection. +class MIDIRootComplex { +public: + MIDIRootComplex() = default; + MIDIRootComplex(const MIDIRootComplex&) = delete; + MIDIRootComplex(MIDIRootComplex&&) = delete; + MIDIRootComplex& operator=(const MIDIRootComplex&) = delete; + MIDIRootComplex& operator=(MIDIRootComplex&&) = delete; + + virtual ~MIDIRootComplex() = default; + + /// Flush as much data as possible from any internal buffers to hardware queues + virtual void flush() = 0; + + /// Poll the root complex, calling back in to the MIDI engine for any new messages. + [[nodiscard]] virtual Error poll() = 0; +}; diff --git a/src/deluge/io/midi/root_complex/din.cpp b/src/deluge/io/midi/root_complex/din.cpp new file mode 100644 index 0000000000..978ff5a37b --- /dev/null +++ b/src/deluge/io/midi/root_complex/din.cpp @@ -0,0 +1,49 @@ +/* + * Copyright © 2024 Synthstrom Audible Limited + * + * This file is part of The Synthstrom Audible Deluge Firmware. + * + * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#include "din.h" + +#include "io/debug/log.h" +#include "io/midi/midi_device_manager.h" +#include "io/midi/midi_engine.h" + +extern "C" { +#include "RZA1/uart/sio_char.h" +} + +DINRootComplex::DINRootComplex() = default; +DINRootComplex::~DINRootComplex() = default; + +void DINRootComplex::flush() { + uartFlushIfNotSending(UART_ITEM_MIDI); +} + +Error DINRootComplex::poll() { + uint8_t thisSerialByte; + uint32_t* timer = uartGetCharWithTiming(TIMING_CAPTURE_ITEM_MIDI, (char*)&thisSerialByte); + + while (timer != nullptr) { + auto err = cable.onReceiveByte(*timer, thisSerialByte); + if (err != Error::NONE) { + return err; + } + + timer = uartGetCharWithTiming(TIMING_CAPTURE_ITEM_MIDI, (char*)&thisSerialByte); + } + + return Error::NO_ERROR_BUT_GET_OUT; +} diff --git a/src/deluge/io/midi/root_complex/din.h b/src/deluge/io/midi/root_complex/din.h new file mode 100644 index 0000000000..6aad30ce17 --- /dev/null +++ b/src/deluge/io/midi/root_complex/din.h @@ -0,0 +1,35 @@ +/* + * Copyright © 2024 Synthstrom Audible Limited + * + * This file is part of The Synthstrom Audible Deluge Firmware. + * + * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#pragma once + +#include "io/midi/cable_types/din.h" +#include "io/midi/midi_root_complex.h" +#include + +class DINRootComplex : public MIDIRootComplex { +private: +public: + /// The one and only DIN cable connection + MIDICableDINPorts cable; + + DINRootComplex(); + ~DINRootComplex() override; + + void flush() override; + [[nodiscard]] Error poll() override; +}; diff --git a/src/deluge/playback/playback_handler.cpp b/src/deluge/playback/playback_handler.cpp index 6876dfd403..2fe1b974af 100644 --- a/src/deluge/playback/playback_handler.cpp +++ b/src/deluge/playback/playback_handler.cpp @@ -122,14 +122,7 @@ extern "C" uint32_t triggerClockRisingEdgesProcessed; // This function will be called repeatedly, at all times, to see if it's time to do a tick, and such void PlaybackHandler::routine() { - - // Check incoming USB MIDI - midiEngine.checkIncomingUsbMidi(); - - // Check incoming Serial MIDI - for (int32_t i = 0; i < 12 && midiEngine.checkIncomingSerialMidi(); i++) { - ; - } + midiEngine.checkIncomingMidi(); // Check analog clock input if (triggerClockRisingEdgesProcessed != triggerClockRisingEdgesReceived) { diff --git a/src/deluge/testing/hardware_testing.cpp b/src/deluge/testing/hardware_testing.cpp index c208613a85..be099220f5 100644 --- a/src/deluge/testing/hardware_testing.cpp +++ b/src/deluge/testing/hardware_testing.cpp @@ -27,6 +27,7 @@ #include "hid/led/indicator_leds.h" #include "hid/matrix/matrix_driver.h" #include "io/debug/log.h" +#include "io/midi/midi_device_manager.h" #include "io/midi/midi_engine.h" #include "processing/engines/audio_engine.h" #include "processing/engines/cv_engine.h" @@ -197,7 +198,7 @@ void readInputsForHardwareTest(bool testButtonStates[9][16]) { } } - midiEngine.checkIncomingSerialMidi(); + auto _ = MIDIDeviceManager::rootDin.poll(); midiEngine.flushMIDI(); encoders::readEncoders(); From a559514f49cd18c0e51110133d17d4bcb0a74364 Mon Sep 17 00:00:00 2001 From: Sapphire Koser Date: Fri, 6 Dec 2024 18:06:40 -0800 Subject: [PATCH 09/10] Move USB data to its own module Also introduces an RAII version of the USB lock --- .../r_usb_basic/src/driver/r_usb_hlibusbip.c | 8 +- .../usb/r_usb_hmidi/src/r_usb_hmidi_driver.c | 3 +- src/deluge/deluge.cpp | 29 ++++--- src/deluge/io/midi/midi_device_manager.cpp | 7 +- src/deluge/io/midi/midi_engine.cpp | 41 +++------- src/deluge/io/midi/midi_engine.h | 1 - src/deluge/io/usb/usb_state.cpp | 35 +++++++++ src/deluge/io/usb/usb_state.h | 76 +++++++++++++++++++ 8 files changed, 143 insertions(+), 57 deletions(-) create mode 100644 src/deluge/io/usb/usb_state.cpp create mode 100644 src/deluge/io/usb/usb_state.h diff --git a/src/RZA1/usb/r_usb_basic/src/driver/r_usb_hlibusbip.c b/src/RZA1/usb/r_usb_basic/src/driver/r_usb_hlibusbip.c index 0f8612c6bd..1ff3a82ff3 100644 --- a/src/RZA1/usb/r_usb_basic/src/driver/r_usb_hlibusbip.c +++ b/src/RZA1/usb/r_usb_basic/src/driver/r_usb_hlibusbip.c @@ -38,10 +38,10 @@ #include "RZA1/usb/userdef/r_usb_hmidi_config.h" #include "definitions.h" -#include "deluge/io/midi/midi_device_manager.h" - #include "deluge/drivers/uart/uart.h" +#include "deluge/io/midi/midi_device_manager.h" #include "deluge/io/midi/midi_engine.h" +#include "deluge/io/usb/usb_state.h" #if ((USB_CFG_DTC == USB_CFG_ENABLE) || (USB_CFG_DMA == USB_CFG_ENABLE)) #include "drivers/usb/r_usb_basic/src/hw/inc/r_usb_dmac.h" @@ -954,7 +954,7 @@ void usb_hstd_receive_start(usb_utr_t* ptr, uint16_t pipe) // I now just call the PSTD one instead of this - it does the same. /*********************************************************************************************************************** Function Name : usb_hstd_read_data - Description : Request to read data from USB FIFO, and manage the size of + Description : Request to read data from USB FIFO, and manage the size of : the data read. Arguments : usb_utr_t *ptr : Pointer to usb_utr_t structure. : uint16_t pipe : Pipe number. @@ -1186,8 +1186,6 @@ void usb_hstd_data_end(usb_utr_t* ptr, uint16_t pipe, uint16_t status) } } /* End of function usb_hstd_data_end() */ -extern usb_utr_t g_usb_midi_recv_utr[][MAX_NUM_USB_MIDI_DEVICES]; - // For when data has been received, as host. Hub stuff is on PIPE9 void usb_hstd_brdy_pipe_process_rohan_midi_and_hub(usb_utr_t* ptr, uint16_t bitsts) { diff --git a/src/RZA1/usb/r_usb_hmidi/src/r_usb_hmidi_driver.c b/src/RZA1/usb/r_usb_hmidi/src/r_usb_hmidi_driver.c index 2870fa1305..7333c1e4fe 100644 --- a/src/RZA1/usb/r_usb_hmidi/src/r_usb_hmidi_driver.c +++ b/src/RZA1/usb/r_usb_hmidi/src/r_usb_hmidi_driver.c @@ -40,6 +40,7 @@ #include "deluge/deluge.h" #include "deluge/drivers/uart/uart.h" +#include "deluge/io/usb/usb_state.h" /****************************************************************************** Exported global variables @@ -61,8 +62,6 @@ static void usb_hmidi_check_result(usb_utr_t* ptr, uint16_t unused, uint16_t sta static usb_er_t usb_hhid_data_trans(usb_utr_t* ptr, uint16_t pipe, uint32_t size, uint8_t* table, usb_cb_t complete); static void usb_hmidi_enumeration_sequence(usb_utr_t* mess); -extern uint8_t currentDeviceNumWithSendPipe[]; - /****************************************************************************** Exported global variables (to be accessed by other files) ******************************************************************************/ diff --git a/src/deluge/deluge.cpp b/src/deluge/deluge.cpp index debdd52c21..5dffdeaa40 100644 --- a/src/deluge/deluge.cpp +++ b/src/deluge/deluge.cpp @@ -19,6 +19,7 @@ #include "RZA1/sdhi/inc/sdif.h" #include "definitions_cxx.hpp" +#include "deluge/io/usb//usb_state.h" #include "drivers/pic/pic.h" #include "gui/ui/audio_recorder.h" #include "gui/ui/browser/browser.h" @@ -549,8 +550,6 @@ void setupOLED() { extern "C" void usb_pstd_pcd_task(void); extern "C" void usb_cstd_usb_task(void); -extern "C" volatile uint32_t usbLock; - extern "C" void usb_main_host(void); void registerTasks() { @@ -843,21 +842,21 @@ extern "C" int32_t deluge_main(void) { deluge::hid::display::swapDisplayType(); } - usbLock = 1; - openUSBHost(); - - // If nothing was plugged in to us as host, we'll go peripheral - // Ideally I'd like to repeatedly switch between host and peripheral mode anytime there's no USB connection. - // To do that, I'd really need to know at any point in time whether the user had just made a connection, just then, - // that hadn't fully initialized yet. I think I sorta have that for host, but not for peripheral yet. - if (!anythingInitiallyAttachedAsUSBHost) { - D_PRINTLN("switching from host to peripheral"); - closeUSBHost(); - openUSBPeripheral(); + { + deluge::io::usb::USBAutoLock lock; + openUSBHost(); + + // If nothing was plugged in to us as host, we'll go peripheral + // Ideally I'd like to repeatedly switch between host and peripheral mode anytime there's no USB connection. + // To do that, I'd really need to know at any point in time whether the user had just made a connection, just + // then, that hadn't fully initialized yet. I think I sorta have that for host, but not for peripheral yet. + if (!anythingInitiallyAttachedAsUSBHost) { + D_PRINTLN("switching from host to peripheral"); + closeUSBHost(); + openUSBPeripheral(); + } } - usbLock = 0; - // Hopefully we can read these files now runtimeFeatureSettings.readSettingsFromFile(); MIDIDeviceManager::readDevicesFromFile(); diff --git a/src/deluge/io/midi/midi_device_manager.cpp b/src/deluge/io/midi/midi_device_manager.cpp index 1c2f9916f8..57d5d5c520 100644 --- a/src/deluge/io/midi/midi_device_manager.cpp +++ b/src/deluge/io/midi/midi_device_manager.cpp @@ -24,17 +24,18 @@ #include "io/midi/device_specific/specific_midi_device.h" #include "io/midi/midi_device.h" #include "io/midi/midi_engine.h" +#include "io/usb/usb_state.h" #include "mem_functions.h" #include "memory/general_memory_allocator.h" #include "storage/storage_manager.h" #include "util/container/vector/named_thing_vector.h" #include "util/misc.h" +using namespace deluge::io::usb; + extern "C" { #include "RZA1/usb/r_usb_basic/src/driver/inc/r_usb_basic_define.h" #include "drivers/uart/uart.h" - -extern uint8_t anyUSBSendingStillHappening[]; } #pragma GCC diagnostic push // This is supported by GCC and other compilers should error (not warn), so turn off for this file @@ -636,7 +637,7 @@ void ConnectedUSBMIDIDevice::bufferMessage(uint32_t fullMessage) { sendDataRingBuf[ringBufWriteIdx & MIDI_SEND_RING_MASK] = fullMessage; ringBufWriteIdx++; - anythingInUSBOutputBuffer = true; + deluge::io::usb::anythingInUSBOutputBuffer = true; } bool ConnectedUSBMIDIDevice::hasBufferedSendData() { diff --git a/src/deluge/io/midi/midi_engine.cpp b/src/deluge/io/midi/midi_engine.cpp index 913181f3c4..474d90b3aa 100644 --- a/src/deluge/io/midi/midi_engine.cpp +++ b/src/deluge/io/midi/midi_engine.cpp @@ -17,6 +17,7 @@ #include "io/midi/midi_engine.h" #include "definitions_cxx.hpp" +#include "deluge/io/usb/usb_state.h" #include "gui/l10n/l10n.h" #include "gui/ui/sound_editor.h" #include "hid/display/display.h" @@ -32,10 +33,11 @@ #include "storage/smsysex.h" #include "version.h" +using namespace deluge::io::usb; + extern "C" { #include "RZA1/uart/sio_char.h" -volatile uint32_t usbLock = 0; void usb_cstd_usb_task(); #include "RZA1/system/iodefine.h" @@ -46,19 +48,6 @@ void usb_cstd_usb_task(); #include "RZA1/usb/r_usb_hmidi/src/inc/r_usb_hmidi.h" #include "RZA1/usb/userdef/r_usb_hmidi_config.h" -extern uint16_t g_usb_peri_connected; - -uint8_t stopSendingAfterDeviceNum[USB_NUM_USBIP]; -uint8_t usbDeviceNumBeingSentToNow[USB_NUM_USBIP]; -uint8_t anyUSBSendingStillHappening[USB_NUM_USBIP]; - -usb_utr_t g_usb_midi_send_utr[USB_NUM_USBIP]; -usb_utr_t g_usb_midi_recv_utr[USB_NUM_USBIP][MAX_NUM_USB_MIDI_DEVICES]; - -extern uint16_t g_usb_hmidi_tmp_ep_tbl[USB_NUM_USBIP][MAX_NUM_USB_MIDI_DEVICES][(USB_EPL * 2) + 1]; - -extern usb_utr_t* g_p_usb_pipe[USB_MAX_PIPE_NO + 1u]; - usb_regadr_t usb_hstd_get_usb_ip_adr(uint16_t ipno); void change_destination_of_send_pipe(usb_utr_t* ptr, uint16_t pipe, uint16_t* tbl, int32_t sq); void usb_send_start_rohan(usb_utr_t* ptr, uint16_t pipe, uint8_t const* data, int32_t size); @@ -70,9 +59,6 @@ uint16_t hw_usb_read_pipectr(usb_utr_t* ptr, uint16_t pipeno); void flushUSBMIDIToHostedDevice(int32_t ip, int32_t d, bool resume = false); -uint8_t currentDeviceNumWithSendPipe[USB_NUM_USBIP][2] = { - MAX_NUM_USB_MIDI_DEVICES, MAX_NUM_USB_MIDI_DEVICES}; // One without, and one with, interrupt endpoints - // We now bypass calling this for successful as peripheral on A1 (see usb_pstd_bemp_pipe_process_rohan_midi()) void usbSendCompleteAsHost(int32_t ip) { @@ -211,8 +197,6 @@ void brdyOccurred(int32_t ip) { MidiEngine midiEngine{}; -bool anythingInUSBOutputBuffer = false; - MidiEngine::MidiEngine() { numSerialMidiInput = 0; currentlyReceivingSysExSerial = false; @@ -273,8 +257,8 @@ void flushUSBMIDIToHostedDevice(int32_t ip, int32_t d, bool resume) { int32_t isInterrupt = (pipeNumber == USB_CFG_HMIDI_INT_SEND); - if (d != currentDeviceNumWithSendPipe[USB_CFG_USE_USBIP][isInterrupt]) { - currentDeviceNumWithSendPipe[USB_CFG_USE_USBIP][isInterrupt] = d; + if (d != currentDeviceNumWithSendPipe[isInterrupt]) { + currentDeviceNumWithSendPipe[isInterrupt] = d; change_destination_of_send_pipe(&g_usb_midi_send_utr[USB_CFG_USE_USBIP], pipeNumber, g_usb_hmidi_tmp_ep_tbl[USB_CFG_USE_USBIP][d], connectedDevice->sq); } @@ -305,7 +289,7 @@ void MidiEngine::flushUSBMIDIOutput() { // is this still relevant? anyUSBSendingStillHappening[ip] acts as the lock between routine and interrupt // on the sending side. all other uses of usbLock seems to be about _receiving_. Can there be a conflict // between sending and receiving as well?? - usbLock = 1; + USBAutoLock lock; for (int32_t ip = 0; ip < USB_NUM_USBIP; ip++) { if (anyUSBSendingStillHappening[ip]) { @@ -342,7 +326,7 @@ void MidiEngine::flushUSBMIDIOutput() { // This next bit was written with multiple devices on hubs in mind, but seems to work for a single MIDI // device too - int32_t midiDeviceNumToSendTo = currentDeviceNumWithSendPipe[ip][0]; // This will do + int32_t midiDeviceNumToSendTo = currentDeviceNumWithSendPipe[0]; // This will do if (midiDeviceNumToSendTo >= MAX_NUM_USB_MIDI_DEVICES) { midiDeviceNumToSendTo = 0; // In case it was set to "none", I think } @@ -405,8 +389,6 @@ void MidiEngine::flushUSBMIDIOutput() { } getOut: {} } - - usbLock = 0; } bool MidiEngine::anythingInOutputBuffer() { @@ -760,9 +742,8 @@ void MidiEngine::checkIncomingUsbMidi() { if (!usbLockNow) { // Have to call this regularly, to do "callbacks" that will grab out the received data - usbLock = 1; + USBAutoLock lock; usb_cstd_usb_task(); - usbLock = 0; } for (int32_t ip = 0; ip < USB_NUM_USBIP; ip++) { @@ -827,10 +808,9 @@ void MidiEngine::checkIncomingUsbMidi() { connectedUSBMIDIDevices[ip][0].currentlyWaitingToReceive = 1; - usbLock = 1; + USBAutoLock lock; g_p_usb_pipe[USB_CFG_PMIDI_BULK_IN] = &g_usb_midi_recv_utr[ip][0]; usb_receive_start_rohan_midi(USB_CFG_PMIDI_BULK_IN); - usbLock = 0; } // Or as host @@ -839,9 +819,8 @@ void MidiEngine::checkIncomingUsbMidi() { // Only allowed to setup receive-transfer if not in the process of sending to various devices. // (Wait, still? Was this just because of that insane bug that's now fixed?) if (usbDeviceNumBeingSentToNow[ip] == stopSendingAfterDeviceNum[ip]) { - usbLock = 1; + USBAutoLock lock; setupUSBHostReceiveTransfer(ip, d); - usbLock = 0; } } } diff --git a/src/deluge/io/midi/midi_engine.h b/src/deluge/io/midi/midi_engine.h index 1cba511677..52c4a07661 100644 --- a/src/deluge/io/midi/midi_engine.h +++ b/src/deluge/io/midi/midi_engine.h @@ -148,7 +148,6 @@ class MidiEngine { uint32_t setupUSBMessage(MIDIMessage message); extern MidiEngine midiEngine; -extern bool anythingInUSBOutputBuffer; extern "C" { #endif diff --git a/src/deluge/io/usb/usb_state.cpp b/src/deluge/io/usb/usb_state.cpp new file mode 100644 index 0000000000..e2f5dc7a8f --- /dev/null +++ b/src/deluge/io/usb/usb_state.cpp @@ -0,0 +1,35 @@ +/* + * Copyright © 2024 Synthstrom Audible Limited + * + * This file is part of The Synthstrom Audible Deluge Firmware. + * + * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#include "usb_state.h" + +namespace deluge::io::usb { +volatile bool usbLock; +bool anythingInUSBOutputBuffer; + +uint8_t stopSendingAfterDeviceNum[USB_NUM_USBIP]; +uint8_t usbDeviceNumBeingSentToNow[USB_NUM_USBIP]; +uint8_t anyUSBSendingStillHappening[USB_NUM_USBIP]; + +} // namespace deluge::io::usb +extern "C" { + +usb_utr_t g_usb_midi_send_utr[USB_NUM_USBIP]; +usb_utr_t g_usb_midi_recv_utr[USB_NUM_USBIP][MAX_NUM_USB_MIDI_DEVICES]; + +uint8_t currentDeviceNumWithSendPipe[2] = {MAX_NUM_USB_MIDI_DEVICES, MAX_NUM_USB_MIDI_DEVICES}; +} diff --git a/src/deluge/io/usb/usb_state.h b/src/deluge/io/usb/usb_state.h new file mode 100644 index 0000000000..914dfbbe78 --- /dev/null +++ b/src/deluge/io/usb/usb_state.h @@ -0,0 +1,76 @@ +/* + * Copyright © 2024 Synthstrom Audible Limited + * + * This file is part of The Synthstrom Audible Deluge Firmware. + * + * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#include "RZA1/usb/r_usb_basic/r_usb_basic_if.h" +#include "definitions.h" + +#ifdef __cplusplus + +namespace deluge::io::usb { + +/// When true, something is using the low-level USB structures. +/// +/// XXX: this is super ill-defined and seems to mostly exist to avoid reentrant ISR problems +extern volatile bool usbLock; + +/// RAII lock control for the USB lock +class USBAutoLock { +public: + USBAutoLock() { usbLock = true; } + ~USBAutoLock() { usbLock = false; } + + USBAutoLock(const USBAutoLock&) = delete; + USBAutoLock(USBAutoLock&&) = delete; + USBAutoLock& operator=(const USBAutoLock&) = delete; + USBAutoLock& operator=(USBAutoLock&&) = delete; +}; + +/// When true, some data is queued to the USB output buffer. +extern bool anythingInUSBOutputBuffer; + +/// Last device to send to +extern uint8_t stopSendingAfterDeviceNum[USB_NUM_USBIP]; + +/// Current hosted device number, used in host send completion callback +extern uint8_t usbDeviceNumBeingSentToNow[USB_NUM_USBIP]; + +/// Flag to prevent reentrant sending +extern uint8_t anyUSBSendingStillHappening[USB_NUM_USBIP]; + +} // namespace deluge::io::usb + +extern "C" { +#endif + +// defined in r_usb_pdriver.c +extern uint16_t g_usb_peri_connected; + +// defined in r_usb_cdataio.c +extern usb_utr_t* g_p_usb_pipe[USB_MAX_PIPE_NO + 1u]; + +extern usb_utr_t g_usb_midi_send_utr[USB_NUM_USBIP]; +extern usb_utr_t g_usb_midi_recv_utr[USB_NUM_USBIP][MAX_NUM_USB_MIDI_DEVICES]; + +// defined in r_usb_hmidi_driver.c +extern uint16_t g_usb_hmidi_tmp_ep_tbl[USB_NUM_USBIP][MAX_NUM_USB_MIDI_DEVICES][(USB_EPL * 2) + 1]; + +// One without, and one with, interrupt endpoints +extern uint8_t currentDeviceNumWithSendPipe[2]; + +#ifdef __cplusplus +} +#endif From d3d5a717f5475f60c14ec52026c3f04c036b13f0 Mon Sep 17 00:00:00 2001 From: Sapphire Koser Date: Fri, 6 Dec 2024 18:06:40 -0800 Subject: [PATCH 10/10] Move USB logic to root complex abstraction This breaks the ability to plug in the USB cable in peripheral mode and have everything automatically link up (connections saved in flash and the active song), but this seems uncommon and should be handled via a `onUSBDeviceAttach` event anyway as it would provide better support when hosting devices. --- .../r_usb_basic/src/driver/r_usb_plibusbip.c | 1 + src/deluge/deluge.cpp | 26 +- src/deluge/gui/menu_item/midi/devices.cpp | 56 +- src/deluge/gui/menu_item/midi/devices.h | 2 +- src/deluge/io/midi/cable_types/usb_common.cpp | 55 +- src/deluge/io/midi/cable_types/usb_common.h | 1 + .../device_specific/specific_midi_device.cpp | 9 +- src/deluge/io/midi/midi_device_manager.cpp | 224 ++++--- src/deluge/io/midi/midi_device_manager.h | 12 +- src/deluge/io/midi/midi_engine.cpp | 570 +----------------- src/deluge/io/midi/midi_engine.h | 13 - src/deluge/io/midi/midi_root_complex.h | 62 ++ src/deluge/io/midi/root_complex/din.h | 5 + .../io/midi/root_complex/usb_hosted.cpp | 314 ++++++++++ src/deluge/io/midi/root_complex/usb_hosted.h | 55 ++ .../io/midi/root_complex/usb_peripheral.cpp | 186 ++++++ .../io/midi/root_complex/usb_peripheral.h | 50 ++ src/deluge/io/usb/usb_state.cpp | 108 +++- src/deluge/io/usb/usb_state.h | 21 + .../util/container/array/resizeable_array.h | 2 +- 20 files changed, 1082 insertions(+), 690 deletions(-) create mode 100644 src/deluge/io/midi/root_complex/usb_hosted.cpp create mode 100644 src/deluge/io/midi/root_complex/usb_hosted.h create mode 100644 src/deluge/io/midi/root_complex/usb_peripheral.cpp create mode 100644 src/deluge/io/midi/root_complex/usb_peripheral.h diff --git a/src/RZA1/usb/r_usb_basic/src/driver/r_usb_plibusbip.c b/src/RZA1/usb/r_usb_basic/src/driver/r_usb_plibusbip.c index b93f3b5c1a..5ca8cc0b4e 100644 --- a/src/RZA1/usb/r_usb_basic/src/driver/r_usb_plibusbip.c +++ b/src/RZA1/usb/r_usb_basic/src/driver/r_usb_plibusbip.c @@ -38,6 +38,7 @@ #include "deluge/drivers/usb/userdef/r_usb_pmidi_config.h" #include "deluge/io/midi/midi_device_manager.h" #include "deluge/io/midi/midi_engine.h" +#include "deluge/io/usb/usb_state.h" #if ((USB_CFG_DTC == USB_CFG_ENABLE) || (USB_CFG_DMA == USB_CFG_ENABLE)) #include "RZA1/usb/r_usb_basic/src/hw/inc/r_usb_dmac.h" diff --git a/src/deluge/deluge.cpp b/src/deluge/deluge.cpp index 5dffdeaa40..f89bb8696f 100644 --- a/src/deluge/deluge.cpp +++ b/src/deluge/deluge.cpp @@ -49,6 +49,8 @@ #include "io/midi/midi_device_manager.h" #include "io/midi/midi_engine.h" #include "io/midi/midi_follow.h" +#include "io/midi/root_complex/usb_hosted.h" +#include "io/midi/root_complex/usb_peripheral.h" #include "lib/printf.h" // IWYU pragma: keep this over rides printf with a non allocating version #include "memory/general_memory_allocator.h" #include "model/clip/instrument_clip.h" @@ -243,14 +245,6 @@ bool readButtonsAndPads() { usbInitializationPeriodComplete = 1; } - /* - if (!inSDRoutine && !closedPeripheral && !anythingInitiallyAttachedAsUSBHost && AudioEngine::audioSampleTimer >= - (44100 << 1)) { D_PRINTLN("closing peripheral"); closeUSBPeripheral(); D_PRINTLN("switching back to host"); - openUSBHost(); - closedPeripheral = true; - } - */ - if (waitingForSDRoutineToEnd) { if (sdRoutineLock) { return false; @@ -846,14 +840,20 @@ extern "C" int32_t deluge_main(void) { deluge::io::usb::USBAutoLock lock; openUSBHost(); - // If nothing was plugged in to us as host, we'll go peripheral - // Ideally I'd like to repeatedly switch between host and peripheral mode anytime there's no USB connection. - // To do that, I'd really need to know at any point in time whether the user had just made a connection, just - // then, that hadn't fully initialized yet. I think I sorta have that for host, but not for peripheral yet. - if (!anythingInitiallyAttachedAsUSBHost) { + if (anythingInitiallyAttachedAsUSBHost) { + MIDIDeviceManager::setUSBRoot(new MIDIRootComplexUSBHosted()); + } + else { + // If nothing was plugged in to us as host, we'll go peripheral + // Ideally I'd like to repeatedly switch between host and peripheral mode anytime there's no USB connection. + // To do that, I'd really need to know at any point in time whether the user had just made a connection, + // just then, that hadn't fully initialized yet. I think I sorta have that for host, but not for peripheral + // yet. D_PRINTLN("switching from host to peripheral"); closeUSBHost(); openUSBPeripheral(); + + // configuredAsPeripheral will set the root complex. } } diff --git a/src/deluge/gui/menu_item/midi/devices.cpp b/src/deluge/gui/menu_item/midi/devices.cpp index 9b3c826cdf..35e49f0367 100644 --- a/src/deluge/gui/menu_item/midi/devices.cpp +++ b/src/deluge/gui/menu_item/midi/devices.cpp @@ -25,6 +25,9 @@ #include "io/midi/cable_types/usb_hosted.h" #include "io/midi/midi_device.h" #include "io/midi/midi_device_manager.h" +#include "io/midi/midi_root_complex.h" +#include "io/midi/root_complex/usb_hosted.h" +#include "io/midi/root_complex/usb_peripheral.h" #include "util/container/static_vector.hpp" #include @@ -37,7 +40,12 @@ static constexpr int32_t lowestDeviceNum = -3; void Devices::beginSession(MenuItem* navigatedBackwardFrom) { bool found = false; if (navigatedBackwardFrom != nullptr) { - for (int32_t idx = lowestDeviceNum; idx < MIDIDeviceManager::hostedMIDIDevices.getNumElements(); idx++) { + // This will technically do the wrong thing when we're in peripheral mode (it'll set the max index to 2 instead + // of 0, which would be accurate) but it should be harmless -- `Devices::getCable` should just return nullptr in + // that case which we handle fine already anyway. + auto maxIndex = + (MIDIDeviceManager::rootUSB != nullptr) ? MIDIDeviceManager::rootUSB->getNumCables() : lowestDeviceNum + 1; + for (int32_t idx = lowestDeviceNum; idx < maxIndex; idx++) { if (getCable(idx) == soundEditor.currentMIDICable) { found = true; this->setValue(idx); @@ -62,10 +70,12 @@ void Devices::beginSession(MenuItem* navigatedBackwardFrom) { void Devices::selectEncoderAction(int32_t offset) { offset = std::clamp(offset, -1, 1); + auto maxIndex = (MIDIDeviceManager::rootUSB == nullptr) ? 0 : MIDIDeviceManager::rootUSB->getNumCables(); + do { int32_t newValue = this->getValue() + offset; - if (newValue >= MIDIDeviceManager::hostedMIDIDevices.getNumElements()) { + if (newValue >= maxIndex) { if (display->haveOLED()) { return; } @@ -75,14 +85,14 @@ void Devices::selectEncoderAction(int32_t offset) { if (display->haveOLED()) { return; } - newValue = MIDIDeviceManager::hostedMIDIDevices.getNumElements() - 1; + newValue = maxIndex - 1; } this->setValue(newValue); soundEditor.currentMIDICable = getCable(this->getValue()); - } while (!soundEditor.currentMIDICable->connectionFlags); + } while (soundEditor.currentMIDICable == nullptr && soundEditor.currentMIDICable->connectionFlags == 0); // Don't show devices which aren't connected. Sometimes we won't even have a name to display for them. if (display->haveOLED()) { @@ -115,24 +125,31 @@ void Devices::selectEncoderAction(int32_t offset) { } MIDICable* Devices::getCable(int32_t deviceIndex) { - if (deviceIndex < lowestDeviceNum || deviceIndex >= MIDIDeviceManager::hostedMIDIDevices.getNumElements()) { + if (deviceIndex < lowestDeviceNum) { D_PRINTLN("impossible device request"); return nullptr; } - switch (deviceIndex) { - case -3: { + + if (deviceIndex == -3) { return &MIDIDeviceManager::rootDin.cable; } - case -2: { - return &MIDIDeviceManager::upstreamUSBMIDICable1; - } - case -1: { - return &MIDIDeviceManager::upstreamUSBMIDICable2; - } - default: { - return static_cast(MIDIDeviceManager::hostedMIDIDevices.getElement(deviceIndex)); - } + + if (MIDIDeviceManager::rootUSB != nullptr) { + auto& rootUSB = *MIDIDeviceManager::rootUSB; + if (deviceIndex < 0) { + if (rootUSB.getType() == RootComplexType::RC_USB_PERIPHERAL && deviceIndex >= -2) { + return rootUSB.getCable(deviceIndex + 2); + } + return nullptr; + } + + if (rootUSB.getType() == RootComplexType::RC_USB_HOST) { + auto& usb = static_cast(rootUSB); + return usb.getCable(deviceIndex); + } } + + return nullptr; } void Devices::drawValue() { @@ -154,11 +171,12 @@ void Devices::drawPixelsForOled() { int32_t selectedRow = -1; - int32_t device_idx = currentScroll; + auto device_idx = currentScroll; size_t row = 0; - while (row < kOLEDMenuNumOptionsVisible && device_idx < MIDIDeviceManager::hostedMIDIDevices.getNumElements()) { + auto max_index = (MIDIDeviceManager::rootUSB == nullptr) ? 0 : MIDIDeviceManager::rootUSB->getNumCables(); + while (row < kOLEDMenuNumOptionsVisible && device_idx < static_cast(max_index)) { MIDICable* cable = getCable(device_idx); - if (cable && cable->connectionFlags != 0u) { + if (cable != nullptr && cable->connectionFlags != 0u) { itemNames.push_back(cable->getDisplayName()); if (device_idx == this->getValue()) { selectedRow = static_cast(row); diff --git a/src/deluge/gui/menu_item/midi/devices.h b/src/deluge/gui/menu_item/midi/devices.h index 30fedcf50a..7403bfeeef 100644 --- a/src/deluge/gui/menu_item/midi/devices.h +++ b/src/deluge/gui/menu_item/midi/devices.h @@ -32,7 +32,7 @@ class Devices final : public Value { void drawPixelsForOled(); private: - size_t currentScroll; + ptrdiff_t currentScroll; }; extern Devices devicesMenu; diff --git a/src/deluge/io/midi/cable_types/usb_common.cpp b/src/deluge/io/midi/cable_types/usb_common.cpp index 46f6829c6c..3aaaed8591 100644 --- a/src/deluge/io/midi/cable_types/usb_common.cpp +++ b/src/deluge/io/midi/cable_types/usb_common.cpp @@ -18,6 +18,41 @@ #include "usb_common.h" #include "io/midi/midi_engine.h" +void MIDICableUSB::checkIncomingSysex(uint8_t const* msg, int32_t ip, int32_t d) { + ConnectedUSBMIDIDevice* connected = &connectedUSBMIDIDevices[ip][d]; + + uint8_t statusType = msg[0] & 15; + int32_t to_read = 0; + bool will_end = false; + if (statusType == 0x4) { + // sysex start or continue + if (msg[1] == 0xf0) { + this->incomingSysexPos = 0; + } + to_read = 3; + } + else if (statusType >= 0x5 && statusType <= 0x7) { + to_read = statusType - 0x4; // read between 1-3 bytes + will_end = true; + } + + for (int32_t i = 0; i < to_read; i++) { + if (this->incomingSysexPos >= sizeof(this->incomingSysexBuffer)) { + // TODO: allocate a GMA buffer to some bigger size + this->incomingSysexPos = 0; + return; // bail out + } + this->incomingSysexBuffer[this->incomingSysexPos++] = msg[i + 1]; + } + + if (will_end) { + if (this->incomingSysexBuffer[0] == 0xf0) { + midiEngine.midiSysexReceived(*this, this->incomingSysexBuffer, this->incomingSysexPos); + } + this->incomingSysexPos = 0; + } +} + void MIDICableUSB::connectedNow(int32_t midiDeviceNum) { connectionFlags |= (1 << midiDeviceNum); needsToSendMCMs = 2; @@ -32,6 +67,24 @@ void MIDICableUSB::sendMCMsNowIfNeeded() { } } +static uint32_t setupUSBMessage(MIDIMessage message) { + // format message per USB midi spec on virtual cable 0 + uint8_t cin; + uint8_t firstByte = (message.channel & 15) | (message.statusType << 4); + + switch (firstByte) { + case 0xF2: // Position pointer + cin = 0x03; + break; + + default: + cin = message.statusType; // Good for voice commands + break; + } + + return ((uint32_t)message.data2 << 24) | ((uint32_t)message.data1 << 16) | ((uint32_t)firstByte << 8) | cin; +} + Error MIDICableUSB::sendMessage(MIDIMessage message) { if (!connectionFlags) { return Error::NONE; @@ -46,7 +99,7 @@ Error MIDICableUSB::sendMessage(MIDIMessage message) { ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][d]; if (connectedDevice->canHaveMIDISent) { uint32_t channeledMessage = fullMessage | (portNumber << 4); - connectedDevice->bufferMessage(fullMessage); + connectedDevice->bufferMessage(channeledMessage); } } } diff --git a/src/deluge/io/midi/cable_types/usb_common.h b/src/deluge/io/midi/cable_types/usb_common.h index 84a6f0668b..56ac3cf31d 100644 --- a/src/deluge/io/midi/cable_types/usb_common.h +++ b/src/deluge/io/midi/cable_types/usb_common.h @@ -32,6 +32,7 @@ class MIDICableUSB : public MIDICable { [[nodiscard]] Error sendSysex(const uint8_t* data, int32_t len) override; [[nodiscard]] size_t sendBufferSpace() const override; + void checkIncomingSysex(uint8_t const* msg, int32_t ip, int32_t d); void connectedNow(int32_t midiDeviceNum); void sendMCMsNowIfNeeded(); uint8_t needsToSendMCMs; diff --git a/src/deluge/io/midi/device_specific/specific_midi_device.cpp b/src/deluge/io/midi/device_specific/specific_midi_device.cpp index f3be4b8698..401549999b 100644 --- a/src/deluge/io/midi/device_specific/specific_midi_device.cpp +++ b/src/deluge/io/midi/device_specific/specific_midi_device.cpp @@ -31,9 +31,12 @@ SpecificMidiDeviceType getSpecificMidiDeviceType(uint16_t vendorId, uint16_t pro void iterateAndCallSpecificDeviceHook(MIDICableUSBHosted::Hook hook) { using namespace MIDIDeviceManager; - for (int32_t i = 0; i < hostedMIDIDevices.getNumElements(); i++) { - auto* specificDevice = static_cast(hostedMIDIDevices.getElement(i)); + if (rootUSB == nullptr || rootUSB->getType() != RootComplexType::RC_USB_HOST) { + return; + } - specificDevice->callHook(hook); + for (auto& c : rootUSB->getCables()) { + auto& cable = static_cast(c); + cable.callHook(hook); } } diff --git a/src/deluge/io/midi/midi_device_manager.cpp b/src/deluge/io/midi/midi_device_manager.cpp index 57d5d5c520..390d1eb2b9 100644 --- a/src/deluge/io/midi/midi_device_manager.cpp +++ b/src/deluge/io/midi/midi_device_manager.cpp @@ -20,10 +20,13 @@ #include "gui/menu_item/mpe/zone_num_member_channels.h" #include "gui/ui/sound_editor.h" #include "hid/display/display.h" +#include "io/midi/cable_types/usb_common.h" #include "io/midi/cable_types/usb_device_cable.h" +#include "io/midi/cable_types/usb_hosted.h" #include "io/midi/device_specific/specific_midi_device.h" #include "io/midi/midi_device.h" #include "io/midi/midi_engine.h" +#include "io/midi/root_complex/usb_peripheral.h" #include "io/usb/usb_state.h" #include "mem_functions.h" #include "memory/general_memory_allocator.h" @@ -48,8 +51,6 @@ PLACE_INTERNAL_FRUNK ConnectedUSBMIDIDevice connectedUSBMIDIDevices[USB_NUM_USBI namespace MIDIDeviceManager { -NamedThingVector hostedMIDIDevices{__builtin_offsetof(MIDICableUSBHosted, name)}; - bool differentiatingInputsByDevice = true; struct USBDev { @@ -59,13 +60,8 @@ struct USBDev { }; std::array usbDeviceCurrentlyBeingSetUp{}; -// This class represents a thing you can send midi too, -// the virtual cable is an implementation detail -MIDICableUSBUpstream upstreamUSBMIDICable1{0}; -MIDICableUSBUpstream upstreamUSBMIDICable2{1}; -MIDICableUSBUpstream upstreamUSBMIDICable3{2}; - DINRootComplex rootDin{}; +MIDIRootComplex* rootUSB{nullptr}; uint8_t lowestLastMemberChannelOfLowerZoneOnConnectedOutput = 15; uint8_t highestLastMemberChannelOfUpperZoneOnConnectedOutput = 0; @@ -74,18 +70,21 @@ bool anyChangesToSave = false; // Gets called within UITimerManager, which may get called during SD card routine. void slowRoutine() { - upstreamUSBMIDICable1.sendMCMsNowIfNeeded(); - upstreamUSBMIDICable2.sendMCMsNowIfNeeded(); - // port3 is not used for channel data - - for (int32_t d = 0; d < hostedMIDIDevices.getNumElements(); d++) { - MIDICableUSBHosted* device = (MIDICableUSBHosted*)hostedMIDIDevices.getElement(d); - device->sendMCMsNowIfNeeded(); - - // This routine placed here because for whatever reason we can't send sysex from hostedDeviceConfigured - if (device->freshly_connected) { - device->hookOnConnected(); - device->freshly_connected = false; // Must be set to false here or the hook will run forever + if (!rootUSB) { + // Nothing to do if there's no USB device connected. + return; + } + + for (auto& usbCable : rootUSB->getCables()) { + auto& cable = static_cast(usbCable); + cable.sendMCMsNowIfNeeded(); + + if (rootUSB->getType() == RootComplexType::RC_USB_HOST) { + auto& hostCable = static_cast(cable); + if (hostCable.freshly_connected) { + hostCable.hookOnConnected(); + hostCable.freshly_connected = false; + } } } } @@ -106,6 +105,12 @@ extern "C" void giveDetailsOfDeviceBeingSetUp(int32_t ip, char const* name, uint // name can be NULL, or an empty String MIDICableUSBHosted* getOrCreateHostedMIDIDeviceFromDetails(String* name, uint16_t vendorId, uint16_t productId) { + MIDIRootComplexUSBHosted* root = getHosted(); + if (getHosted() == nullptr) { + return nullptr; + } + + auto& hostedMIDIDevices = root->getHostedMIDIDevices(); // Do we know any details about this device already? @@ -122,7 +127,7 @@ MIDICableUSBHosted* getOrCreateHostedMIDIDeviceFromDetails(String* name, uint16_ auto* device = static_cast(hostedMIDIDevices.getElement(i)); // Update vendor and product id, if we have those - if (vendorId) { + if (vendorId != 0u) { device->vendorId = vendorId; device->productId = productId; } @@ -214,14 +219,13 @@ void recountSmallestMPEZones() { lowestLastMemberChannelOfLowerZoneOnConnectedOutput = 15; highestLastMemberChannelOfUpperZoneOnConnectedOutput = 0; - recountSmallestMPEZonesForCable(upstreamUSBMIDICable1); - recountSmallestMPEZonesForCable(upstreamUSBMIDICable2); - recountSmallestMPEZonesForCable(rootDin.cable); - - for (int32_t d = 0; d < hostedMIDIDevices.getNumElements(); d++) { - MIDICableUSBHosted* cable = (MIDICableUSBHosted*)hostedMIDIDevices.getElement(d); - recountSmallestMPEZonesForCable(*cable); + if (rootUSB) { + for (auto& cable : rootUSB->getCables()) { + recountSmallestMPEZonesForCable(cable); + } } + + recountSmallestMPEZonesForCable(rootDin.cable); } // Create the midi device configuration and add to the USB midi array @@ -294,19 +298,22 @@ extern "C" void configuredAsPeripheral(int32_t ip) { // Leave this - we'll use this device for all upstream ports ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][0]; + auto* root = new MIDIRootComplexUSBPeripheral(); + MIDIDeviceManager::setUSBRoot(root); + // add second port here connectedDevice->setup(); - connectedDevice->cable[0] = &upstreamUSBMIDICable1; - connectedDevice->cable[1] = &upstreamUSBMIDICable2; - connectedDevice->cable[2] = &upstreamUSBMIDICable3; + for (auto i = 0; i < 3; ++i) { + auto* cable = static_cast(root->getCable(i)); + cable->connectedNow(0); + connectedDevice->cable[i] = cable; + } + connectedDevice->maxPortConnected = 2; connectedDevice->canHaveMIDISent = 1; anyUSBSendingStillHappening[ip] = 0; // Initialize this. There's obviously nothing sending yet right now. - upstreamUSBMIDICable1.connectedNow(0); - upstreamUSBMIDICable2.connectedNow(0); - upstreamUSBMIDICable3.connectedNow(0); recountSmallestMPEZones(); } @@ -314,11 +321,10 @@ extern "C" void detachedAsPeripheral(int32_t ip) { // will need to reset all devices if more are added int32_t ports = connectedUSBMIDIDevices[ip][0].maxPortConnected; for (int32_t i = 0; i <= ports; i++) { + auto* cable = connectedUSBMIDIDevices[ip][0].cable[i]; + cable->connectionFlags = 0; connectedUSBMIDIDevices[ip][0].cable[i] = nullptr; } - upstreamUSBMIDICable1.connectionFlags = 0; - upstreamUSBMIDICable2.connectionFlags = 0; - upstreamUSBMIDICable3.connectionFlags = 0; anyUSBSendingStillHappening[ip] = 0; // Reset this again. Been meaning to do this, and can no longer quite remember // reason or whether technically essential, but adds to safety at least. @@ -346,14 +352,24 @@ MIDICable* readDeviceReferenceFromFile(Deserializer& reader) { } else if (!strcmp(tagName, "port")) { char const* port = reader.readTagOrAttributeValue(); - if (!strcmp(port, "upstreamUSB")) { - device = &upstreamUSBMIDICable1; - } - else if (!strcmp(port, "upstreamUSB2")) { - device = &upstreamUSBMIDICable2; - } - else if (!strcmp(port, "upstreamUSB3")) { - device = &upstreamUSBMIDICable3; + constexpr char const* const kUpstreamUSB = "upstreamUSB"; + constexpr auto kUpstreamUSBLen = (std::string{kUpstreamUSB}).length(); + + if (rootUSB && rootUSB->getType() == RootComplexType::RC_USB_PERIPHERAL + && !strncmp(kUpstreamUSB, port, strlen(kUpstreamUSB))) { + switch (port[kUpstreamUSBLen]) { + case '\0': + device = rootUSB->getCable(0); + break; + case '2': + device = rootUSB->getCable(1); + break; + case '3': + device = rootUSB->getCable(2); + break; + default: + break; + } } else if (!strcmp(port, "din")) { device = &rootDin.cable; @@ -383,14 +399,16 @@ static MIDICable* readCableFromFlash(uint8_t const* memory) { if (vendorId == VENDOR_ID_NONE) { cable = nullptr; } - else if (vendorId == VENDOR_ID_UPSTREAM_USB) { - cable = &upstreamUSBMIDICable1; - } - else if (vendorId == VENDOR_ID_UPSTREAM_USB2) { - cable = &upstreamUSBMIDICable2; - } - else if (vendorId == VENDOR_ID_UPSTREAM_USB3) { - cable = &upstreamUSBMIDICable3; + else if (rootUSB && rootUSB->getType() == RootComplexType::RC_USB_PERIPHERAL) { + if (vendorId == VENDOR_ID_UPSTREAM_USB) { + cable = rootUSB->getCable(0); + } + else if (vendorId == VENDOR_ID_UPSTREAM_USB2) { + cable = rootUSB->getCable(1); + } + else if (vendorId == VENDOR_ID_UPSTREAM_USB3) { + cable = rootUSB->getCable(2); + } } else if (vendorId == VENDOR_ID_DIN) { cable = &rootDin.cable; @@ -429,12 +447,12 @@ void writeDevicesToFile() { } anyChangesToSave = false; - bool anyWorthWritting = rootDin.cable.worthWritingToFile() || upstreamUSBMIDICable1.worthWritingToFile() - || upstreamUSBMIDICable2.worthWritingToFile(); - if (!anyWorthWritting) { - for (int32_t d = 0; d < hostedMIDIDevices.getNumElements(); d++) { - MIDICableUSBHosted* device = (MIDICableUSBHosted*)hostedMIDIDevices.getElement(d); - if (device->worthWritingToFile()) { + bool anyWorthWritting = rootDin.cable.worthWritingToFile(); + + // First, see if it's even worth writing anything + if (!anyWorthWritting && rootUSB != nullptr) { + for (auto& cable : rootUSB->getCables()) { + if (cable.worthWritingToFile()) { anyWorthWritting = true; break; } @@ -461,20 +479,30 @@ void writeDevicesToFile() { if (rootDin.cable.worthWritingToFile()) { rootDin.cable.writeToFile(writer, "dinPorts"); } - if (upstreamUSBMIDICable1.worthWritingToFile()) { - upstreamUSBMIDICable1.writeToFile(writer, "upstreamUSBDevice"); - } - if (upstreamUSBMIDICable2.worthWritingToFile()) { - upstreamUSBMIDICable2.writeToFile(writer, "upstreamUSBDevice2"); - } - for (int32_t d = 0; d < hostedMIDIDevices.getNumElements(); d++) { - MIDICableUSBHosted* device = (MIDICableUSBHosted*)hostedMIDIDevices.getElement(d); - if (device->worthWritingToFile()) { - device->writeToFile(writer, "hostedUSBDevice"); + if (rootUSB != nullptr) { + switch (rootUSB->getType()) { + case RootComplexType::RC_DIN: + // illegal + break; + case RootComplexType::RC_USB_PERIPHERAL: + if (rootUSB->getCable(0)->worthWritingToFile()) { + rootUSB->getCable(0)->writeToFile(writer, "upstreamUSBDevice"); + } + if (rootUSB->getCable(1)->worthWritingToFile()) { + rootUSB->getCable(1)->writeToFile(writer, "upstreamUSBDevice2"); + } + break; + case RootComplexType::RC_USB_HOST: + for (auto& cable : rootUSB->getCables()) { + auto& cableHosted = static_cast(cable); + if (cableHosted.worthWritingToFile()) { + cableHosted.writeToFile(writer, "hostedUSBDevice"); + } + cableHosted.hookOnWriteHostedDeviceToFile(); + } + break; } - // Stow this for the hook point later - device->hookOnWriteHostedDeviceToFile(); } writer.writeClosingTag("midiDevices"); @@ -519,17 +547,30 @@ void readDevicesFromFile() { if (!strcmp(tagName, "dinPorts")) { rootDin.cable.readFromFile(reader); } - else if (!strcmp(tagName, "upstreamUSBDevice")) { - upstreamUSBMIDICable1.readFromFile(reader); - } - else if (!strcmp(tagName, "upstreamUSBDevice2")) { - upstreamUSBMIDICable2.readFromFile(reader); - } - else if (!strcmp(tagName, "upstreamUSBDevice3")) { - upstreamUSBMIDICable3.readFromFile(reader); - } - else if (!strcmp(tagName, "hostedUSBDevice")) { - readAHostedDeviceFromFile(reader); + else if (rootUSB != nullptr) { + auto type = rootUSB->getType(); + if (type == RootComplexType::RC_USB_PERIPHERAL) { + constexpr char const* const kUpstreamUSB = "upstreamUSBDevice"; + constexpr auto kUpstreamUSBLen = (std::string{kUpstreamUSB}).length(); + + if (strncmp(kUpstreamUSB, tagName, strlen(kUpstreamUSB)) == 0) { + + switch (tagName[kUpstreamUSBLen]) { + case '\0': + rootUSB->getCable(0)->readFromFile(reader); + break; + case '2': + rootUSB->getCable(1)->readFromFile(reader); + break; + case '3': + rootUSB->getCable(3)->readFromFile(reader); + break; + } + } + } + else if (type == RootComplexType::RC_USB_HOST && strcmp(tagName, "hostedUSBDevice") == 0) { + readAHostedDeviceFromFile(reader); + } } reader.exitTag(); @@ -544,6 +585,7 @@ void readDevicesFromFile() { successfullyReadDevicesFromFile = true; } +/// Read a single hosted USB device. This assumes the root complex is a MIDIRootComplexUSBHosted void readAHostedDeviceFromFile(Deserializer& reader) { MIDICableUSBHosted* device = nullptr; @@ -619,13 +661,29 @@ void readAHostedDeviceFromFile(Deserializer& reader) { if (device) {} } +void setUSBRoot(MIDIRootComplex* root) { + delete rootUSB; + rootUSB = root; +} + +MIDIRootComplexUSBHosted* getHosted() { + if (rootUSB == nullptr) { + return nullptr; + } + if (rootUSB->getType() != RootComplexType::RC_USB_HOST) { + return nullptr; + } + + return static_cast(rootUSB); +} + } // namespace MIDIDeviceManager void ConnectedUSBMIDIDevice::bufferMessage(uint32_t fullMessage) { uint32_t queued = ringBufWriteIdx - ringBufReadIdx; if (queued > 16) { if (!anyUSBSendingStillHappening[0]) { - midiEngine.flushUSBMIDIOutput(); + midiEngine.flushMIDI(); } queued = ringBufWriteIdx - ringBufReadIdx; } diff --git a/src/deluge/io/midi/midi_device_manager.h b/src/deluge/io/midi/midi_device_manager.h index 83cfdbf96d..64bd5bbfd4 100644 --- a/src/deluge/io/midi/midi_device_manager.h +++ b/src/deluge/io/midi/midi_device_manager.h @@ -21,6 +21,7 @@ #include "io/midi/cable_types/usb_common.h" #include "io/midi/cable_types/usb_device_cable.h" #include "io/midi/root_complex/din.h" +#include "io/midi/root_complex/usb_hosted.h" #include "util/container/vector/named_thing_vector.h" class Serializer; class Deserializer; @@ -111,15 +112,16 @@ void writeDevicesToFile(); void readAHostedDeviceFromFile(Deserializer& reader); void readDevicesFromFile(); -extern MIDICableUSBUpstream upstreamUSBMIDICable1; -extern MIDICableUSBUpstream upstreamUSBMIDICable2; -extern MIDICableUSBUpstream upstreamUSBMIDICable3; +/// Configure the MIDI root complex. Should be an instance of MIDIRootComplexUSBHosted or MIDIRootCOmplexUSBPeripheral, +/// or nullptr (representing disconnection). +void setUSBRoot(MIDIRootComplex* root); extern DINRootComplex rootDin; +extern MIDIRootComplex* rootUSB; -extern bool differentiatingInputsByDevice; +MIDIRootComplexUSBHosted* getHosted(); -extern NamedThingVector hostedMIDIDevices; +extern bool differentiatingInputsByDevice; extern uint8_t lowestLastMemberChannelOfLowerZoneOnConnectedOutput; extern uint8_t highestLastMemberChannelOfUpperZoneOnConnectedOutput; diff --git a/src/deluge/io/midi/midi_engine.cpp b/src/deluge/io/midi/midi_engine.cpp index 474d90b3aa..b3ab7f1b1e 100644 --- a/src/deluge/io/midi/midi_engine.cpp +++ b/src/deluge/io/midi/midi_engine.cpp @@ -38,161 +38,7 @@ using namespace deluge::io::usb; extern "C" { #include "RZA1/uart/sio_char.h" -void usb_cstd_usb_task(); - -#include "RZA1/system/iodefine.h" -#include "RZA1/usb/r_usb_basic/r_usb_basic_if.h" -#include "RZA1/usb/r_usb_basic/src/hw/inc/r_usb_bitdefine.h" -#include "drivers/usb/userdef/r_usb_pmidi_config.h" - -#include "RZA1/usb/r_usb_hmidi/src/inc/r_usb_hmidi.h" -#include "RZA1/usb/userdef/r_usb_hmidi_config.h" - -usb_regadr_t usb_hstd_get_usb_ip_adr(uint16_t ipno); -void change_destination_of_send_pipe(usb_utr_t* ptr, uint16_t pipe, uint16_t* tbl, int32_t sq); -void usb_send_start_rohan(usb_utr_t* ptr, uint16_t pipe, uint8_t const* data, int32_t size); -void usb_receive_start_rohan_midi(uint16_t pipe); -void usb_pstd_set_stall(uint16_t pipe); -void usb_cstd_set_nak(usb_utr_t* ptr, uint16_t pipe); -void hw_usb_clear_pid(usb_utr_t* ptr, uint16_t pipeno, uint16_t data); -uint16_t hw_usb_read_pipectr(usb_utr_t* ptr, uint16_t pipeno); - -void flushUSBMIDIToHostedDevice(int32_t ip, int32_t d, bool resume = false); - -// We now bypass calling this for successful as peripheral on A1 (see usb_pstd_bemp_pipe_process_rohan_midi()) -void usbSendCompleteAsHost(int32_t ip) { - - int32_t midiDeviceNum = usbDeviceNumBeingSentToNow[ip]; - - ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][midiDeviceNum]; - - connectedDevice->numBytesSendingNow = 0; // We just do this instead from caller on A1 (see comment above) - - // check if there was more to send on the same device, then resume sending - bool has_more = connectedDevice->consumeSendData(); - if (has_more) { - // TODO: do some cooperative scheduling here. so if there is a flood of data - // on connected device 1 and we just want to send a few notes on device 2, - // make sure device 2 ges a fair shot now and then - - flushUSBMIDIToHostedDevice(ip, midiDeviceNum, true); - return; - } - - // If that was the last device we were going to send to, that send's been done, so we can just get out. - if (midiDeviceNum == stopSendingAfterDeviceNum[ip]) { - anyUSBSendingStillHappening[ip] = 0; - return; - } - - while (true) { - midiDeviceNum++; - if (midiDeviceNum >= MAX_NUM_USB_MIDI_DEVICES) { - midiDeviceNum -= MAX_NUM_USB_MIDI_DEVICES; - } - connectedDevice = &connectedUSBMIDIDevices[ip][midiDeviceNum]; - if (connectedDevice->cable[0] && connectedDevice->numBytesSendingNow) { - // If here, we got a connected device, so flush - flushUSBMIDIToHostedDevice(ip, midiDeviceNum); - return; - } - if (midiDeviceNum == stopSendingAfterDeviceNum[ip]) { // If reached end of devices and last one got disconnected - // in the interim (very rare) - usbDeviceNumBeingSentToNow[ip] = stopSendingAfterDeviceNum[ip]; - anyUSBSendingStillHappening[ip] = 0; - return; - } - } -} - -// We now bypass calling this for successful as peripheral on A1 (see usb_pstd_bemp_pipe_process_rohan_midi()) -void usbSendCompletePeripheralOrA1(usb_utr_t* p_mess, uint16_t data1, uint16_t data2) { - - // If error, forget about device. - // No actually don't - sometimes there'll be an error if another device connected or disconnected from hub during - // fast MIDI sending. This seems to happen even though I've stopped it from setting up or down the out-pipe as it - // goes - if (p_mess->status == USB_DATA_ERR) { - uartPrintln("send error!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); - // g_usb_host_connected[deviceNum] = 0; - } - -#if USB_NUM_USBIP == 1 - int32_t ip = 0; -#else - int32_t ip = p_mess->ip; -#endif - - usbSendCompleteAsHost(ip); -} - -void usbSendCompleteAsPeripheral(int32_t ip) { - ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][0]; - connectedDevice->numBytesSendingNow = 0; // Even easier! - // - - // I think this could happen as part of a detach see detachedAsPeripheral() - if (anyUSBSendingStillHappening[ip] == 0) { - return; - } - - bool has_more = connectedDevice->consumeSendData(); - if (has_more) { - // this is already the case: - // anyUSBSendingStillHappening[ip] = 1; - - g_usb_midi_send_utr[ip].tranlen = connectedDevice->numBytesSendingNow; - g_usb_midi_send_utr[ip].p_tranadr = connectedDevice->dataSendingNow; - - usb_send_start_rohan(NULL, USB_CFG_PMIDI_BULK_OUT, connectedDevice->dataSendingNow, - connectedDevice->numBytesSendingNow); - } - else { - // this effectively serves as a lock, does the sending part of the device, including the read part of the - // ring buffer "belong" to ongoing/scheduled interrupts. Document this better. - anyUSBSendingStillHappening[0] = 0; - } -} - -void usbReceiveComplete(int32_t ip, int32_t deviceNum, int32_t tranlen) { - ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][deviceNum]; - - connectedDevice->numBytesReceived = 64 - tranlen; // Seems wack, but yet, tranlen is now how many bytes didn't get - // received out of the original transfer size - // Warning - sometimes (with a Teensy, e.g. my knob box), length will be 0. Not sure why - but we need to cope with - // that case. - - connectedDevice->currentlyWaitingToReceive = 0; // Take note that we need to set up another receive -} - -void usbReceiveCompletePeripheralOrA1(usb_utr_t* p_mess, uint16_t data1, uint16_t data2) { - -#if USB_NUM_USBIP == 1 - int32_t ip = 0; -#else - int32_t ip = p_mess->ip; -#endif - - if (p_mess->status == USB_DATA_ERR) { - return; // Can happen if user disconnects device - totally normal - } - - int32_t deviceNum = p_mess - &g_usb_midi_recv_utr[ip][0]; - - // Are there actually any other possibilities that could happen here? Can't remember. - if (p_mess->status != USB_DATA_SHT) { - uartPrint("status: "); - uartPrintNumber(p_mess->status); - } - - usbReceiveComplete(ip, deviceNum, p_mess->tranlen); -} - -uint32_t timeLastBRDY[USB_NUM_USBIP]; - -void brdyOccurred(int32_t ip) { - timeLastBRDY[ip] = DMACnNonVolatile(SSI_TX_DMA_CHANNEL).CRSA_n; // Reading this not as volatile works fine -} +extern void usb_cstd_usb_task(); } MidiEngine midiEngine{}; @@ -212,185 +58,11 @@ MidiEngine::MidiEngine() { midiTakeover = MIDITakeoverMode::JUMP; midiSelectKitRow = false; - g_usb_peri_connected = 0; // Needs initializing with A2 driver - - for (int32_t ip = 0; ip < USB_NUM_USBIP; ip++) { - - // This might not be used due to the change in r_usb_hlibusbip (deluge is host) to call usbSendCompleteAsHost() - // directly and the change in r_usb_plibusbip (deluge is pheriperal) to just set some variables or it might be - // used for some other interrups like error conditions??? - // TODO: try to delet this and see if something breaks.. - g_usb_midi_send_utr[ip].complete = (usb_cb_t)usbSendCompletePeripheralOrA1; - - g_usb_midi_send_utr[ip].p_setup = 0; /* Setup message address set */ - g_usb_midi_send_utr[ip].segment = USB_TRAN_END; - g_usb_midi_send_utr[ip].ip = ip; - g_usb_midi_send_utr[ip].ipp = usb_hstd_get_usb_ip_adr(ip); - - for (int32_t d = 0; d < MAX_NUM_USB_MIDI_DEVICES; d++) { - g_usb_midi_recv_utr[ip][d].p_tranadr = connectedUSBMIDIDevices[ip][d].receiveData; - g_usb_midi_recv_utr[ip][d].complete = (usb_cb_t)usbReceiveCompletePeripheralOrA1; - - g_usb_midi_recv_utr[ip][d].p_setup = 0; /* Setup message address set */ - g_usb_midi_recv_utr[ip][d].segment = USB_TRAN_END; - g_usb_midi_recv_utr[ip][d].ip = ip; - g_usb_midi_recv_utr[ip][d].ipp = usb_hstd_get_usb_ip_adr(ip); - } - } + usbSetup(); eventStackTop_ = eventStack_.begin(); } -void flushUSBMIDIToHostedDevice(int32_t ip, int32_t d, bool resume) { - - ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][d]; - // there was an assumption that the pipe wouldn't have changed if we were resuming a transfer but that has turned - // out not to be true if hubs are involved. A hub transaction seems to be able to run before the - // usbSendCompleteAsHost interrupt is called and changes the pipe, and then the next write doesn't go anywhere - // useful - int32_t pipeNumber = g_usb_hmidi_tmp_ep_tbl[USB_CFG_USE_USBIP][d][0]; - g_usb_midi_send_utr[USB_CFG_USE_USBIP].keyword = pipeNumber; - g_usb_midi_send_utr[USB_CFG_USE_USBIP].tranlen = connectedDevice->numBytesSendingNow; - g_usb_midi_send_utr[USB_CFG_USE_USBIP].p_tranadr = connectedDevice->dataSendingNow; - - usbDeviceNumBeingSentToNow[USB_CFG_USE_USBIP] = d; - - int32_t isInterrupt = (pipeNumber == USB_CFG_HMIDI_INT_SEND); - - if (d != currentDeviceNumWithSendPipe[isInterrupt]) { - currentDeviceNumWithSendPipe[isInterrupt] = d; - change_destination_of_send_pipe(&g_usb_midi_send_utr[USB_CFG_USE_USBIP], pipeNumber, - g_usb_hmidi_tmp_ep_tbl[USB_CFG_USE_USBIP][d], connectedDevice->sq); - } - - connectedDevice->sq = !connectedDevice->sq; - - g_p_usb_pipe[pipeNumber] = &g_usb_midi_send_utr[USB_CFG_USE_USBIP]; - - usb_send_start_rohan(&g_usb_midi_send_utr[USB_CFG_USE_USBIP], pipeNumber, connectedDevice->dataSendingNow, - connectedDevice->numBytesSendingNow); -} - -int32_t MidiEngine::getPotentialNumConnectedUSBMIDIDevices(int32_t ip) { - bool potentiallyAHost = (g_usb_usbmode == USB_HOST); - // bool aPeripheral = g_usb_peri_connected; - return potentiallyAHost ? MAX_NUM_USB_MIDI_DEVICES : 1; -} - -// Warning - this will sometimes (not always) be called in an ISR -void MidiEngine::flushUSBMIDIOutput() { - - if (usbLock) { - return; - } - - anythingInUSBOutputBuffer = false; - - // is this still relevant? anyUSBSendingStillHappening[ip] acts as the lock between routine and interrupt - // on the sending side. all other uses of usbLock seems to be about _receiving_. Can there be a conflict - // between sending and receiving as well?? - USBAutoLock lock; - - for (int32_t ip = 0; ip < USB_NUM_USBIP; ip++) { - if (anyUSBSendingStillHappening[ip]) { - // still sending, call me later maybe - anythingInUSBOutputBuffer = true; - continue; - } - - bool potentiallyAHost = (g_usb_usbmode == USB_HOST); - bool aPeripheral = g_usb_peri_connected; - - if (aPeripheral) { - ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][0]; - - if (!connectedDevice->consumeSendData()) { - continue; - } - - g_usb_midi_send_utr[ip].keyword = USB_CFG_PMIDI_BULK_OUT; - g_usb_midi_send_utr[ip].tranlen = connectedDevice->numBytesSendingNow; - g_usb_midi_send_utr[ip].p_tranadr = connectedDevice->dataSendingNow; - - usbDeviceNumBeingSentToNow[ip] = 0; - anyUSBSendingStillHappening[ip] = 1; - - g_p_usb_pipe[USB_CFG_PMIDI_BULK_OUT] = &g_usb_midi_send_utr[ip]; - usb_send_start_rohan(NULL, USB_CFG_PMIDI_BULK_OUT, connectedDevice->dataSendingNow, - connectedDevice->numBytesSendingNow); - - // when done, usbSendCompleteAsPeripheral() will be called in an interrupt - } - - else if (potentiallyAHost) { - // This next bit was written with multiple devices on hubs in mind, but seems to work for a single MIDI - // device too - - int32_t midiDeviceNumToSendTo = currentDeviceNumWithSendPipe[0]; // This will do - if (midiDeviceNumToSendTo >= MAX_NUM_USB_MIDI_DEVICES) { - midiDeviceNumToSendTo = 0; // In case it was set to "none", I think - } - - int32_t newStopSendingAfter = midiDeviceNumToSendTo - 1; - if (newStopSendingAfter < 0) { - newStopSendingAfter += MAX_NUM_USB_MIDI_DEVICES; - } - - // Make sure that's on a connected device - it probably would be... - while (true) { - ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][midiDeviceNumToSendTo]; - if (connectedDevice->cable[0] && connectedDevice->hasBufferedSendData()) { - break; // We found a connected one - } - if (midiDeviceNumToSendTo == newStopSendingAfter) { - goto getOut; // If back where we started, none are connected. Could this really happen? Probably. - } - midiDeviceNumToSendTo++; - if (midiDeviceNumToSendTo >= MAX_NUM_USB_MIDI_DEVICES) { - midiDeviceNumToSendTo = 0; // Wrap back to start of list - } - } - - // Stop after a device which we know is connected - while (true) { - ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][newStopSendingAfter]; - - if (connectedDevice->cable[0] && connectedDevice->hasBufferedSendData()) { - break; // We found a connected one - } - - newStopSendingAfter--; - if (newStopSendingAfter < 0) { - newStopSendingAfter += MAX_NUM_USB_MIDI_DEVICES; - } - } - - // Copy the buffers for all devices - int32_t d = midiDeviceNumToSendTo; - while (true) { - ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][d]; - - if (connectedDevice->cable[0]) { - connectedDevice->consumeSendData(); - } - if (d == newStopSendingAfter) { - break; - } - d++; - if (d >= MAX_NUM_USB_MIDI_DEVICES) { - d = 0; - } - } - - stopSendingAfterDeviceNum[ip] = newStopSendingAfter; - anyUSBSendingStillHappening[ip] = 1; - - flushUSBMIDIToHostedDevice(ip, midiDeviceNumToSendTo); - } -getOut: {} - } -} - bool MidiEngine::anythingInOutputBuffer() { return anythingInUSBOutputBuffer || (bool)uartGetTxBufferFullnessByItem(UART_ITEM_MIDI); } @@ -507,61 +179,33 @@ void MidiEngine::sendMidi(MIDISource source, MIDIMessage message, int32_t filter --eventStackTop_; } -uint32_t setupUSBMessage(MIDIMessage message) { - // format message per USB midi spec on virtual cable 0 - uint8_t cin; - uint8_t firstByte = (message.channel & 15) | (message.statusType << 4); - - switch (firstByte) { - case 0xF2: // Position pointer - cin = 0x03; - break; - - default: - cin = message.statusType; // Good for voice commands - break; +void MidiEngine::sendUsbMidi(MIDIMessage message, int32_t filter) { + // If no USB device is connected, don't send anything. Otherwise, we send to all cables. + if (MIDIDeviceManager::rootUSB == nullptr) { + return; } - return ((uint32_t)message.data2 << 24) | ((uint32_t)message.data1 << 16) | ((uint32_t)firstByte << 8) | cin; -} - -void MidiEngine::sendUsbMidi(MIDIMessage message, int32_t filter) { - // TODO: Differentiate between ports on usb midi - bool isSystemMessage = message.isSystemMessage(); - - // formats message per USB midi spec on virtual cable 0 - uint32_t fullMessage = setupUSBMessage(message); - for (int32_t ip = 0; ip < USB_NUM_USBIP; ip++) { - int32_t potentialNumDevices = getPotentialNumConnectedUSBMIDIDevices(ip); - - for (int32_t d = 0; d < potentialNumDevices; d++) { - ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][d]; - if (!connectedDevice->canHaveMIDISent) { - continue; - } - int32_t maxPort = connectedDevice->maxPortConnected; - for (int32_t p = 0; p <= maxPort; p++) { - // if device exists, it's not port 3 (for sysex) - if (connectedDevice->cable[p] - && connectedDevice->cable[p] != &MIDIDeviceManager::upstreamUSBMIDICable3) { - // if it's a clock (or sysex technically but we don't send that to this function) - // or if it's a message that this channel wants - if (connectedDevice->cable[p]->wantsToOutputMIDIOnChannel(message, filter)) { - - // Or with the port to add the cable number to the full message. This - // is a bit hacky but it works - uint32_t channeled_message = fullMessage | (p << 4); - connectedDevice->bufferMessage(channeled_message); - } - } - } + for (auto& cable : MIDIDeviceManager::rootUSB->getCables()) { + if (cable.wantsToOutputMIDIOnChannel(message, filter)) { + cable.sendMessage(message); } } } void MidiEngine::checkIncomingMidi() { + if (!usbLock) { + // Have to call this regularly, to do "callbacks" that will grab out the received data + USBAutoLock lock; + usb_cstd_usb_task(); + } + // Check incoming USB MIDI - midiEngine.checkIncomingUsbMidi(); + if (MIDIDeviceManager::rootUSB != nullptr) { + auto error = MIDIDeviceManager::rootUSB->poll(); + if (error != Error::NONE && error != Error::NO_ERROR_BUT_GET_OUT) { + D_PRINTLN("USB poll error: %d\n", static_cast(error)); + } + } // Check incoming Serial MIDI for (int32_t i = 0; i < 12; i++) { @@ -577,76 +221,15 @@ void MidiEngine::checkIncomingMidi() { // Warning - this will sometimes (not always) be called in an ISR void MidiEngine::flushMIDI() { - flushUSBMIDIOutput(); + if (MIDIDeviceManager::rootUSB) { + MIDIDeviceManager::rootUSB->flush(); + } MIDIDeviceManager::rootDin.flush(); } // Lock USB before calling this! -void MidiEngine::setupUSBHostReceiveTransfer(int32_t ip, int32_t midiDeviceNum) { - connectedUSBMIDIDevices[ip][midiDeviceNum].currentlyWaitingToReceive = 1; - - int32_t pipeNumber = g_usb_hmidi_tmp_ep_tbl[USB_CFG_USE_USBIP][midiDeviceNum][USB_EPL]; - - g_usb_midi_recv_utr[USB_CFG_USE_USBIP][midiDeviceNum].keyword = pipeNumber; - g_usb_midi_recv_utr[USB_CFG_USE_USBIP][midiDeviceNum].tranlen = 64; - - g_p_usb_pipe[pipeNumber] = &g_usb_midi_recv_utr[USB_CFG_USE_USBIP][midiDeviceNum]; - - // uint16_t startTime = *TCNT[TIMER_SYSTEM_SUPERFAST]; - - usb_receive_start_rohan_midi(pipeNumber); - - /* - uint16_t endTime = *TCNT[TIMER_SYSTEM_SUPERFAST]; - uint16_t duration = endTime - startTime; - uint32_t timePassedNS = superfastTimerCountToNS(duration); - uartPrint("send setup duration, nSec: "); - uartPrintNumber(timePassedNS); - */ -} - uint8_t usbCurrentlyInitialized = false; -void MidiEngine::checkIncomingUsbSysex(uint8_t const* msg, int32_t ip, int32_t d, int32_t cableIdx) { - ConnectedUSBMIDIDevice* connected = &connectedUSBMIDIDevices[ip][d]; - if (cableIdx > connectedUSBMIDIDevices[ip][d].maxPortConnected) { - // fallback to cable 0 since we don't support more than one port on hosted devices yet - cableIdx = 0; - } - MIDICable& cable = *connectedUSBMIDIDevices[ip][d].cable[cableIdx]; - - uint8_t statusType = msg[0] & 15; - int32_t to_read = 0; - bool will_end = false; - if (statusType == 0x4) { - // sysex start or continue - if (msg[1] == 0xf0) { - cable.incomingSysexPos = 0; - } - to_read = 3; - } - else if (statusType >= 0x5 && statusType <= 0x7) { - to_read = statusType - 0x4; // read between 1-3 bytes - will_end = true; - } - - for (int32_t i = 0; i < to_read; i++) { - if (cable.incomingSysexPos >= sizeof(cable.incomingSysexBuffer)) { - // TODO: allocate a GMA buffer to some bigger size - cable.incomingSysexPos = 0; - return; // bail out - } - cable.incomingSysexBuffer[cable.incomingSysexPos++] = msg[i + 1]; - } - - if (will_end) { - if (cable.incomingSysexBuffer[0] == 0xf0) { - midiSysexReceived(cable, cable.incomingSysexBuffer, cable.incomingSysexPos); - } - cable.incomingSysexPos = 0; - } -} - bool developerSysexCodeReceived = false; void MidiEngine::midiSysexReceived(MIDICable& cable, uint8_t* data, int32_t len) { @@ -723,111 +306,6 @@ void MidiEngine::midiSysexReceived(MIDICable& cable, uint8_t* data, int32_t len) } } -extern "C" { - -extern uint8_t currentlyAccessingCard; -} - -void MidiEngine::checkIncomingUsbMidi() { - - if (!usbCurrentlyInitialized - || currentlyAccessingCard != 0) { // hack to avoid SysEx handlers clashing with other sd-card activity. - if (currentlyAccessingCard != 0) { - // D_PRINTLN("checkIncomingUsbMidi seeing currentlyAccessingCard non-zero"); - } - return; - } - - bool usbLockNow = usbLock; - - if (!usbLockNow) { - // Have to call this regularly, to do "callbacks" that will grab out the received data - USBAutoLock lock; - usb_cstd_usb_task(); - } - - for (int32_t ip = 0; ip < USB_NUM_USBIP; ip++) { - - bool aPeripheral = (g_usb_usbmode != USB_HOST); - if (aPeripheral && !g_usb_peri_connected) { - continue; - } - // assumes only single device in peripheral mode - int32_t numDevicesNow = aPeripheral ? 1 : MAX_NUM_USB_MIDI_DEVICES; - - for (int32_t d = 0; d < numDevicesNow; d++) { - if (connectedUSBMIDIDevices[ip][d].cable[0] && !connectedUSBMIDIDevices[ip][d].currentlyWaitingToReceive) { - - int32_t bytesReceivedHere = connectedUSBMIDIDevices[ip][d].numBytesReceived; - if (bytesReceivedHere) { - - connectedUSBMIDIDevices[ip][d].numBytesReceived = 0; - - __restrict__ uint8_t const* readPos = connectedUSBMIDIDevices[ip][d].receiveData; - const uint8_t* const stopAt = readPos + bytesReceivedHere; - - // Receive all the stuff from this device - for (; readPos < stopAt; readPos += 4) { - - uint8_t statusType = readPos[0] & 0x0F; - uint8_t cable = (readPos[0] & 0xF0) >> 4; - uint8_t channel = readPos[1] & 0x0F; - uint8_t data1 = readPos[2]; - uint8_t data2 = readPos[3]; - - if (statusType < 0x08) { - if (statusType == 2 || statusType == 3) { // 2 or 3 byte system common messages - statusType = 0x0F; - } - else { // Invalid, or sysex, or something - checkIncomingUsbSysex(readPos, ip, d, cable); - continue; - } - } - // select appropriate device based on the cable number - if (cable > connectedUSBMIDIDevices[ip][d].maxPortConnected) { - // fallback to cable 0 since we don't support more than one port on hosted devices yet - cable = 0; - } - midiMessageReceived(*connectedUSBMIDIDevices[ip][d].cable[cable], statusType, channel, data1, - data2, &timeLastBRDY[ip]); - } - } - - if (usbLockNow) { - continue; - } - - // And maybe setup transfer to receive more data - - // As peripheral - if (aPeripheral) { - - g_usb_midi_recv_utr[ip][0].keyword = USB_CFG_PMIDI_BULK_IN; - g_usb_midi_recv_utr[ip][0].tranlen = 64; - - connectedUSBMIDIDevices[ip][0].currentlyWaitingToReceive = 1; - - USBAutoLock lock; - g_p_usb_pipe[USB_CFG_PMIDI_BULK_IN] = &g_usb_midi_recv_utr[ip][0]; - usb_receive_start_rohan_midi(USB_CFG_PMIDI_BULK_IN); - } - - // Or as host - else if (connectedUSBMIDIDevices[ip][d].cable[0]) { - - // Only allowed to setup receive-transfer if not in the process of sending to various devices. - // (Wait, still? Was this just because of that insane bug that's now fixed?) - if (usbDeviceNumBeingSentToNow[ip] == stopSendingAfterDeviceNum[ip]) { - USBAutoLock lock; - setupUSBHostReceiveTransfer(ip, d); - } - } - } - } - } -} - #define MISSING_MESSAGE_CHECK 0 #if MISSING_MESSAGE_CHECK diff --git a/src/deluge/io/midi/midi_engine.h b/src/deluge/io/midi/midi_engine.h index 52c4a07661..8aff03841c 100644 --- a/src/deluge/io/midi/midi_engine.h +++ b/src/deluge/io/midi/midi_engine.h @@ -65,9 +65,6 @@ class MidiEngine { void sendNote(MIDISource source, bool on, int32_t note, uint8_t velocity, uint8_t channel, int32_t filter); void sendCC(MIDISource source, int32_t channel, int32_t cc, int32_t value, int32_t filter); - void checkIncomingUsbMidi(); - void checkIncomingUsbSysex(uint8_t const* message, int32_t ip, int32_t d, int32_t cable); - void sendMidi(MIDISource source, MIDIMessage message, int32_t filter = kMIDIOutputFilterNoMPE, bool sendUSB = true); void sendClock(MIDISource source, bool sendUSB = true, int32_t howMany = 1); void sendStart(MIDISource source); @@ -90,8 +87,6 @@ class MidiEngine { void sendChannelAftertouch(MIDISource source, int32_t channel, uint8_t value, int32_t filter); void sendPolyphonicAftertouch(MIDISource source, int32_t channel, uint8_t value, uint8_t noteCode, int32_t filter); bool anythingInOutputBuffer(); - void setupUSBHostReceiveTransfer(int32_t ip, int32_t midiDeviceNum); - void flushUSBMIDIOutput(); // If bit "16" (actually bit 4) is 1, this is a program change. (Wait, still?) LearnedMIDI globalMIDICommands[kNumGlobalMIDICommands]; @@ -141,20 +136,12 @@ class MidiEngine { EventStackStorage eventStack_; /// Top of the event stack. If this is equal to eventStack_.begin(), the stack is empty. EventStackStorage::iterator eventStackTop_; - - int32_t getPotentialNumConnectedUSBMIDIDevices(int32_t ip); }; -uint32_t setupUSBMessage(MIDIMessage message); - extern MidiEngine midiEngine; extern "C" { #endif -extern uint16_t g_usb_usbmode; - -void usbSendCompleteAsHost(int32_t ip); // used when deluge is in host mode -void usbSendCompleteAsPeripheral(int32_t ip); // used in peripheral mode #ifdef __cplusplus } #endif diff --git a/src/deluge/io/midi/midi_root_complex.h b/src/deluge/io/midi/midi_root_complex.h index 3f061970d5..b689917d42 100644 --- a/src/deluge/io/midi/midi_root_complex.h +++ b/src/deluge/io/midi/midi_root_complex.h @@ -18,6 +18,15 @@ #pragma once #include "definitions_cxx.hpp" +#include "io/midi/midi_device.h" + +enum class RootComplexType { + RC_DIN, + RC_USB_PERIPHERAL, + /// needs the RC_ prefix even though this is an enum class because USB_HOST is a preprocessor macro in some of the + /// RZA1 headers + RC_USB_HOST, +}; /// Represents a group of cables we can do I/O on. /// @@ -25,6 +34,52 @@ /// bridge to the PCIe bus by the bus controller. Similarly, this class represents the interface from the Deluge /// software to a MIDI device connection. class MIDIRootComplex { + class CableIterator { + private: + MIDIRootComplex& parent_; + size_t index_; + + public: + CableIterator(const CableIterator&) = default; + CableIterator(CableIterator&&) = default; + CableIterator& operator=(const CableIterator&) = delete; + CableIterator& operator=(CableIterator&&) = delete; + + CableIterator(MIDIRootComplex& parent, size_t index) : parent_{parent}, index_{index} {} + + CableIterator& operator++() { + index_++; + return *this; + } + + CableIterator operator++(int) { + auto pre = *this; + index_++; + return pre; + } + + friend bool operator==(CableIterator const& a, CableIterator const& b) { + return &a.parent_ == &b.parent_ && a.index_ == b.index_; + }; + + friend bool operator!=(CableIterator const& a, CableIterator const& b) { + return &a.parent_ != &b.parent_ || a.index_ != b.index_; + }; + + MIDICable& operator*() { return *parent_.getCable(index_); } + MIDICable* operator->() { return parent_.getCable(index_); } + }; + + class CableSet { + public: + MIDIRootComplex& parent; + + explicit CableSet(MIDIRootComplex& ccplex) : parent{ccplex} {} + + CableIterator begin() { return CableIterator{parent, 0}; } + CableIterator end() { return CableIterator{parent, parent.getNumCables()}; } + }; + public: MIDIRootComplex() = default; MIDIRootComplex(const MIDIRootComplex&) = delete; @@ -34,6 +89,13 @@ class MIDIRootComplex { virtual ~MIDIRootComplex() = default; + [[nodiscard]] virtual RootComplexType getType() const = 0; + + [[nodiscard]] virtual size_t getNumCables() const = 0; + [[nodiscard]] virtual MIDICable* getCable(size_t cableIdx) = 0; + + CableSet getCables() { return CableSet{*this}; } + /// Flush as much data as possible from any internal buffers to hardware queues virtual void flush() = 0; diff --git a/src/deluge/io/midi/root_complex/din.h b/src/deluge/io/midi/root_complex/din.h index 6aad30ce17..ffbce853fe 100644 --- a/src/deluge/io/midi/root_complex/din.h +++ b/src/deluge/io/midi/root_complex/din.h @@ -30,6 +30,11 @@ class DINRootComplex : public MIDIRootComplex { DINRootComplex(); ~DINRootComplex() override; + [[nodiscard]] RootComplexType getType() const override { return RootComplexType::RC_DIN; }; + + [[nodiscard]] size_t getNumCables() const override { return 1; } + [[nodiscard]] MIDICable* getCable(size_t cableIdx) override { return (cableIdx == 0) ? &cable : nullptr; } + void flush() override; [[nodiscard]] Error poll() override; }; diff --git a/src/deluge/io/midi/root_complex/usb_hosted.cpp b/src/deluge/io/midi/root_complex/usb_hosted.cpp new file mode 100644 index 0000000000..caf860b592 --- /dev/null +++ b/src/deluge/io/midi/root_complex/usb_hosted.cpp @@ -0,0 +1,314 @@ +/* + * Copyright © 2024 Synthstrom Audible Limited + * + * This file is part of The Synthstrom Audible Deluge Firmware. + * + * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#include "usb_hosted.h" +#include "deluge/io/usb/usb_state.h" +#include "drivers/uart/uart.h" +#include "io/debug/log.h" +#include "io/midi/midi_device_manager.h" +#include "io/midi/midi_engine.h" + +using namespace deluge::io::usb; + +MIDIRootComplexUSBHosted::MIDIRootComplexUSBHosted() = default; + +MIDIRootComplexUSBHosted::~MIDIRootComplexUSBHosted() { + // Clean up the pointers to our cables + for (auto i = 0; i < MAX_NUM_USB_MIDI_DEVICES; ++i) { + auto& connectedDevice = connectedUSBMIDIDevices[0][i]; + for (auto j = 0; j < ((sizeof(connectedDevice.cable) / sizeof(connectedDevice.cable[0]))); ++j) { + connectedDevice.cable[j] = nullptr; + } + } +} + +void flushUSBMIDIToHostedDevice(int32_t ip, int32_t d, bool resume = false); + +extern "C" { + +#include "RZA1/usb/userdef/r_usb_hmidi_config.h" + +extern uint8_t currentlyAccessingCard; +extern void usb_send_start_rohan(usb_utr_t* ptr, uint16_t pipe, uint8_t const* data, int32_t size); +extern void change_destination_of_send_pipe(usb_utr_t* ptr, uint16_t pipe, uint16_t* tbl, int32_t sq); + +// We now bypass calling this for successful as peripheral on A1 (see usb_pstd_bemp_pipe_process_rohan_midi()) +void usbSendCompleteAsHost(int32_t ip) { + + int32_t midiDeviceNum = usbDeviceNumBeingSentToNow[ip]; + + ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][midiDeviceNum]; + + connectedDevice->numBytesSendingNow = 0; // We just do this instead from caller on A1 (see comment above) + + // check if there was more to send on the same device, then resume sending + bool has_more = connectedDevice->consumeSendData(); + if (has_more) { + // TODO: do some cooperative scheduling here. so if there is a flood of data + // on connected device 1 and we just want to send a few notes on device 2, + // make sure device 2 ges a fair shot now and then + + flushUSBMIDIToHostedDevice(ip, midiDeviceNum, true); + return; + } + + // If that was the last device we were going to send to, that send's been done, so we can just get out. + if (midiDeviceNum == stopSendingAfterDeviceNum[ip]) { + anyUSBSendingStillHappening[ip] = 0; + return; + } + + while (true) { + midiDeviceNum++; + if (midiDeviceNum >= MAX_NUM_USB_MIDI_DEVICES) { + midiDeviceNum -= MAX_NUM_USB_MIDI_DEVICES; + } + connectedDevice = &connectedUSBMIDIDevices[ip][midiDeviceNum]; + if (connectedDevice->cable[0] && connectedDevice->numBytesSendingNow) { + // If here, we got a connected device, so flush + flushUSBMIDIToHostedDevice(ip, midiDeviceNum); + return; + } + if (midiDeviceNum == stopSendingAfterDeviceNum[ip]) { // If reached end of devices and last one got disconnected + // in the interim (very rare) + usbDeviceNumBeingSentToNow[ip] = stopSendingAfterDeviceNum[ip]; + anyUSBSendingStillHappening[ip] = 0; + return; + } + } +} +} + +void setupUSBHostReceiveTransfer(int32_t ip, int32_t midiDeviceNum) { + connectedUSBMIDIDevices[ip][midiDeviceNum].currentlyWaitingToReceive = 1; + + int32_t pipeNumber = g_usb_hmidi_tmp_ep_tbl[USB_CFG_USE_USBIP][midiDeviceNum][USB_EPL]; + + g_usb_midi_recv_utr[USB_CFG_USE_USBIP][midiDeviceNum].keyword = pipeNumber; + g_usb_midi_recv_utr[USB_CFG_USE_USBIP][midiDeviceNum].tranlen = 64; + + g_p_usb_pipe[pipeNumber] = &g_usb_midi_recv_utr[USB_CFG_USE_USBIP][midiDeviceNum]; + + // uint16_t startTime = *TCNT[TIMER_SYSTEM_SUPERFAST]; + + usb_receive_start_rohan_midi(pipeNumber); + + /* + uint16_t endTime = *TCNT[TIMER_SYSTEM_SUPERFAST]; + uint16_t duration = endTime - startTime; + uint32_t timePassedNS = superfastTimerCountToNS(duration); + uartPrint("send setup duration, nSec: "); + uartPrintNumber(timePassedNS); + */ +} + +void flushUSBMIDIToHostedDevice(int32_t ip, int32_t d, bool resume) { + ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][d]; + // there was an assumption that the pipe wouldn't have changed if we were resuming a transfer but that has turned + // out not to be true if hubs are involved. A hub transaction seems to be able to run before the + // usbSendCompleteAsHost interrupt is called and changes the pipe, and then the next write doesn't go anywhere + // useful + int32_t pipeNumber = g_usb_hmidi_tmp_ep_tbl[USB_CFG_USE_USBIP][d][0]; + g_usb_midi_send_utr[USB_CFG_USE_USBIP].keyword = pipeNumber; + g_usb_midi_send_utr[USB_CFG_USE_USBIP].tranlen = connectedDevice->numBytesSendingNow; + g_usb_midi_send_utr[USB_CFG_USE_USBIP].p_tranadr = connectedDevice->dataSendingNow; + + usbDeviceNumBeingSentToNow[USB_CFG_USE_USBIP] = d; + + int32_t isInterrupt = (pipeNumber == USB_CFG_HMIDI_INT_SEND); + + if (d != currentDeviceNumWithSendPipe[isInterrupt]) { + currentDeviceNumWithSendPipe[isInterrupt] = d; + change_destination_of_send_pipe(&g_usb_midi_send_utr[USB_CFG_USE_USBIP], pipeNumber, + g_usb_hmidi_tmp_ep_tbl[USB_CFG_USE_USBIP][d], connectedDevice->sq); + } + + connectedDevice->sq = !connectedDevice->sq; + + g_p_usb_pipe[pipeNumber] = &g_usb_midi_send_utr[USB_CFG_USE_USBIP]; + + usb_send_start_rohan(&g_usb_midi_send_utr[USB_CFG_USE_USBIP], pipeNumber, connectedDevice->dataSendingNow, + connectedDevice->numBytesSendingNow); +} + +void MIDIRootComplexUSBHosted::flush() { + if (usbLock) { + return; + } + + constexpr int ip = 0; + + anythingInUSBOutputBuffer = false; + + // is this still relevant? anyUSBSendingStillHappening[ip] acts as the lock between routine and interrupt + // on the sending side. all other uses of usbLock seems to be about _receiving_. Can there be a conflict + // between sending and receiving as well?? + USBAutoLock lock; + + if (anyUSBSendingStillHappening[ip]) { + // still sending, call me later maybe + anythingInUSBOutputBuffer = true; + return; + } + + // This next bit was written with multiple devices on hubs in mind, but seems to work for a single MIDI + // device too + + int32_t midiDeviceNumToSendTo = currentDeviceNumWithSendPipe[0]; // This will do + if (midiDeviceNumToSendTo >= MAX_NUM_USB_MIDI_DEVICES) { + midiDeviceNumToSendTo = 0; // In case it was set to "none", I think + } + + int32_t newStopSendingAfter = midiDeviceNumToSendTo - 1; + if (newStopSendingAfter < 0) { + newStopSendingAfter += MAX_NUM_USB_MIDI_DEVICES; + } + + // Make sure that's on a connected device - it probably would be... + while (true) { + ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][midiDeviceNumToSendTo]; + if (connectedDevice->cable[0] && connectedDevice->hasBufferedSendData()) { + break; // We found a connected one + } + if (midiDeviceNumToSendTo == newStopSendingAfter) { + return; + } + midiDeviceNumToSendTo++; + if (midiDeviceNumToSendTo >= MAX_NUM_USB_MIDI_DEVICES) { + midiDeviceNumToSendTo = 0; // Wrap back to start of list + } + } + + // Stop after a device which we know is connected + while (true) { + ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][newStopSendingAfter]; + + if (connectedDevice->cable[0] && connectedDevice->hasBufferedSendData()) { + break; // We found a connected one + } + + newStopSendingAfter--; + if (newStopSendingAfter < 0) { + newStopSendingAfter += MAX_NUM_USB_MIDI_DEVICES; + } + } + + // Copy the buffers for all devices + int32_t d = midiDeviceNumToSendTo; + while (true) { + ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][d]; + + if (connectedDevice->cable[0]) { + connectedDevice->consumeSendData(); + } + if (d == newStopSendingAfter) { + break; + } + d++; + if (d >= MAX_NUM_USB_MIDI_DEVICES) { + d = 0; + } + } + + stopSendingAfterDeviceNum[ip] = newStopSendingAfter; + anyUSBSendingStillHappening[ip] = 1; + + flushUSBMIDIToHostedDevice(ip, midiDeviceNumToSendTo); +} + +Error MIDIRootComplexUSBHosted::poll() { + if (currentlyAccessingCard != 0) { + // D_PRINTLN("checkIncomingUsbMidi seeing currentlyAccessingCard non-zero"); + return Error::NO_ERROR_BUT_GET_OUT; + } + + bool usbLockNow = usbLock; + + // TODO: delete this, we don't ever need to iterate over IPs + constexpr auto ip = 0; + + // assumes only single device in peripheral mode + int32_t numDevicesNow = MAX_NUM_USB_MIDI_DEVICES; + + for (int32_t d = 0; d < numDevicesNow; d++) { + auto& connectedDevice = connectedUSBMIDIDevices[ip][d]; + if (connectedDevice.cable[0] != nullptr && connectedDevice.currentlyWaitingToReceive == 0u) { + + int32_t bytesReceivedHere = connectedDevice.numBytesReceived; + if (bytesReceivedHere) { + + connectedDevice.numBytesReceived = 0; + + const uint8_t* __restrict__ readPos = connectedDevice.receiveData; + const uint8_t* const stopAt = readPos + bytesReceivedHere; + + // Receive all the stuff from this device + for (; readPos < stopAt; readPos += 4) { + + uint8_t statusType = readPos[0] & 0x0F; + uint8_t cable = (readPos[0] & 0xF0) >> 4; + uint8_t channel = readPos[1] & 0x0F; + uint8_t data1 = readPos[2]; + uint8_t data2 = readPos[3]; + + if (statusType < 0x08) { + if (statusType == 2 || statusType == 3) { // 2 or 3 byte system common messages + statusType = 0x0F; + } + else { // Invalid, or sysex, or something + // XXX: this collapses all cables to cable 0, but we only technically support 1 cable on + // remote USB devices for now.. + connectedDevice.cable[0]->checkIncomingSysex(readPos, ip, d); + continue; + } + } + // select appropriate device based on the cable number + if (cable > connectedDevice.maxPortConnected) { + // fallback to cable 0 since we don't support more than one port on hosted devices yet + cable = 0; + } + midiEngine.midiMessageReceived(*connectedDevice.cable[cable], statusType, channel, data1, data2, + &timeLastBRDY[ip]); + } + } + + // If this is a reentrant invocation, don't do transfer setup + if (usbLockNow) { + continue; + } + + // And maybe setup transfer to receive more data + if (connectedDevice.cable[0] != nullptr) { + // Only allowed to setup receive-transfer if not in the process of sending to various devices. (Wait, + // still? Was this just because of that insane bug that's now fixed?) + if (usbDeviceNumBeingSentToNow[ip] == stopSendingAfterDeviceNum[ip]) { + USBAutoLock lock; + setupUSBHostReceiveTransfer(ip, d); + } + } + } + } + + return Error::NONE; +} + +MIDICable* MIDIRootComplexUSBHosted::getCable(size_t index) { + if (index < 0 || index >= hostedMIDIDevices_.getNumElements()) { + return nullptr; + } + return static_cast(hostedMIDIDevices_.getElement(static_cast(index))); +} diff --git a/src/deluge/io/midi/root_complex/usb_hosted.h b/src/deluge/io/midi/root_complex/usb_hosted.h new file mode 100644 index 0000000000..a16a37e517 --- /dev/null +++ b/src/deluge/io/midi/root_complex/usb_hosted.h @@ -0,0 +1,55 @@ +/* + * Copyright © 2024 Synthstrom Audible Limited + * + * This file is part of The Synthstrom Audible Deluge Firmware. + * + * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#pragma once + +#include "io/midi/cable_types/usb_hosted.h" +#include "io/midi/midi_root_complex.h" +#include "util/container/vector/named_thing_vector.h" +#include + +class MIDIRootComplexUSBHosted : public MIDIRootComplex { +private: +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-offsetof" + NamedThingVector hostedMIDIDevices_{__builtin_offsetof(MIDICableUSBHosted, name)}; +#pragma GCC diagnostic pop + +public: + MIDIRootComplexUSBHosted(const MIDIRootComplexUSBHosted&) = delete; + MIDIRootComplexUSBHosted(MIDIRootComplexUSBHosted&&) = delete; + MIDIRootComplexUSBHosted& operator=(const MIDIRootComplexUSBHosted&) = delete; + MIDIRootComplexUSBHosted& operator=(MIDIRootComplexUSBHosted&&) = delete; + + MIDIRootComplexUSBHosted(); + ~MIDIRootComplexUSBHosted() override; + + [[nodiscard]] RootComplexType getType() const override { return RootComplexType::RC_USB_HOST; } + + [[nodiscard]] size_t getNumCables() const override { return hostedMIDIDevices_.getNumElements(); } + [[nodiscard]] MIDICable* getCable(size_t index) override; + + [[nodiscard]] NamedThingVector& getHostedMIDIDevices() { return hostedMIDIDevices_; } + [[nodiscard]] NamedThingVector const& getHostedMIDIDevices() const { return hostedMIDIDevices_; } + + void flush() override; + [[nodiscard]] Error poll() override; +}; + +extern "C" { +void usbSendCompleteAsHost(int32_t ip); +} diff --git a/src/deluge/io/midi/root_complex/usb_peripheral.cpp b/src/deluge/io/midi/root_complex/usb_peripheral.cpp new file mode 100644 index 0000000000..f6c553721d --- /dev/null +++ b/src/deluge/io/midi/root_complex/usb_peripheral.cpp @@ -0,0 +1,186 @@ +/* + * Copyright © 2024 Synthstrom Audible Limited + * + * This file is part of The Synthstrom Audible Deluge Firmware. + * + * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#include "usb_peripheral.h" + +#include "io/midi/midi_device_manager.h" +#include "io/midi/midi_engine.h" +#include "io/usb/usb_state.h" + +using namespace deluge::io::usb; + +extern "C" { +#include "drivers/usb/userdef/r_usb_pmidi_config.h" + +extern uint8_t currentlyAccessingCard; + +extern void usb_send_start_rohan(usb_utr_t* ptr, uint16_t pipe, uint8_t const* data, int32_t size); + +void usbSendCompleteAsPeripheral(int32_t ip) { + ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][0]; + connectedDevice->numBytesSendingNow = 0; // Even easier! + + // I think this could happen as part of a detach see detachedAsPeripheral() + if (anyUSBSendingStillHappening[ip] == 0) { + return; + } + + bool has_more = connectedDevice->consumeSendData(); + if (has_more) { + // this is already the case: + // anyUSBSendingStillHappening[ip] = 1; + + g_usb_midi_send_utr[ip].tranlen = connectedDevice->numBytesSendingNow; + g_usb_midi_send_utr[ip].p_tranadr = connectedDevice->dataSendingNow; + + usb_send_start_rohan(NULL, USB_CFG_PMIDI_BULK_OUT, connectedDevice->dataSendingNow, + connectedDevice->numBytesSendingNow); + } + else { + // this effectively serves as a lock, does the sending part of the device, including the read part of the + // ring buffer "belong" to ongoing/scheduled interrupts. Document this better. + anyUSBSendingStillHappening[0] = 0; + } +} +} + +MIDIRootComplexUSBPeripheral::MIDIRootComplexUSBPeripheral() : cables_{0, 1, 2} { +} + +MIDIRootComplexUSBPeripheral::~MIDIRootComplexUSBPeripheral() { + // Clean up the pointers to our cables + for (auto i = 0; i < MAX_NUM_USB_MIDI_DEVICES; ++i) { + auto& connectedDevice = connectedUSBMIDIDevices[0][i]; + for (auto j = 0; j < ((sizeof(connectedDevice.cable) / sizeof(connectedDevice.cable[0]))); ++j) { + connectedDevice.cable[j] = nullptr; + } + } +} + +void MIDIRootComplexUSBPeripheral::flush() { + constexpr int ip = 0; + + if (usbLock) { + return; + } + + anythingInUSBOutputBuffer = false; + + // is this still relevant? anyUSBSendingStillHappening[ip] acts as the lock between routine and interrupt + // on the sending side. all other uses of usbLock seems to be about _receiving_. Can there be a conflict + // between sending and receiving as well?? + USBAutoLock lock; + + ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][0]; + + if (anyUSBSendingStillHappening[ip]) { + // still sending, call me later maybe + anythingInUSBOutputBuffer = true; + return; + } + + if (!connectedDevice->consumeSendData()) { + return; + } + + g_usb_midi_send_utr[ip].keyword = USB_CFG_PMIDI_BULK_OUT; + g_usb_midi_send_utr[ip].tranlen = connectedDevice->numBytesSendingNow; + g_usb_midi_send_utr[ip].p_tranadr = connectedDevice->dataSendingNow; + + usbDeviceNumBeingSentToNow[ip] = 0; + anyUSBSendingStillHappening[ip] = 1; + + g_p_usb_pipe[USB_CFG_PMIDI_BULK_OUT] = &g_usb_midi_send_utr[ip]; + usb_send_start_rohan(NULL, USB_CFG_PMIDI_BULK_OUT, connectedDevice->dataSendingNow, + connectedDevice->numBytesSendingNow); + + // when done, usbSendCompleteAsPeripheral() will be called in an interrupt +} + +Error MIDIRootComplexUSBPeripheral::poll() { + if (currentlyAccessingCard != 0) { + // D_PRINTLN("checkIncomingUsbMidi seeing currentlyAccessingCard non-zero"); + return Error::NO_ERROR_BUT_GET_OUT; + } + + bool usbLockNow = usbLock; + + constexpr auto ip = 0; + constexpr auto d = 0; + + // assumes only single device in peripheral mode + int32_t numDevicesNow = 1; + + auto& connectedDevice = connectedUSBMIDIDevices[ip][d]; + + if (connectedDevice.cable[0] && !connectedDevice.currentlyWaitingToReceive) { + int32_t bytesReceivedHere = connectedDevice.numBytesReceived; + if (bytesReceivedHere) { + + connectedDevice.numBytesReceived = 0; + + uint8_t const* __restrict__ readPos = connectedDevice.receiveData; + uint8_t const* const stopAt = readPos + bytesReceivedHere; + + // Receive all the stuff from this device + for (; readPos < stopAt; readPos += 4) { + + uint8_t statusType = readPos[0] & 0x0F; + uint8_t cable = (readPos[0] & 0xF0) >> 4; + uint8_t channel = readPos[1] & 0x0F; + uint8_t data1 = readPos[2]; + uint8_t data2 = readPos[3]; + + if (statusType < 0x08) { + if (statusType == 2 || statusType == 3) { // 2 or 3 byte system common messages + statusType = 0x0F; + } + else { // Invalid, or sysex, or something + if (cable < this->cables_.size()) { + this->cables_[cable].checkIncomingSysex(readPos, ip, d); + } + continue; + } + } + // select appropriate device based on the cable number + if (cable > connectedDevice.maxPortConnected) { + // fallback to cable 0 since we don't support more than one port on hosted devices yet + cable = 0; + } + midiEngine.midiMessageReceived(*connectedDevice.cable[cable], statusType, channel, data1, data2, + &timeLastBRDY[ip]); + } + } + + if (usbLockNow) { + return Error::NONE; + } + + // And maybe setup transfer to receive more data + + g_usb_midi_recv_utr[ip][0].keyword = USB_CFG_PMIDI_BULK_IN; + g_usb_midi_recv_utr[ip][0].tranlen = 64; + + connectedUSBMIDIDevices[ip][0].currentlyWaitingToReceive = 1; + + USBAutoLock lock; + g_p_usb_pipe[USB_CFG_PMIDI_BULK_IN] = &g_usb_midi_recv_utr[ip][0]; + usb_receive_start_rohan_midi(USB_CFG_PMIDI_BULK_IN); + } + + return Error::NONE; +} diff --git a/src/deluge/io/midi/root_complex/usb_peripheral.h b/src/deluge/io/midi/root_complex/usb_peripheral.h new file mode 100644 index 0000000000..a2c82be044 --- /dev/null +++ b/src/deluge/io/midi/root_complex/usb_peripheral.h @@ -0,0 +1,50 @@ +/* + * Copyright © 2024 Synthstrom Audible Limited + * + * This file is part of The Synthstrom Audible Deluge Firmware. + * + * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +#pragma once + +#include "io/midi/cable_types/usb_device_cable.h" +#include "io/midi/midi_root_complex.h" +#include + +class MIDIRootComplexUSBPeripheral : public MIDIRootComplex { +private: + using CableArray = std::array; + + CableArray cables_; + +public: + MIDIRootComplexUSBPeripheral(const MIDIRootComplexUSBPeripheral&) = delete; + MIDIRootComplexUSBPeripheral(MIDIRootComplexUSBPeripheral&&) = delete; + MIDIRootComplexUSBPeripheral& operator=(const MIDIRootComplexUSBPeripheral&) = delete; + MIDIRootComplexUSBPeripheral& operator=(MIDIRootComplexUSBPeripheral&&) = delete; + + MIDIRootComplexUSBPeripheral(); + ~MIDIRootComplexUSBPeripheral() override; + + [[nodiscard]] size_t getNumCables() const override { + // This returns 2, not 3, because the 3rd cable is secret (only used by sysex infrastructure) + return 2; + } + + [[nodiscard]] MIDICable* getCable(size_t index) override { return &cables_[index]; } + + [[nodiscard]] RootComplexType getType() const override { return RootComplexType::RC_USB_PERIPHERAL; } + + void flush() override; + [[nodiscard]] Error poll() override; +}; diff --git a/src/deluge/io/usb/usb_state.cpp b/src/deluge/io/usb/usb_state.cpp index e2f5dc7a8f..d16206844f 100644 --- a/src/deluge/io/usb/usb_state.cpp +++ b/src/deluge/io/usb/usb_state.cpp @@ -16,6 +16,83 @@ */ #include "usb_state.h" +#include "RZA1/system/iodefines/dmac_iodefine.h" +#include "io/midi/midi_device_manager.h" +#include "io/midi/root_complex/usb_hosted.h" + +extern "C" { +#include "drivers/uart/uart.h" + +// XXX: manually declared here instead of including r_usb_extern.h becuse that header doesn't compile as C++ code. +extern usb_regadr_t usb_hstd_get_usb_ip_adr(uint16_t ipno); + +usb_utr_t g_usb_midi_send_utr[USB_NUM_USBIP]; +usb_utr_t g_usb_midi_recv_utr[USB_NUM_USBIP][MAX_NUM_USB_MIDI_DEVICES]; + +uint8_t currentDeviceNumWithSendPipe[2] = {MAX_NUM_USB_MIDI_DEVICES, MAX_NUM_USB_MIDI_DEVICES}; + +uint32_t timeLastBRDY[USB_NUM_USBIP]; + +void usbReceiveComplete(int32_t ip, int32_t deviceNum, int32_t tranlen) { + ConnectedUSBMIDIDevice* connectedDevice = &connectedUSBMIDIDevices[ip][deviceNum]; + + connectedDevice->numBytesReceived = 64 - tranlen; // Seems wack, but yet, tranlen is now how many bytes didn't get + // received out of the original transfer size + // Warning - sometimes (with a Teensy, e.g. my knob box), length will be 0. Not sure why - but we need to cope with + // that case. + + connectedDevice->currentlyWaitingToReceive = 0; // Take note that we need to set up another receive +} + +// We now bypass calling this for successful as peripheral on A1 (see usb_pstd_bemp_pipe_process_rohan_midi()) +void usbSendCompletePeripheralOrA1(usb_utr_t* p_mess, uint16_t data1, uint16_t data2) { + // If error, forget about device. + // No actually don't - sometimes there'll be an error if another device connected or disconnected from hub during + // fast MIDI sending. This seems to happen even though I've stopped it from setting up or down the out-pipe as it + // goes + if (p_mess->status == USB_DATA_ERR) { + if constexpr (ALPHA_OR_BETA_VERSION) { + uartPrintln("USB Send error"); + } + // g_usb_host_connected[deviceNum] = 0; + } + +#if USB_NUM_USBIP == 1 + int32_t ip = 0; +#else + int32_t ip = p_mess->ip; +#endif + + usbSendCompleteAsHost(ip); +} + +void usbReceiveCompletePeripheralOrA1(usb_utr_t* p_mess, uint16_t data1, uint16_t data2) { + +#if USB_NUM_USBIP == 1 + int32_t ip = 0; +#else + int32_t ip = p_mess->ip; +#endif + + if (p_mess->status == USB_DATA_ERR) { + return; // Can happen if user disconnects device - totally normal + } + + int32_t deviceNum = p_mess - &g_usb_midi_recv_utr[ip][0]; + + // Are there actually any other possibilities that could happen here? Can't remember. + if (p_mess->status != USB_DATA_SHT) { + uartPrint("status: "); + uartPrintNumber(p_mess->status); + } + + usbReceiveComplete(ip, deviceNum, p_mess->tranlen); +} + +void brdyOccurred(int32_t ip) { + timeLastBRDY[ip] = DMACnNonVolatile(SSI_TX_DMA_CHANNEL).CRSA_n; // Reading this not as volatile works fine +} +} namespace deluge::io::usb { volatile bool usbLock; @@ -25,11 +102,32 @@ uint8_t stopSendingAfterDeviceNum[USB_NUM_USBIP]; uint8_t usbDeviceNumBeingSentToNow[USB_NUM_USBIP]; uint8_t anyUSBSendingStillHappening[USB_NUM_USBIP]; -} // namespace deluge::io::usb -extern "C" { +void usbSetup() { + g_usb_peri_connected = 0; // Needs initializing with A2 driver -usb_utr_t g_usb_midi_send_utr[USB_NUM_USBIP]; -usb_utr_t g_usb_midi_recv_utr[USB_NUM_USBIP][MAX_NUM_USB_MIDI_DEVICES]; + for (int32_t ip = 0; ip < USB_NUM_USBIP; ip++) { -uint8_t currentDeviceNumWithSendPipe[2] = {MAX_NUM_USB_MIDI_DEVICES, MAX_NUM_USB_MIDI_DEVICES}; + // This might not be used due to the change in r_usb_hlibusbip (deluge is host) to call usbSendCompleteAsHost() + // directly and the change in r_usb_plibusbip (deluge is pheriperal) to just set some variables or it might be + // used for some other interrups like error conditions??? + // TODO: try to delet this and see if something breaks.. + g_usb_midi_send_utr[ip].complete = (usb_cb_t)usbSendCompletePeripheralOrA1; + + g_usb_midi_send_utr[ip].p_setup = 0; /* Setup message address set */ + g_usb_midi_send_utr[ip].segment = USB_TRAN_END; + g_usb_midi_send_utr[ip].ip = ip; + g_usb_midi_send_utr[ip].ipp = usb_hstd_get_usb_ip_adr(ip); + + for (int32_t d = 0; d < MAX_NUM_USB_MIDI_DEVICES; d++) { + g_usb_midi_recv_utr[ip][d].p_tranadr = connectedUSBMIDIDevices[ip][d].receiveData; + g_usb_midi_recv_utr[ip][d].complete = (usb_cb_t)usbReceiveCompletePeripheralOrA1; + + g_usb_midi_recv_utr[ip][d].p_setup = 0; /* Setup message address set */ + g_usb_midi_recv_utr[ip][d].segment = USB_TRAN_END; + g_usb_midi_recv_utr[ip][d].ip = ip; + g_usb_midi_recv_utr[ip][d].ipp = usb_hstd_get_usb_ip_adr(ip); + } + } } + +} // namespace deluge::io::usb diff --git a/src/deluge/io/usb/usb_state.h b/src/deluge/io/usb/usb_state.h index 914dfbbe78..f429503b9e 100644 --- a/src/deluge/io/usb/usb_state.h +++ b/src/deluge/io/usb/usb_state.h @@ -51,6 +51,9 @@ extern uint8_t usbDeviceNumBeingSentToNow[USB_NUM_USBIP]; /// Flag to prevent reentrant sending extern uint8_t anyUSBSendingStillHappening[USB_NUM_USBIP]; +/// up internal USB state +void usbSetup(); + } // namespace deluge::io::usb extern "C" { @@ -71,6 +74,24 @@ extern uint16_t g_usb_hmidi_tmp_ep_tbl[USB_NUM_USBIP][MAX_NUM_USB_MIDI_DEVICES][ // One without, and one with, interrupt endpoints extern uint8_t currentDeviceNumWithSendPipe[2]; +extern uint16_t g_usb_usbmode; + +/// Timestamp of the last BRDY event, from the SSI_TX_DMA_CHANNEL +extern uint32_t timeLastBRDY[USB_NUM_USBIP]; + +/// Start a receive operation on a given pipe +void usb_receive_start_rohan_midi(uint16_t pipe); + +// Used when deluge is in host mode. +// +// Implemented in root_complex/usb_hosted.cpp +void usbSendCompleteAsHost(int32_t ip); + +// Used when deluge is in peripheral mode. +// +// Implemented in root_complex/usb_peripheral.cpp +void usbSendCompleteAsPeripheral(int32_t ip); + #ifdef __cplusplus } #endif diff --git a/src/deluge/util/container/array/resizeable_array.h b/src/deluge/util/container/array/resizeable_array.h index 852e45fa11..fe4e89b63a 100644 --- a/src/deluge/util/container/array/resizeable_array.h +++ b/src/deluge/util/container/array/resizeable_array.h @@ -53,7 +53,7 @@ class ResizeableArray { return (char* __restrict__)memory + (absoluteIndex * elementSize); } - [[gnu::always_inline]] inline int32_t getNumElements() { return numElements; } + [[gnu::always_inline]] inline int32_t getNumElements() const { return numElements; } uint32_t elementSize; bool emptyingShouldFreeMemory;