diff --git a/Cargo.toml b/Cargo.toml index 637142e..94eed3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,12 +18,15 @@ bstr = { version = "1.0.0", default-features = false, features = [ micromath = "1.1.1" strum = { version = "0.24.1", features = ["derive"], optional = true } log = { version = "0.4", optional = true } +defmt = { version = "0.3", optional = true } [dev-dependencies] midir = "0.8.0" [features] default = ["std", "sysex", "file"] -std = ["strum", "log"] -sysex = ["bstr"] -file = ["sysex"] +std = ["strum", "log", "alloc"] +sysex = ["bstr", "alloc"] +file = ["sysex", "alloc"] +defmt = ["dep:defmt", "defmt/alloc"] +alloc = [] diff --git a/src/channel_mode.rs b/src/channel_mode.rs index b37086a..beba6bd 100644 --- a/src/channel_mode.rs +++ b/src/channel_mode.rs @@ -1,8 +1,8 @@ use super::parse_error::*; -use crate::util::*; -use alloc::vec::Vec; +use crate::{io::Write, util::*}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] /// Channel-level messages that should alter the mode of the receiver. Used in [`MidiMsg`](crate::MidiMsg). pub enum ChannelModeMsg { /// Sound playing on the channel should be stopped as soon as possible, per GM2. @@ -21,40 +21,25 @@ pub enum ChannelModeMsg { } impl ChannelModeMsg { - pub(crate) fn extend_midi(&self, v: &mut Vec) { - v.push(0xB0); - self.extend_midi_running(v); + pub(crate) fn extend_midi(&self, ch: u8, mut v: impl Write) -> Result<(), E> { + v.write(&[0xB0 + ch])?; + self.extend_midi_running(v) } - pub(crate) fn extend_midi_running(&self, v: &mut Vec) { + pub(crate) fn extend_midi_running(&self, mut v: impl Write) -> Result<(), E> { match self { - ChannelModeMsg::AllSoundOff => { - v.push(120); - v.push(0); - } - ChannelModeMsg::ResetAllControllers => { - v.push(121); - v.push(0); - } - ChannelModeMsg::LocalControl(on) => { - v.push(122); - v.push(if *on { 127 } else { 0 }); - } - ChannelModeMsg::AllNotesOff => { - v.push(123); - v.push(0); - } - ChannelModeMsg::OmniMode(on) => { - v.push(if *on { 125 } else { 124 }); - v.push(0); - } - ChannelModeMsg::PolyMode(m) => { - v.push(if *m == PolyMode::Poly { 127 } else { 126 }); - v.push(match *m { + ChannelModeMsg::AllSoundOff => v.write(&[120, 0]), + ChannelModeMsg::ResetAllControllers => v.write(&[121, 0]), + ChannelModeMsg::LocalControl(on) => v.write(&[122, if *on { 127 } else { 0 }]), + ChannelModeMsg::AllNotesOff => v.write(&[123, 0]), + ChannelModeMsg::OmniMode(on) => v.write(&[if *on { 125 } else { 124 }, 0]), + ChannelModeMsg::PolyMode(m) => v.write(&[ + if *m == PolyMode::Poly { 127 } else { 126 }, + match *m { PolyMode::Poly => 0, PolyMode::Mono(n) => n.min(16), - }) - } + }, + ]), } } @@ -90,6 +75,7 @@ impl ChannelModeMsg { /// Used by [`ChannelModeMsg::PolyMode`]. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum PolyMode { /// Request that the receiver be monophonic, with the given number M representing the /// number of channels that should be dedicated. Since this is sent with a `ChannelModeMsg` diff --git a/src/channel_voice.rs b/src/channel_voice.rs index b311026..ebadca7 100644 --- a/src/channel_voice.rs +++ b/src/channel_voice.rs @@ -1,13 +1,17 @@ +use crate::io::Write; use crate::ReceiverContext; use super::parse_error::*; use super::util::*; +#[cfg(test)] use alloc::vec; +#[cfg(feature = "alloc")] use alloc::vec::Vec; /// Channel-level messages that act on a voice. For instance, turning notes on off, /// or modifying sounding notes. Used in [`MidiMsg`](crate::MidiMsg). #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ChannelVoiceMsg { /// Turn on a note NoteOn { @@ -50,19 +54,19 @@ pub enum ChannelVoiceMsg { } impl ChannelVoiceMsg { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, ch: u8, mut v: impl Write) -> Result<(), E> { match self { - ChannelVoiceMsg::NoteOff { .. } => v.push(0x80), - ChannelVoiceMsg::NoteOn { .. } => v.push(0x90), - ChannelVoiceMsg::HighResNoteOff { .. } => v.push(0x80), - ChannelVoiceMsg::HighResNoteOn { .. } => v.push(0x90), - ChannelVoiceMsg::PolyPressure { .. } => v.push(0xA0), - ChannelVoiceMsg::ControlChange { .. } => v.push(0xB0), - ChannelVoiceMsg::ProgramChange { .. } => v.push(0xC0), - ChannelVoiceMsg::ChannelPressure { .. } => v.push(0xD0), - ChannelVoiceMsg::PitchBend { .. } => v.push(0xE0), + ChannelVoiceMsg::NoteOff { .. } => v.write(&[0x80 + ch])?, + ChannelVoiceMsg::NoteOn { .. } => v.write(&[0x90 + ch])?, + ChannelVoiceMsg::HighResNoteOff { .. } => v.write(&[0x80 + ch])?, + ChannelVoiceMsg::HighResNoteOn { .. } => v.write(&[0x90 + ch])?, + ChannelVoiceMsg::PolyPressure { .. } => v.write(&[0xA0 + ch])?, + ChannelVoiceMsg::ControlChange { .. } => v.write(&[0xB0 + ch])?, + ChannelVoiceMsg::ProgramChange { .. } => v.write(&[0xC0 + ch])?, + ChannelVoiceMsg::ChannelPressure { .. } => v.write(&[0xD0 + ch])?, + ChannelVoiceMsg::PitchBend { .. } => v.write(&[0xE0 + ch])?, } - self.extend_midi_running(v); + self.extend_midi_running(ch, v) } // Can this message be extended by another? @@ -140,42 +144,28 @@ impl ChannelVoiceMsg { } /// Out of necessity, pushes a Channel message after the note message for `HighResNoteOn/Off` - pub(crate) fn extend_midi_running(&self, v: &mut Vec) { + pub(crate) fn extend_midi_running( + &self, + ch: u8, + mut v: impl Write, + ) -> Result<(), E> { match *self { - ChannelVoiceMsg::NoteOff { note, velocity } => { - v.push(to_u7(note)); - v.push(to_u7(velocity)); - } - ChannelVoiceMsg::NoteOn { note, velocity } => { - v.push(to_u7(note)); - v.push(to_u7(velocity)); - } - ChannelVoiceMsg::HighResNoteOff { note, velocity } => { - let [msb, lsb] = to_u14(velocity); - push_u7(note, v); - v.push(msb); - v.push(0xB0); - v.push(0x58); - v.push(lsb); + ChannelVoiceMsg::NoteOff { note, velocity } + | ChannelVoiceMsg::NoteOn { note, velocity } => { + v.write(&[to_u7(note), to_u7(velocity)]) } - ChannelVoiceMsg::HighResNoteOn { note, velocity } => { + ChannelVoiceMsg::HighResNoteOff { note, velocity } + | ChannelVoiceMsg::HighResNoteOn { note, velocity } => { let [msb, lsb] = to_u14(velocity); - push_u7(note, v); - v.push(msb); - v.push(0xB0); - v.push(0x58); - v.push(lsb); + v.write(&[to_u7(note), msb, 0xB0 + ch, 0x58, lsb]) } ChannelVoiceMsg::PolyPressure { note, pressure } => { - v.push(to_u7(note)); - v.push(to_u7(pressure)); + v.write(&[to_u7(note), to_u7(pressure)]) } ChannelVoiceMsg::ControlChange { control } => control.extend_midi_running(v), - ChannelVoiceMsg::ProgramChange { program } => v.push(to_u7(program)), - ChannelVoiceMsg::ChannelPressure { pressure } => v.push(to_u7(pressure)), - ChannelVoiceMsg::PitchBend { bend } => { - push_u14(bend, v); - } + ChannelVoiceMsg::ProgramChange { program } => v.write(&[to_u7(program)]), + ChannelVoiceMsg::ChannelPressure { pressure } => v.write(&[to_u7(pressure)]), + ChannelVoiceMsg::PitchBend { bend } => push_u14(bend, v), } } @@ -363,6 +353,7 @@ pub enum ControlNumber { /// /// When deserializing and [`complex_cc`](crate::ReceiverContext) is false (the default), only [ControlChange::CC] values are returned. "Simple" CC values represent the control parameter with a number, while "complex" variants capture the semantics of the spec. Simple can be turned into their complex counterparts using the [`to_complex`](ControlChange::to_complex) method, or vis-versa using the [`to_simple`](ControlChange::to_simple) and [`to_simple_high_res`](ControlChange::to_simple_high_res) methods. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ControlChange { /// "Simple" Control Change message. /// @@ -844,25 +835,23 @@ impl ControlChange { } } - fn high_res_cc(v: &mut Vec, control: u8, value: u16) { + fn high_res_cc(mut v: impl Write, control: u8, value: u16) -> Result<(), E> { let [msb, lsb] = to_u14(value); - v.push(control); - v.push(msb); - v.push(control + 32); - v.push(lsb); + v.write(&[control, msb, control + 32, lsb]) } - fn undefined(v: &mut Vec, control: u8, value: u8) { - v.push(control.min(119)); - v.push(to_u7(value)); + fn undefined(mut v: impl Write, control: u8, value: u8) -> Result<(), E> { + v.write(&[control.min(119), to_u7(value)]) } - fn undefined_high_res(v: &mut Vec, control1: u8, control2: u8, value: u16) { + fn undefined_high_res( + mut v: impl Write, + control1: u8, + control2: u8, + value: u16, + ) -> Result<(), E> { let [msb, lsb] = to_u14(value); - v.push(control1.min(119)); - v.push(msb); - v.push(control2.min(119)); - v.push(lsb); + v.write(&[control1.min(119), msb, control2.min(119), lsb]) } fn is_msb(&self) -> bool { @@ -893,13 +882,14 @@ impl ControlChange { matches!(self, Self::CC { control, .. } if (&32..&64).contains(&control) || control == &98 || control == &100) } + #[cfg(feature = "alloc")] pub fn to_midi_running(&self) -> Vec { - let mut r: Vec = vec![]; - self.extend_midi_running(&mut r); + let mut r = Vec::new(); + self.extend_midi_running(&mut r).expect("Vec can't expand?"); r } - pub fn extend_midi_running(&self, v: &mut Vec) { + pub fn extend_midi_running(&self, mut v: impl Write) -> Result<(), E> { match *self { ControlChange::BankSelect(x) => ControlChange::high_res_cc(v, 0, x), ControlChange::ModWheel(x) => ControlChange::high_res_cc(v, 1, x), @@ -922,132 +912,66 @@ impl ControlChange { ControlChange::GeneralPurpose2(x) => ControlChange::high_res_cc(v, 17, x), ControlChange::GeneralPurpose3(x) => ControlChange::high_res_cc(v, 18, x), ControlChange::GeneralPurpose4(x) => ControlChange::high_res_cc(v, 19, x), - ControlChange::GeneralPurpose5(x) => { - v.push(80); - v.push(to_u7(x)); - } - ControlChange::GeneralPurpose6(x) => { - v.push(82); - v.push(to_u7(x)); - } - ControlChange::GeneralPurpose7(x) => { - v.push(83); - v.push(to_u7(x)); - } - ControlChange::GeneralPurpose8(x) => { - v.push(84); - v.push(to_u7(x)); - } - ControlChange::Hold(x) => { - v.push(64); - v.push(to_u7(x)); - } - ControlChange::Hold2(x) => { - v.push(69); - v.push(to_u7(x)); - } - ControlChange::TogglePortamento(on) => { - v.push(65); - v.push(if on { 127 } else { 0 }); - } - ControlChange::Sostenuto(x) => { - v.push(66); - v.push(to_u7(x)); - } - ControlChange::SoftPedal(x) => { - v.push(67); - v.push(to_u7(x)); - } - ControlChange::ToggleLegato(on) => { - v.push(68); - v.push(if on { 127 } else { 0 }); - } + ControlChange::GeneralPurpose5(x) => v.write(&[80, to_u7(x)]), + ControlChange::GeneralPurpose6(x) => v.write(&[82, to_u7(x)]), + ControlChange::GeneralPurpose7(x) => v.write(&[83, to_u7(x)]), + ControlChange::GeneralPurpose8(x) => v.write(&[84, to_u7(x)]), + ControlChange::Hold(x) => v.write(&[64, to_u7(x)]), + ControlChange::Hold2(x) => v.write(&[69, to_u7(x)]), + ControlChange::TogglePortamento(on) => v.write(&[65, if on { 127 } else { 0 }]), + ControlChange::Sostenuto(x) => v.write(&[66, to_u7(x)]), + ControlChange::SoftPedal(x) => v.write(&[67, to_u7(x)]), + ControlChange::ToggleLegato(on) => v.write(&[68, if on { 127 } else { 0 }]), ControlChange::SoundVariation(x) | ControlChange::SoundControl1(x) => { - v.push(70); - v.push(to_u7(x)); - } - ControlChange::Timbre(x) | ControlChange::SoundControl2(x) => { - v.push(71); - v.push(to_u7(x)); + v.write(&[70, to_u7(x)]) } + ControlChange::Timbre(x) | ControlChange::SoundControl2(x) => v.write(&[71, to_u7(x)]), ControlChange::ReleaseTime(x) | ControlChange::SoundControl3(x) => { - v.push(72); - v.push(to_u7(x)); + v.write(&[72, to_u7(x)]) } ControlChange::AttackTime(x) | ControlChange::SoundControl4(x) => { - v.push(73); - v.push(to_u7(x)); + v.write(&[73, to_u7(x)]) } ControlChange::Brightness(x) | ControlChange::SoundControl5(x) => { - v.push(74); - v.push(to_u7(x)); + v.write(&[74, to_u7(x)]) } ControlChange::DecayTime(x) | ControlChange::SoundControl6(x) => { - v.push(75); - v.push(to_u7(x)); + v.write(&[75, to_u7(x)]) } ControlChange::VibratoRate(x) | ControlChange::SoundControl7(x) => { - v.push(76); - v.push(to_u7(x)); + v.write(&[76, to_u7(x)]) } ControlChange::VibratoDepth(x) | ControlChange::SoundControl8(x) => { - v.push(77); - v.push(to_u7(x)); + v.write(&[77, to_u7(x)]) } ControlChange::VibratoDelay(x) | ControlChange::SoundControl9(x) => { - v.push(78); - v.push(to_u7(x)); - } - ControlChange::SoundControl10(x) => { - v.push(79); - v.push(to_u7(x)); - } - ControlChange::PortamentoControl(x) => { - v.push(84); - v.push(to_u7(x)); - } - ControlChange::HighResVelocity(x) => { - v.push(88); - v.push(to_u7(x)); + v.write(&[78, to_u7(x)]) } + ControlChange::SoundControl10(x) => v.write(&[79, to_u7(x)]), + ControlChange::PortamentoControl(x) => v.write(&[84, to_u7(x)]), + ControlChange::HighResVelocity(x) => v.write(&[88, to_u7(x)]), ControlChange::Effects1Depth(x) | ControlChange::ReverbSendLevel(x) => { - v.push(91); - v.push(to_u7(x)); + v.write(&[91, to_u7(x)]) } ControlChange::Effects2Depth(x) | ControlChange::TremoloDepth(x) => { - v.push(92); - v.push(to_u7(x)); + v.write(&[92, to_u7(x)]) } ControlChange::Effects3Depth(x) | ControlChange::ChorusSendLevel(x) => { - v.push(93); - v.push(to_u7(x)); + v.write(&[93, to_u7(x)]) } ControlChange::Effects4Depth(x) | ControlChange::CelesteDepth(x) => { - v.push(94); - v.push(to_u7(x)); + v.write(&[94, to_u7(x)]) } ControlChange::Effects5Depth(x) | ControlChange::PhaserDepth(x) => { - v.push(95); - v.push(to_u7(x)); + v.write(&[95, to_u7(x)]) } // Parameters - ControlChange::Parameter(p) => p.extend_midi_running(v), + ControlChange::Parameter(p) => p.extend_midi_running(&mut v), ControlChange::DataEntry(x) => ControlChange::high_res_cc(v, 6, x), - ControlChange::DataEntry2(msb, lsb) => { - v.push(6); - v.push(msb); - v.push(6 + 32); - v.push(lsb); - } - ControlChange::DataIncrement(x) => { - v.push(96); - v.push(to_u7(x)); - } - ControlChange::DataDecrement(x) => { - v.push(97); - v.push(to_u7(x)); - } + ControlChange::DataEntry2(msb, lsb) => v.write(&[6, msb, 6 + 32, lsb]), + ControlChange::DataIncrement(x) => v.write(&[96, to_u7(x)]), + ControlChange::DataDecrement(x) => v.write(&[97, to_u7(x)]), } } @@ -1297,6 +1221,7 @@ impl ControlChange { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] /// Used by [`ControlChange::Parameter`]. "Entry" Parameters can be used to set the given parameters: /// they will first select that parameter, then send a [`ControlChange::DataEntry`] with the given value. pub enum Parameter { @@ -1386,210 +1311,110 @@ pub enum Parameter { } impl Parameter { - fn extend_midi_running(&self, v: &mut Vec) { + fn extend_midi_running(&self, v: &mut impl Write) -> Result<(), E> { match self { - Self::Null => { - v.push(100); - v.push(0x7F); - v.push(101); - v.push(0x7F); - } - Self::PitchBendSensitivity => { - v.push(100); - v.push(0); - v.push(101); - v.push(0); - } + Self::Null => v.write(&[100, 0x7F, 101, 0x7F]), + Self::PitchBendSensitivity => v.write(&[100, 0, 101, 0]), Self::PitchBendSensitivityEntry(c, f) => { - Self::PitchBendSensitivity.extend_midi_running(v); + Self::PitchBendSensitivity.extend_midi_running(v)?; // Data entry: - v.push(6); - v.push(*c); - v.push(6 + 32); - v.push((*f).min(100)); - } - Self::FineTuning => { - v.push(100); - v.push(1); - v.push(101); - v.push(0); + v.write(&[6, *c, 6 + 32, (*f).min(100)]) } + Self::FineTuning => v.write(&[100, 1, 101, 0]), Self::FineTuningEntry(x) => { - Self::FineTuning.extend_midi_running(v); + Self::FineTuning.extend_midi_running(v)?; // Data entry: let [msb, lsb] = i_to_u14(*x); - v.push(6); - v.push(msb); - v.push(6 + 32); - v.push(lsb); - } - Self::CoarseTuning => { - v.push(100); - v.push(2); - v.push(101); - v.push(0); + v.write(&[6, msb, 6 + 32, lsb]) } + Self::CoarseTuning => v.write(&[100, 2, 101, 0]), Self::CoarseTuningEntry(x) => { - Self::CoarseTuning.extend_midi_running(v); + Self::CoarseTuning.extend_midi_running(v)?; // Data entry: let msb = i_to_u7(*x); - v.push(6); - v.push(msb); - v.push(6 + 32); - v.push(0); - } - Self::TuningProgramSelect => { - v.push(100); - v.push(3); - v.push(101); - v.push(0); + v.write(&[6, msb, 6 + 32, 0]) } + Self::TuningProgramSelect => v.write(&[100, 3, 101, 0]), Self::TuningProgramSelectEntry(x) => { - Self::TuningProgramSelect.extend_midi_running(v); + Self::TuningProgramSelect.extend_midi_running(v)?; // Data entry (MSB only) - v.push(6); - v.push(*x); - } - Self::TuningBankSelect => { - v.push(100); - v.push(4); - v.push(101); - v.push(0); + v.write(&[6, *x]) } + Self::TuningBankSelect => v.write(&[100, 4, 101, 0]), Self::TuningBankSelectEntry(x) => { - Self::TuningBankSelect.extend_midi_running(v); + Self::TuningBankSelect.extend_midi_running(v)?; // Data entry (MSB only) - v.push(6); - v.push(*x); - } - Self::ModulationDepthRange => { - v.push(100); - v.push(5); - v.push(101); - v.push(0); + v.write(&[6, *x]) } + Self::ModulationDepthRange => v.write(&[100, 5, 101, 0]), Self::ModulationDepthRangeEntry(x) => { - Self::ModulationDepthRange.extend_midi_running(v); + Self::ModulationDepthRange.extend_midi_running(v)?; // Data entry - ControlChange::high_res_cc(v, 6, *x); - } - Self::PolyphonicExpression => { - v.push(100); - v.push(6); - v.push(101); - v.push(0); + ControlChange::high_res_cc(v, 6, *x) } + Self::PolyphonicExpression => v.write(&[100, 6, 101, 0]), Self::PolyphonicExpressionEntry(x) => { - Self::PolyphonicExpression.extend_midi_running(v); + Self::PolyphonicExpression.extend_midi_running(v)?; // Data entry (MSB only) - v.push(6); - v.push((*x).min(16)); - } - Self::AzimuthAngle3DSound => { - v.push(100); - v.push(0); - v.push(101); - v.push(61); // 3D Sound + v.write(&[6, (*x).min(16)]) } + Self::AzimuthAngle3DSound => v.write(&[100, 0, 101, 61]), Self::AzimuthAngle3DSoundEntry(x) => { - Self::AzimuthAngle3DSound.extend_midi_running(v); + Self::AzimuthAngle3DSound.extend_midi_running(v)?; // Data entry - ControlChange::high_res_cc(v, 6, *x); - } - Self::ElevationAngle3DSound => { - v.push(100); - v.push(1); - v.push(101); - v.push(61); // 3D Sound + ControlChange::high_res_cc(v, 6, *x) } + Self::ElevationAngle3DSound => v.write(&[100, 1, 101, 61]), Self::ElevationAngle3DSoundEntry(x) => { - Self::ElevationAngle3DSound.extend_midi_running(v); + Self::ElevationAngle3DSound.extend_midi_running(v)?; // Data entry - ControlChange::high_res_cc(v, 6, *x); - } - Self::Gain3DSound => { - v.push(100); - v.push(2); - v.push(101); - v.push(61); // 3D Sound + ControlChange::high_res_cc(v, 6, *x) } + Self::Gain3DSound => v.write(&[100, 2, 101, 61]), Self::Gain3DSoundEntry(x) => { - Self::Gain3DSound.extend_midi_running(v); + Self::Gain3DSound.extend_midi_running(v)?; // Data entry - ControlChange::high_res_cc(v, 6, *x); - } - Self::DistanceRatio3DSound => { - v.push(100); - v.push(3); - v.push(101); - v.push(61); // 3D Sound + ControlChange::high_res_cc(v, 6, *x) } + Self::DistanceRatio3DSound => v.write(&[100, 3, 101, 0]), Self::DistanceRatio3DSoundEntry(x) => { - Self::DistanceRatio3DSound.extend_midi_running(v); + Self::DistanceRatio3DSound.extend_midi_running(v)?; // Data entry - ControlChange::high_res_cc(v, 6, *x); - } - Self::MaxiumumDistance3DSound => { - v.push(100); - v.push(4); - v.push(101); - v.push(61); // 3D Sound + ControlChange::high_res_cc(v, 6, *x) } + Self::MaxiumumDistance3DSound => v.write(&[100, 4, 101, 61]), Self::MaxiumumDistance3DSoundEntry(x) => { - Self::MaxiumumDistance3DSound.extend_midi_running(v); + Self::MaxiumumDistance3DSound.extend_midi_running(v)?; // Data entry - ControlChange::high_res_cc(v, 6, *x); - } - Self::GainAtMaxiumumDistance3DSound => { - v.push(100); - v.push(5); - v.push(101); - v.push(61); // 3D Sound + ControlChange::high_res_cc(v, 6, *x) } + Self::GainAtMaxiumumDistance3DSound => v.write(&[100, 5, 101, 61]), Self::GainAtMaxiumumDistance3DSoundEntry(x) => { - Self::GainAtMaxiumumDistance3DSound.extend_midi_running(v); + Self::GainAtMaxiumumDistance3DSound.extend_midi_running(v)?; // Data entry - ControlChange::high_res_cc(v, 6, *x); - } - Self::ReferenceDistanceRatio3DSound => { - v.push(100); - v.push(6); - v.push(101); - v.push(61); // 3D Sound + ControlChange::high_res_cc(v, 6, *x) } + Self::ReferenceDistanceRatio3DSound => v.write(&[100, 6, 101, 61]), Self::ReferenceDistanceRatio3DSoundEntry(x) => { - Self::ReferenceDistanceRatio3DSound.extend_midi_running(v); + Self::ReferenceDistanceRatio3DSound.extend_midi_running(v)?; // Data entry - ControlChange::high_res_cc(v, 6, *x); - } - Self::PanSpreadAngle3DSound => { - v.push(100); - v.push(7); - v.push(101); - v.push(61); // 3D Sound + ControlChange::high_res_cc(v, 6, *x) } + Self::PanSpreadAngle3DSound => v.write(&[100, 7, 101, 61]), Self::PanSpreadAngle3DSoundEntry(x) => { - Self::PanSpreadAngle3DSound.extend_midi_running(v); + Self::PanSpreadAngle3DSound.extend_midi_running(v)?; // Data entry - ControlChange::high_res_cc(v, 6, *x); - } - Self::RollAngle3DSound => { - v.push(100); - v.push(8); - v.push(101); - v.push(61); // 3D Sound + ControlChange::high_res_cc(v, 6, *x) } + Self::RollAngle3DSound => v.write(&[100, 8, 101, 61]), Self::RollAngle3DSoundEntry(x) => { - Self::RollAngle3DSound.extend_midi_running(v); + Self::RollAngle3DSound.extend_midi_running(v)?; // Data entry - ControlChange::high_res_cc(v, 6, *x); + ControlChange::high_res_cc(v, 6, *x) } Self::Unregistered(x) => { let [msb, lsb] = to_u14(*x); - v.push(98); - v.push(lsb); - v.push(99); - v.push(msb); + v.write(&[98, lsb, 99, msb]) } } } diff --git a/src/file.rs b/src/file.rs index c4b0731..5bf28d3 100644 --- a/src/file.rs +++ b/src/file.rs @@ -12,9 +12,12 @@ use micromath::F32Ext; use core::error; +use crate::Seek; +use crate::Write; + use super::{ - Channel, HighResTimeCode, MidiMsg, ParseError, ReceiverContext, SystemExclusiveMsg, - TimeCodeType, util::*, + util::*, Channel, HighResTimeCode, MidiMsg, ParseError, ReceiverContext, SystemExclusiveMsg, + TimeCodeType, }; // Standard Midi File 1.0 (SMF): RP-001 support @@ -172,9 +175,9 @@ impl MidiFile { /// Turn a `MidiFile` into a series of bytes. pub fn to_midi(&self) -> Vec { let mut r: Vec = vec![]; - self.header.extend_midi(&mut r); + self.header.extend_midi(&mut r).expect("Vec can't expand?"); for track in &self.tracks { - track.extend_midi(&mut r); + track.extend_midi(&mut r).expect("Vec can't expand?"); } r } @@ -265,22 +268,20 @@ impl Header { Ok(()) } - fn extend_midi(&self, v: &mut Vec) { - v.extend_from_slice(b"MThd"); - push_u32(6, v); // Length of header, always 6 bytes + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + v.write(b"MThd")?; + push_u32(6, &mut v)?; // Length of header, always 6 bytes - self.format.extend_midi(v); - push_u16(self.num_tracks, v); + self.format.extend_midi(&mut v)?; + push_u16(self.num_tracks, &mut v)?; match self.division { - Division::TicksPerQuarterNote(tpqn) => { - push_u16(tpqn, v); - } + Division::TicksPerQuarterNote(tpqn) => push_u16(tpqn, &mut v), Division::TimeCode { frames_per_second, ticks_per_frame, } => { - v.push(0b1000_0000 | frames_per_second as u8); - v.push(ticks_per_frame); + v.push(0b1000_0000 | frames_per_second as u8)?; + v.push(ticks_per_frame) } } } @@ -315,11 +316,11 @@ impl SMFFormat { )) } - fn extend_midi(&self, v: &mut Vec) { + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { - SMFFormat::SingleTrack => v.extend_from_slice(&[0, 0]), - SMFFormat::MultiTrack => v.extend_from_slice(&[0, 1]), - SMFFormat::MultiSong => v.extend_from_slice(&[0, 2]), + SMFFormat::SingleTrack => v.write(&[0, 0]), + SMFFormat::MultiTrack => v.write(&[0, 1]), + SMFFormat::MultiSong => v.write(&[0, 2]), } } } @@ -452,23 +453,23 @@ impl Track { Ok(()) } - fn extend_midi(&self, v: &mut Vec) { + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { Track::Midi(events) => { - v.extend_from_slice(b"MTrk"); - let s = v.len(); - push_u32(0, v); // We will fill this in after we know the length + v.write(b"MTrk")?; + let mut v = v.make_seekable().unwrap(); + let s = v.tell()?; + + push_u32(0, &mut v)?; // We will fill this in after we know the length for event in events { - event.extend_midi(v); + event.extend_midi(&mut v)?; } - let e = v.len(); + let e = v.tell()?; // Fill in the length - v[s..s + 4].copy_from_slice(&(e as u32 - s as u32 - 4).to_be_bytes()); - } - Track::AlienChunk(data) => { - v.extend_from_slice(data); + v.write_at(&(e as u32 - s as u32 - 4).to_be_bytes(), s) } + Track::AlienChunk(data) => v.write(&data), } } } @@ -589,7 +590,7 @@ impl TrackEvent { } } - fn extend_midi(&self, v: &mut Vec) { + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { if matches!( self.event, MidiMsg::SystemRealTime { @@ -598,12 +599,12 @@ impl TrackEvent { ) { #[cfg(feature = "std")] log::warn!("SMF contains System Reset event, which is not valid. Skipping."); - return; + return Ok(()); } else if self.event.is_invalid() { - return; + return Ok(()); } - push_vlq(self.delta_time, v); + push_vlq(self.delta_time, &mut v)?; // TODO this doesn't handle running-status events let event = self.event.to_midi(); @@ -616,18 +617,19 @@ impl TrackEvent { | MidiMsg::SystemRealTime { .. } ); if is_meta { - v.push(0xFF); + v.push(0xFF)?; } else if is_system { // We always use the 0xF7 format for system events, since it can be used for all system events, not just system exclusive - v.push(0xF7); - push_vlq(event.len() as u32, v); + v.push(0xF7)?; + push_vlq(event.len() as u32, &mut v)?; } - v.extend_from_slice(&event); + v.write(&event) } } /// A meta event in a Standard Midi File #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Meta { /// Must occur at the start of a track, and specifies the sequence number of the track. In a MultiSong file, this is the "pattern" number that identifies the song for cueing purposes. SequenceNumber(u16), @@ -768,93 +770,93 @@ impl Meta { } } - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { Meta::SequenceNumber(n) => { - v.push(0x00); - push_vlq(2, v); - v.extend_from_slice(&n.to_be_bytes()); + v.push(0x00)?; + push_vlq(2, &mut v)?; + v.write(&n.to_be_bytes()) } Meta::Text(s) => { - v.push(0x01); + v.push(0x01)?; let bytes = s.as_bytes(); - push_vlq(bytes.len() as u32, v); - v.extend_from_slice(bytes); + push_vlq(bytes.len() as u32, &mut v)?; + v.write(bytes) } Meta::Copyright(s) => { - v.push(0x02); + v.push(0x02)?; let bytes = s.as_bytes(); - push_vlq(bytes.len() as u32, v); - v.extend_from_slice(bytes); + push_vlq(bytes.len() as u32, &mut v)?; + v.write(bytes) } Meta::TrackName(s) => { - v.push(0x03); + v.push(0x03)?; let bytes = s.as_bytes(); - push_vlq(bytes.len() as u32, v); - v.extend_from_slice(bytes); + push_vlq(bytes.len() as u32, &mut v)?; + v.write(bytes) } Meta::InstrumentName(s) => { - v.push(0x04); + v.push(0x04)?; let bytes = s.as_bytes(); - push_vlq(bytes.len() as u32, v); - v.extend_from_slice(bytes); + push_vlq(bytes.len() as u32, &mut v)?; + v.write(bytes) } Meta::Lyric(s) => { - v.push(0x05); + v.push(0x05)?; let bytes = s.as_bytes(); - push_vlq(bytes.len() as u32, v); - v.extend_from_slice(bytes); + push_vlq(bytes.len() as u32, &mut v)?; + v.write(bytes) } Meta::Marker(s) => { - v.push(0x06); + v.push(0x06)?; let bytes = s.as_bytes(); - push_vlq(bytes.len() as u32, v); - v.extend_from_slice(bytes); + push_vlq(bytes.len() as u32, &mut v)?; + v.write(bytes) } Meta::CuePoint(s) => { - v.push(0x07); + v.push(0x07)?; let bytes = s.as_bytes(); - push_vlq(bytes.len() as u32, v); - v.extend_from_slice(bytes); + push_vlq(bytes.len() as u32, &mut v)?; + v.write(bytes) } Meta::ChannelPrefix(n) => { - v.push(0x20); - push_vlq(1, v); - v.push(*n as u8); + v.push(0x20)?; + push_vlq(1, &mut v)?; + v.push(*n as u8) } Meta::EndOfTrack => { - v.push(0x2F); - push_vlq(0, v); + v.push(0x2F)?; + push_vlq(0, &mut v) } Meta::SetTempo(n) => { - v.push(0x51); - push_vlq(3, v); - v.extend_from_slice(&n.to_be_bytes()[1..]); + v.push(0x51)?; + push_vlq(3, &mut v)?; + v.write(&n.to_be_bytes()[1..]) } Meta::SmpteOffset(t) => { - v.push(0x54); - push_vlq(5, v); - t.extend_midi(v); + v.push(0x54)?; + push_vlq(5, &mut v)?; + t.extend_midi(v) } Meta::TimeSignature(s) => { - v.push(0x58); - push_vlq(4, v); - s.extend_midi(v); + v.push(0x58)?; + push_vlq(4, &mut v)?; + s.extend_midi(v) } Meta::KeySignature(k) => { - v.push(0x59); - push_vlq(2, v); - k.extend_midi(v); + v.push(0x59)?; + push_vlq(2, &mut v)?; + k.extend_midi(v) } Meta::SequencerSpecific(d) => { - v.push(0x7F); - push_vlq(d.len() as u32, v); - v.extend_from_slice(d); + v.push(0x7F)?; + push_vlq(d.len() as u32, &mut v)?; + v.write(d) } Meta::Unknown { meta_type, data } => { - v.push(*meta_type); - push_vlq(data.len() as u32, v); - v.extend_from_slice(data); + v.push(*meta_type)?; + push_vlq(data.len() as u32, &mut v)?; + v.write(data) } } } @@ -862,6 +864,7 @@ impl Meta { /// A time signature occurring in a Standard Midi File. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FileTimeSignature { /// The numerator of the time signature, as it would be notated. pub numerator: u8, @@ -888,16 +891,17 @@ impl FileTimeSignature { }) } - pub(crate) fn extend_midi(&self, v: &mut Vec) { - v.push(self.numerator); - v.push((self.denominator as f32).log2() as u8); - v.push(self.clocks_per_metronome_tick); - v.push(self.thirty_second_notes_per_24_clocks); + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + v.push(self.numerator)?; + v.push((self.denominator as f32).log2() as u8)?; + v.push(self.clocks_per_metronome_tick)?; + v.push(self.thirty_second_notes_per_24_clocks) } } /// A key signature occurring in a Standard Midi File. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct KeySignature { /// Negative for number of flats, positive for number of sharps pub key: i8, @@ -916,9 +920,9 @@ impl KeySignature { }) } - pub(crate) fn extend_midi(&self, v: &mut Vec) { - v.push(self.key as u8); - v.push(self.scale); + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + v.push(self.key as u8)?; + v.push(self.scale) } } @@ -974,9 +978,9 @@ mod tests { #[test] fn test_file_serde() { + use crate::message::MidiMsg; use crate::Channel; use crate::ChannelVoiceMsg; - use crate::message::MidiMsg; // Create a simple MIDI file let mut file = MidiFile::default(); diff --git a/src/general_midi.rs b/src/general_midi.rs index 5058799..b1a39fa 100644 --- a/src/general_midi.rs +++ b/src/general_midi.rs @@ -7,6 +7,7 @@ use strum::{Display, EnumIter, EnumString}; /// /// Used in [`UniversalNonRealTimeMsg::GeneralMidi`](crate::UniversalNonRealTimeMsg::GeneralMidi) #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum GeneralMidi { GM1 = 1, GM2 = 3, diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 0000000..16c660c --- /dev/null +++ b/src/io.rs @@ -0,0 +1,436 @@ +//! Provides abstractions over writers, even in `no_std` environments. +//! +//! When the `std` feature is enabled, `IoWrap` and `SeekableWrap` provide a bridge between the +//! `std::io` API and the `midly::io` API. +//! Besides, `write` methods that work with `midly::io::Write` types usually provide a `write_std` +//! variant that works with `std::io::Write` types when the `std` feature is enabled. + +use core::marker::PhantomData; + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +pub type StdResult = core::result::Result; + +/// Either `Ok(())` or the error specific to the `W` writer. +pub type WriteResult = StdResult<(), ::Error>; + +/// A `Write` trait available even in `no_std` environments, and with per-type errors. +pub trait Write { + /// The error type specific to the writer. + type Error; + /// `Self` when the type is seekable, and `NotSeekable` otherwise. + type Seekable: Write + Seek; + + /// Push a single byte + fn push(&mut self, b: u8) -> WriteResult; + /// Write a slice of data to the writer. + /// + /// Should error if not all of the data could be written. + fn write(&mut self, buf: &[u8]) -> WriteResult { + self.write_iter(buf.iter().copied()) + } + /// Write a slice of data to the writer. + /// + /// Should error if not all of the data could be written. + fn write_iter(&mut self, mut data: impl Iterator) -> WriteResult { + while let Some(b) = data.next() { + self.push(b)?; + } + Ok(()) + } + /// Create an "invalid input"-style error from a string literal. + fn invalid_input(msg: &'static str) -> Self::Error; + /// Make this writer seekable, if possible. + #[inline] + fn make_seekable(&mut self) -> Option<&mut Self::Seekable> { + None + } +} + +/// A `Seek` trait available even in `no_std` environments. +/// +/// Not all of the `Seek` functionality is required, only the functionality required to write MIDI +/// files. +pub trait Seek: Write { + /// Where is the writer currently at. + fn tell(&mut self) -> StdResult; + /// Write a slice of data at the given absolute position, and return to the end of the writer + /// afterwards. + fn write_at(&mut self, buf: &[u8], pos: u64) -> WriteResult; +} + +#[derive(Copy, Clone, Debug)] +enum Never {} + +/// The type used for the [`Seekable`](trait.Write.html#associatedtype.Seekable) associated type on +/// non-seekable writers. +#[derive(Debug)] +pub struct NotSeekable { + _phantom: PhantomData, + never: Never, +} +impl Clone for NotSeekable { + fn clone(&self) -> Self { + *self + } +} +impl Copy for NotSeekable {} +impl NotSeekable { + /// A `NotSeekable` value should never exist. + /// Because of this, this function never returns, as it could never have been called. + #[inline] + pub fn as_never(&self) -> ! { + match self.never {} + } +} + +impl Write for NotSeekable { + type Error = W::Error; + type Seekable = Self; + #[inline] + fn invalid_input(msg: &'static str) -> W::Error { + W::invalid_input(msg) + } + #[inline] + fn push(&mut self, _: u8) -> WriteResult { + self.as_never() + } +} + +impl Seek for NotSeekable { + #[inline] + fn tell(&mut self) -> StdResult { + self.as_never() + } + #[inline] + fn write_at(&mut self, _: &[u8], _: u64) -> WriteResult { + self.as_never() + } +} + +impl<'a, W: Write> Write for &'a mut W { + type Error = W::Error; + type Seekable = W::Seekable; + #[inline] + fn write(&mut self, buf: &[u8]) -> WriteResult { + W::write(self, buf) + } + #[inline] + fn invalid_input(msg: &'static str) -> W::Error { + W::invalid_input(msg) + } + #[inline] + fn make_seekable(&mut self) -> Option<&mut W::Seekable> { + W::make_seekable(self) + } + #[inline] + fn write_iter(&mut self, data: impl Iterator) -> WriteResult { + W::write_iter(self, data) + } + #[inline] + fn push(&mut self, b: u8) -> WriteResult { + W::push(self, b) + } +} + +#[cfg(feature = "alloc")] +impl Write for Vec { + type Error = &'static str; + type Seekable = Self; + #[inline] + fn write(&mut self, buf: &[u8]) -> WriteResult { + self.extend_from_slice(buf); + Ok(()) + } + #[inline] + fn push(&mut self, b: u8) -> WriteResult { + self.push(b); + Ok(()) + } + #[inline] + fn invalid_input(msg: &'static str) -> &'static str { + msg + } + #[inline] + fn make_seekable(&mut self) -> Option<&mut Self> { + Some(self) + } +} +#[cfg(feature = "alloc")] +impl Seek for Vec { + #[inline] + fn tell(&mut self) -> StdResult { + Ok(self.len() as u64) + } + #[inline] + fn write_at(&mut self, buf: &[u8], pos: u64) -> WriteResult { + let out = self + .get_mut(pos as usize..pos as usize + buf.len()) + .ok_or("invalid seekback")?; + out.copy_from_slice(buf); + Ok(()) + } +} + +/// A seekable writer over an in-memory buffer. +/// +/// Available even when the `std` and `alloc` features are disabled. +#[derive(Debug)] +pub struct Cursor<'a> { + buf: &'a mut [u8], + cur: usize, +} +impl<'a> Cursor<'a> { + /// Create a new cursor located at the start of the given buffer. + #[inline] + pub fn new(buffer: &mut [u8]) -> Cursor { + Cursor { + buf: buffer, + cur: 0, + } + } + + /// Create a cursor from a buffer and the cursor within it. + /// + /// # Panics + /// + /// Panics if `cursor > buffer.len()`. + #[inline] + pub fn from_parts(buffer: &mut [u8], cursor: usize) -> Cursor { + assert!( + cursor <= buffer.len(), + "cursor beyond the end of the buffer" + ); + Cursor { + buf: buffer, + cur: cursor, + } + } + + /// Yield the underlying buffer and the cursor within it. + /// + /// The cursor is guaranteed to be `cursor <= buffer.len()`. + #[inline] + pub fn into_parts(self) -> (&'a mut [u8], usize) { + (self.buf, self.cur) + } + + /// Get a reference to the whole underlying buffer. + #[inline] + pub fn slice(&self) -> &[u8] { + self.buf + } + + /// Get a mutable reference to the whole underlying buffer. + #[inline] + pub fn slice_mut(&mut self) -> &mut [u8] { + self.buf + } + + /// Get the position of the cursor. + #[inline] + pub fn cursor(&self) -> usize { + self.cur + } + + /// Get a reference to the written portion of the buffer. + #[inline] + pub fn written(&self) -> &[u8] { + &self.buf[..self.cur] + } + + /// Get a reference to the portion of the buffer that is not yet written. + #[inline] + pub fn unwritten(&self) -> &[u8] { + &self.buf[self.cur..] + } + + /// Split the buffer into the written and unwritten parts. + #[inline] + pub fn split(&self) -> (&[u8], &[u8]) { + self.buf.split_at(self.cur) + } + + /// Get a mutable reference to the written portion of the buffer. + #[inline] + pub fn written_mut(&mut self) -> &mut [u8] { + &mut self.buf[..self.cur] + } + + /// Get a mutable reference to the portion of the buffer that is not yet written. + #[inline] + pub fn unwritten_mut(&mut self) -> &mut [u8] { + &mut self.buf[self.cur..] + } + + /// Split the buffer into the written and unwritten parts. + #[inline] + pub fn split_mut(&mut self) -> (&mut [u8], &mut [u8]) { + self.buf.split_at_mut(self.cur) + } +} +impl<'a> Write for Cursor<'a> { + type Error = CursorError; + type Seekable = Self; + #[inline] + fn write(&mut self, buf: &[u8]) -> WriteResult { + let end = self.cur + buf.len(); + if end > self.buf.len() { + Err(CursorError::OutOfSpace) + } else { + self.buf[self.cur..end].copy_from_slice(buf); + self.cur += buf.len(); + Ok(()) + } + } + #[inline] + fn invalid_input(msg: &'static str) -> CursorError { + CursorError::InvalidInput(msg) + } + #[inline] + fn make_seekable(&mut self) -> Option<&mut Self> { + Some(self) + } + fn push(&mut self, b: u8) -> WriteResult { + if self.cur >= self.buf.len() { + return Err(CursorError::OutOfSpace); + }; + self.buf[self.cur] = b; + self.cur += 1; + Ok(()) + } +} +impl<'a> Seek for Cursor<'a> { + #[inline] + fn tell(&mut self) -> StdResult { + Ok(self.cur as u64) + } + #[inline] + fn write_at(&mut self, buf: &[u8], pos: u64) -> WriteResult { + let out = self + .buf + .get_mut(pos as usize..pos as usize + buf.len()) + .ok_or(CursorError::OutOfSpace)?; + out.copy_from_slice(buf); + Ok(()) + } +} + +/// The errors that can arise when writing to an in-memory buffer. +#[derive(Debug, Clone)] +pub enum CursorError { + /// The in-memory buffer was too small. + OutOfSpace, + /// The input SMF was invalid. + InvalidInput(&'static str), +} +impl<'a> Write for &'a mut [u8] { + type Error = CursorError; + type Seekable = NotSeekable; + #[inline] + fn write(&mut self, buf: &[u8]) -> WriteResult { + if buf.len() > self.len() { + self.copy_from_slice(&buf[..self.len()]); + *self = &mut []; + Err(CursorError::OutOfSpace) + } else { + self[..buf.len()].copy_from_slice(buf); + let slice = core::mem::replace(self, &mut []); + *self = &mut slice[buf.len()..]; + Ok(()) + } + } + #[inline] + fn invalid_input(msg: &'static str) -> CursorError { + CursorError::InvalidInput(msg) + } + #[inline] + fn push(&mut self, b: u8) -> WriteResult { + if self.len() == 0 { + return Err(CursorError::OutOfSpace); + }; + self[0] = b; + let slice = core::mem::replace(self, &mut []); + *self = &mut slice[1..]; + Ok(()) + } +} + +/// Bridge between a `midly::io::Write` type and a `std::io::Write` type. +/// +/// Always available, but only implements `midly::io::Write` when the `std` feature is enabled. +#[derive(Debug, Clone, Default)] +pub struct IoWrap(pub T); +#[cfg(feature = "std")] +impl Write for IoWrap { + type Error = io::Error; + type Seekable = NotSeekable; + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result<()> { + io::Write::write_all(&mut self.0, buf) + } + #[inline] + fn invalid_input(msg: &'static str) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, msg) + } +} + +/// Bridge between a `midly::io::{Write, Seek}` type and a `std::io::{Write, Seek}` type. +/// +/// Always available, but only implements `midly::io::{Write, Seek}` when the `std` feature is +/// enabled. +#[derive(Debug, Clone, Default)] +pub struct SeekableWrap(pub T); +#[cfg(feature = "std")] +impl Write for SeekableWrap { + type Error = io::Error; + type Seekable = Self; + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result<()> { + io::Write::write_all(&mut self.0, buf) + } + #[inline] + fn invalid_input(msg: &'static str) -> io::Error { + io::Error::new(io::ErrorKind::InvalidInput, msg) + } + #[inline] + fn make_seekable(&mut self) -> Option<&mut Self> { + Some(self) + } +} +#[cfg(feature = "std")] +impl Seek for SeekableWrap { + #[inline] + fn tell(&mut self) -> io::Result { + io::Seek::seek(&mut self.0, io::SeekFrom::Current(0)) + } + #[inline] + fn write_at(&mut self, buf: &[u8], pos: u64) -> io::Result<()> { + io::Seek::seek(&mut self.0, io::SeekFrom::Start(pos))?; + self.write(buf)?; + io::Seek::seek(&mut self.0, io::SeekFrom::End(0))?; + Ok(()) + } +} + +/// Counts the amount of bytes written to it, but otherwise ignores the actual bytes written. +pub(crate) struct WriteCounter(pub u64); +impl Write for WriteCounter { + type Error = &'static str; + type Seekable = NotSeekable; + #[inline] + fn write(&mut self, buf: &[u8]) -> WriteResult { + self.0 += buf.len() as u64; + Ok(()) + } + #[inline] + fn invalid_input(msg: &'static str) -> &'static str { + msg + } + #[inline] + fn push(&mut self, _: u8) -> WriteResult { + self.0 += 1; + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index ca6c79b..233a374 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,6 +143,7 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "alloc")] extern crate alloc; mod util; @@ -180,6 +181,9 @@ pub use file::*; mod message; pub use message::*; +mod io; +pub use io::*; + // A helper used in tests #[cfg(test)] pub fn test_serialization(msg: MidiMsg, ctx: &mut ReceiverContext) { diff --git a/src/message.rs b/src/message.rs index 39f8319..f855945 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,7 +1,11 @@ +#[cfg(test)] use alloc::vec; +#[cfg(feature = "alloc")] use alloc::vec::Vec; use core::convert::TryFrom; +use crate::io::Write; + use super::{ ChannelModeMsg, ChannelVoiceMsg, ParseError, ReceiverContext, SystemCommonMsg, SystemRealTimeMsg, @@ -15,6 +19,7 @@ use super::Meta; /// The primary interface of this library. Used to encode MIDI messages. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum MidiMsg { /// Channel-level messages that act on a voice, such as turning notes on and off. ChannelVoice { @@ -65,9 +70,10 @@ pub enum MidiMsg { impl MidiMsg { /// Turn a `MidiMsg` into a series of bytes. + #[cfg(feature = "alloc")] pub fn to_midi(&self) -> Vec { - let mut r: Vec = vec![]; - self.extend_midi(&mut r); + let mut r = Vec::new(); + self.extend_midi(&mut r).expect("Vec can't expand?"); r } @@ -319,35 +325,20 @@ impl MidiMsg { /// Turn a set of `MidiMsg`s into a series of bytes, with fewer allocations than /// repeatedly concatenating the results of `to_midi`. + #[cfg(feature = "alloc")] pub fn messages_to_midi(msgs: &[Self]) -> Vec { - let mut r: Vec = vec![]; + let mut r = Vec::new(); for m in msgs.iter() { - m.extend_midi(&mut r); + m.extend_midi(&mut r).expect("Vec can't expand?"); } r } - /// Given a `Vec`, append this `MidiMsg` to it. - pub fn extend_midi(&self, v: &mut Vec) { + pub fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { - MidiMsg::ChannelVoice { channel, msg } => { - let p = v.len(); - msg.extend_midi(v); - v[p] += *channel as u8; - match msg { - ChannelVoiceMsg::HighResNoteOff { .. } - | ChannelVoiceMsg::HighResNoteOn { .. } => { - v[p + 3] += *channel as u8; - } - _ => (), - } - } - MidiMsg::RunningChannelVoice { msg, .. } => msg.extend_midi_running(v), - MidiMsg::ChannelMode { channel, msg } => { - let p = v.len(); - msg.extend_midi(v); - v[p] += *channel as u8; - } + MidiMsg::ChannelVoice { channel, msg } => msg.extend_midi(*channel as u8, &mut v), + MidiMsg::RunningChannelVoice { msg, .. } => msg.extend_midi_running(0, v), + MidiMsg::ChannelMode { channel, msg } => msg.extend_midi(*channel as u8, &mut v), MidiMsg::RunningChannelMode { msg, .. } => msg.extend_midi_running(v), MidiMsg::SystemCommon { msg } => msg.extend_midi(v), MidiMsg::SystemRealTime { msg } => msg.extend_midi(v), @@ -358,6 +349,7 @@ impl MidiMsg { #[cfg(feature = "file")] MidiMsg::Invalid { .. } => { // Do nothing + Ok(()) } } } @@ -467,6 +459,7 @@ impl MidiMsg { } } +#[cfg(feature = "alloc")] impl From<&MidiMsg> for Vec { fn from(m: &MidiMsg) -> Vec { m.to_midi() @@ -499,6 +492,7 @@ use strum::{EnumIter, EnumString}; /// The MIDI channel, 1-16. Used by [`MidiMsg`] and elsewhere. #[cfg_attr(feature = "std", derive(EnumIter, EnumString))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum Channel { diff --git a/src/parse_error.rs b/src/parse_error.rs index a59869a..43ece1d 100644 --- a/src/parse_error.rs +++ b/src/parse_error.rs @@ -1,8 +1,9 @@ -use alloc::fmt; use core::error; +use core::fmt; /// Returned when [`MidiMsg::from_midi`](crate::MidiMsg::from_midi) and similar where not successful. #[derive(Debug, PartialEq, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ParseError { /// The given input ended before a `MidiMsg` could be fully formed. UnexpectedEnd, diff --git a/src/system_common.rs b/src/system_common.rs index edd819d..116290c 100644 --- a/src/system_common.rs +++ b/src/system_common.rs @@ -1,12 +1,14 @@ +use crate::io::Write; + use super::parse_error::*; use super::time_code::*; use super::util::*; use super::ReceiverContext; -use alloc::vec::Vec; /// A fairly limited set of messages, generally for device synchronization. /// Used in [`MidiMsg`](crate::MidiMsg). #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SystemCommonMsg { /// The first of 8 "quarter frame" messages, which are meant to be sent 4 per "frame". /// These messages function similarly to [`SystemRealTimeMsg::TimingClock`](crate::SystemRealTimeMsg::TimingClock) @@ -31,49 +33,22 @@ pub enum SystemCommonMsg { } impl SystemCommonMsg { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { - SystemCommonMsg::TimeCodeQuarterFrame1(qf) => { - v.push(0xF1); - v.push(qf.to_nibbles()[0]); - } - SystemCommonMsg::TimeCodeQuarterFrame2(qf) => { - v.push(0xF1); - v.push(qf.to_nibbles()[1]); - } - SystemCommonMsg::TimeCodeQuarterFrame3(qf) => { - v.push(0xF1); - v.push(qf.to_nibbles()[2]); - } - SystemCommonMsg::TimeCodeQuarterFrame4(qf) => { - v.push(0xF1); - v.push(qf.to_nibbles()[3]); - } - SystemCommonMsg::TimeCodeQuarterFrame5(qf) => { - v.push(0xF1); - v.push(qf.to_nibbles()[4]); - } - SystemCommonMsg::TimeCodeQuarterFrame6(qf) => { - v.push(0xF1); - v.push(qf.to_nibbles()[5]); - } - SystemCommonMsg::TimeCodeQuarterFrame7(qf) => { - v.push(0xF1); - v.push(qf.to_nibbles()[6]); - } - SystemCommonMsg::TimeCodeQuarterFrame8(qf) => { - v.push(0xF1); - v.push(qf.to_nibbles()[7]); - } + SystemCommonMsg::TimeCodeQuarterFrame1(qf) => v.write(&[0xF1, qf.to_nibbles()[0]]), + SystemCommonMsg::TimeCodeQuarterFrame2(qf) => v.write(&[0xF1, qf.to_nibbles()[1]]), + SystemCommonMsg::TimeCodeQuarterFrame3(qf) => v.write(&[0xF1, qf.to_nibbles()[2]]), + SystemCommonMsg::TimeCodeQuarterFrame4(qf) => v.write(&[0xF1, qf.to_nibbles()[3]]), + SystemCommonMsg::TimeCodeQuarterFrame5(qf) => v.write(&[0xF1, qf.to_nibbles()[4]]), + SystemCommonMsg::TimeCodeQuarterFrame6(qf) => v.write(&[0xF1, qf.to_nibbles()[5]]), + SystemCommonMsg::TimeCodeQuarterFrame7(qf) => v.write(&[0xF1, qf.to_nibbles()[6]]), + SystemCommonMsg::TimeCodeQuarterFrame8(qf) => v.write(&[0xF1, qf.to_nibbles()[7]]), SystemCommonMsg::SongPosition(pos) => { - v.push(0xF2); - push_u14(*pos, v); - } - SystemCommonMsg::SongSelect(song) => { - v.push(0xF3); - v.push(to_u7(*song)); + v.write(&[0xF2])?; + push_u14(*pos, v) } - SystemCommonMsg::TuneRequest => v.push(0xF6), + SystemCommonMsg::SongSelect(song) => v.write(&[0xF3, to_u7(*song)]), + SystemCommonMsg::TuneRequest => v.write(&[0xF6]), } } diff --git a/src/system_exclusive/controller_destination.rs b/src/system_exclusive/controller_destination.rs index 7d947e9..5024f1d 100644 --- a/src/system_exclusive/controller_destination.rs +++ b/src/system_exclusive/controller_destination.rs @@ -1,6 +1,7 @@ use crate::message::Channel; use crate::parse_error::*; use crate::util::*; +use crate::Write; use alloc::vec::Vec; /// Allows for the selection of the destination of a channel pressure/poly key pressure message. @@ -8,6 +9,7 @@ use alloc::vec::Vec; /// /// Defined in CA-022. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ControllerDestination { pub channel: Channel, /// Any number of (ControlledParameter, range) pairs @@ -15,12 +17,13 @@ pub struct ControllerDestination { } impl ControllerDestination { - pub(crate) fn extend_midi(&self, v: &mut Vec) { - v.push(self.channel as u8); + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + v.push(self.channel as u8)?; for (p, r) in self.param_ranges.iter() { - v.push(*p as u8); - push_u7(*r, v); + v.push(*p as u8)?; + push_u7(*r, &mut v)?; } + Ok(()) } #[allow(dead_code)] @@ -34,6 +37,7 @@ impl ControllerDestination { /// /// Defined in CA-022. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ControlChangeControllerDestination { pub channel: Channel, /// A control number between `0x01` - `0x1F` or `0x40` - `0x5F` @@ -44,17 +48,18 @@ pub struct ControlChangeControllerDestination { } impl ControlChangeControllerDestination { - pub(crate) fn extend_midi(&self, v: &mut Vec) { - v.push(self.channel as u8); + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + v.push(self.channel as u8)?; if self.control_number < 0x40 { - v.push(self.control_number.clamp(0x01, 0x1F)); + v.push(self.control_number.clamp(0x01, 0x1F))?; } else { - v.push(self.control_number.clamp(0x40, 0x5F)); + v.push(self.control_number.clamp(0x40, 0x5F))?; } for (p, r) in self.param_ranges.iter() { - v.push(*p as u8); - push_u7(*r, v); + v.push(*p as u8)?; + push_u7(*r, &mut v)?; } + Ok(()) } #[allow(dead_code)] @@ -67,6 +72,7 @@ impl ControlChangeControllerDestination { /// The parameters that can be controlled by [`ControllerDestination`] or /// [`ControlChangeControllerDestination`]. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ControlledParameter { PitchControl = 0, FilterCutoffControl = 1, diff --git a/src/system_exclusive/file_dump.rs b/src/system_exclusive/file_dump.rs index fafa0df..edeb7a7 100644 --- a/src/system_exclusive/file_dump.rs +++ b/src/system_exclusive/file_dump.rs @@ -1,6 +1,7 @@ use super::DeviceID; use crate::parse_error::*; use crate::util::*; +use crate::Write; use alloc::vec::Vec; use bstr::BString; @@ -33,8 +34,50 @@ pub enum FileDumpMsg { }, } +#[cfg(feature = "defmt")] +impl defmt::Format for FileDumpMsg { + fn format(&self, fmt: defmt::Formatter) { + match self { + Self::Header { + sender_device, + file_type, + length, + name, + } => { + defmt::write!( + fmt, + "FileDumpMsg::Header {{ sender_device: {:?}, file_type: {:?}, length: {}, name: {:?} }}", + sender_device, file_type, length, name.as_slice() + ) + } + Self::Packet { + running_count, + data, + } => { + defmt::write!( + fmt, + "FileDumpMsg::Packet {{ running_count: {}, data: {:?} }}", + running_count, + data + ) + } + Self::Request { + requester_device, + file_type, + name, + } => { + defmt::write!( + fmt, + "FileDumpMsg::Request {{ requester_device: {:?}, file_type: {:?}, name: {:?} }}", + requester_device, file_type, name.as_slice() + ) + } + } + } +} + impl FileDumpMsg { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { Self::Header { sender_device, @@ -42,37 +85,36 @@ impl FileDumpMsg { length, name, } => { - v.push(0x1); - v.push(sender_device.to_u8()); - file_type.extend_midi(v); - push_u28(*length, v); - v.extend_from_slice(name); + v.push(01)?; + v.push(sender_device.to_u8())?; + file_type.extend_midi(&mut v)?; + push_u28(*length, &mut v)?; + v.write(name) } Self::Packet { running_count, data, .. } => { - v.push(0x2); - v.push(to_u7(*running_count)); + v.push(02)?; + v.push(to_u7(*running_count))?; let mut len = data.len().min(112); // Add number of extra encoded bytes // (/ 7 is -1 of actual number of encoded bytes, but it's sent as length - 1) len += len / 7; assert!(len < 128); - v.push(len as u8); - v.extend(Self::encode_data(data)); - v.push(0); // Checksum <- Will be written over by `SystemExclusiveMsg.extend_midi` + v.push(len as u8)?; + v.write(&Self::encode_data(data)) } Self::Request { requester_device, file_type, name, } => { - v.push(0x3); - v.push(requester_device.to_u8()); - file_type.extend_midi(v); - v.extend_from_slice(name); + v.push(03)?; + v.push(requester_device.to_u8())?; + file_type.extend_midi(&mut v)?; + v.write(name) } } } @@ -119,6 +161,7 @@ impl FileDumpMsg { /// A four-character file type used by [`FileDumpMsg`]. #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum FileType { MIDI, MIEX, @@ -130,15 +173,15 @@ pub enum FileType { } impl FileType { - fn extend_midi(&self, v: &mut Vec) { + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { - Self::MIDI => b"MIDI".iter().for_each(|c| v.push(*c)), - Self::MIEX => b"MIEX".iter().for_each(|c| v.push(*c)), - Self::ESEQ => b"ESEQ".iter().for_each(|c| v.push(*c)), - Self::TEXT => b"TEXT".iter().for_each(|c| v.push(*c)), - Self::BIN => b"BIN ".iter().for_each(|c| v.push(*c)), - Self::MAC => b"MAC ".iter().for_each(|c| v.push(*c)), - Self::Custom(chars) => chars[0..4].iter().for_each(|c| v.push(*c)), + Self::MIDI => v.write(b"MIDI"), + Self::MIEX => v.write(b"MIEX"), + Self::ESEQ => v.write(b"ESEQ"), + Self::TEXT => v.write(b"TEXT"), + Self::BIN => v.write(b"BIN "), + Self::MAC => v.write(b"MAC "), + Self::Custom(chars) => v.write(chars), } } } diff --git a/src/system_exclusive/file_reference.rs b/src/system_exclusive/file_reference.rs index b28969d..6797180 100644 --- a/src/system_exclusive/file_reference.rs +++ b/src/system_exclusive/file_reference.rs @@ -1,5 +1,6 @@ use crate::parse_error::*; use crate::util::*; +use crate::Write; use alloc::vec::Vec; use bstr::BString; @@ -44,25 +45,67 @@ pub enum FileReferenceMsg { }, } +#[cfg(feature = "defmt")] +impl defmt::Format for FileReferenceMsg { + fn format(&self, fmt: defmt::Formatter) { + match self { + Self::Open { + ctx, + file_type, + url, + } => { + defmt::write!( + fmt, + "Open {{ ctx: {}, file_type: {:?}, url: {} }}", + ctx, + file_type, + url.as_slice() + ); + } + Self::SelectContents { ctx, map } => { + defmt::write!(fmt, "SelectContents {{ ctx: {}, map: {:?} }}", ctx, map); + } + Self::OpenSelectContents { + ctx, + file_type, + url, + map, + } => { + defmt::write!( + fmt, + "OpenSelectContents {{ ctx: {}, file_type: {:?}, url: {}, map: {:?} }}", + ctx, + file_type, + url.as_slice(), + map + ); + } + Self::Close { ctx } => { + defmt::write!(fmt, "Close {{ ctx: {} }}", ctx); + } + } + } +} + impl FileReferenceMsg { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { Self::Open { ctx, file_type, url, } => { - push_u14(*ctx, v); + push_u14(*ctx, &mut v)?; let len = 4 + url.len().min(260) + 1; - push_u14(len as u16, v); - file_type.extend_midi(v); - v.extend_from_slice(&url[0..url.len().min(260)]); - v.push(0); // Null terminate URL + push_u14(len as u16, &mut v)?; + file_type.extend_midi(&mut v)?; + v.write(&url[0..url.len().min(260)])?; + v.push(0) // Null terminate URL } Self::SelectContents { ctx, map } => { - push_u14(*ctx, v); - push_u14(map.len() as u16, v); - map.extend_midi(v); + push_u14(*ctx, &mut v)?; + push_u14(map.len() as u16, &mut v)?; + map.extend_midi(&mut v) } Self::OpenSelectContents { ctx, @@ -70,18 +113,18 @@ impl FileReferenceMsg { url, map, } => { - push_u14(*ctx, v); + push_u14(*ctx, &mut v)?; let len = 4 + url.len().min(260) + 1 + map.len(); - push_u14(len as u16, v); - file_type.extend_midi(v); - v.extend_from_slice(&url[0..url.len().min(260)]); - v.push(0); // Null terminate URL - map.extend_midi(v); + push_u14(len as u16, &mut v)?; + file_type.extend_midi(&mut v)?; + v.write(&url[0..url.len().min(260)])?; + v.push(0)?; // Null terminate URL + map.extend_midi(v) } Self::Close { ctx } => { - push_u14(*ctx, v); - v.push(0); // Len is zero - v.push(0); // And here's another byte for some reason ¯\_(ツ)_/¯ + push_u14(*ctx, &mut v)?; + v.push(0)?; // Len is zero + v.push(0) // And here's another byte for some reason ¯\_(ツ)_/¯ } } } @@ -94,6 +137,7 @@ impl FileReferenceMsg { /// The file type of a given file, as used by [`FileReferenceMsg`]. #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum FileReferenceType { DLS, SF2, @@ -101,17 +145,18 @@ pub enum FileReferenceType { } impl FileReferenceType { - fn extend_midi(&self, v: &mut Vec) { + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { - Self::DLS => b"DLS ".iter().for_each(|c| v.push(*c)), - Self::SF2 => b"SF2 ".iter().for_each(|c| v.push(*c)), - Self::WAV => b"WAV ".iter().for_each(|c| v.push(*c)), + Self::DLS => v.write(b"DLS "), + Self::SF2 => v.write(b"SF2 "), + Self::WAV => v.write(b"WAV "), } } } /// How to map a `DLS` or `SF2` file for MIDI reference. Used by [`SelectMap`]. #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SoundFileMap { /// MIDI bank number required to select sound for playing. 0-16383 pub dst_bank: u16, @@ -144,11 +189,11 @@ impl Default for SoundFileMap { } impl SoundFileMap { - fn extend_midi(&self, v: &mut Vec) { - push_u14(self.dst_bank, v); - push_u7(self.dst_prog, v); - push_u14(self.src_bank, v); - push_u7(self.src_prog, v); + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + push_u14(self.dst_bank, &mut v)?; + push_u7(self.dst_prog, &mut v)?; + push_u14(self.src_bank, &mut v)?; + push_u7(self.src_prog, &mut v)?; let mut flags: u8 = 0; if self.src_drum { flags += 1 << 0; @@ -156,13 +201,14 @@ impl SoundFileMap { if self.dst_drum { flags += 1 << 1; } - v.push(flags); - push_u7(self.volume, v); + v.push(flags)?; + push_u7(self.volume, v) } } /// How to map a `WAV` file for MIDI reference. Used by [`SelectMap`]. #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct WAVMap { /// MIDI bank number required to select sound for playing. 0-16383 pub dst_bank: u16, @@ -182,16 +228,16 @@ pub struct WAVMap { } impl WAVMap { - fn extend_midi(&self, v: &mut Vec) { - push_u14(self.dst_bank, v); - push_u7(self.dst_prog, v); - push_u7(self.base, v); - push_u7(self.lokey, v); - push_u7(self.hikey, v); + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + push_u14(self.dst_bank, &mut v)?; + push_u7(self.dst_prog, &mut v)?; + push_u7(self.base, &mut v)?; + push_u7(self.lokey, &mut v)?; + push_u7(self.hikey, &mut v)?; let [msb, lsb] = i_to_u14(self.fine); - v.push(lsb); - v.push(msb); - push_u7(self.volume, v); + v.push(lsb)?; + v.push(msb)?; + push_u7(self.volume, v) } } @@ -211,6 +257,7 @@ impl Default for WAVMap { /// How to map a file for MIDI reference. Used by [`FileReferenceMsg::SelectContents`]. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SelectMap { /// Used for DLS or SF2 files. No more than 127 `SoundFileMap`s. /// @@ -239,7 +286,7 @@ pub enum SelectMap { } impl SelectMap { - fn extend_midi(&self, v: &mut Vec) { + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { Self::WAV(m) => m.extend_midi(v), Self::WAVBankOffset { @@ -247,39 +294,40 @@ impl SelectMap { bank_offset, src_drum, } => { - map.extend_midi(v); - v.push(0); // count - v.push(0); // Extension ID 1 - v.push(1); // Extension ID 2 - v.push(3); // len - push_u14(*bank_offset, v); + map.extend_midi(&mut v)?; + v.push(0)?; // count + v.push(0)?; // Extension ID 1 + v.push(1)?; // Extension ID 2 + v.push(3)?; // len + push_u14(*bank_offset, &mut v)?; let mut flags: u8 = 0; if *src_drum { flags += 1 << 0; } - push_u7(flags, v); + push_u7(flags, v) } Self::SoundFileBankOffset { bank_offset, src_drum, } => { - v.push(0); // count - v.push(0); // Extension ID 1 - v.push(1); // Extension ID 2 - v.push(3); // len - push_u14(*bank_offset, v); + v.push(0)?; // count + v.push(0)?; // Extension ID 1 + v.push(1)?; // Extension ID 2 + v.push(3)?; // len + push_u14(*bank_offset, &mut v)?; let mut flags: u8 = 0; if *src_drum { flags += 1 << 0; } - push_u7(flags, v); + push_u7(flags, v) } Self::SoundFile(maps) => { let count = maps.len().min(127); - push_u7(count as u8, v); + push_u7(count as u8, &mut v)?; for m in maps[0..count].iter() { - m.extend_midi(v); + m.extend_midi(&mut v)?; } + Ok(()) } } } diff --git a/src/system_exclusive/global_parameter.rs b/src/system_exclusive/global_parameter.rs index 6da84cd..4dc6554 100644 --- a/src/system_exclusive/global_parameter.rs +++ b/src/system_exclusive/global_parameter.rs @@ -1,5 +1,6 @@ use crate::parse_error::*; use crate::util::*; +use crate::Write; use alloc::vec; use alloc::vec::Vec; #[allow(unused_imports)] @@ -13,6 +14,7 @@ use micromath::F32Ext; /// /// This C/A is much more permissive than most, and thus has a pretty awkward interface. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GlobalParameterControl { /// Between 0 and 127 `SlotPath`s, with each successive path representing a child /// of the preceding value. No paths refers to the "top level" @@ -139,19 +141,20 @@ impl GlobalParameterControl { } } - pub(crate) fn extend_midi(&self, v: &mut Vec) { - v.push(self.slot_paths.len().min(127) as u8); - push_u7(self.param_id_width, v); - push_u7(self.value_width, v); + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + v.push(self.slot_paths.len().min(127) as u8)?; + push_u7(self.param_id_width, &mut v)?; + push_u7(self.value_width, &mut v)?; for (i, sp) in self.slot_paths.iter().enumerate() { if i > 127 { break; } - sp.extend_midi(v); + sp.extend_midi(&mut v)?; } for p in self.params.iter() { - p.extend_midi_with_limits(v, self.param_id_width.max(1), self.value_width.max(1)); + p.extend_midi_with_limits(&mut v, self.param_id_width.max(1), self.value_width.max(1))?; } + Ok(()) } #[allow(dead_code)] @@ -163,6 +166,7 @@ impl GlobalParameterControl { /// The "slot" of the device being referred to by [`GlobalParameterControl`]. /// Values other than `Unregistered` come from the General MIDI 2 spec. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SlotPath { Reverb, Chorus, @@ -171,19 +175,19 @@ pub enum SlotPath { } impl SlotPath { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { Self::Reverb => { - v.push(1); - v.push(1); + v.push(1)?; + v.push(1) } Self::Chorus => { - v.push(1); - v.push(2); + v.push(1)?; + v.push(2) } Self::Unregistered(a, b) => { - push_u7(*a, v); // MSB first ¯\_(ツ)_/¯ - push_u7(*b, v); + push_u7(*a, &mut v)?; // MSB first ¯\_(ツ)_/¯ + push_u7(*b, v) } } } @@ -196,34 +200,36 @@ impl SlotPath { /// An `id`:`value` pair that must line up with the [`GlobalParameterControl`] that it is placed in. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GlobalParameter { pub id: Vec, pub value: Vec, } impl GlobalParameter { - pub(crate) fn extend_midi_with_limits( + pub(crate) fn extend_midi_with_limits( &self, - v: &mut Vec, + mut v: impl Write, param_id_width: u8, value_width: u8, - ) { + ) -> Result<(), E> { for i in 0..param_id_width { // MSB first if let Some(x) = self.id.get(i as usize) { - push_u7(*x, v); + push_u7(*x, &mut v)?; } else { - v.push(0); + v.push(0)?; } } for i in (0..value_width).rev() { // LSB first if let Some(x) = self.value.get(i as usize) { - push_u7(*x, v); + push_u7(*x, &mut v)?; } else { - v.push(0); + v.push(0)?; } } + Ok(()) } #[allow(dead_code)] diff --git a/src/system_exclusive/key_based_instrument_control.rs b/src/system_exclusive/key_based_instrument_control.rs index 3f6d212..9b3e9fb 100644 --- a/src/system_exclusive/key_based_instrument_control.rs +++ b/src/system_exclusive/key_based_instrument_control.rs @@ -1,6 +1,7 @@ use crate::message::Channel; use crate::parse_error::*; use crate::util::*; +use crate::Write; use alloc::vec::Vec; /// Intended to act like Control Change messages, but targeted at an individual key. @@ -9,6 +10,7 @@ use alloc::vec::Vec; /// /// Defined in CA-023. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct KeyBasedInstrumentControl { pub channel: Channel, /// The MIDI key number. @@ -24,17 +26,18 @@ pub struct KeyBasedInstrumentControl { } impl KeyBasedInstrumentControl { - pub(crate) fn extend_midi(&self, v: &mut Vec) { - v.push(self.channel as u8); - push_u7(self.key, v); + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + v.push(self.channel as u8)?; + push_u7(self.key, &mut v)?; for (cc, x) in self.control_values.iter().cloned() { if cc == 0x06 || cc == 0x26 || cc == 0x60 || cc == 0x65 || cc >= 0x78 { - v.push(1); + v.push(1)?; } else { - v.push(cc); + v.push(cc)?; } - push_u7(x, v); + push_u7(x, &mut v)?; } + Ok(()) } #[allow(dead_code)] diff --git a/src/system_exclusive/machine_control.rs b/src/system_exclusive/machine_control.rs index 42c151b..2b74132 100644 --- a/src/system_exclusive/machine_control.rs +++ b/src/system_exclusive/machine_control.rs @@ -1,5 +1,6 @@ use crate::parse_error::*; use crate::time_code::*; +use crate::Write; use alloc::vec::Vec; /// A MIDI Machine Control Command. @@ -10,6 +11,7 @@ use alloc::vec::Vec; /// /// As defined in MIDI Machine Control 1.0 (MMA0016 / RP013) #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum MachineControlCommandMsg { Stop, Play, @@ -38,7 +40,7 @@ pub enum MachineControlCommandMsg { } impl MachineControlCommandMsg { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { Self::Stop => v.push(0x01), Self::Play => v.push(0x02), @@ -54,20 +56,20 @@ impl MachineControlCommandMsg { Self::CommandErrorReset => v.push(0x0C), Self::MMCReset => v.push(0x0D), Self::LocateInformationField(f) => { - v.push(0x44); - v.push(0x2); // Byte count - v.push(0x0); // Sub command - v.push(*f as u8); + v.push(0x44)?; + v.push(2)?; // Byte count + v.push(0)?; // Sub command + v.push(*f as u8) } Self::LocateTarget(stc) => { - v.push(0x44); - v.push(0x6); // Byte count - v.push(0x1); // Sub command - stc.extend_midi(v); + v.push(0x44)?; + v.push(6)?; // Byte count + v.push(1)?; // Sub command + stc.extend_midi(&mut v) } Self::Wait => v.push(0x01), Self::Resume => v.push(0x01), - Self::Unimplemented(d) => v.extend_from_slice(d), + Self::Unimplemented(d) => v.write(d), } } @@ -81,6 +83,7 @@ impl MachineControlCommandMsg { /// /// As defined in MIDI Machine Control 1.0 (MMA0016 / RP013) #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum InformationField { SelectedTimeCode = 0x01, SelectedMasterCode = 0x02, @@ -108,6 +111,7 @@ pub enum InformationField { /// /// As defined in MIDI Machine Control 1.0 (MMA0016 / RP013) #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum MachineControlResponseMsg { /// Used to represent all unimplemented MCR messages. /// Is inherently not guaranteed to be a valid message. @@ -115,9 +119,9 @@ pub enum MachineControlResponseMsg { } impl MachineControlResponseMsg { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { - Self::Unimplemented(d) => v.extend_from_slice(d), + Self::Unimplemented(d) => v.write(d), } } diff --git a/src/system_exclusive/mod.rs b/src/system_exclusive/mod.rs index ed74a6f..1d46c41 100644 --- a/src/system_exclusive/mod.rs +++ b/src/system_exclusive/mod.rs @@ -21,16 +21,19 @@ pub use tuning::*; use alloc::vec::Vec; -use super::ReceiverContext; +use crate::Write; + use super::general_midi::GeneralMidi; use super::parse_error::*; use super::time_code::*; use super::util::*; +use super::ReceiverContext; /// The bulk of the MIDI spec lives here, in "Universal System Exclusive" messages. /// Also used for manufacturer-specific messages. /// Used in [`MidiMsg`](crate::MidiMsg). #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SystemExclusiveMsg { /// An arbitrary set of 7-bit "bytes", the meaning of which must be derived from the /// message, the definition of which is determined by the given manufacturer. @@ -52,58 +55,62 @@ pub enum SystemExclusiveMsg { } impl SystemExclusiveMsg { - pub(crate) fn extend_midi(&self, v: &mut Vec, first_byte_is_f0: bool) { + pub(crate) fn extend_midi( + &self, + mut v: impl Write, + first_byte_is_f0: bool, + ) -> Result<(), E> { if first_byte_is_f0 { - v.push(0xF0); + v.push(0xF0)?; } match self { SystemExclusiveMsg::Commercial { id, data } => { - id.extend_midi(v); - data.iter().for_each(|d| v.push(to_u7(*d))); + id.extend_midi(&mut v)?; + for d in data { + v.write(&[to_u7(*d)])?; + } } SystemExclusiveMsg::NonCommercial { data } => { - v.push(0x7D); - data.iter().for_each(|d| v.push(to_u7(*d))); + v.write(&[0x7D])?; + for d in data { + v.write(&[to_u7(*d)])?; + } } SystemExclusiveMsg::UniversalRealTime { device, msg } => { - v.push(0x7F); - v.push(device.to_u8()); - msg.extend_midi(v); + v.write(&[0x7F, device.to_u8()])?; + msg.extend_midi(&mut v)?; } SystemExclusiveMsg::UniversalNonRealTime { device, msg } => { - let p = v.len(); - v.push(0x7E); - v.push(device.to_u8()); - msg.extend_midi(v); - if let UniversalNonRealTimeMsg::SampleDump(SampleDumpMsg::Packet { .. }) = msg { - let q = v.len(); - v[q - 1] = checksum(&v[p..q - 1]); - } - if let UniversalNonRealTimeMsg::KeyBasedTuningDump(_) = msg { - let q = v.len(); - v[q - 1] = checksum(&v[p..q - 1]); - } - if let UniversalNonRealTimeMsg::ScaleTuning1Byte(_) = msg { - let q = v.len(); - v[q - 1] = checksum(&v[p..q - 1]); - } - if let UniversalNonRealTimeMsg::ScaleTuning2Byte(_) = msg { - let q = v.len(); - v[q - 1] = checksum(&v[p..q - 1]); - } - if let UniversalNonRealTimeMsg::FileDump(FileDumpMsg::Packet { .. }) = msg { - let q = v.len(); - v[q - 1] = checksum(&v[p..q - 1]); + if matches!( + msg, + UniversalNonRealTimeMsg::SampleDump(SampleDumpMsg::Packet { .. }) + | UniversalNonRealTimeMsg::KeyBasedTuningDump(_) + | UniversalNonRealTimeMsg::ScaleTuning1Byte(_) + | UniversalNonRealTimeMsg::ScaleTuning2Byte(_) + | UniversalNonRealTimeMsg::FileDump(FileDumpMsg::Packet { .. }) + ) { + let mut w = ChecksummingWriter::new(&mut v); + w.write(&[0x7E, device.to_u8()])?; + msg.extend_midi(&mut w)?; + w.finish()?; + } else { + v.write(&[0x7E, device.to_u8()])?; + msg.extend_midi(&mut v)?; } } } - v.push(0xF7); + v.write(&[0xF7])?; + Ok(()) } fn sysex_bytes_from_midi(m: &[u8], first_byte_is_f0: bool) -> Result<&[u8], ParseError> { if first_byte_is_f0 && m.first() != Some(&0xF0) { return Err(ParseError::UndefinedSystemExclusiveMessage( - m.first().copied(), + if let Some(first_byte) = m.first() { + Some(*first_byte) + } else { + None + }, )); } let offset = if first_byte_is_f0 { 1 } else { 0 }; @@ -123,7 +130,7 @@ impl SystemExclusiveMsg { ctx: &mut ReceiverContext, ) -> Result<(Self, usize), ParseError> { let m = Self::sysex_bytes_from_midi(m, !ctx.is_smf_sysex)?; - match m.first() { + match m.get(0) { Some(0x7D) => Ok(( Self::NonCommercial { data: m[1..].to_vec(), @@ -165,16 +172,15 @@ impl SystemExclusiveMsg { /// If second byte is None, it is a one-byte ID. /// The first byte in a one-byte ID may not be greater than 0x7C. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ManufacturerID(pub u8, pub Option); impl ManufacturerID { - fn extend_midi(&self, v: &mut Vec) { + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { if let Some(second) = self.1 { - v.push(0x00); - v.push(to_u7(self.0)); - v.push(to_u7(second)); + v.write(&[0x00, to_u7(self.0), to_u7(second)]) } else { - v.push(self.0.min(0x7C)) + v.write(&[self.0.min(0x7C)]) } } @@ -208,16 +214,17 @@ impl From<(u8, u8)> for ManufacturerID { /// The device ID being addressed, either a number between 0-126 or `AllCall` (all devices). /// Used by [`SystemExclusiveMsg::UniversalNonRealTime`] and [`SystemExclusiveMsg::UniversalRealTime`]. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DeviceID { Device(u8), AllCall, } impl DeviceID { - fn to_u8(self) -> u8 { + fn to_u8(&self) -> u8 { match self { Self::AllCall => 0x7F, - Self::Device(x) => to_u7(x), + Self::Device(x) => to_u7(*x), } } @@ -233,6 +240,7 @@ impl DeviceID { /// A diverse range of messages for real-time applications. Used by [`SystemExclusiveMsg::UniversalRealTime`]. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum UniversalRealTimeMsg { /// For use when a [`SystemCommonMsg::TimeCodeQuarterFrame`](crate::SystemCommonMsg::TimeCodeQuarterFrame1) is not appropriate: /// When rewinding, fast-forwarding, or otherwise locating and cueing, where sending quarter frame @@ -285,118 +293,118 @@ pub enum UniversalRealTimeMsg { } impl UniversalRealTimeMsg { - fn extend_midi(&self, v: &mut Vec) { + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { UniversalRealTimeMsg::TimeCodeFull(code) => { - v.push(0x1); - v.push(0x1); - code.extend_midi(v); + v.push(01)?; + v.push(01)?; + code.extend_midi(&mut v) } UniversalRealTimeMsg::TimeCodeUserBits(user_bits) => { - v.push(0x1); - v.push(0x2); + v.push(01)?; + v.push(02)?; let [ub1, ub2, ub3, ub4, ub5, ub6, ub7, ub8, ub9] = user_bits.to_nibbles(); - v.extend_from_slice(&[ub1, ub2, ub3, ub4, ub5, ub6, ub7, ub8, ub9]); + v.write(&[ub1, ub2, ub3, ub4, ub5, ub6, ub7, ub8, ub9]) } UniversalRealTimeMsg::ShowControl(msg) => { - v.push(0x2); - msg.extend_midi(v); + v.push(02)?; + msg.extend_midi(&mut v) } UniversalRealTimeMsg::BarMarker(marker) => { - v.push(0x3); - v.push(0x1); - marker.extend_midi(v); + v.push(03)?; + v.push(01)?; + marker.extend_midi(&mut v) } UniversalRealTimeMsg::TimeSignature(signature) => { - v.push(0x3); - v.push(0x2); - signature.extend_midi(v); + v.push(03)?; + v.push(02)?; + signature.extend_midi(&mut v) } UniversalRealTimeMsg::TimeSignatureDelayed(signature) => { - v.push(0x3); - v.push(0x42); - signature.extend_midi(v); + v.push(03)?; + v.push(0x42)?; + signature.extend_midi(&mut v) } UniversalRealTimeMsg::MasterVolume(vol) => { - v.push(0x4); - v.push(0x1); - push_u14(*vol, v); + v.push(04)?; + v.push(01)?; + push_u14(*vol, v) } UniversalRealTimeMsg::MasterBalance(bal) => { - v.push(0x4); - v.push(0x2); - push_u14(*bal, v); + v.push(04)?; + v.push(02)?; + push_u14(*bal, v) } UniversalRealTimeMsg::MasterFineTuning(t) => { - v.push(0x4); - v.push(0x3); + v.push(04)?; + v.push(03)?; let [msb, lsb] = i_to_u14(*t); - v.push(lsb); - v.push(msb); + v.push(lsb)?; + v.push(msb) } UniversalRealTimeMsg::MasterCoarseTuning(t) => { - v.push(0x4); - v.push(0x4); - v.push(i_to_u7(*t)); + v.push(04)?; + v.push(04)?; + v.push(i_to_u7(*t)) } UniversalRealTimeMsg::GlobalParameterControl(gp) => { - v.push(0x4); - v.push(0x5); - gp.extend_midi(v); + v.push(04)?; + v.push(05)?; + gp.extend_midi(&mut v) } UniversalRealTimeMsg::TimeCodeCueing(msg) => { - v.push(0x5); - msg.extend_midi(v); + v.push(05)?; + msg.extend_midi(&mut v) } UniversalRealTimeMsg::MachineControlCommand(msg) => { - v.push(0x6); - msg.extend_midi(v); + v.push(06)?; + msg.extend_midi(&mut v) } UniversalRealTimeMsg::MachineControlResponse(msg) => { - v.push(0x7); - msg.extend_midi(v); + v.push(07)?; + msg.extend_midi(&mut v) } UniversalRealTimeMsg::TuningNoteChange(note_change) => { - v.push(0x8); + v.push(08)?; v.push(if note_change.tuning_bank_num.is_some() { - 0x7 + 07 } else { - 0x2 - }); + 02 + })?; if let Some(bank_num) = note_change.tuning_bank_num { - v.push(to_u7(bank_num)) + v.push(to_u7(bank_num))?; } - note_change.extend_midi(v); + note_change.extend_midi(&mut v) } UniversalRealTimeMsg::ScaleTuning1Byte(tuning) => { - v.push(0x8); - v.push(0x8); - tuning.extend_midi(v); + v.push(08)?; + v.push(08)?; + tuning.extend_midi(&mut v) } UniversalRealTimeMsg::ScaleTuning2Byte(tuning) => { - v.push(0x8); - v.push(0x9); - tuning.extend_midi(v); + v.push(08)?; + v.push(09)?; + tuning.extend_midi(&mut v) } UniversalRealTimeMsg::ChannelPressureControllerDestination(d) => { - v.push(0x9); - v.push(0x1); - d.extend_midi(v); + v.push(09)?; + v.push(01)?; + d.extend_midi(&mut v) } UniversalRealTimeMsg::PolyphonicKeyPressureControllerDestination(d) => { - v.push(0x9); - v.push(0x2); - d.extend_midi(v); + v.push(09)?; + v.push(02)?; + d.extend_midi(&mut v) } UniversalRealTimeMsg::ControlChangeControllerDestination(d) => { - v.push(0x9); - v.push(0x3); - d.extend_midi(v); + v.push(09)?; + v.push(03)?; + d.extend_midi(&mut v) } UniversalRealTimeMsg::KeyBasedInstrumentControl(control) => { - v.push(0xA); - v.push(0x1); - control.extend_midi(v); + v.push(0x0A)?; + v.push(01)?; + control.extend_midi(&mut v) } } } @@ -407,7 +415,7 @@ impl UniversalRealTimeMsg { } match (m[0], m[1]) { - (0x1, 0x1) => { + (01, 01) => { if m.len() > 6 { Err(ParseError::Invalid( "Extra bytes after a UniversalRealTimeMsg::TimeCodeFull", @@ -425,6 +433,7 @@ impl UniversalRealTimeMsg { /// A diverse range of messages for non-real-time applications. Used by [`SystemExclusiveMsg::UniversalNonRealTime`]. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum UniversalNonRealTimeMsg { /// Used to transmit sampler data. SampleDump(SampleDumpMsg), @@ -474,134 +483,101 @@ pub enum UniversalNonRealTimeMsg { } impl UniversalNonRealTimeMsg { - fn extend_midi(&self, v: &mut Vec) { + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { UniversalNonRealTimeMsg::SampleDump(msg) => { match msg { - SampleDumpMsg::Header { .. } => v.push(0x1), - SampleDumpMsg::Packet { .. } => v.push(0x2), - SampleDumpMsg::Request { .. } => v.push(0x3), - SampleDumpMsg::LoopPointTransmission { .. } => { - v.push(0x5); - v.push(0x1); - } - SampleDumpMsg::LoopPointsRequest { .. } => { - v.push(0x5); - v.push(0x2); - } - } - msg.extend_midi(v); + SampleDumpMsg::Header { .. } => v.write(&[01])?, + SampleDumpMsg::Packet { .. } => v.write(&[02])?, + SampleDumpMsg::Request { .. } => v.write(&[03])?, + SampleDumpMsg::LoopPointTransmission { .. } => v.write(&[05, 01])?, + SampleDumpMsg::LoopPointsRequest { .. } => v.write(&[05, 02])?, + }; + msg.extend_midi(v) } UniversalNonRealTimeMsg::ExtendedSampleDump(msg) => { - v.push(0x5); + v.write(&[05])?; match msg { - ExtendedSampleDumpMsg::SampleName { .. } => v.push(0x3), - ExtendedSampleDumpMsg::SampleNameRequest { .. } => v.push(0x4), - ExtendedSampleDumpMsg::Header { .. } => v.push(0x5), - ExtendedSampleDumpMsg::LoopPointTransmission { .. } => v.push(0x6), - ExtendedSampleDumpMsg::LoopPointsRequest { .. } => v.push(0x7), - } - msg.extend_midi(v); + ExtendedSampleDumpMsg::SampleName { .. } => v.write(&[03])?, + ExtendedSampleDumpMsg::SampleNameRequest { .. } => v.write(&[04])?, + ExtendedSampleDumpMsg::Header { .. } => v.write(&[05])?, + ExtendedSampleDumpMsg::LoopPointTransmission { .. } => v.write(&[06])?, + ExtendedSampleDumpMsg::LoopPointsRequest { .. } => v.write(&[07])?, + }; + msg.extend_midi(v) } UniversalNonRealTimeMsg::TimeCodeCueingSetup(msg) => { - v.push(0x4); - msg.extend_midi(v); - } - UniversalNonRealTimeMsg::IdentityRequest => { - v.push(0x6); - v.push(0x1); + v.write(&[04])?; + msg.extend_midi(v) } + UniversalNonRealTimeMsg::IdentityRequest => v.write(&[06, 01]), UniversalNonRealTimeMsg::IdentityReply(identity) => { - v.push(0x6); - v.push(0x2); - identity.extend_midi(v); + v.write(&[06, 02])?; + identity.extend_midi(v) } UniversalNonRealTimeMsg::FileDump(msg) => { - v.push(0x7); - msg.extend_midi(v); + v.write(&[07])?; + msg.extend_midi(v) } UniversalNonRealTimeMsg::TuningBulkDumpRequest(program_num, bank_num) => { - v.push(0x8); - v.push(if bank_num.is_some() { 0x3 } else { 0x0 }); + v.write(&[08])?; if let Some(bank_num) = bank_num { - v.push(to_u7(*bank_num)) + v.write(&[03, to_u7(*bank_num)])?; + } else { + v.write(&[00])?; } - v.push(to_u7(*program_num)); + v.write(&[to_u7(*program_num)]) } UniversalNonRealTimeMsg::KeyBasedTuningDump(tuning) => { - v.push(0x8); - v.push(if tuning.tuning_bank_num.is_some() { - 0x4 - } else { - 0x1 - }); - tuning.extend_midi(v); + v.write(&[ + 08, + if tuning.tuning_bank_num.is_some() { + 04 + } else { + 01 + }, + ])?; + tuning.extend_midi(v) } UniversalNonRealTimeMsg::ScaleTuningDump1Byte(tuning) => { - v.push(0x8); - v.push(0x5); - tuning.extend_midi(v); + v.write(&[08, 05])?; + tuning.extend_midi(v) } UniversalNonRealTimeMsg::ScaleTuningDump2Byte(tuning) => { - v.push(0x8); - v.push(0x6); - tuning.extend_midi(v); + v.write(&[08, 06])?; + tuning.extend_midi(v) } UniversalNonRealTimeMsg::TuningNoteChange(tuning) => { - v.push(0x8); - v.push(0x7); - if let Some(bank_num) = tuning.tuning_bank_num { - v.push(to_u7(bank_num)) - } else { - v.push(0x0); // Fallback to Bank 0 - } - tuning.extend_midi(v); + v.write(&[08, 07, to_u7(tuning.tuning_bank_num.unwrap_or_default())])?; + tuning.extend_midi(v) } UniversalNonRealTimeMsg::ScaleTuning1Byte(tuning) => { - v.push(0x8); - v.push(0x8); - tuning.extend_midi(v); + v.write(&[08, 08])?; + tuning.extend_midi(v) } UniversalNonRealTimeMsg::ScaleTuning2Byte(tuning) => { - v.push(0x8); - v.push(0x9); - tuning.extend_midi(v); - } - UniversalNonRealTimeMsg::GeneralMidi(gm) => { - v.push(0x9); - v.push(*gm as u8); + v.write(&[08, 09])?; + tuning.extend_midi(v) } + UniversalNonRealTimeMsg::GeneralMidi(gm) => v.write(&[09, *gm as u8]), UniversalNonRealTimeMsg::FileReference(msg) => { - v.push(0xB); - match msg { - FileReferenceMsg::Open { .. } => v.push(0x1), - FileReferenceMsg::SelectContents { .. } => v.push(0x2), - FileReferenceMsg::OpenSelectContents { .. } => v.push(0x3), - FileReferenceMsg::Close { .. } => v.push(0x4), - } - msg.extend_midi(v); + v.write(&[ + 0x0B, + match msg { + FileReferenceMsg::Open { .. } => 01, + FileReferenceMsg::SelectContents { .. } => 02, + FileReferenceMsg::OpenSelectContents { .. } => 03, + FileReferenceMsg::Close { .. } => 04, + }, + ])?; + msg.extend_midi(v) } - UniversalNonRealTimeMsg::EOF => { - v.push(0x7B); - v.push(0x0); - } - UniversalNonRealTimeMsg::Wait => { - v.push(0x7C); - v.push(0x0); - } - UniversalNonRealTimeMsg::Cancel => { - v.push(0x7D); - v.push(0x0); - } - UniversalNonRealTimeMsg::NAK(packet_num) => { - v.push(0x7E); - v.push(to_u7(*packet_num)); - } - UniversalNonRealTimeMsg::ACK(packet_num) => { - v.push(0x7F); - v.push(to_u7(*packet_num)); - } + UniversalNonRealTimeMsg::EOF => v.write(&[0x7B, 0]), + UniversalNonRealTimeMsg::Wait => v.write(&[0x7C, 0]), + UniversalNonRealTimeMsg::Cancel => v.write(&[0x7D, 0]), + UniversalNonRealTimeMsg::NAK(packet_num) => v.write(&[0x7E, to_u7(*packet_num)]), + UniversalNonRealTimeMsg::ACK(packet_num) => v.write(&[0x7F, to_u7(*packet_num)]), } } @@ -611,7 +587,7 @@ impl UniversalNonRealTimeMsg { } match (m[0], m[1]) { - (0x6, 0x2) => { + (06, 02) => { if m.len() < 3 { return Err(crate::ParseError::UnexpectedEnd); } @@ -626,6 +602,7 @@ impl UniversalNonRealTimeMsg { /// that this message is sent from. /// Used by [`UniversalNonRealTimeMsg::IdentityReply`]. #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct IdentityReply { pub id: ManufacturerID, pub family: u16, @@ -635,18 +612,18 @@ pub struct IdentityReply { } impl IdentityReply { - fn extend_midi(&self, v: &mut Vec) { - self.id.extend_midi(v); - push_u14(self.family, v); - push_u14(self.family_member, v); - v.push(to_u7(self.software_revision.0)); - v.push(to_u7(self.software_revision.1)); - v.push(to_u7(self.software_revision.2)); - v.push(to_u7(self.software_revision.3)); + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + self.id.extend_midi(&mut v)?; + push_u14(self.family, &mut v)?; + push_u14(self.family_member, &mut v)?; + v.push(to_u7(self.software_revision.0))?; + v.push(to_u7(self.software_revision.1))?; + v.push(to_u7(self.software_revision.2))?; + v.push(to_u7(self.software_revision.3)) } fn from_midi(m: &[u8]) -> Result { - let (manufacturer_id, shift) = ManufacturerID::from_midi(m)?; + let (manufacturer_id, shift) = ManufacturerID::from_midi(&m)?; if m.len() < shift + 8 { return Err(crate::ParseError::UnexpectedEnd); } diff --git a/src/system_exclusive/notation.rs b/src/system_exclusive/notation.rs index 1045a38..a09501a 100644 --- a/src/system_exclusive/notation.rs +++ b/src/system_exclusive/notation.rs @@ -1,5 +1,6 @@ use crate::parse_error::*; use crate::util::*; +use crate::Write; use alloc::vec; use alloc::vec::Vec; @@ -7,6 +8,7 @@ use alloc::vec::Vec; /// is optionally indicated by this message. /// Used by [`UniversalRealTimeMsg::BarMarker`](crate::UniversalRealTimeMsg::BarMarker). #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum BarMarker { /// "Actually, we're not running right now, so there is no bar." Don't know why this is used. NotRunning, @@ -19,23 +21,19 @@ pub enum BarMarker { } impl BarMarker { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match *self { Self::NotRunning => { // Most negative number - v.push(0x00); - v.push(0x40); - } - Self::CountIn(x) => { - push_i14(-(x.min(8191) as i16), v); - } - Self::Number(x) => { - push_i14(x.min(8191) as i16, v); + v.push(0x00)?; + v.push(0x40) } + Self::CountIn(x) => push_i14(-(x.min(8191) as i16), v), + Self::Number(x) => push_i14(x.min(8191) as i16, v), Self::RunningUnknown => { // Most positive number - v.push(0x7F); - v.push(0x3F); + v.push(0x7F)?; + v.push(0x3F) } } } @@ -49,6 +47,7 @@ impl BarMarker { /// Used to communicate a new time signature to the receiver. /// Used by [`UniversalRealTimeMsg`](crate::UniversalRealTimeMsg). #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct TimeSignature { /// The base time signature. pub signature: Signature, @@ -74,17 +73,18 @@ impl Default for TimeSignature { } impl TimeSignature { - pub(crate) fn extend_midi(&self, v: &mut Vec) { - v.push((4 + (self.compound.len() * 2)).min(126) as u8); // Bytes to follow - self.signature.extend_midi(v); - v.push(to_u7(self.midi_clocks_in_metronome_click)); - v.push(to_u7(self.thirty_second_notes_in_midi_quarter_note)); + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + v.push((4 + (self.compound.len() * 2)).min(126) as u8)?; // Bytes to follow + self.signature.extend_midi(&mut v)?; + v.push(to_u7(self.midi_clocks_in_metronome_click))?; + v.push(to_u7(self.thirty_second_notes_in_midi_quarter_note))?; for (i, s) in self.compound.iter().enumerate() { if i >= 61 { break; } - s.extend_midi(v); + s.extend_midi(&mut v)?; } + Ok(()) } #[allow(dead_code)] @@ -95,6 +95,7 @@ impl TimeSignature { /// A [time signature](https://en.wikipedia.org/wiki/Time_signature). Used by [`TimeSignature`]. #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Signature { /// Number of beats in a bar. pub beats: u8, @@ -103,9 +104,9 @@ pub struct Signature { } impl Signature { - fn extend_midi(&self, v: &mut Vec) { - v.push(to_u7(self.beats)); - v.push(self.beat_value.to_u8()); + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + v.push(to_u7(self.beats))?; + v.push(self.beat_value.to_u8()) } #[allow(dead_code)] @@ -125,6 +126,7 @@ impl Default for Signature { /// The note value of a beat, used by [`Signature`]. #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum BeatValue { Whole, Half, diff --git a/src/system_exclusive/sample_dump.rs b/src/system_exclusive/sample_dump.rs index 2cb81b5..b0f1a73 100644 --- a/src/system_exclusive/sample_dump.rs +++ b/src/system_exclusive/sample_dump.rs @@ -1,11 +1,13 @@ use crate::parse_error::*; use crate::util::*; +use crate::Write; use alloc::vec::Vec; use bstr::BString; /// Used to request and transmit sampler data. /// Used by [`UniversalNonRealTimeMsg::SampleDump`](crate::UniversalNonRealTimeMsg::SampleDump). #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SampleDumpMsg { /// Request that the receiver send the given sample. Request { @@ -57,7 +59,7 @@ pub enum SampleDumpMsg { } impl SampleDumpMsg { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { Self::Header { sample_num, @@ -68,32 +70,22 @@ impl SampleDumpMsg { sustain_loop_end, loop_type, } => { - push_u14(*sample_num, v); - v.push((*format).clamp(8, 28)); - push_u21(*period, v); - push_u21(*length, v); - push_u21(*sustain_loop_start, v); - push_u21(*sustain_loop_end, v); - v.push(*loop_type as u8); + push_u14(*sample_num, &mut v)?; + v.write(&[(*format).clamp(8, 28)])?; + push_u21(*period, &mut v)?; + push_u21(*length, &mut v)?; + push_u21(*sustain_loop_start, &mut v)?; + push_u21(*sustain_loop_end, &mut v)?; + v.write(&[*loop_type as u8]) } Self::Packet { running_count, data, } => { - let mut p: [u8; 120] = [0; 120]; - for (i, b) in data.iter().enumerate() { - if i > 119 { - break; - } - p[i] = to_u7(*b); - } - v.push(to_u7(*running_count)); - v.extend_from_slice(&p); - v.push(0); // Checksum <- Will be written over by `SystemExclusiveMsg.extend_midi` - } - Self::Request { sample_num } => { - push_u14(*sample_num, v); + v.push(to_u7(*running_count))?; + v.write_iter(data.iter().map(|b| to_u7(*b))) } + Self::Request { sample_num } => push_u14(*sample_num, v), Self::LoopPointTransmission { sample_num, loop_num, @@ -101,18 +93,18 @@ impl SampleDumpMsg { start_addr, end_addr, } => { - push_u14(*sample_num, v); - loop_num.extend_midi(v); - v.push(*loop_type as u8); - push_u21(*start_addr, v); - push_u21(*end_addr, v); + push_u14(*sample_num, &mut v)?; + loop_num.extend_midi(&mut v)?; + v.push(*loop_type as u8)?; + push_u21(*start_addr, &mut v)?; + push_u21(*end_addr, &mut v) } Self::LoopPointsRequest { sample_num, loop_num, } => { - push_u14(*sample_num, v); - loop_num.extend_midi(v); + push_u14(*sample_num, &mut v)?; + loop_num.extend_midi(v) } } } @@ -138,6 +130,7 @@ impl SampleDumpMsg { /// What loop a [`SampleDumpMsg`] or [`ExtendedSampleDumpMsg`] is referring to. #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum LoopNumber { /// A loop with the given ID, 0-16382. Loop(u16), @@ -148,16 +141,10 @@ pub enum LoopNumber { } impl LoopNumber { - fn extend_midi(&self, v: &mut Vec) { + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { - Self::RequestAll => { - v.push(0x7F); - v.push(0x7F); - } - Self::DeleteAll => { - v.push(0x7F); - v.push(0x7F); - } + Self::RequestAll => v.write(&[0x7F, 0x7F]), + Self::DeleteAll => v.write(&[0x7F, 0x7F]), Self::Loop(x) => push_u14(*x, v), } } @@ -165,6 +152,7 @@ impl LoopNumber { /// The type of loop being described by a [`SampleDumpMsg`]. #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum LoopType { /// Forward only Forward = 0, @@ -226,8 +214,71 @@ pub enum ExtendedSampleDumpMsg { }, } +#[cfg(feature = "defmt")] +impl defmt::Format for ExtendedSampleDumpMsg { + fn format(&self, fmt: defmt::Formatter) { + match self { + Self::Header { + sample_num, + format, + sample_rate, + length, + sustain_loop_start, + sustain_loop_end, + loop_type, + num_channels, + } => { + defmt::write!( + fmt, + "ExtendedSampleDumpMsg::Header {{ sample_num: {}, format: {}, sample_rate: {}, length: {}, sustain_loop_start: {}, sustain_loop_end: {}, loop_type: {:?}, num_channels: {} }}", + sample_num, format, sample_rate, length, sustain_loop_start, sustain_loop_end, loop_type, num_channels + ) + } + Self::LoopPointTransmission { + sample_num, + loop_num, + loop_type, + start_addr, + end_addr, + } => { + defmt::write!( + fmt, + "ExtendedSampleDumpMsg::LoopPointTransmission {{ sample_num: {}, loop_num: {:?}, loop_type: {:?}, start_addr: {}, end_addr: {} }}", + sample_num, loop_num, loop_type, start_addr, end_addr + ) + } + Self::LoopPointsRequest { + sample_num, + loop_num, + } => { + defmt::write!( + fmt, + "ExtendedSampleDumpMsg::LoopPointsRequest {{ sample_num: {}, loop_num: {:?} }}", + sample_num, + loop_num + ) + } + Self::SampleName { sample_num, name } => { + defmt::write!( + fmt, + "ExtendedSampleDumpMsg::SampleName {{ sample_num: {}, name: {:?} }}", + sample_num, + name.as_slice() + ) + } + Self::SampleNameRequest { sample_num } => { + defmt::write!( + fmt, + "ExtendedSampleDumpMsg::SampleNameRequest {{ sample_num: {} }}", + sample_num + ) + } + } + } +} + impl ExtendedSampleDumpMsg { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { Self::Header { sample_num, @@ -239,20 +290,20 @@ impl ExtendedSampleDumpMsg { loop_type, num_channels, } => { - push_u14(*sample_num, v); - v.push((*format).clamp(8, 28)); + push_u14(*sample_num, &mut v)?; + v.push((*format).clamp(8, 28))?; let sample_rate = sample_rate.max(0.0); let sample_rate_integer = (sample_rate as u64) as f64; // for lack of no_std f64 floor - push_u28(sample_rate_integer as u32, v); + push_u28(sample_rate_integer as u32, &mut v)?; push_u28( ((sample_rate - sample_rate_integer) * ((1 << 28) as f64)) as u32, - v, - ); - push_u35((*length).min(34359738368), v); - push_u35((*sustain_loop_start).min(34359738367), v); - push_u35((*sustain_loop_end).min(34359738367), v); - v.push(*loop_type as u8); - push_u7(*num_channels, v); + &mut v, + )?; + push_u35((*length).min(34359738368), &mut v)?; + push_u35((*sustain_loop_start).min(34359738367), &mut v)?; + push_u35((*sustain_loop_end).min(34359738367), &mut v)?; + v.push(*loop_type as u8)?; + push_u7(*num_channels, &mut v) } Self::LoopPointTransmission { sample_num, @@ -261,29 +312,27 @@ impl ExtendedSampleDumpMsg { start_addr, end_addr, } => { - push_u14(*sample_num, v); - loop_num.extend_midi(v); - v.push(*loop_type as u8); - push_u35(*start_addr, v); - push_u35(*end_addr, v); + push_u14(*sample_num, &mut v)?; + loop_num.extend_midi(&mut v)?; + v.push(*loop_type as u8)?; + push_u35(*start_addr, &mut v)?; + push_u35(*end_addr, &mut v) } Self::LoopPointsRequest { sample_num, loop_num, } => { - push_u14(*sample_num, v); - loop_num.extend_midi(v); + push_u14(*sample_num, &mut v)?; + loop_num.extend_midi(v) } Self::SampleName { sample_num, name } => { - push_u14(*sample_num, v); - v.push(0); // Language tag length (0 is the only allowable value) + push_u14(*sample_num, &mut v)?; + v.push(0)?; // Language tag length (0 is the only allowable value) let len = name.len().min(127); - v.push(len as u8); - v.extend_from_slice(&name[0..len]); - } - Self::SampleNameRequest { sample_num } => { - push_u14(*sample_num, v); + v.push(len as u8)?; + v.write(&name[0..len]) } + Self::SampleNameRequest { sample_num } => push_u14(*sample_num, v), } } @@ -295,6 +344,7 @@ impl ExtendedSampleDumpMsg { /// The type of loop being described by a [`SampleDumpMsg`]. #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ExtendedLoopType { /// A forward, unidirectional loop Forward = 0x00, diff --git a/src/system_exclusive/show_control.rs b/src/system_exclusive/show_control.rs index b5b415e..85065a7 100644 --- a/src/system_exclusive/show_control.rs +++ b/src/system_exclusive/show_control.rs @@ -1,7 +1,8 @@ -use crate::parse_error::*; +use crate::{parse_error::*, Write}; use alloc::vec::Vec; #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] /// A MIDI Show Control command. /// Used by [`UniversalRealTimeMsg::ShowControl`](crate::UniversalRealTimeMsg::ShowControl). /// @@ -16,9 +17,9 @@ pub enum ShowControlMsg { } impl ShowControlMsg { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { - Self::Unimplemented(d) => v.extend_from_slice(d), + Self::Unimplemented(d) => v.write(d), } } diff --git a/src/system_exclusive/tuning.rs b/src/system_exclusive/tuning.rs index 5177095..47ebb96 100644 --- a/src/system_exclusive/tuning.rs +++ b/src/system_exclusive/tuning.rs @@ -1,10 +1,12 @@ use crate::parse_error::*; use crate::util::*; +use crate::Write; use alloc::vec::Vec; /// Change the tunings of one or more notes, either real-time or not. /// Used by [`UniversalNonRealTimeMsg`](crate::UniversalNonRealTimeMsg) and [`UniversalRealTimeMsg`](crate::UniversalRealTimeMsg). #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct TuningNoteChange { /// Which tuning program is targeted, 0-127. See [`Parameter::TuningProgramSelect`](crate::Parameter::TuningProgramSelect). pub tuning_program_num: u8, @@ -16,21 +18,22 @@ pub struct TuningNoteChange { } impl TuningNoteChange { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { // The tuning_bank_num is pushed by the caller if needed - push_u7(self.tuning_program_num, v); - push_u7(self.tunings.len() as u8, v); + push_u7(self.tuning_program_num, &mut v)?; + push_u7(self.tunings.len() as u8, &mut v)?; for (note, tuning) in self.tunings.iter() { - push_u7(*note, v); + push_u7(*note, &mut v)?; if let Some(tuning) = tuning { - tuning.extend_midi(v); + tuning.extend_midi(&mut v)?; } else { // "No change" - v.push(0x7F); - v.push(0x7F); - v.push(0x7F); + v.push(0x7F)?; + v.push(0x7F)?; + v.push(0x7F)?; } } + Ok(()) } #[allow(dead_code)] @@ -42,6 +45,7 @@ impl TuningNoteChange { /// Set the tunings of all 128 notes. /// Used by [`UniversalNonRealTimeMsg`](crate::UniversalNonRealTimeMsg). #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct KeyBasedTuningDump { /// Which tuning program is targeted, 0-127. See [`Parameter::TuningProgramSelect`](crate::Parameter::TuningProgramSelect). pub tuning_program_num: u8, @@ -57,14 +61,12 @@ pub struct KeyBasedTuningDump { } impl KeyBasedTuningDump { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { if let Some(bank_num) = self.tuning_bank_num { - v.push(to_u7(bank_num)) - } - push_u7(self.tuning_program_num, v); - for ch in self.name.iter() { - v.push(*ch); + v.push(to_u7(bank_num))?; } + push_u7(self.tuning_program_num, &mut v)?; + v.write(&self.name)?; let mut i = 0; loop { if i >= 128 { @@ -72,22 +74,22 @@ impl KeyBasedTuningDump { } if let Some(tuning) = self.tunings.get(i) { if let Some(tuning) = tuning { - tuning.extend_midi(v); + tuning.extend_midi(&mut v)?; } else { // "No change" - v.push(0x7F); - v.push(0x7F); - v.push(0x7F); + v.push(0x7F)?; + v.push(0x7F)?; + v.push(0x7F)?; } } else { // The equivalent of equal temperament tuning - push_u7(i as u8, v); - v.push(0); - v.push(0); + push_u7(i as u8, &mut v)?; + v.push(0)?; + v.push(0)?; } i += 1; } - v.push(0); // Checksum <- Will be written over by `SystemExclusiveMsg.extend_midi` + Ok(()) } #[allow(dead_code)] @@ -98,6 +100,7 @@ impl KeyBasedTuningDump { /// Used to represent a tuning by [`TuningNoteChange`] and [`KeyBasedTuningDump`]. #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Tuning { /// The semitone corresponding with the same MIDI note number, 0-127 pub semitone: u8, @@ -127,11 +130,11 @@ impl Tuning { } } - fn extend_midi(&self, v: &mut Vec) { - push_u7(self.semitone, v); + fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + push_u7(self.semitone, &mut v)?; let [msb, lsb] = to_u14(self.fraction); - v.push(msb); // For some reason this is the opposite order of everything else??? - v.push(lsb); + v.push(msb)?; // For some reason this is the opposite order of everything else??? + v.push(lsb) } } @@ -140,6 +143,7 @@ impl Tuning { /// /// As defined in MIDI Tuning Updated Specification (CA-020/CA-021/RP-020) #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ScaleTuningDump1Byte { /// Which tuning program is targeted, 0-127. See [`Parameter::TuningProgramSelect`](crate::Parameter::TuningProgramSelect). pub tuning_program_num: u8, @@ -154,18 +158,11 @@ pub struct ScaleTuningDump1Byte { } impl ScaleTuningDump1Byte { - pub(crate) fn extend_midi(&self, v: &mut Vec) { - push_u7(self.tuning_bank_num, v); - push_u7(self.tuning_program_num, v); - for ch in self.name.iter() { - v.push(*ch); - } - - for t in self.tuning.iter() { - v.push(i_to_u7(*t)); - } - - v.push(0); // Checksum <- Will be written over by `SystemExclusiveMsg.extend_midi` + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + push_u7(self.tuning_bank_num, &mut v)?; + push_u7(self.tuning_program_num, &mut v)?; + v.write(&self.name)?; + v.write_iter(self.tuning.iter().copied().map(i_to_u7)) } #[allow(dead_code)] @@ -179,6 +176,7 @@ impl ScaleTuningDump1Byte { /// /// As defined in MIDI Tuning Updated Specification (CA-020/CA-021/RP-020) #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ScaleTuningDump2Byte { /// Which tuning program is targeted, 0-127. See [`Parameter::TuningProgramSelect`](crate::Parameter::TuningProgramSelect). pub tuning_program_num: u8, @@ -193,20 +191,16 @@ pub struct ScaleTuningDump2Byte { } impl ScaleTuningDump2Byte { - pub(crate) fn extend_midi(&self, v: &mut Vec) { - push_u7(self.tuning_bank_num, v); - push_u7(self.tuning_program_num, v); - for ch in self.name.iter() { - v.push(*ch); - } + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + push_u7(self.tuning_bank_num, &mut v)?; + push_u7(self.tuning_program_num, &mut v)?; - for t in self.tuning.iter() { - let [msb, lsb] = i_to_u14(*t); - v.push(lsb); - v.push(msb); - } + v.write(&self.name)?; - v.push(0); // Checksum <- Will be written over by `SystemExclusiveMsg.extend_midi` + v.write_iter(self.tuning.iter().copied().flat_map(|t| { + let [msb, lsb] = i_to_u14(t); + [lsb, msb] + })) } #[allow(dead_code)] @@ -220,6 +214,7 @@ impl ScaleTuningDump2Byte { /// /// As defined in MIDI Tuning Updated Specification (CA-020/CA-021/RP-020) #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ScaleTuning1Byte { pub channels: ChannelBitMap, /// 12 semitones of tuning adjustments repeated over all octaves, starting with C @@ -229,11 +224,9 @@ pub struct ScaleTuning1Byte { } impl ScaleTuning1Byte { - pub(crate) fn extend_midi(&self, v: &mut Vec) { - self.channels.extend_midi(v); - for t in self.tuning.iter() { - v.push(i_to_u7(*t)); - } + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + self.channels.extend_midi(&mut v)?; + v.write_iter(self.tuning.iter().copied().map(i_to_u7)) } #[allow(dead_code)] @@ -247,6 +240,7 @@ impl ScaleTuning1Byte { /// /// As defined in MIDI Tuning Updated Specification (CA-020/CA-021/RP-020) #[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ScaleTuning2Byte { pub channels: ChannelBitMap, /// 12 semitones of tuning adjustments repeated over all octaves, starting with C @@ -256,13 +250,14 @@ pub struct ScaleTuning2Byte { } impl ScaleTuning2Byte { - pub(crate) fn extend_midi(&self, v: &mut Vec) { - self.channels.extend_midi(v); + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { + self.channels.extend_midi(&mut v)?; for t in self.tuning.iter() { let [msb, lsb] = i_to_u14(*t); - v.push(lsb); - v.push(msb); + v.push(lsb)?; + v.push(msb)?; } + Ok(()) } #[allow(dead_code)] @@ -273,6 +268,7 @@ impl ScaleTuning2Byte { /// The set of channels to apply this tuning message to. Used by [`ScaleTuning1Byte`] and [`ScaleTuning2Byte`]. #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ChannelBitMap { pub channel_1: bool, pub channel_2: bool, @@ -320,7 +316,7 @@ impl ChannelBitMap { Self::default() } - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { let mut byte1: u8 = 0; if self.channel_16 { byte1 += 1 << 1; @@ -328,7 +324,7 @@ impl ChannelBitMap { if self.channel_15 { byte1 += 1 << 0; } - v.push(byte1); + v.push(byte1)?; let mut byte2: u8 = 0; if self.channel_14 { @@ -352,7 +348,7 @@ impl ChannelBitMap { if self.channel_8 { byte2 += 1 << 0; } - v.push(byte2); + v.push(byte2)?; let mut byte3: u8 = 0; if self.channel_7 { @@ -376,7 +372,7 @@ impl ChannelBitMap { if self.channel_1 { byte3 += 1 << 0; } - v.push(byte3); + v.push(byte3) } #[allow(dead_code)] diff --git a/src/system_real_time.rs b/src/system_real_time.rs index 42556e3..5fab4ad 100644 --- a/src/system_real_time.rs +++ b/src/system_real_time.rs @@ -1,9 +1,11 @@ +use crate::io::Write; + use super::parse_error::*; -use alloc::vec::Vec; /// A fairly limited set of messages used for device synchronization. /// Used in [`MidiMsg`](crate::MidiMsg). #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SystemRealTimeMsg { /// Used to synchronize clocks. Sent at a rate of 24 per quarter note. TimingClock, @@ -25,14 +27,14 @@ pub enum SystemRealTimeMsg { } impl SystemRealTimeMsg { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { - Self::TimingClock => v.push(0xF8), - Self::Start => v.push(0xFA), - Self::Continue => v.push(0xFB), - Self::Stop => v.push(0xFC), - Self::ActiveSensing => v.push(0xFE), - Self::SystemReset => v.push(0xFF), + Self::TimingClock => v.write(&[0xF8]), + Self::Start => v.write(&[0xFA]), + Self::Continue => v.write(&[0xFB]), + Self::Stop => v.write(&[0xFC]), + Self::ActiveSensing => v.write(&[0xFE]), + Self::SystemReset => v.write(&[0xFF]), } } diff --git a/src/time_code.rs b/src/time_code.rs index 692f0f6..f9489cd 100644 --- a/src/time_code.rs +++ b/src/time_code.rs @@ -7,6 +7,7 @@ use super::util::*; /// /// As defined in the MIDI Time Code spec (MMA0001 / RP004 / RP008) #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct TimeCode { /// The position in frames, 0-29 pub frames: u8, @@ -84,6 +85,7 @@ impl TimeCode { /// /// See [the SMTPE time code standard](https://en.wikipedia.org/wiki/SMPTE_timecode). #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum TimeCodeType { /// 24 Frames per second FPS24 = 0, @@ -120,13 +122,14 @@ mod sysex_types { use super::*; use crate::MidiMsg; use crate::ParseError; + use crate::Write; use alloc::vec::Vec; use bstr::BString; impl TimeCode { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { let [frame, seconds, minutes, codehour] = self.to_bytes(); - v.extend_from_slice(&[codehour, minutes, seconds, frame]); + v.write(&[codehour, minutes, seconds, frame]) } pub(crate) fn from_midi(m: &[u8]) -> Result { @@ -145,6 +148,7 @@ mod sysex_types { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// Like [`TimeCode`] but includes `fractional_frames`. Used in `TimeCodeCueingSetupMsg` and the SMF `Meta` event. /// /// As defined in the MIDI Time Code spec (MMA0001 / RP004 / RP008) @@ -175,9 +179,9 @@ mod sysex_types { ] } - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { let [fractional_frames, frames, seconds, minutes, codehour] = self.to_bytes(); - v.extend_from_slice(&[codehour, minutes, seconds, frames, fractional_frames]); + v.write(&[codehour, minutes, seconds, frames, fractional_frames]) } pub(crate) fn from_midi(v: &[u8]) -> Result<(Self, usize), ParseError> { @@ -200,6 +204,7 @@ mod sysex_types { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// Like [`TimeCode`] but uses `subframes` to optionally include status flags, and fractional frames. /// Also may be negative. Used in [`MachineControlCommandMsg`](crate::MachineControlCommandMsg). /// @@ -245,9 +250,9 @@ mod sysex_types { [self.subframes.to_byte(), frames] } - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { let [subframes, frames, seconds, minutes, codehour] = self.to_bytes(); - v.extend_from_slice(&[codehour, minutes, seconds, frames, subframes]); + v.write(&[codehour, minutes, seconds, frames, subframes]) } #[allow(dead_code)] @@ -272,6 +277,7 @@ mod sysex_types { /// Used by [`StandardTimeCode`]. #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SubFrames { /// The position in fractional frames, 0-99 FractionalFrames(u8), @@ -296,6 +302,7 @@ mod sysex_types { /// Used by [`StandardTimeCode`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct TimeCodeStatus { pub estimated_code: bool, pub invalid_code: bool, @@ -327,6 +334,7 @@ mod sysex_types { /// /// As defined in the MIDI Time Code spec (MMA0001 / RP004 / RP008) #[derive(Debug, Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct UserBits { /// Full bytes can be used here. Sent such that the first is considered /// the "most significant" value @@ -507,76 +515,195 @@ mod sysex_types { }, } + #[cfg(feature = "defmt")] + impl defmt::Format for TimeCodeCueingSetupMsg { + fn format(&self, fmt: defmt::Formatter) { + match self { + Self::TimeCodeOffset { time_code } => { + defmt::write!( + fmt, + "TimeCodeCueingSetupMsg::TimeCodeOffset {{ time_code: {:?} }}", + time_code + ) + } + Self::EnableEventList => { + defmt::write!(fmt, "TimeCodeCueingSetupMsg::EnableEventList") + } + Self::DisableEventList => { + defmt::write!(fmt, "TimeCodeCueingSetupMsg::DisableEventList") + } + Self::ClearEventList => { + defmt::write!(fmt, "TimeCodeCueingSetupMsg::ClearEventList") + } + Self::SystemStop => { + defmt::write!(fmt, "TimeCodeCueingSetupMsg::SystemStop") + } + Self::EventListRequest { time_code } => { + defmt::write!( + fmt, + "TimeCodeCueingSetupMsg::EventListRequest {{ time_code: {:?} }}", + time_code + ) + } + Self::PunchIn { + time_code, + event_number, + } => { + defmt::write!( + fmt, + "TimeCodeCueingSetupMsg::PunchIn {{ time_code: {:?}, event_number: {} }}", + time_code, + event_number + ) + } + Self::PunchOut { + time_code, + event_number, + } => { + defmt::write!( + fmt, + "TimeCodeCueingSetupMsg::PunchOut {{ time_code: {:?}, event_number: {} }}", + time_code, + event_number + ) + } + Self::DeletePunchIn { + time_code, + event_number, + } => { + defmt::write!(fmt, "TimeCodeCueingSetupMsg::DeletePunchIn {{ time_code: {:?}, event_number: {} }}", time_code, event_number) + } + Self::DeletePunchOut { + time_code, + event_number, + } => { + defmt::write!(fmt, "TimeCodeCueingSetupMsg::DeletePunchOut {{ time_code: {:?}, event_number: {} }}", time_code, event_number) + } + Self::EventStart { + time_code, + event_number, + additional_information, + } => { + defmt::write!(fmt, "TimeCodeCueingSetupMsg::EventStart {{ time_code: {:?}, event_number: {}, additional_information: {:?} }}", time_code, event_number, additional_information) + } + Self::EventStop { + time_code, + event_number, + additional_information, + } => { + defmt::write!(fmt, "TimeCodeCueingSetupMsg::EventStop {{ time_code: {:?}, event_number: {}, additional_information: {:?} }}", time_code, event_number, additional_information) + } + Self::DeleteEventStart { + time_code, + event_number, + } => { + defmt::write!(fmt, "TimeCodeCueingSetupMsg::DeleteEventStart {{ time_code: {:?}, event_number: {} }}", time_code, event_number) + } + Self::DeleteEventStop { + time_code, + event_number, + } => { + defmt::write!(fmt, "TimeCodeCueingSetupMsg::DeleteEventStop {{ time_code: {:?}, event_number: {} }}", time_code, event_number) + } + Self::Cue { + time_code, + event_number, + additional_information, + } => { + defmt::write!(fmt, "TimeCodeCueingSetupMsg::Cue {{ time_code: {:?}, event_number: {}, additional_information: {:?} }}", time_code, event_number, additional_information) + } + Self::DeleteCue { + time_code, + event_number, + } => { + defmt::write!( + fmt, + "TimeCodeCueingSetupMsg::DeleteCue {{ time_code: {:?}, event_number: {} }}", + time_code, + event_number + ) + } + Self::EventName { + time_code, + event_number, + name, + } => { + defmt::write!(fmt, "TimeCodeCueingSetupMsg::EventName {{ time_code: {:?}, event_number: {}, name: {:?} }}", time_code, event_number, name.as_slice()) + } + } + } + } + impl TimeCodeCueingSetupMsg { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { Self::TimeCodeOffset { time_code } => { - v.push(0x00); - time_code.extend_midi(v); - v.push(0x00); - v.push(0x00); + v.push(0x00)?; + time_code.extend_midi(&mut v)?; + v.push(0x00)?; + v.push(0x00) } Self::EnableEventList => { - v.push(0x00); - HighResTimeCode::default().extend_midi(v); - v.push(0x01); - v.push(0x00); + v.push(0x00)?; + HighResTimeCode::default().extend_midi(&mut v)?; + v.push(0x01)?; + v.push(0x00) } Self::DisableEventList => { - v.push(0x00); - HighResTimeCode::default().extend_midi(v); - v.push(0x02); - v.push(0x00); + v.push(0x00)?; + HighResTimeCode::default().extend_midi(&mut v)?; + v.push(0x02)?; + v.push(0x00) } Self::ClearEventList => { - v.push(0x00); - HighResTimeCode::default().extend_midi(v); - v.push(0x03); - v.push(0x00); + v.push(0x00)?; + HighResTimeCode::default().extend_midi(&mut v)?; + v.push(0x03)?; + v.push(0x00) } Self::SystemStop => { - v.push(0x00); - HighResTimeCode::default().extend_midi(v); - v.push(0x04); - v.push(0x00); + v.push(0x00)?; + HighResTimeCode::default().extend_midi(&mut v)?; + v.push(0x04)?; + v.push(0x00) } Self::EventListRequest { time_code } => { - v.push(0x00); - time_code.extend_midi(v); - v.push(0x05); - v.push(0x00); + v.push(0x00)?; + time_code.extend_midi(&mut v)?; + v.push(0x05)?; + v.push(0x00) } Self::PunchIn { time_code, event_number, } => { - v.push(0x01); - time_code.extend_midi(v); - push_u14(*event_number, v); + v.push(0x01)?; + time_code.extend_midi(&mut v)?; + push_u14(*event_number, v) } Self::PunchOut { time_code, event_number, } => { - v.push(0x02); - time_code.extend_midi(v); - push_u14(*event_number, v); + v.push(0x02)?; + time_code.extend_midi(&mut v)?; + push_u14(*event_number, v) } Self::DeletePunchIn { time_code, event_number, } => { - v.push(0x03); - time_code.extend_midi(v); - push_u14(*event_number, v); + v.push(0x03)?; + time_code.extend_midi(&mut v)?; + push_u14(*event_number, v) } Self::DeletePunchOut { time_code, event_number, } => { - v.push(0x04); - time_code.extend_midi(v); - push_u14(*event_number, v); + v.push(0x04)?; + time_code.extend_midi(&mut v)?; + push_u14(*event_number, v) } Self::EventStart { time_code, @@ -584,13 +711,13 @@ mod sysex_types { additional_information, } => { if additional_information.is_empty() { - v.push(0x05); + v.push(0x05)?; } else { - v.push(0x07); + v.push(0x07)?; } - time_code.extend_midi(v); - push_u14(*event_number, v); - push_nibblized_midi(additional_information, v); + time_code.extend_midi(&mut v)?; + push_u14(*event_number, &mut v)?; + push_nibblized_midi(additional_information, v) } Self::EventStop { time_code, @@ -598,29 +725,29 @@ mod sysex_types { additional_information, } => { if additional_information.is_empty() { - v.push(0x06); + v.push(0x06)?; } else { - v.push(0x08); + v.push(0x08)?; } - time_code.extend_midi(v); - push_u14(*event_number, v); - push_nibblized_midi(additional_information, v); + time_code.extend_midi(&mut v)?; + push_u14(*event_number, &mut v)?; + push_nibblized_midi(additional_information, v) } Self::DeleteEventStart { time_code, event_number, } => { - v.push(0x09); - time_code.extend_midi(v); - push_u14(*event_number, v); + v.push(0x09)?; + time_code.extend_midi(&mut v)?; + push_u14(*event_number, v) } Self::DeleteEventStop { time_code, event_number, } => { - v.push(0x0A); - time_code.extend_midi(v); - push_u14(*event_number, v); + v.push(0x0A)?; + time_code.extend_midi(&mut v)?; + push_u14(*event_number, v) } Self::Cue { time_code, @@ -628,31 +755,31 @@ mod sysex_types { additional_information, } => { if additional_information.is_empty() { - v.push(0x0B); + v.push(0x0B)?; } else { - v.push(0x0C); + v.push(0x0C)?; } - time_code.extend_midi(v); - push_u14(*event_number, v); - push_nibblized_midi(additional_information, v); + time_code.extend_midi(&mut v)?; + push_u14(*event_number, &mut v)?; + push_nibblized_midi(additional_information, v) } Self::DeleteCue { time_code, event_number, } => { - v.push(0x0D); - time_code.extend_midi(v); - push_u14(*event_number, v); + v.push(0x0D)?; + time_code.extend_midi(&mut v)?; + push_u14(*event_number, v) } Self::EventName { time_code, event_number, name, } => { - v.push(0x0E); - time_code.extend_midi(v); - push_u14(*event_number, v); - push_nibblized_name(name, v); + v.push(0x0E)?; + time_code.extend_midi(&mut v)?; + push_u14(*event_number, &mut v)?; + push_nibblized_name(name, v) } } } @@ -693,81 +820,134 @@ mod sysex_types { }, } - fn push_nibblized_midi(msgs: &[MidiMsg], v: &mut Vec) { + #[cfg(feature = "defmt")] + impl defmt::Format for TimeCodeCueingMsg { + fn format(&self, fmt: defmt::Formatter) { + match self { + TimeCodeCueingMsg::SystemStop => defmt::write!(fmt, "SystemStop"), + TimeCodeCueingMsg::PunchIn { event_number } => { + defmt::write!(fmt, "PunchIn({:x})", event_number) + } + TimeCodeCueingMsg::PunchOut { event_number } => { + defmt::write!(fmt, "PunchOut({:x})", event_number) + } + TimeCodeCueingMsg::EventStart { + event_number, + additional_information, + } => { + defmt::write!( + fmt, + "EventStart({:x}, {:x})", + event_number, + additional_information.len() + ) + } + TimeCodeCueingMsg::EventStop { + event_number, + additional_information, + } => { + defmt::write!( + fmt, + "EventStop({:x}, {:x})", + event_number, + additional_information.len() + ) + } + TimeCodeCueingMsg::Cue { + event_number, + additional_information, + } => { + defmt::write!( + fmt, + "Cue({:x}, {:x})", + event_number, + additional_information.len() + ) + } + TimeCodeCueingMsg::EventName { event_number, name } => { + defmt::write!(fmt, "EventName({:x}, {})", event_number, name.as_slice()) + } + } + } + } + + fn push_nibblized_midi(msgs: &[MidiMsg], mut v: impl Write) -> Result<(), E> { for msg in msgs.iter() { for b in msg.to_midi().iter() { let [msn, lsn] = to_nibble(*b); - v.push(lsn); - v.push(msn); + v.push(lsn)?; + v.push(msn)?; } } + Ok(()) } - fn push_nibblized_name(name: &BString, v: &mut Vec) { + fn push_nibblized_name(name: &BString, mut v: impl Write) -> Result<(), E> { // Not sure if this actually handles newlines correctly for b in name.iter() { let [msn, lsn] = to_nibble(*b); - v.push(lsn); - v.push(msn); + v.push(lsn)?; + v.push(msn)?; } + Ok(()) } impl TimeCodeCueingMsg { - pub(crate) fn extend_midi(&self, v: &mut Vec) { + pub(crate) fn extend_midi(&self, mut v: impl Write) -> Result<(), E> { match self { Self::SystemStop => { - v.push(0x00); - v.push(0x04); - v.push(0x00); + v.push(0x00)?; + v.push(0x04)?; + v.push(0x00) } Self::PunchIn { event_number } => { - v.push(0x01); - push_u14(*event_number, v); + v.push(0x01)?; + push_u14(*event_number, v) } Self::PunchOut { event_number } => { - v.push(0x02); - push_u14(*event_number, v); + v.push(0x02)?; + push_u14(*event_number, v) } Self::EventStart { event_number, additional_information, } => { if additional_information.is_empty() { - v.push(0x05); + v.push(0x05)?; } else { - v.push(0x07); + v.push(0x07)?; } - push_u14(*event_number, v); - push_nibblized_midi(additional_information, v); + push_u14(*event_number, &mut v)?; + push_nibblized_midi(additional_information, v) } Self::EventStop { event_number, additional_information, } => { if additional_information.is_empty() { - v.push(0x06); + v.push(0x06)?; } else { - v.push(0x08); + v.push(0x08)?; } - push_u14(*event_number, v); - push_nibblized_midi(additional_information, v); + push_u14(*event_number, &mut v)?; + push_nibblized_midi(additional_information, v) } Self::Cue { event_number, additional_information, } => { if additional_information.is_empty() { - v.push(0x0B); + v.push(0x0B)?; } else { - v.push(0x0C); + v.push(0x0C)?; } - push_u14(*event_number, v); - push_nibblized_midi(additional_information, v); + push_u14(*event_number, &mut v)?; + push_nibblized_midi(additional_information, v) } Self::EventName { event_number, name } => { - v.push(0x0E); - push_u14(*event_number, v); - push_nibblized_name(name, v); + v.push(0x0E)?; + push_u14(*event_number, &mut v)?; + push_nibblized_name(name, v) } } } diff --git a/src/util.rs b/src/util.rs index 674b056..046d571 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,5 +1,6 @@ +use crate::io::Write; + use super::ParseError; -use alloc::vec::Vec; use micromath::F32Ext; #[inline] @@ -106,8 +107,8 @@ pub fn to_nibble(x: u8) -> [u8; 2] { } #[inline] -pub fn push_u7(x: u8, v: &mut Vec) { - v.push(to_u7(x)); +pub fn push_u7(x: u8, mut v: impl Write) -> Result<(), E> { + v.write(&[to_u7(x)]) } // #[inline] @@ -116,10 +117,10 @@ pub fn push_u7(x: u8, v: &mut Vec) { // } #[inline] -pub fn push_u14(x: u16, v: &mut Vec) { +pub fn push_u14(x: u16, mut v: impl Write) -> Result<(), E> { let [msb, lsb] = to_u14(x); - v.push(lsb); - v.push(msb); + v.write(&[lsb, msb])?; + Ok(()) } /// Given a frequency in Hertz, returns a floating point midi note number with 1.0 = 100 cents @@ -145,40 +146,81 @@ pub fn freq_to_midi_note_cents(freq: f32) -> (u8, f32) { #[cfg(feature = "sysex")] mod sysex_util { - use alloc::vec::Vec; + use crate::{NotSeekable, Write}; #[inline] - pub fn push_i14(x: i16, v: &mut Vec) { + pub fn push_i14(x: i16, mut v: impl Write) -> Result<(), E> { let [msb, lsb] = to_i14(x); - v.push(lsb); - v.push(msb); + v.push(lsb)?; + v.push(msb) } #[inline] - pub fn push_u21(x: u32, v: &mut Vec) { + pub fn push_u21(x: u32, mut v: impl Write) -> Result<(), E> { let [msb, b, lsb] = to_u21(x); - v.push(lsb); - v.push(b); - v.push(msb); + v.write(&[lsb, b, msb]) } #[inline] - pub fn push_u28(x: u32, v: &mut Vec) { + pub fn push_u28(x: u32, mut v: impl Write) -> Result<(), E> { let [mmsb, msb, lsb, llsb] = to_u28(x); - v.push(llsb); - v.push(lsb); - v.push(msb); - v.push(mmsb); + v.push(llsb)?; + v.push(lsb)?; + v.push(msb)?; + v.push(mmsb) } #[inline] - pub fn push_u35(x: u64, v: &mut Vec) { + pub fn push_u35(x: u64, mut v: impl Write) -> Result<(), E> { let [msb, b2, b3, b4, lsb] = to_u35(x); - v.push(lsb); - v.push(b4); - v.push(b3); - v.push(b2); - v.push(msb); + v.push(lsb)?; + v.push(b4)?; + v.push(b3)?; + v.push(b2)?; + v.push(msb) + } + + pub struct ChecksummingWriter> { + writer: W, + checksum: u8, + } + + impl> ChecksummingWriter { + pub fn new(writer: W) -> Self { + Self { + writer, + checksum: 0, + } + } + + pub fn finish(mut self) -> Result { + self.writer.write(&[self.checksum])?; + Ok(self.writer) + } + } + + impl> Write for ChecksummingWriter { + type Error = E; + type Seekable = NotSeekable; + + #[inline] + fn write(&mut self, bytes: &[u8]) -> Result<(), Self::Error> { + for b in bytes { + self.checksum ^= *b; + } + self.writer.write(bytes) + } + + #[inline] + fn invalid_input(msg: &'static str) -> Self::Error { + W::invalid_input(msg) + } + + #[inline] + fn push(&mut self, b: u8) -> crate::WriteResult { + self.checksum ^= b; + self.writer.push(b) + } } pub fn checksum(bytes: &[u8]) -> u8 { @@ -255,42 +297,43 @@ pub use sysex_util::*; #[cfg(feature = "file")] mod file_util { + use crate::Write; + use super::ParseError; - use alloc::vec::Vec; use core::convert::TryInto; #[inline] - pub fn push_u16(x: u16, v: &mut Vec) { + pub fn push_u16(x: u16, mut v: impl Write) -> Result<(), E> { let [b1, b2] = x.to_be_bytes(); - v.push(b1); - v.push(b2); + v.push(b1)?; + v.push(b2) } #[inline] - pub fn push_u32(x: u32, v: &mut Vec) { + pub fn push_u32(x: u32, mut v: impl Write) -> Result<(), E> { let [b1, b2, b3, b4] = x.to_be_bytes(); - v.push(b1); - v.push(b2); - v.push(b3); - v.push(b4); + v.push(b1)?; + v.push(b2)?; + v.push(b3)?; + v.push(b4) } // Variable length quanity - pub fn push_vlq(x: u32, v: &mut Vec) { + pub fn push_vlq(x: u32, mut v: impl Write) -> Result<(), E> { if x < 0x00000080 { - v.push(x as u8 & 0b01111111); + v.push(x as u8 & 0b01111111) } else if x < 0x00004000 { - v.push(((x >> 7) as u8 & 0b01111111) + 0b10000000); - v.push(x as u8 & 0b01111111); + v.push(((x >> 7) as u8 & 0b01111111) + 0b10000000)?; + v.push(x as u8 & 0b01111111) } else if x < 0x00200000 { - v.push(((x >> 14) as u8 & 0b01111111) + 0b10000000); - v.push(((x >> 7) as u8 & 0b01111111) + 0b10000000); - v.push(x as u8 & 0b01111111); + v.push(((x >> 14) as u8 & 0b01111111) + 0b10000000)?; + v.push(((x >> 7) as u8 & 0b01111111) + 0b10000000)?; + v.push(x as u8 & 0b01111111) } else if x <= 0x0FFFFFFF { - v.push(((x >> 21) as u8 & 0b01111111) + 0b10000000); - v.push(((x >> 14) as u8 & 0b01111111) + 0b10000000); - v.push(((x >> 7) as u8 & 0b01111111) + 0b10000000); - v.push(x as u8 & 0b01111111); + v.push(((x >> 21) as u8 & 0b01111111) + 0b10000000)?; + v.push(((x >> 14) as u8 & 0b01111111) + 0b10000000)?; + v.push(((x >> 7) as u8 & 0b01111111) + 0b10000000)?; + v.push(x as u8 & 0b01111111) } else { panic!("Cannot use such a large number as a variable quantity") } @@ -486,7 +529,7 @@ mod tests { fn test_vlq() { fn test(x: u32, expected_len: usize) { let mut v = Vec::new(); - push_vlq(x, &mut v); + push_vlq(x, &mut v).unwrap(); let (y, len) = read_vlq(&v).unwrap(); assert_eq!( x, y,