-
Notifications
You must be signed in to change notification settings - Fork 2
Add ATmega32U4 USB MIDI 2.0 device example (stock Arduino core) #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # arduino-cli builds into ~/.cache/arduino by default; these appear only if | ||
| # you compile with --build-path . or export locally. | ||
| /build/ | ||
| *.elf | ||
| *.hex | ||
| *.bin | ||
| *.eep | ||
| *.lss | ||
| *.map |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,205 @@ | ||
| # [midi2](../..) | Device MIDI 2.0 | ||
| ## A complete USB MIDI 2.0 device on the ATmega32U4 (Arduino Leonardo / Pro Micro / Micro) | ||
|
|
||
| [](https://github.com/midi2-dev/MIDI2.0Workbench) | ||
|
|
||
|  | ||
|
|
||
| A full USB MIDI 2.0 device on the **ATmega32U4**, the Arduino-friendly sibling of | ||
| [atmega32u4-device-baremetal](../atmega32u4-device-baremetal/): same midi2 C99 | ||
| core, same USB MIDI 2.0 class descriptors, but the USB transport is the | ||
| **[midi2duino](https://github.com/sauloverissimo/midi2duino)** library | ||
| (PluggableUSB, the stock Arduino core, no LUFA and no TinyUSB) instead of LUFA. | ||
| The midi2 core builds and parses the Universal MIDI Packets; midi2duino carries | ||
| them. It flashes with a normal Arduino upload, no manual reset ritual. | ||
|
|
||
| It enumerates as a MIDI 2.0 device (UMP), answers UMP Stream discovery and MIDI-CI | ||
| Discovery, cycles a readable showcase of the MIDI 2.0 message surface every three | ||
| seconds, and echoes back everything it receives. The whole device is 17 KB of | ||
| flash and 1.3 KB of SRAM on a 16 MHz 8-bit part. | ||
|
|
||
| ## Always-UMP: what the Arduino core costs us | ||
|
|
||
| The stock Arduino AVR core (`USBCore.cpp`) accepts the USB `SET_INTERFACE` | ||
| request with an empty handler and never forwards it to PluggableUSB, so the | ||
| firmware cannot observe the host switching the MIDIStreaming interface from | ||
| alt 0 (MIDI 1.0) to alt 1 (MIDI 2.0). This device therefore **always speaks | ||
| UMP** on the bulk endpoints: the descriptors advertise both alternate settings | ||
| for correct enumeration, a MIDI 2.0 host selects alt 1 and receives UMP, and | ||
| that is the supported path. The LUFA baremetal recipe owns the USB stack and can | ||
| track the alt switch, so it additionally offers a MIDI 1.0 fallback on alt 0; | ||
| that fallback is not reachable here. Target a MIDI 2.0 host (Linux UMP, Windows | ||
| MIDI Services, macOS). | ||
|
|
||
| ## USB identity | ||
|
|
||
| | Field | Value | | ||
| |---|---| | ||
| | VID:PID | `CAFE:40D1` (educational VID, development only) | | ||
| | Product | `ATmega32U4 MIDI 2.0` | | ||
| | Manufacturer | `midi2.diy` | | ||
| | UMP Endpoint Name | `ATmega32U4 MIDI 2.0` | | ||
| | FB 0 | `Main` (Bidirectional, 1 group, MIDI 1.0 + 2.0 protocols) | | ||
| | MIDI-CI | Discovery + NAK, capabilities `0x00` (nothing over-advertised), Manufacturer `{0x7D, 0x00, 0x00}`, Model `0x0002` | | ||
|
|
||
| PID `0x40D1` and Model `0x0002` distinguish this device from the LUFA baremetal | ||
| recipe (`0x40D0` / `0x0001`), so a host enumerating both side by side sees | ||
| distinct endpoints and separate caches. The USB descriptor VID/PID/strings come | ||
| from the Arduino build properties (see [Build](#build)); the UMP Endpoint Name | ||
| and the MIDI-CI identity live in the firmware and are reported regardless. Forks | ||
| into real products must replace both `idVendor` and `idProduct`. | ||
|
|
||
| ## Layering | ||
|
|
||
| | Layer | Owns | | ||
| |---|---| | ||
| | Arduino AVR core (external, `arduino:avr`) | USB device core: enumeration, EP0, control transfers, bulk endpoint banks | | ||
| | [midi2duino](https://github.com/sauloverissimo/midi2duino) library (external) | the PluggableUSB node: raw USB-MIDI descriptors (alt 0 MIDI 1.0, alt 1 MIDI 2.0, Group Terminal Block via class GET_DESCRIPTOR), the two bulk endpoints, the UMP word pump (RX backpressure is a plain NAK) | | ||
| | midi2 C99 core ([`../../src`](../../src), vendored under `src/`) | typed dispatch, SysEx7 reassembly, MIDI-CI responder, UMP builders | | ||
| | `src/stream_responder.c` / `src/ci_responder.c` | device identity over UMP Stream and MIDI-CI | | ||
| | `src/midi2_usb.*` | the `Midi2Usb` veneer: wires the responders, pumps USB, exposes readable `send*` helpers. No `std::function`, no heap | | ||
| | `atmega32u4-device-arduino.ino` | the application: identity setup and the showcase loop | | ||
|
|
||
| ## Build | ||
|
|
||
| Requires the **Arduino AVR Boards** core and the | ||
| **[midi2duino](https://github.com/sauloverissimo/midi2duino)** library (the midi2 | ||
| core is vendored under `src/`): | ||
|
|
||
| ```bash | ||
| arduino-cli core install arduino:avr | ||
| arduino-cli lib install midi2duino | ||
| ``` | ||
|
|
||
| Compile for the Leonardo (primary target), overriding the USB identity inline: | ||
|
|
||
| ```bash | ||
| arduino-cli compile -b arduino:avr:leonardo \ | ||
| --build-property build.vid=0xCAFE \ | ||
| --build-property build.pid=0x40D1 \ | ||
| --build-property 'build.usb_product="ATmega32U4 MIDI 2.0"' \ | ||
| --build-property 'build.usb_manufacturer="midi2.diy"' \ | ||
| . | ||
| ``` | ||
|
|
||
| Upload (the CDC serial port stays enabled, so the 1200 bps touch triggers the | ||
| Caterina bootloader automatically, no manual reset): | ||
|
|
||
| ```bash | ||
| arduino-cli upload -b arduino:avr:leonardo -p /dev/ttyACM0 . | ||
| ``` | ||
|
|
||
| **Arduino IDE users**: copy [boards.local.txt](boards.local.txt) next to the | ||
| core's `boards.txt` to apply the identity override, then open the `.ino`, pick | ||
| your board, and click Upload. Without the override the device still works and | ||
| still reports `ATmega32U4 MIDI 2.0` on the UMP wire; only the raw USB descriptor | ||
| VID/PID/strings fall back to the board's Arduino defaults. | ||
|
|
||
| **Other ATmega32U4 boards**: `-b arduino:avr:micro` (Arduino Micro) works the | ||
| same way. The SparkFun Pro Micro needs the SparkFun AVR board package | ||
| (`-b SparkFun:avr:promicro`); the sketch is identical, only the FQBN changes. | ||
|
|
||
| ## Footprint | ||
|
|
||
| Measured with avr-gcc 7.3.0, `-Os -flto --gc-sections`, full firmware | ||
| (Arduino core USB + CDC + descriptors + pump + midi2 dispatch/proc/CI + app): | ||
|
|
||
| | Region | Used | Available | | ||
| |---|---|---| | ||
| | Flash | 17.1 KB (61%) | 28 KB (32 KB minus Caterina) | | ||
| | SRAM | 1.26 KB (49%) | 2.5 KB | | ||
|
|
||
| The CDC serial interface is kept on for the automatic-reset upload flow; that is | ||
| the main SRAM cost over the LUFA baremetal recipe, and it is what buys the | ||
| click-to-upload experience. | ||
|
|
||
| ## Spec coverage | ||
|
|
||
| A readable, representative tour of the MIDI 2.0 surface. Everything below is | ||
| emitted by the showcase in `atmega32u4-device-arduino.ino`, group 0, and built | ||
| with the core's `midi2_msg_*` helpers. | ||
|
|
||
| | UMP message type | What the showcase sends | Spec | | ||
| |---|---|---| | ||
| | MT 0x4 Channel Voice 2.0 | Note On/Off (16-bit velocity), CC (32-bit), Program Change, Pitch Bend (32-bit), Channel Pressure, Poly Pressure, Per-Note Pitch Bend | M2-104-UM 7.4 | | ||
| | MT 0xD Flex Data | Set Tempo, Set Time Signature | M2-104-UM 7.5 | | ||
| | MT 0x3 Data64 | SysEx7 single packet | M2-104-UM 7.7 | | ||
| | MT 0x5 Data128 | SysEx8 single packet, Mixed Data Set (header + payload) | M2-104-UM 7.8/7.9 | | ||
| | MT 0xF UMP Stream | Endpoint Info, Device Identity, Endpoint Name, Product Instance Id, FB Info/Name, Stream Config (answered on request) | M2-104-UM 7.1 | | ||
|
|
||
| | MIDI-CI surface | Status | | ||
| |---|---| | ||
| | Discovery | answered with a per-boot randomized MUID (EEPROM boot counter + timer/frame jitter) | | ||
| | Unsupported categories | NAK; capabilities advertised as `0x00`, so nothing is over-promised | | ||
|
|
||
| Inbound: SysEx7 is reassembled by the core (`midi2_proc`) and delivered to the | ||
| MIDI-CI responder; any other UMP received is echoed straight back. | ||
|
|
||
| ### What this recipe does NOT cover (and why) | ||
|
|
||
| - **MIDI 1.0 fallback (alt 0).** The stock Arduino core hides `SET_INTERFACE`, | ||
| so the firmware cannot switch modes. Always-UMP only. Use the LUFA baremetal | ||
| sibling for a device that is also musical on a MIDI 1.0-only host. | ||
| - **Property Exchange / Profiles.** Capabilities are `0x00` by design; this is | ||
| a Discovery-and-play device, honest about advertising nothing it does not | ||
| answer. | ||
|
|
||
| ## Validation | ||
|
|
||
| - Linux (ALSA UMP): the kernel selects alt 1, reads the GTB and creates a | ||
| native MIDI 2.0 endpoint. `/proc/asound/card*/midi0` shows `Num Blocks: 1` | ||
| and the Function Block named `Main` (bidirectional), not a generic | ||
| `Group 1-1`. | ||
| - UMP echo: notes sent to the device come straight back (`amidi -d`). | ||
| - MIDI-CI: Discovery Reply with a per-boot randomized MUID. | ||
| - Windows MIDI Services: enumerates as `ATmega32U4 MIDI 2.0`, Native data | ||
| format UMP, one bidirectional Function Block. | ||
| - LED_BUILTIN toggles once per showcase cycle as a heartbeat. | ||
|
|
||
| Enumerated and monitored with the Windows MIDI Services Console: | ||
|
|
||
|  | ||
|
|
||
| The device appears as a native MIDI 2.0 endpoint alongside the standard loopbacks. | ||
|
|
||
|  | ||
|
|
||
| Native data format UMP, MIDI 1.0 + 2.0 protocols, one static bidirectional | ||
| Function Block (`Main`, Group 1). | ||
|
|
||
|  | ||
|
|
||
| The showcase on the wire: MIDI 2.0 Channel Voice, Flex Data, SysEx7, SysEx8 and | ||
| Mixed Data Set. The console's Decoded column does not yet render SysEx8 / MDS / | ||
| Flex payloads, so those rows read blank there; the raw UMP words in the Data | ||
| column are correct. | ||
|
|
||
| Pair with a MIDI 2.0 host recipe such as the RP2040/RP2350 hosts under | ||
| [midi2cpp/examples](https://github.com/sauloverissimo/midi2cpp), or plug it into | ||
| the same host you validate the | ||
| [atmega32u4-device-baremetal](../atmega32u4-device-baremetal/) sibling with. | ||
|
|
||
| ## What lives where | ||
|
|
||
| ``` | ||
| atmega32u4-device-arduino/ | ||
| atmega32u4-device-arduino.ino application: identity + showcase loop | ||
| boards.local.txt USB identity override for the Arduino core | ||
| board/ board photo + Windows MIDI Services captures | ||
| src/ | ||
| midi2_usb.h / .cpp Midi2Usb veneer (begin/task + send helpers) | ||
| stream_responder.c / .h UMP Stream identity responder | ||
| ci_responder.c / .h MIDI-CI Discovery responder | ||
| board.h UMP Endpoint Name | ||
| midi2_*.c / .h midi2 C99 core (symlinked from ../../src) | ||
| ``` | ||
|
|
||
| The USB transport comes from the [midi2duino](https://github.com/sauloverissimo/midi2duino) | ||
| library, installed separately; the midi2 C99 core is vendored under `src/` via | ||
| symlink. | ||
|
|
||
| ## License | ||
|
|
||
| MIT, same as the midi2 library. Builds on the Arduino AVR core (LGPL, linked as | ||
| the platform) with a standard Arduino sketch; no core sources are redistributed | ||
| here. |
67 changes: 67 additions & 0 deletions
67
examples/atmega32u4-device-arduino/atmega32u4-device-arduino.ino
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| // midi2 / atmega32u4-device-arduino | ||
| // USB MIDI 2.0 device on the ATmega32U4 (Arduino Leonardo / Pro Micro / Micro). | ||
| // | ||
| // The Arduino-friendly sibling of examples/atmega32u4-device-baremetal: same | ||
| // midi2 C99 core, same USB MIDI 2.0 class descriptors, but the USB transport is | ||
| // the midi2duino library (PluggableUSB, stock Arduino core) instead of LUFA, so | ||
| // it flashes with a normal Arduino upload (no manual double-tap reset). | ||
| // | ||
| // Requires: Arduino AVR Boards core (arduino:avr) and the midi2duino library | ||
| // (Library Manager, or github.com/sauloverissimo/midi2duino). The midi2 C99 | ||
| // core and the responder glue live under src/ and compile with the sketch. | ||
| // | ||
| // USB identity (VID 0xCAFE, PID 0x40D1, Manufacturer "midi2.diy", | ||
| // Product "ATmega32U4 MIDI 2.0") comes from boards.local.txt / the build | ||
| // properties in the README. On the UMP wire: Endpoint Name "ATmega32U4 MIDI 2.0", | ||
| // one bidirectional Function Block "Main" on group 0. | ||
| // | ||
| // Always-UMP: the stock AVR core does not surface SET_INTERFACE, so this device | ||
| // always speaks MIDI 2.0 (UMP) and targets MIDI 2.0 hosts. See the README. | ||
|
|
||
| #include "src/midi2_usb.h" | ||
|
|
||
| Midi2Usb midi; | ||
|
|
||
| static uint32_t lastDemo = 0; | ||
| static uint8_t demoNote = 60; | ||
|
|
||
| void setup() { | ||
| pinMode(LED_BUILTIN, OUTPUT); | ||
| midi.begin(); | ||
| } | ||
|
|
||
| void loop() { | ||
| midi.task(); // pump USB, answer Discovery / MIDI-CI, echo input | ||
|
|
||
| uint32_t now = millis(); | ||
| if (now - lastDemo < 3000) | ||
| return; | ||
| lastDemo = now; | ||
| digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); | ||
|
|
||
| // A readable tour of the MIDI 2.0 surface, one message family per line. | ||
| // Channel Voice 2.0 (32-bit resolution) | ||
| midi.sendNoteOn(0, demoNote, 0xC000); | ||
| delay(150); | ||
| midi.sendNoteOff(0, demoNote); | ||
| midi.sendControlChange(0, 74, 0x80000000); // filter cutoff, half scale | ||
| midi.sendPitchBend(0, 0xC0000000); | ||
| midi.sendChannelPressure(0, 0x60000000); | ||
| midi.sendProgramChange(0, 42); | ||
| midi.sendPolyPressure(0, demoNote, 0x40000000); | ||
| midi.sendPerNotePitchBend(0, demoNote, 0x90000000); // MIDI 2.0 exclusive | ||
|
|
||
| // Flex Data | ||
| midi.sendTempo(50000000); // 120 BPM in 10-ns ticks per quarter | ||
| midi.sendTimeSignature(4, 2); // 4/4 | ||
|
|
||
| // Data messages: SysEx7 + SysEx8 + Mixed Data Set (educational mfr 0x7D) | ||
| static const uint8_t sx7[] = {0x7D, 0x01, 0x02, 0x03, 0x04, 0x05}; | ||
| midi.sendSysEx7(sx7, sizeof sx7); | ||
| static const uint8_t sx8[] = {0x7D, 0x01, 0x02, 0x03, 0x04}; | ||
| midi.sendSysEx8(0, sx8, sizeof sx8); | ||
| static const uint8_t mds[] = {0x4D, 0x44, 0x53, 0x21}; // "MDS!" | ||
| midi.sendMds(1, mds, sizeof mds); | ||
|
|
||
| demoNote = (demoNote >= 72) ? 60 : (uint8_t)(demoNote + 1); | ||
| } |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # USB identity override for the atmega32u4-device-arduino recipe. | ||
| # | ||
| # The Arduino AVR core takes the USB descriptor VID / PID / strings from these | ||
| # build properties. Copy this file next to the core's boards.txt to make the | ||
| # Arduino IDE (and arduino-cli) build the device with the midi2.diy identity: | ||
| # | ||
| # ~/.arduino15/packages/arduino/hardware/avr/<version>/boards.local.txt | ||
| # (Linux; adjust the path on macOS/Windows) | ||
| # | ||
| # arduino-cli users can skip the copy and pass the same values inline instead: | ||
| # | ||
| # --build-property build.vid=0xCAFE --build-property build.pid=0x40D1 \ | ||
| # --build-property 'build.usb_product="ATmega32U4 MIDI 2.0"' \ | ||
| # --build-property 'build.usb_manufacturer="midi2.diy"' | ||
| # | ||
| # Without the override the device still works and still reports "ATmega32U4 | ||
| # MIDI 2.0" as its UMP Endpoint Name and MIDI-CI identity (that lives in the | ||
| # firmware); only the raw USB descriptor VID/PID/strings fall back to the | ||
| # board's Arduino defaults. | ||
|
|
||
| leonardo.build.vid=0xCAFE | ||
| leonardo.build.pid=0x40D1 | ||
| leonardo.build.usb_product="ATmega32U4 MIDI 2.0" | ||
| leonardo.build.usb_manufacturer="midi2.diy" | ||
|
|
||
| micro.build.vid=0xCAFE | ||
| micro.build.pid=0x40D1 | ||
| micro.build.usb_product="ATmega32U4 MIDI 2.0" | ||
| micro.build.usb_manufacturer="midi2.diy" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| /* Board identity for the Arduino recipe. | ||
| * | ||
| * Only the UMP Endpoint Name lives here (consumed by stream_responder.c). | ||
| * The USB descriptor strings (Manufacturer / Product) come from the Arduino | ||
| * core build properties (USB_MANUFACTURER / USB_PRODUCT), set in | ||
| * boards.local.txt, not from a LUFA-style wide string. The user LED is driven | ||
| * from the sketch through LED_BUILTIN, so no register map is needed here. | ||
| * | ||
| * All ATmega32U4 boards (Leonardo, Pro Micro, Micro) share this one name: | ||
| * they are the same silicon and enumerate identically. The physical board is | ||
| * distinguished by its photos and README, not on the wire. | ||
| */ | ||
| #ifndef BOARD_H | ||
| #define BOARD_H | ||
|
|
||
| #define BOARD_NAME "ATmega32U4 MIDI 2.0" | ||
|
|
||
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| #include <avr/io.h> | ||
| #include <avr/eeprom.h> | ||
| #include "ci_responder.h" | ||
| #include <midi2duino.h> | ||
| #include "midi2_ci.h" | ||
|
|
||
| /* Same educational identity as the stream responder. NOTE: the CI builder | ||
| * serializes the manufacturer LSB-first (wire Byte 1 = value bits 0..7), | ||
| * the OPPOSITE of midi2_msg_stream_device_identity. 0x7D must sit in the | ||
| * low byte here to reach wire Byte 1. */ | ||
| #define CI_MFR 0x0000007DUL /* wire: {0x7D, 0x00, 0x00} */ | ||
| #define CI_FAMILY 0x0001 | ||
| #define CI_MODEL 0x0002 /* distinct from the baremetal recipe (0x0001) */ | ||
| #define CI_VERSION 0x00010000UL | ||
|
|
||
| static midi2_ci_state g_ci; | ||
| static uint8_t g_profiles[1][5]; /* slots exist, none added */ | ||
| static midi2_ci_property g_props[1]; | ||
|
|
||
| /* xorshift32 for the 28-bit MUID on a part with no TRNG. The seed mixes an | ||
| * EEPROM boot counter (monotonic across power cycles, so two boots can never | ||
| * repeat a seed) with Timer1 phase and the USB frame number (jitter). */ | ||
| static uint32_t g_rng_state; | ||
| static uint32_t EEMEM ee_boot_count; | ||
|
|
||
| static uint32_t ci_rng(void *ctx) { | ||
| (void)ctx; | ||
| if (g_rng_state == 0) { | ||
| uint32_t boots = eeprom_read_dword(&ee_boot_count); | ||
| if (boots == 0xFFFFFFFFUL) | ||
| boots = 0; /* fresh EEPROM */ | ||
| eeprom_update_dword(&ee_boot_count, boots + 1); | ||
| g_rng_state = ((boots + 1) * 0x9E3779B9UL) /* golden-ratio spread */ | ||
| ^ (((uint32_t)UDFNUMH << 24) | ((uint32_t)UDFNUML << 16) | TCNT1); | ||
| if (g_rng_state == 0) | ||
| g_rng_state = 1; | ||
| } | ||
| g_rng_state ^= g_rng_state << 13; | ||
| g_rng_state ^= g_rng_state >> 17; | ||
| g_rng_state ^= g_rng_state << 5; | ||
| return g_rng_state; | ||
| } | ||
|
|
||
| static uint32_t ci_write_fn(const uint32_t *words, uint32_t count, void *ctx) { | ||
| (void)ctx; | ||
| for (uint32_t i = 0; i < count; i++) | ||
| if (!midi2duino_write_word(words[i])) | ||
| return i; /* ring full: partial write reported */ | ||
| return count; | ||
| } | ||
|
|
||
| void ci_responder_init(void) { | ||
| midi2_ci_init(&g_ci, ci_rng(0), g_profiles, 1, g_props, 1); | ||
| midi2_ci_set_rng(&g_ci, ci_rng, 0); | ||
| midi2_ci_set_write_fn(&g_ci, ci_write_fn, 0); | ||
| midi2_ci_set_identity(&g_ci, CI_MFR, CI_FAMILY, CI_MODEL, CI_VERSION); | ||
| midi2_ci_set_capabilities(&g_ci, 0x00); /* Discovery only, honest */ | ||
| midi2_ci_set_nak_on_unknown(&g_ci, true); | ||
| } | ||
|
|
||
| void ci_responder_feed_sysex7(uint8_t group, const uint8_t *data, uint16_t len) { | ||
| (void)midi2_ci_process_sysex(&g_ci, group, data, len); | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MIDI-CI replies are emitted as two-word SysEx7 UMP packets, but this callback queues each word separately; if the TX ring has only one free slot while a Discovery reply is being generated, the first word is accepted and the second fails, leaving an incomplete UMP at the head of the ring that can block or corrupt later USB output. Use the complete-message write path so either the whole packet is queued or none of it is.
Useful? React with 👍 / 👎.