Skip to content

Commit

Permalink
Merge pull request #21 from rust-embedded-community/next
Browse files Browse the repository at this point in the history
Version 0.4.0
  • Loading branch information
sourcebox authored Jan 3, 2025
2 parents ab10d5b + 60e817b commit 69e1e1e
Show file tree
Hide file tree
Showing 28 changed files with 955 additions and 381 deletions.
64 changes: 64 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,70 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [0.4.0] - 2025-01-03

This release focuses on:

- Increased usability by simplifying the internal module structure.
- Interfacing with third-party crates like `midi-types`.
- Support for System Exclusive messages (SysEx).

**Important:**

- The `message` module containing the `Message` struct and related types is now gated behind the `message-types` feature. This feature is enabled by default.
- The constants `USB_AUDIO_CLASS`, `USB_AUDIOCONTROL_SUBCLASS` and `USB_MIDISTREAMING_SUBCLASS` are now private to prevent them from being used in the device setup. Doing so would lead to incorrect enumeration on certain hosts (e.g. macOS).

### Added

- `UsbMidiEventPacket::cable_number` function.
- `UsbMidiEventPacket::header` function.
- `UsbMidiEventPacket::payload_bytes` function.
- `UsbMidiEventPacket::as_raw_bytes` function.
- `UsbMidiEventPacket::to_raw_bytes` function.
- `UsbMidiEventPacket::try_from_payload_bytes` function.
- `UsbMidiEventPacket::is_sysex` function.
- `UsbMidiEventPacket::is_sysex_start` function.
- `UsbMidiEventPacket::is_sysex_end` function.
- `TryFrom<&UsbMidiEventPacket>` implementation for `Message` type.
- `Message::into_packet` function.
- `Message::code_index_number` function.
- `CodeIndexNumber::try_from_payload` function.
- `CodeIndexNumber::payload_size` function.
- `CableNumber::Cable0` as default value.
- `FromOverFlow<u8> for U4` implementation.
- `FromClamped<u8> for U4` implementation.
- Re-exports of common items in crate root.

### Changed

- Renamed `MidiClass` to `UsbMidiClass`.
- Renamed `UsbMidiClass::send_message` function to `UsbMidiClass::send_packet`.
- Renamed `midi_device` module to `class`.
- Renamed `usb_midi` module to `packet` and moved it into crate root.
- Renamed `midi_packet_reader` module to `reader`.
- Renamed `MidiPacketBufferReader` to `UsbMidiPacketReader`.
- Renamed `MidiPacketParsingError` to `UsbMidiEventPacketError`
- Renamed `MidiReadError` to `UsbMidiReadError`
- Moved `usb_midi_event_packet` code into parent `packet` module.
- Moved `channel` and `notes` modules into `message` module.
- Moved `message` module to crate root.
- Moved `byte` submodules into `message::data` module.
- Moved `from_traits` code into parent `data` module.
- Consolidated separate `InvalidCableNumber` struct into `UsbMidiEventPacketError`.
- Consolidated separate `InvalidCodeIndexNumber` struct into `UsbMidiEventPacketError`.
- Converted `CodeIndexNumber` struct to enum.
- Moved descriptor constants into class module and made them private.
- Set edition to 2021.

### Removed

- `UsbMidiEventPacket::cable_number` field, use function instead.
- `UsbMidiEventPacket::message` field, use `Message::try_from(&UsbMidiEventPacket)` instead.
- `UsbMidiEventPacket::from_midi` function, use `Message::into_packet` instead.
- `CodeIndexNumber::find_from_message` function, use `Message::code_index_number` instead.
- `From<CableNumber> for U4` implementation.
- `From<CodeIndexNumber> for U4` implementation.

## [0.3.0] - 2024-05-27

### Changed
Expand Down
11 changes: 8 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
[package]
name = "usbd-midi"
version = "0.3.0"
version = "0.4.0"
authors = [
"Beau Trepp <[email protected]>",
"Florian Jung <[email protected]>",
"Oliver Rockstedt <[email protected]>",
]
edition = "2018"
edition = "2021"
description = "A USB MIDI implementation for usb-device."
homepage = "https://github.com/rust-embedded-community/usbd-midi"
repository = "https://github.com/rust-embedded-community/usbd-midi"
license = "MIT"
categories = ["no-std", "embedded", "hardware-support"]
keywords = ["usb", "midi"]

[features]
default = ["message-types"]
message-types = ["dep:num_enum"]

[dependencies]
usb-device = "0.3"

[dependencies.num_enum]
version = "0.7.2"
version = "0.7.3"
default-features = false
optional = true

[lints.rust]
missing_docs = "warn"
41 changes: 20 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,31 @@ This crate requires the use of a HAL that implements the `usb-device` traits.

Turn on an LED as long as note C2 is pressed. The example only shows the hardware-independent parts.

```rust
```rust ignore
use usb_device::prelude::*;
use usbd_midi::{
data::{
midi::{channel::Channel, message::Message, notes::Note},
usb_midi::midi_packet_reader::MidiPacketBufferReader,
},
midi_device::MidiClass,
message::{channel::Channel, notes::Note},
Message,
UsbMidiClass,
UsbMidiPacketReader,
};

// Prerequisites, must be setup according to the used board.
let mut led = todo!(); // Must implement `embedded_hal::digital::OutputPin`.
let usb_bus = todo!(); // Must be of type `usb_device::bus::UsbBusAllocator`.

// Create a MIDI class with 1 input and 1 output jack.
let mut midi = MidiClass::new(&usb_bus, 1, 1).unwrap();

let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x5e4))
.device_class(0)
.device_sub_class(0)
.strings(&[StringDescriptors::default()
.manufacturer("Music Company")
.product("MIDI Device")
.serial_number("12345678")])
.unwrap()
.build();
let mut midi = UsbMidiClass::new(&usb_bus, 1, 1).unwrap();

let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x5e4))
.device_class(0)
.device_sub_class(0)
.strings(&[StringDescriptors::default()
.manufacturer("Music Company")
.product("MIDI Device")
.serial_number("12345678")])
.unwrap()
.build();

loop {
if !usb_dev.poll(&mut [&mut midi]) {
Expand All @@ -47,10 +46,10 @@ loop {
let mut buffer = [0; 64];

if let Ok(size) = midi.read(&mut buffer) {
let buffer_reader = MidiPacketBufferReader::new(&buffer, size);
for packet in buffer_reader.into_iter() {
let packet_reader = UsbMidiPacketReader::new(&buffer, size);
for packet in packet_reader.into_iter() {
if let Ok(packet) = packet {
match packet.message {
match Message::try_from(&packet).unwrap() {
Message::NoteOn(Channel1, Note::C2, ..) => {
led.set_low().unwrap();
},
Expand All @@ -73,7 +72,7 @@ the `usb-device` crate:

Cargo.toml:

```
```ignore
usb-device = { version = ">=0.3.2", features = ["control-buffer-256"] }
```

Expand Down
2 changes: 2 additions & 0 deletions examples/example-esp32s3/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ esp-backtrace = { version = "0.14.2", features = [
esp-println = { version = "0.12.0", features = ["esp32s3", "log"] }
usb-device = { version = "0.3.2", features = ["control-buffer-256"] }
usbd-midi = { path = "../../" }
midi-convert = "0.2.0"
heapless = "0.8.0"
31 changes: 31 additions & 0 deletions examples/example-esp32s3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# ESP32-S3 Example

This example was developed and tested on an [ESP32-S3-DevKitC-1](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32s3/esp32-s3-devkitc-1/index.html) using the [esp-hal crate](https://crates.io/crates/esp-hal).

It features:

- Sending and receiving of regular MIDI messages.
- Sending and receiving of MIDI System Exclusive messages with buffering.
- Conversion of USB MIDI packets from and to types provided by the [midi-types crate](https://crates.io/crates/midi-types).

It does not provide a fully production-ready setup, especially time-critical tasks like polling the USB bus in an interrupt and managing bus timeouts are out of scope of this example.

## Requirements

To build the example, an installed toolchain for the Xtensa target is required. Please refer to the [Rust on ESP book](https://docs.esp-rs.org/book/) for further instructions.

You can build the example by running:

cargo build --release

If [espflash](https://crates.io/crates/espflash) is installed, you can flash the example to the board and run it:

cargo run --release

## Functionality

- Incoming MIDI messages are logged to the console.
- Pressing and releasing the *BOOT* button on the board sends MIDI messages.
- A received *Device Inquiry* SysEx request is responded to the host.

Please note that all chosen vendor and product ids and names are just for demonstration purposes and should not be used with a real product.
Loading

0 comments on commit 69e1e1e

Please sign in to comment.