diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index b757f7d0..f3cc09c5 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -8,6 +8,9 @@ code then it will not be listed here. ## [2022-07-04] +- There is a new `NoteEvent::Choke` event the host can send to a plugin to let + it know that it should immediately terminate all sound associated with a voice + or a key. - There is a new `NoteEvent::VoiceTerminated` event to let the host know a voice has been terminated. This needs to be output by CLAP plugins that support polyphonic modulation. diff --git a/src/midi.rs b/src/midi.rs index f3e493e8..598bfaf4 100644 --- a/src/midi.rs +++ b/src/midi.rs @@ -9,10 +9,10 @@ pub use midi_consts::channel_event::control_change; pub enum MidiConfig { /// The plugin will not have a note input port and will thus not receive any not events. None, - /// The plugin receives note on/off events, pressure, and potentially a couple standardized - /// expression types depending on the plugin standard and host. If the plugin sets up - /// configuration for polyphonic modulation (see [`ClapPlugin`][crate::prelude::ClapPlugin]) and - /// assigns polyphonic modulation IDs to some of its parameters, then it will also receive + /// The plugin receives note on/off/choke events, pressure, and potentially a couple + /// standardized expression types depending on the plugin standard and host. If the plugin sets + /// up configuration for polyphonic modulation (see [`ClapPlugin`][crate::prelude::ClapPlugin]) + /// and assigns polyphonic modulation IDs to some of its parameters, then it will also receive /// polyphonic modulation events. Basic, /// The plugin receives full MIDI CCs as well as pitch bend information. For VST3 plugins this @@ -58,6 +58,19 @@ pub enum NoteEvent { /// 127 levels available in MIDI. velocity: f32, }, + /// A note choke event, available on [`MidiConfig::Basic`] and up. When the host sends this to + /// the plugin, it indicates that a voice or all sound associated with a note should immediately + /// stop playing. + Choke { + timing: u32, + /// A unique identifier for this note, if available. Using this to refer to a note is + /// required when allowing overlapping voices for CLAP plugins. + voice_id: Option, + /// The note's channel, from 0 to 16. + channel: u8, + /// The note's MIDI key number, from 0 to 127. + note: u8, + }, /// Sent by the plugin to the host to indicate that a voice has ended. This **needs** to be sent /// when a voice terminates when using polyphonic modulation. Otherwise you can ignore this /// event. @@ -216,6 +229,7 @@ impl NoteEvent { match &self { NoteEvent::NoteOn { timing, .. } => *timing, NoteEvent::NoteOff { timing, .. } => *timing, + NoteEvent::Choke { timing, .. } => *timing, NoteEvent::VoiceTerminated { timing, .. } => *timing, NoteEvent::PolyPressure { timing, .. } => *timing, NoteEvent::PolyVolume { timing, .. } => *timing, @@ -235,6 +249,7 @@ impl NoteEvent { match &self { NoteEvent::NoteOn { voice_id, .. } => *voice_id, NoteEvent::NoteOff { voice_id, .. } => *voice_id, + NoteEvent::Choke { voice_id, .. } => *voice_id, NoteEvent::VoiceTerminated { voice_id, .. } => *voice_id, NoteEvent::PolyPressure { voice_id, .. } => *voice_id, NoteEvent::PolyVolume { voice_id, .. } => *voice_id, @@ -371,7 +386,8 @@ impl NoteEvent { cc, (value * 127.0).round().clamp(0.0, 127.0) as u8, ]), - NoteEvent::VoiceTerminated { .. } + NoteEvent::Choke { .. } + | NoteEvent::VoiceTerminated { .. } | NoteEvent::PolyVolume { .. } | NoteEvent::PolyPan { .. } | NoteEvent::PolyTuning { .. } @@ -387,6 +403,7 @@ impl NoteEvent { match self { NoteEvent::NoteOn { timing, .. } => *timing -= samples, NoteEvent::NoteOff { timing, .. } => *timing -= samples, + NoteEvent::Choke { timing, .. } => *timing -= samples, NoteEvent::VoiceTerminated { timing, .. } => *timing -= samples, NoteEvent::PolyPressure { timing, .. } => *timing -= samples, NoteEvent::PolyVolume { timing, .. } => *timing -= samples, diff --git a/src/wrapper/clap/wrapper.rs b/src/wrapper/clap/wrapper.rs index 9f782087..55cdac84 100644 --- a/src/wrapper/clap/wrapper.rs +++ b/src/wrapper/clap/wrapper.rs @@ -4,12 +4,12 @@ use clap_sys::events::{ clap_event_header, clap_event_midi, clap_event_note, clap_event_note_expression, clap_event_param_gesture, clap_event_param_mod, clap_event_param_value, clap_event_transport, clap_input_events, clap_output_events, CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_IS_LIVE, - CLAP_EVENT_MIDI, CLAP_EVENT_NOTE_END, CLAP_EVENT_NOTE_EXPRESSION, CLAP_EVENT_NOTE_OFF, - CLAP_EVENT_NOTE_ON, CLAP_EVENT_PARAM_GESTURE_BEGIN, CLAP_EVENT_PARAM_GESTURE_END, - CLAP_EVENT_PARAM_MOD, CLAP_EVENT_PARAM_VALUE, CLAP_EVENT_TRANSPORT, - CLAP_NOTE_EXPRESSION_BRIGHTNESS, CLAP_NOTE_EXPRESSION_EXPRESSION, CLAP_NOTE_EXPRESSION_PAN, - CLAP_NOTE_EXPRESSION_PRESSURE, CLAP_NOTE_EXPRESSION_TUNING, CLAP_NOTE_EXPRESSION_VIBRATO, - CLAP_NOTE_EXPRESSION_VOLUME, CLAP_TRANSPORT_HAS_BEATS_TIMELINE, + CLAP_EVENT_MIDI, CLAP_EVENT_NOTE_CHOKE, CLAP_EVENT_NOTE_END, CLAP_EVENT_NOTE_EXPRESSION, + CLAP_EVENT_NOTE_OFF, CLAP_EVENT_NOTE_ON, CLAP_EVENT_PARAM_GESTURE_BEGIN, + CLAP_EVENT_PARAM_GESTURE_END, CLAP_EVENT_PARAM_MOD, CLAP_EVENT_PARAM_VALUE, + CLAP_EVENT_TRANSPORT, CLAP_NOTE_EXPRESSION_BRIGHTNESS, CLAP_NOTE_EXPRESSION_EXPRESSION, + CLAP_NOTE_EXPRESSION_PAN, CLAP_NOTE_EXPRESSION_PRESSURE, CLAP_NOTE_EXPRESSION_TUNING, + CLAP_NOTE_EXPRESSION_VIBRATO, CLAP_NOTE_EXPRESSION_VOLUME, CLAP_TRANSPORT_HAS_BEATS_TIMELINE, CLAP_TRANSPORT_HAS_SECONDS_TIMELINE, CLAP_TRANSPORT_HAS_TEMPO, CLAP_TRANSPORT_HAS_TIME_SIGNATURE, CLAP_TRANSPORT_IS_LOOP_ACTIVE, CLAP_TRANSPORT_IS_PLAYING, CLAP_TRANSPORT_IS_RECORDING, CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL, @@ -1328,6 +1328,23 @@ impl Wrapper

{ false } + (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_CHOKE) => { + if P::MIDI_INPUT >= MidiConfig::Basic { + let event = &*(event as *const clap_event_note); + input_events.push_back(NoteEvent::Choke { + timing: raw_event.time - current_sample_idx as u32, + voice_id: if event.note_id != -1 { + Some(event.note_id) + } else { + None + }, + channel: event.channel as u8, + note: event.key as u8, + }); + } + + false + } (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_EXPRESSION) => { if P::MIDI_INPUT >= MidiConfig::Basic { // TODO: Add support for the other expression types