Skip to content
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

Release/0.7.0 #33

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading