Simplify conversion from SysExMessage to buffers
There's no need to provide this as an argument anymore.
This commit is contained in:
parent
0deb8ab1cd
commit
5dbc76ef69
5 changed files with 38 additions and 53 deletions
39
src/midi.rs
39
src/midi.rs
|
@ -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:?}"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Add table
Reference in a new issue