Skip to content

Commit

Permalink
chore: merge branch 'release/0.7.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Ben Leadbetter committed Jan 18, 2025
2 parents be7871d + 30ea119 commit 180e121
Show file tree
Hide file tree
Showing 40 changed files with 1,755 additions and 97 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# 0.7.0
* feat!: packet types
* Packet types for each ump message type.
* Messages are formed of packets.
* The Packets trait has Item = Packet. (breaking change)
* feat: allow messages to be created with external backing buffers
* docs: improve warning formatting in top level readme
* docs: improve readme example

# 0.6.5
* docs: improve readme example

Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "midi2"
version = "0.6.5"
version = "0.7.0"
description = "Ergonomic, versatile, strong types wrapping MIDI 2.0 message data."
edition = "2021"
readme = "README.md"
Expand Down Expand Up @@ -37,7 +37,7 @@ utility = []
[dependencies]
derive_more = { version = "0.99.17", features = ["from"], default-features = false }
fixed = "1.27.0"
midi2_proc = { version = "0.6.5", path = "midi2_proc" }
midi2_proc = { version = "0.7.0", path = "midi2_proc" }
ux = "0.1.6"

[dev-dependencies]
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ This implementation of MIDI 2.0 is based on the 1.1 revision of the specificatio
See [the official MIDI 2.0 specification](https://midi.org/)
for more details on the data protocol standard.

## ⚠️ **Note!** ⚠️

This crate is still in its alpha phase and is not
recommended for production.
> [!CAUTION]
>
> This project is still in early development.
> Expect breaking changes and bugs, and please report any issues you encounter.
We would welcome contributions!
Please refer to the [CONTRIBUTOR.md](CONTRIBUTOR.md)
Expand Down Expand Up @@ -157,7 +157,7 @@ You'll want to setup midi2 without default features to compile
without the `std` feature.

```toml
midi2 = { version = "0.6.5", default-features = false, features = ["channel-voice2", "sysex7"], }
midi2 = { version = "0.7.0", default-features = false, features = ["channel-voice2", "sysex7"], }
```

### Generic Representation
Expand Down
1 change: 1 addition & 0 deletions examples/handling_messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use midi2::prelude::*;

fn handle_message(buffer: &[u32]) {
match UmpMessage::try_from(buffer) {
#[cfg(feature = "channel-voice2")]
Ok(UmpMessage::ChannelVoice2(m)) => {
println!("Channel Voice2: channel: {}", m.channel());
match m {
Expand Down
2 changes: 1 addition & 1 deletion midi2_proc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "midi2_proc"
description = "Internal procedural macro crate. Only intended for use with midi2"
version = "0.6.5"
version = "0.7.0"
edition = "2021"
readme = "README.md"
license = "MIT OR Apache-2.0"
Expand Down
17 changes: 13 additions & 4 deletions midi2_proc/src/derives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,16 +375,25 @@ pub fn debug(item: TokenStream1) -> TokenStream1 {
_ => panic!("Only enums and structs supported"),
};
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let buffer_id = common::buffer_generic(generics)
.expect("Expected buffer generic")
.ident();

let buffer_id = if ident == "Packet" {
// special handling
// always a u32 slice
quote! {crate::buffer::UNIT_ID_U32}
} else {
let buffer_ident = common::buffer_generic(generics)
.expect("Expected buffer generic")
.ident();
quote! {<<#buffer_ident as crate::buffer::Buffer>::Unit as crate::buffer::UnitPrivate>::UNIT_ID}
};

quote! {
impl #impl_generics core::fmt::Debug for #ident #ty_generics #where_clause {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
use crate::BufferAccess as BufferAccessDeriveDebug;

fmt.write_fmt(format_args!("{}([", stringify!(#ident)))?;
match <<#buffer_id as crate::buffer::Buffer>::Unit as crate::buffer::UnitPrivate>::UNIT_ID {
match #buffer_id {
crate::buffer::UNIT_ID_U8 => {
use crate::buffer::SpecialiseU8 as SpecialiseU8DeriveDebug;

Expand Down
58 changes: 57 additions & 1 deletion midi2_proc/src/generate_ci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,33 @@ fn new_impl(root_ident: &syn::Ident, properties: &[Property]) -> TokenStream {
}
}

fn new_with_buffer_impl(root_ident: &syn::Ident, properties: &[Property]) -> TokenStream {
let initialise_properties = initialise_property_statements(properties, quote! {B});
quote! {
impl<const VERSION: u8,
B: crate::buffer::Bytes
+ crate::buffer::BufferMut
+ crate::buffer::BufferResize
> #root_ident<VERSION, B>
{
/// Create a new message backed by a resizable buffer.
pub fn new_with_buffer(buffer: B) -> Self where Self: crate::ci::version::CiVersion<VERSION>
{
let mut sysex7 = crate::sysex7::Sysex7::<B>::new_with_buffer(buffer);
let payload_size = <Self as crate::traits::MinSize<B>>::MIN_SIZE - 2;
<crate::sysex7::Sysex7<B> as crate::SysexInternal<B>>::resize(&mut sysex7, payload_size);
let buffer_ref_mut = <crate::sysex7::Sysex7<B> as crate::BufferAccess<B>>::buffer_access_mut(&mut sysex7);
if buffer_ref_mut.buffer().len() > 5 {
// write the version
buffer_ref_mut.buffer_mut()[5] = VERSION;
}
#initialise_properties
#root_ident::<VERSION, B>(sysex7)
}
}
}
}

fn try_new_impl(root_ident: &syn::Ident, properties: &[Property]) -> TokenStream {
let initialise_properties = initialise_property_statements(properties, quote! {B});
quote! {
Expand All @@ -354,6 +381,31 @@ fn try_new_impl(root_ident: &syn::Ident, properties: &[Property]) -> TokenStream
}
}

fn try_new_with_buffer_impl(root_ident: &syn::Ident, properties: &[Property]) -> TokenStream {
let initialise_properties = initialise_property_statements(properties, quote! {B});
quote! {
impl<const VERSION: u8,
B: crate::buffer::Bytes
+ crate::buffer::BufferMut
+ crate::buffer::BufferTryResize
> #root_ident<VERSION, B>
{
/// Create a new message backed by a buffer with fallible resize.
pub fn try_new_with_buffer(buffer: B) -> Result<Self, crate::error::BufferOverflow> where Self: crate::ci::version::CiVersion<VERSION>
{
let mut sysex7 = crate::sysex7::Sysex7::<B>::try_new_with_buffer(buffer)?;
let payload_size = <Self as crate::traits::MinSize<B>>::MIN_SIZE - 2;
<crate::sysex7::Sysex7<B> as crate::SysexInternal<B>>::try_resize(&mut sysex7, payload_size)?;
let buffer_ref_mut = <crate::sysex7::Sysex7<B> as crate::BufferAccess<B>>::buffer_access_mut(&mut sysex7);
// write the version
buffer_ref_mut.buffer_mut()[5] = VERSION;
#initialise_properties
Ok(#root_ident::<VERSION, B>(sysex7))
}
}
}
}

fn ci_version_impls(root_ident: &syn::Ident, args: &GenerateCiArgs) -> TokenStream {
let mut ret = TokenStream::new();

Expand Down Expand Up @@ -490,7 +542,7 @@ fn try_from_slice_impl(root_ident: &syn::Ident, properties: &[Property]) -> Toke
type Error = crate::error::InvalidData;
fn try_from(buffer: &'a [u8]) -> core::result::Result<Self, Self::Error> {
if buffer.len() < <Self as crate::traits::MinSize<&[u8]>>::MIN_SIZE {
return Err(crate::error::InvalidData("Slice is too short"));
return Err(crate::error::InvalidData(crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT));
}
if buffer[5] != VERSION {
return Err(crate::error::InvalidData("Incorrect CI version"));
Expand Down Expand Up @@ -550,6 +602,8 @@ pub fn generate_ci(attrs: TokenStream1, item: TokenStream1) -> TokenStream1 {
let min_size_impl = min_size_impl(root_ident, &args);
let new_impl = new_impl(root_ident, &properties);
let try_new_impl = try_new_impl(root_ident, &properties);
let new_with_buffer = new_with_buffer_impl(root_ident, &properties);
let try_new_with_buffer = try_new_with_buffer_impl(root_ident, &properties);
let ci_version_impls = ci_version_impls(root_ident, &args);
let deref_sysex7_impl = deref_sysex7_impl(root_ident);
let message_impl = message_impl(root_ident, &properties);
Expand All @@ -574,6 +628,8 @@ pub fn generate_ci(attrs: TokenStream1, item: TokenStream1) -> TokenStream1 {
#ci_version_impls
#rebuffer_from_impl
#try_rebuffer_from_impl
#new_with_buffer
#try_new_with_buffer
});

tokens.into()
Expand Down
64 changes: 64 additions & 0 deletions midi2_proc/src/generate_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,66 @@ fn new_impl(
}
}

fn new_with_buffer_impl(
root_ident: &syn::Ident,
args: &GenerateMessageArgs,
properties: &[Property],
) -> TokenStream {
let constraint = generic_buffer_constraint(args);
let initialise_properties = initialise_property_statements(properties, quote! {B});
quote! {
impl<B: #constraint
+ crate::buffer::BufferMut
+ crate::buffer::BufferResize
> #root_ident<B>
{
/// Create a new message backed by the provided resizable buffer.
/// The provided buffer will be zeroed at initialization.
pub fn new_with_buffer(mut buffer: B) -> #root_ident<B>
{
let buffer_ref_mut = &mut buffer;
let sz = <Self as crate::traits::MinSize<B>>::MIN_SIZE;
buffer_ref_mut.resize(sz);
for b in &mut buffer_ref_mut.buffer_mut()[..sz] {
*b = <<B as crate::buffer::Buffer>::Unit as crate::buffer::Unit>::zero();
}
#initialise_properties
#root_ident::<B>(buffer)
}
}
}
}

fn try_new_with_buffer_impl(
root_ident: &syn::Ident,
args: &GenerateMessageArgs,
properties: &[Property],
) -> TokenStream {
let constraint = generic_buffer_constraint(args);
let initialise_properties = initialise_property_statements(properties, quote! {B});
quote! {
impl<B: #constraint
+ crate::buffer::BufferMut
+ crate::buffer::BufferTryResize
> #root_ident<B>
{
/// Create a new message backed by the provided buffer.
/// The provided buffer will be zeroed at initialization.
pub fn try_new_with_buffer(mut buffer: B) -> Result<#root_ident<B>, crate::error::BufferOverflow>
{
let buffer_ref_mut = &mut buffer;
let sz = <Self as crate::traits::MinSize<B>>::MIN_SIZE;
buffer_ref_mut.try_resize(sz)?;
for b in &mut buffer_ref_mut.buffer_mut()[..sz] {
*b = <<B as crate::buffer::Buffer>::Unit as crate::buffer::Unit>::zero();
}
#initialise_properties
Ok(#root_ident::<B>(buffer))
}
}
}
}

fn new_array_impl(
root_ident: &syn::Ident,
args: &GenerateMessageArgs,
Expand Down Expand Up @@ -732,6 +792,8 @@ pub fn generate_message(attrs: TokenStream1, item: TokenStream1) -> TokenStream1
let rebuffer_from_impl = rebuffer_from_impl(root_ident, &args);
let try_rebuffer_from_impl = try_rebuffer_from_impl(root_ident, &args);
let new_impl = new_impl(root_ident, &args, &properties);
let new_with_buffer_impl = new_with_buffer_impl(root_ident, &args, &properties);
let try_new_with_buffer_impl = try_new_with_buffer_impl(root_ident, &args, &properties);
let new_array_impl = new_array_impl(root_ident, &args, &properties);
let try_new_impl = try_new_impl(root_ident, &args, &properties);
let clone_impl = clone_impl(root_ident, &args);
Expand All @@ -749,6 +811,8 @@ pub fn generate_message(attrs: TokenStream1, item: TokenStream1) -> TokenStream1
#rebuffer_from_impl
#try_rebuffer_from_impl
#new_impl
#new_with_buffer_impl
#try_new_with_buffer_impl
#new_array_impl
#try_new_impl
#clone_impl
Expand Down
32 changes: 32 additions & 0 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,16 @@ impl<const SIZE: usize, U: Unit> BufferTryResize for [U; SIZE] {
}
}

impl<U: Unit> BufferTryResize for &mut [U] {
fn try_resize(&mut self, size: usize) -> Result<(), BufferOverflow> {
if size > self.len() {
Err(BufferOverflow)
} else {
Ok(())
}
}
}

#[cfg(any(feature = "std", test))]
impl<U: Unit> Buffer for std::vec::Vec<U> {
type Unit = U;
Expand Down Expand Up @@ -257,6 +267,28 @@ impl<U: Unit> BufferDefault for std::vec::Vec<U> {
}
}

#[cfg(any(feature = "std", test))]
impl<U: Unit> Buffer for &mut std::vec::Vec<U> {
type Unit = U;
fn buffer(&self) -> &[Self::Unit] {
self
}
}

#[cfg(any(feature = "std", test))]
impl<U: Unit> BufferMut for &mut std::vec::Vec<U> {
fn buffer_mut(&mut self) -> &mut [<Self as Buffer>::Unit] {
self
}
}

#[cfg(any(feature = "std", test))]
impl<U: Unit> BufferResize for &mut std::vec::Vec<U> {
fn resize(&mut self, size: usize) {
std::vec::Vec::resize(*self, size, U::zero());
}
}

pub(crate) const UNIT_ID_U8: u8 = 0;
pub(crate) const UNIT_ID_U32: u8 = 1;

Expand Down
8 changes: 6 additions & 2 deletions src/channel_voice1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod control_change;
mod key_pressure;
mod note_off;
mod note_on;
mod packet;
mod pitch_bend;
mod program_change;

Expand All @@ -15,6 +16,7 @@ pub use control_change::*;
pub use key_pressure::*;
pub use note_off::*;
pub use note_on::*;
pub use packet::Packet;
pub use pitch_bend::*;
pub use program_change::*;

Expand Down Expand Up @@ -52,7 +54,9 @@ impl<'a, U: crate::buffer::Unit> core::convert::TryFrom<&'a [U]> for ChannelVoic
type Error = crate::error::InvalidData;
fn try_from(buffer: &'a [U]) -> Result<Self, Self::Error> {
if buffer.is_empty() {
return Err(crate::error::InvalidData("Slice is too short"));
return Err(crate::error::InvalidData(
crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT,
));
};
Ok(match status(buffer) {
channel_pressure::STATUS => ChannelPressure::try_from(buffer)?.into(),
Expand Down Expand Up @@ -183,7 +187,7 @@ mod test {
fn packets() {
let message = ChannelVoice1::try_from(&[0x2FD6_0900_u32][..]).unwrap();
let mut packets = message.packets();
assert_eq!(packets.next(), Some(&[0x2FD6_0900_u32][..]));
assert_eq!(&*packets.next().unwrap(), &[0x2FD6_0900_u32][..]);
assert_eq!(packets.next(), None);
}

Expand Down
4 changes: 3 additions & 1 deletion src/channel_voice1/channel_pressure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ mod tests {
fn from_empty_data() {
assert_eq!(
ChannelPressure::try_from(&<[u32; 0] as Default>::default()[..]),
Err(crate::error::InvalidData("Slice is too short")),
Err(crate::error::InvalidData(
crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT
)),
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/channel_voice1/control_change.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ mod tests {
let buffer = [0x2AB7_3637_u32];
let message = ControlChange::try_from(&buffer[..]).unwrap();
let mut packets = message.packets();
assert_eq!(packets.next(), Some(&[0x2AB7_3637_u32][..]));
assert_eq!(&*packets.next().unwrap(), &[0x2AB7_3637_u32][..]);
assert_eq!(packets.next(), None);
}
}
Loading

0 comments on commit 180e121

Please sign in to comment.