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
/// be because it's not possible to use associated constants in type definitions.
#[derive(Debug, Clone)]
pub enum MidiResult {
pub enum MidiResult<S: SysExMessage> {
/// A basic three byte MIDI event.
Basic([u8; 3]),
/// A SysEx event. The message was written to the buffer provided to [`NoteEvent::as_midi()`].
/// The `usize` value indicates the message's actual length, including headers and end of SysEx
/// byte.
SysEx(usize),
/// A SysEx event. The message was written to the `S::Buffer` and may include padding at the
/// end. The `usize` value indicates the message's actual length, including headers and end of
/// SysEx byte.
SysEx(S::Buffer, usize),
}
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
/// 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.
///
/// 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> {
pub fn as_midi(self) -> Option<MidiResult<S>> {
match self {
NoteEvent::NoteOn {
timing: _,
@ -575,7 +569,8 @@ impl<S: SysExMessage> NoteEvent<S> {
// `message` is serialized and written to `sysex_buffer`, and the result contains the
// message's actual length
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::VoiceTerminated { .. }
@ -624,9 +619,9 @@ mod tests {
/// Converts an event to and from MIDI. Panics if any part of the conversion fails.
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::SysEx(_) => panic!("Unexpected SysEx result"),
MidiResult::SysEx(_, _) => panic!("Unexpected SysEx result"),
};
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 {
MessageType::Foo(x) => {
*buffer = [0xf0, 0x69, (x * 127.0).round() as u8, 0xf7];
4
}
MessageType::Foo(x) => ([0xf0, 0x69, (x * 127.0).round() as u8, 0xf7], 4),
}
}
}
@ -767,10 +759,9 @@ mod tests {
message,
};
let mut sysex_buffer = [0; 4];
match event.as_midi(&mut sysex_buffer) {
Some(MidiResult::SysEx(length)) => {
assert_eq!(sysex_buffer[..length], [0xf0, 0x69, 127, 0xf7])
match event.as_midi() {
Some(MidiResult::SysEx(padded_sysex_buffer, length)) => {
assert_eq!(padded_sysex_buffer[..length], [0xf0, 0x69, 127, 0xf7])
}
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].
fn from_buffer(buffer: &[u8]) -> Option<Self>;
/// Serialize this message object as a SysEx message in `buffer`, returning the message's length
/// in bytes. This should contain the full message including headers and the EOX byte, see the
/// trait's docstring for more information.
fn to_buffer(self, buffer: &mut Self::Buffer) -> usize;
/// Serialize this message object as a SysEx message in a byte buffer. This returns a buffer
/// alongside the message's length in bytes. The buffer may contain padding at the end. This
/// should contain the full message including headers and the EOX byte, see the trait's
/// docstring for more information.
fn to_buffer(self) -> (Self::Buffer, usize);
}
/// A default implementation plugins that don't need SysEx support can use.
@ -44,7 +45,7 @@ impl SysExMessage for () {
None
}
fn to_buffer(self, _buffer: &mut [u8; 0]) -> usize {
0
fn to_buffer(self) -> (Self::Buffer, usize) {
([], 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
// 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::SysEx(_)) => unreachable!(
Some(MidiResult::SysEx(_, _)) => unreachable!(
"Basic MIDI event read as SysEx, something's gone horribly wrong"
),
None => unreachable!("Missing MIDI conversion for MIDI event"),
@ -1343,12 +1343,10 @@ impl<P: ClapPlugin> Wrapper<P> {
if P::MIDI_OUTPUT >= MidiConfig::Basic =>
{
// SysEx is supported on the basic MIDI config so this is separate
let mut sysex_buffer = Default::default();
let length = message.to_buffer(&mut sysex_buffer);
let sysex_buffer = sysex_buffer.borrow();
nih_debug_assert!(sysex_buffer.len() >= length);
let sysex_buffer = &sysex_buffer[..length];
let (padded_sysex_buffer, length) = message.to_buffer();
let padded_sysex_buffer = padded_sysex_buffer.borrow();
nih_debug_assert!(padded_sysex_buffer.len() >= length);
let sysex_buffer = &padded_sysex_buffer[..length];
let event = clap_event_midi_sysex {
header: clap_event_header {

View file

@ -136,8 +136,7 @@ impl<P: Plugin> Backend<P> for Jack {
for event in output_events.drain(..) {
let timing = event.timing();
let mut sysex_buffer = Default::default();
match event.as_midi(&mut sysex_buffer) {
match event.as_midi() {
Some(MidiResult::Basic(midi_data)) => {
let write_result = midi_writer.write(&jack::RawMidi {
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");
}
Some(MidiResult::SysEx(length)) => {
// This feels a bit like gymnastics, but if the event was a SysEx
// event then `sysex_buffer` now contains the full message plus
// possibly some padding at the end
let sysex_buffer = sysex_buffer.borrow();
nih_debug_assert!(length <= sysex_buffer.len());
Some(MidiResult::SysEx(padded_sysex_buffer, length)) => {
// `sysex_buffer` may contain padding
let padded_sysex_buffer = padded_sysex_buffer.borrow();
nih_debug_assert!(length <= padded_sysex_buffer.len());
let write_result = midi_writer.write(&jack::RawMidi {
time: timing,
bytes: &sysex_buffer[..length],
bytes: &padded_sysex_buffer[..length],
});
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 }
if P::MIDI_OUTPUT >= MidiConfig::Basic =>
{
let mut sysex_buffer = Default::default();
let length = message.to_buffer(&mut sysex_buffer);
let sysex_buffer = sysex_buffer.borrow();
nih_debug_assert!(sysex_buffer.len() >= length);
let sysex_buffer = &sysex_buffer[..length];
let (padded_sysex_buffer, length) = message.to_buffer();
let padded_sysex_buffer = padded_sysex_buffer.borrow();
nih_debug_assert!(padded_sysex_buffer.len() >= length);
let sysex_buffer = &padded_sysex_buffer[..length];
vst3_event.type_ = EventTypes::kDataEvent as u16;
vst3_event.event.data = DataEvent {