-
Notifications
You must be signed in to change notification settings - Fork 6.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
samples: subsys: usb: midi: add new sample
Add a sample application that demonstrates how to use the new USB-MIDI 2.0 device class. This shows how to set up the device tree, how to exchange MIDI data with the host, and how to use the data accessors provided by the new API. Signed-off-by: Titouan Christophe <[email protected]>
- Loading branch information
Showing
7 changed files
with
215 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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 @@ | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
cmake_minimum_required(VERSION 3.20.0) | ||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) | ||
project(usb_midi) | ||
|
||
include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake) | ||
FILE(GLOB app_sources src/*.c) | ||
target_sources(app PRIVATE ${app_sources}) |
This file contains 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 @@ | ||
# Copyright (c) 2024 Titouan Christophe | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
# Source common USB sample options used to initialize new experimental USB | ||
# device stack. The scope of these options is limited to USB samples in project | ||
# tree, you cannot use them in your own application. | ||
source "samples/subsys/usb/common/Kconfig.sample_usbd" | ||
|
||
source "Kconfig.zephyr" |
This file contains 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,84 @@ | ||
.. zephyr:code-sample:: midi | ||
:name: USB MIDI device class | ||
:relevant-api: usbd_api usb_midi input_events | ||
|
||
Implements a simple USB-MIDI loopback and keyboard device. | ||
|
||
Overview | ||
******** | ||
|
||
This sample demonstrates how to implement a USB MIDI device. It can run on | ||
any board with a USB device controller. This sample sends all MIDI1 messages | ||
sent to the device back to the host. In addition, presses and release on | ||
input keys (such as the board user buttons) are sent as MIDI1 note on and | ||
note off events. | ||
|
||
The application exposes a single USB-MIDI interface with a single bidirectional | ||
group terminal. This allows exchanging data with the host on a "virtual wire" | ||
that carries MIDI1 messages, pretty much like a standard USB-MIDI in/out adapter | ||
would provide. The loopback acts as if a real MIDI cable was connected between | ||
the output and the input, and the input keys act as a MIDI keyboard. | ||
|
||
Building and Running | ||
******************** | ||
|
||
The code can be found in :zephyr_file:`samples/subsys/usb/midi`. | ||
|
||
To build and flash the application: | ||
|
||
.. zephyr-app-commands:: | ||
:zephyr-app: samples/subsys/usb/midi | ||
:board: nucleo_f429zi | ||
:goals: build flash | ||
:compact: | ||
|
||
Using the MIDI interface | ||
************************ | ||
|
||
Once this sample is flashed, connect the device USB port to a host computer | ||
with MIDI support. For example, on Linux, you can use alsa to access the device: | ||
|
||
.. code-block:: console | ||
$ amidi -l | ||
Dir Device Name | ||
IO hw:2,1,0 Group 1 (USBD MIDI Sample) | ||
On Mac OS you can use the system tool "Audio MIDI Setup" to view the device, | ||
see https://support.apple.com/guide/audio-midi-setup/set-up-midi-devices-ams875bae1e0/mac | ||
|
||
The "USBD MIDI Sample" interface should also appear in any program with MIDI | ||
support; like your favorite Digital Audio Workstation or synthetizer. If you | ||
don't have any such program at hand, there are some webmidi programs online, | ||
for example: https://muted.io/piano/. | ||
|
||
Testing loopback | ||
**************** | ||
|
||
Open a first shell, and start dumping MIDI events: | ||
|
||
.. code-block:: console | ||
$ amidi -p hw:2,1,0 -d | ||
Then, in a second shell, send some MIDI events (for example note-on/note-off): | ||
|
||
.. code-block:: console | ||
$ amidi -p hw:2,1,0 -S "90427f 804200" | ||
These events should then appear in the first shell (dump) | ||
|
||
On devboards with a user button, press it and observe that there are some note | ||
on/note off events delivered to the first shell (dump) | ||
|
||
.. code-block:: console | ||
$ amidi -p hw:2,1,0 -d | ||
90 40 7F | ||
80 40 7F | ||
90 40 7F | ||
80 40 7F | ||
[...] |
This file contains 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,19 @@ | ||
/* | ||
* Copyright (c) 2024 Titouan Christophe | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
/ { | ||
usb_midi: usb_midi { | ||
compatible = "zephyr,usb-midi"; | ||
status = "okay"; | ||
#address-cells = <1>; | ||
#size-cells = <1>; | ||
|
||
midi_in_out@0 { | ||
reg = <0 1>; | ||
protocol = "midi1-up-to-128b"; | ||
}; | ||
}; | ||
}; |
This file contains 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,11 @@ | ||
CONFIG_USB_DEVICE_STACK_NEXT=y | ||
CONFIG_USBD_MIDI_CLASS=y | ||
|
||
CONFIG_SAMPLE_USBD_PRODUCT="USBD MIDI Sample" | ||
CONFIG_SAMPLE_USBD_PID=0x0010 | ||
|
||
CONFIG_INPUT=y | ||
|
||
CONFIG_LOG=y | ||
CONFIG_USBD_LOG_LEVEL_WRN=y | ||
CONFIG_UDC_DRIVER_LOG_LEVEL_WRN=y |
This file contains 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,11 @@ | ||
sample: | ||
name: USB MIDI 2.0 device class sample | ||
tests: | ||
sample.usb_device_next.midi: | ||
depends_on: usbd | ||
tags: usb | ||
harness: console | ||
harness_config: | ||
type: one_line | ||
regex: | ||
- "USB device support enabled" |
This file contains 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,72 @@ | ||
/* | ||
* Copyright (c) 2024 Titouan Christophe | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* @file | ||
* @brief Sample application for USB MIDI 2.0 device class | ||
*/ | ||
|
||
#include <sample_usbd.h> | ||
|
||
#include <zephyr/device.h> | ||
#include <zephyr/input/input.h> | ||
#include <zephyr/usb/class/usb_midi.h> | ||
|
||
#include <zephyr/logging/log.h> | ||
LOG_MODULE_REGISTER(sample_usb_midi, LOG_LEVEL_INF); | ||
|
||
static const struct device *const midi = DEVICE_DT_GET(DT_NODELABEL(usb_midi)); | ||
|
||
static void key_press(struct input_event *evt, void *user_data) | ||
{ | ||
/* Only handle key presses in the 7bit MIDI range */ | ||
if (evt->type != INPUT_EV_KEY || evt->code > 0x7f) { | ||
return; | ||
} | ||
uint8_t command = evt->value ? MIDI_NOTE_ON : MIDI_NOTE_OFF; | ||
uint8_t channel = 0; | ||
uint8_t note = evt->code; | ||
uint8_t velocity = 100; | ||
|
||
usb_midi_send(midi, UMP_MIDI1(0, command, channel, note, velocity)); | ||
} | ||
INPUT_CALLBACK_DEFINE(NULL, key_press, NULL); | ||
|
||
static void on_midi_packet(const struct device *dev, const uint32_t *ump) | ||
{ | ||
LOG_INF("Received MIDI packet (MT=%X)", UMP_MT(ump)); | ||
|
||
/* Only send MIDI1 channel voice messages back to the host */ | ||
if (UMP_MT(ump) == MT_MIDI1_CHANNEL_VOICE) { | ||
LOG_INF("Send back MIDI1 message %X%X %02X %02X", UMP_MIDI1_CHANNEL(ump), | ||
UMP_MIDI1_COMMAND(ump), UMP_MIDI1_P1(ump), UMP_MIDI1_P2(ump)); | ||
usb_midi_send(dev, ump); | ||
} | ||
} | ||
|
||
int main(void) | ||
{ | ||
struct usbd_context *sample_usbd; | ||
|
||
if (!device_is_ready(midi)) { | ||
LOG_ERR("MIDI device not ready"); | ||
return -1; | ||
} | ||
|
||
usb_midi_set_callback(midi, on_midi_packet); | ||
|
||
sample_usbd = sample_usbd_init_device(NULL); | ||
if (sample_usbd == NULL) { | ||
LOG_ERR("Failed to initialize USB device"); | ||
return -1; | ||
} | ||
|
||
if (!usbd_can_detect_vbus(sample_usbd) && usbd_enable(sample_usbd)) { | ||
LOG_ERR("Failed to enable device support"); | ||
return -1; | ||
} | ||
|
||
LOG_INF("USB device support enabled"); | ||
return 0; | ||
} |