1
0
Fork 0

Simplify conversion from SysExMessage to buffers

There's no need to provide this as an argument anymore.
This commit is contained in:
Robbert van der Helm 2023-01-31 22:03:40 +01:00
parent 0deb8ab1cd
commit 5dbc76ef69
5 changed files with 38 additions and 53 deletions

View file

@ -321,13 +321,13 @@ pub enum NoteEvent<S: SysExMessage> {
/// The result of converting a `NoteEvent<S>` to MIDI. This is a bit weirder than it would have to /// The result of converting a `NoteEvent<S>` to MIDI. This is a bit weirder than it would have to
/// be because it's not possible to use associated constants in type definitions. /// be because it's not possible to use associated constants in type definitions.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum MidiResult { pub enum MidiResult<S: SysExMessage> {
/// A basic three byte MIDI event. /// A basic three byte MIDI event.
Basic([u8; 3]), Basic([u8; 3]),
/// A SysEx event. The message was written to the buffer provided to [`NoteEvent::as_midi()`]. /// A SysEx event. The message was written to the `S::Buffer` and may include padding at the
/// The `usize` value indicates the message's actual length, including headers and end of SysEx /// end. The `usize` value indicates the message's actual length, including headers and end of
/// byte. /// SysEx byte.
SysEx(usize), SysEx(S::Buffer, usize),
} }
impl<S: SysExMessage> NoteEvent<S> { impl<S: SysExMessage> NoteEvent<S> {
@ -487,13 +487,7 @@ impl<S: SysExMessage> NoteEvent<S> {
/// Create a MIDI message from this note event. Returns `None` if this even does not have a /// Create a MIDI message from this note event. Returns `None` if this even does not have a
/// direct MIDI equivalent. `PolyPressure` will be converted to polyphonic key pressure, but the /// direct MIDI equivalent. `PolyPressure` will be converted to polyphonic key pressure, but the
/// other polyphonic note expression types will not be converted to MIDI CC messages. /// other polyphonic note expression types will not be converted to MIDI CC messages.
/// pub fn as_midi(self) -> Option<MidiResult<S>> {
/// The `sysex_buffer` is an `[u8; N]` buffer with a length depending on SysEx message type `S`.
/// If this event contained SysEx data, then the result is written to the buffer and the
/// message's length in bytes is returned. This weird approach is needed because it's not
/// possible to use associated constants in types. Otherwise the buffer could be stored in
/// `MidiResult`.
pub fn as_midi(self, sysex_buffer: &mut S::Buffer) -> Option<MidiResult> {
match self { match self {
NoteEvent::NoteOn { NoteEvent::NoteOn {
timing: _, timing: _,
@ -575,7 +569,8 @@ impl<S: SysExMessage> NoteEvent<S> {
// `message` is serialized and written to `sysex_buffer`, and the result contains the // `message` is serialized and written to `sysex_buffer`, and the result contains the
// message's actual length // message's actual length
NoteEvent::MidiSysEx { timing: _, message } => { NoteEvent::MidiSysEx { timing: _, message } => {
Some(MidiResult::SysEx(message.to_buffer(sysex_buffer))) let (padded_sysex_buffer, length) = message.to_buffer();
Some(MidiResult::SysEx(padded_sysex_buffer, length))
} }
NoteEvent::Choke { .. } NoteEvent::Choke { .. }
| NoteEvent::VoiceTerminated { .. } | NoteEvent::VoiceTerminated { .. }
@ -624,9 +619,9 @@ mod tests {
/// Converts an event to and from MIDI. Panics if any part of the conversion fails. /// Converts an event to and from MIDI. Panics if any part of the conversion fails.
fn roundtrip_basic_event(event: NoteEvent<()>) -> NoteEvent<()> { fn roundtrip_basic_event(event: NoteEvent<()>) -> NoteEvent<()> {
let midi_data = match event.as_midi(&mut Default::default()).unwrap() { let midi_data = match event.as_midi().unwrap() {
MidiResult::Basic(midi_data) => midi_data, MidiResult::Basic(midi_data) => midi_data,
MidiResult::SysEx(_) => panic!("Unexpected SysEx result"), MidiResult::SysEx(_, _) => panic!("Unexpected SysEx result"),
}; };
NoteEvent::from_midi(TIMING, &midi_data).unwrap() NoteEvent::from_midi(TIMING, &midi_data).unwrap()
@ -735,12 +730,9 @@ mod tests {
} }
} }
fn to_buffer(self, buffer: &mut Self::Buffer) -> usize { fn to_buffer(self) -> (Self::Buffer, usize) {
match self { match self {
MessageType::Foo(x) => { MessageType::Foo(x) => ([0xf0, 0x69, (x * 127.0).round() as u8, 0xf7], 4),
*buffer = [0xf0, 0x69, (x * 127.0).round() as u8, 0xf7];
4
}
} }
} }
} }
@ -767,10 +759,9 @@ mod tests {
message, message,
}; };
let mut sysex_buffer = [0; 4]; match event.as_midi() {
match event.as_midi(&mut sysex_buffer) { Some(MidiResult::SysEx(padded_sysex_buffer, length)) => {
Some(MidiResult::SysEx(length)) => { assert_eq!(padded_sysex_buffer[..length], [0xf0, 0x69, 127, 0xf7])
assert_eq!(sysex_buffer[..length], [0xf0, 0x69, 127, 0xf7])
} }
result => panic!("Unexpected result: {result:?}"), result => panic!("Unexpected result: {result:?}"),
} }

View file

@ -30,10 +30,11 @@ pub trait SysExMessage: Debug + Clone + PartialEq + Send + Sync {
/// matches the received message. It is not padded to match [`Buffer`][Self::Buffer]. /// matches the received message. It is not padded to match [`Buffer`][Self::Buffer].
fn from_buffer(buffer: &[u8]) -> Option<Self>; fn from_buffer(buffer: &[u8]) -> Option<Self>;
/// Serialize this message object as a SysEx message in `buffer`, returning the message's length /// Serialize this message object as a SysEx message in a byte buffer. This returns a buffer
/// in bytes. This should contain the full message including headers and the EOX byte, see the /// alongside the message's length in bytes. The buffer may contain padding at the end. This
/// trait's docstring for more information. /// should contain the full message including headers and the EOX byte, see the trait's
fn to_buffer(self, buffer: &mut Self::Buffer) -> usize; /// docstring for more information.
fn to_buffer(self) -> (Self::Buffer, usize);
} }
/// A default implementation plugins that don't need SysEx support can use. /// A default implementation plugins that don't need SysEx support can use.
@ -44,7 +45,7 @@ impl SysExMessage for () {
None None
} }
fn to_buffer(self, _buffer: &mut [u8; 0]) -> usize { fn to_buffer(self) -> (Self::Buffer, usize) {
0 ([], 0)
} }
} }

View file

@ -1317,9 +1317,9 @@ impl<P: ClapPlugin> Wrapper<P> {
{ {
// NIH-plug already includes MIDI conversion functions, so we'll reuse those for // NIH-plug already includes MIDI conversion functions, so we'll reuse those for
// the MIDI events // the MIDI events
let midi_data = match midi_event.as_midi(&mut Default::default()) { let midi_data = match midi_event.as_midi() {
Some(MidiResult::Basic(midi_data)) => midi_data, Some(MidiResult::Basic(midi_data)) => midi_data,
Some(MidiResult::SysEx(_)) => unreachable!( Some(MidiResult::SysEx(_, _)) => unreachable!(
"Basic MIDI event read as SysEx, something's gone horribly wrong" "Basic MIDI event read as SysEx, something's gone horribly wrong"
), ),
None => unreachable!("Missing MIDI conversion for MIDI event"), None => unreachable!("Missing MIDI conversion for MIDI event"),
@ -1343,12 +1343,10 @@ impl<P: ClapPlugin> Wrapper<P> {
if P::MIDI_OUTPUT >= MidiConfig::Basic => if P::MIDI_OUTPUT >= MidiConfig::Basic =>
{ {
// SysEx is supported on the basic MIDI config so this is separate // SysEx is supported on the basic MIDI config so this is separate
let mut sysex_buffer = Default::default(); let (padded_sysex_buffer, length) = message.to_buffer();
let padded_sysex_buffer = padded_sysex_buffer.borrow();
let length = message.to_buffer(&mut sysex_buffer); nih_debug_assert!(padded_sysex_buffer.len() >= length);
let sysex_buffer = sysex_buffer.borrow(); let sysex_buffer = &padded_sysex_buffer[..length];
nih_debug_assert!(sysex_buffer.len() >= length);
let sysex_buffer = &sysex_buffer[..length];
let event = clap_event_midi_sysex { let event = clap_event_midi_sysex {
header: clap_event_header { header: clap_event_header {

View file

@ -136,8 +136,7 @@ impl<P: Plugin> Backend<P> for Jack {
for event in output_events.drain(..) { for event in output_events.drain(..) {
let timing = event.timing(); let timing = event.timing();
let mut sysex_buffer = Default::default(); match event.as_midi() {
match event.as_midi(&mut sysex_buffer) {
Some(MidiResult::Basic(midi_data)) => { Some(MidiResult::Basic(midi_data)) => {
let write_result = midi_writer.write(&jack::RawMidi { let write_result = midi_writer.write(&jack::RawMidi {
time: timing, time: timing,
@ -146,15 +145,13 @@ impl<P: Plugin> Backend<P> for Jack {
nih_debug_assert!(write_result.is_ok(), "The MIDI buffer is full"); nih_debug_assert!(write_result.is_ok(), "The MIDI buffer is full");
} }
Some(MidiResult::SysEx(length)) => { Some(MidiResult::SysEx(padded_sysex_buffer, length)) => {
// This feels a bit like gymnastics, but if the event was a SysEx // `sysex_buffer` may contain padding
// event then `sysex_buffer` now contains the full message plus let padded_sysex_buffer = padded_sysex_buffer.borrow();
// possibly some padding at the end nih_debug_assert!(length <= padded_sysex_buffer.len());
let sysex_buffer = sysex_buffer.borrow();
nih_debug_assert!(length <= sysex_buffer.len());
let write_result = midi_writer.write(&jack::RawMidi { let write_result = midi_writer.write(&jack::RawMidi {
time: timing, time: timing,
bytes: &sysex_buffer[..length], bytes: &padded_sysex_buffer[..length],
}); });
nih_debug_assert!(write_result.is_ok(), "The MIDI buffer is full"); nih_debug_assert!(write_result.is_ok(), "The MIDI buffer is full");

View file

@ -1740,12 +1740,10 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
NoteEvent::MidiSysEx { timing: _, message } NoteEvent::MidiSysEx { timing: _, message }
if P::MIDI_OUTPUT >= MidiConfig::Basic => if P::MIDI_OUTPUT >= MidiConfig::Basic =>
{ {
let mut sysex_buffer = Default::default(); let (padded_sysex_buffer, length) = message.to_buffer();
let padded_sysex_buffer = padded_sysex_buffer.borrow();
let length = message.to_buffer(&mut sysex_buffer); nih_debug_assert!(padded_sysex_buffer.len() >= length);
let sysex_buffer = sysex_buffer.borrow(); let sysex_buffer = &padded_sysex_buffer[..length];
nih_debug_assert!(sysex_buffer.len() >= length);
let sysex_buffer = &sysex_buffer[..length];
vst3_event.type_ = EventTypes::kDataEvent as u16; vst3_event.type_ = EventTypes::kDataEvent as u16;
vst3_event.event.data = DataEvent { vst3_event.event.data = DataEvent {