1
0
Fork 0

Add VoiceTerminated events

These need to be output by a plugin when using polyphonic
modulation (which is currently not yet supported by NIH-plug).
This commit is contained in:
Robbert van der Helm 2022-07-04 19:29:56 +02:00
parent a2f8a9bebf
commit d027a6319f
4 changed files with 65 additions and 11 deletions

View file

@ -8,10 +8,13 @@ code then it will not be listed here.
## [2022-07-04] ## [2022-07-04]
- 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.
- Most `NoteEvent` variants now have an additional `voice_id` field.
- The `CLAP_DESCRIPTION`, `CLAP_MANUAL_URL`, and `CLAP_SUPPORT_URL` associated - The `CLAP_DESCRIPTION`, `CLAP_MANUAL_URL`, and `CLAP_SUPPORT_URL` associated
constants from the `ClapPlugin` are now optional and have the type constants from the `ClapPlugin` are now optional and have the type
`Option<&'static str>` instead of `&'static str`. `Option<&'static str>` instead of `&'static str`.
- Most `NoteEvent` variants now have an additional `voice_id` field.
## [2022-07-02] ## [2022-07-02]

View file

@ -10,7 +10,10 @@ pub enum MidiConfig {
/// The plugin will not have a note input port and will thus not receive any not events. /// The plugin will not have a note input port and will thus not receive any not events.
None, None,
/// The plugin receives note on/off events, pressure, and potentially a couple standardized /// The plugin receives note on/off events, pressure, and potentially a couple standardized
/// expression types depending on the plugin standard and host. /// 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, Basic,
/// The plugin receives full MIDI CCs as well as pitch bend information. For VST3 plugins this /// The plugin receives full MIDI CCs as well as pitch bend information. For VST3 plugins this
/// involves adding 130*16 parameters to bind to the the 128 MIDI CCs, pitch bend, and channel /// involves adding 130*16 parameters to bind to the the 128 MIDI CCs, pitch bend, and channel
@ -55,6 +58,19 @@ pub enum NoteEvent {
/// 127 levels available in MIDI. /// 127 levels available in MIDI.
velocity: f32, velocity: f32,
}, },
/// 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.
VoiceTerminated {
timing: u32,
/// The voice's unique identifier. Setting this allows a single voice to be terminated if
/// the plugin allows multiple overlapping voices for a single key.
voice_id: Option<i32>,
/// The note's channel, from 0 to 16, and the note's MIDI key number, from 0 to 127.
channel: u8,
/// The note's MIDI key number
note: u8,
},
/// A polyphonic note pressure/aftertouch event, available on [`MidiConfig::Basic`] and up. Not /// A polyphonic note pressure/aftertouch event, available on [`MidiConfig::Basic`] and up. Not
/// all hosts may support polyphonic aftertouch. /// all hosts may support polyphonic aftertouch.
/// ///
@ -200,6 +216,7 @@ impl NoteEvent {
match &self { match &self {
NoteEvent::NoteOn { timing, .. } => *timing, NoteEvent::NoteOn { timing, .. } => *timing,
NoteEvent::NoteOff { timing, .. } => *timing, NoteEvent::NoteOff { timing, .. } => *timing,
NoteEvent::VoiceTerminated { timing, .. } => *timing,
NoteEvent::PolyPressure { timing, .. } => *timing, NoteEvent::PolyPressure { timing, .. } => *timing,
NoteEvent::PolyVolume { timing, .. } => *timing, NoteEvent::PolyVolume { timing, .. } => *timing,
NoteEvent::PolyPan { timing, .. } => *timing, NoteEvent::PolyPan { timing, .. } => *timing,
@ -218,6 +235,7 @@ impl NoteEvent {
match &self { match &self {
NoteEvent::NoteOn { voice_id, .. } => *voice_id, NoteEvent::NoteOn { voice_id, .. } => *voice_id,
NoteEvent::NoteOff { voice_id, .. } => *voice_id, NoteEvent::NoteOff { voice_id, .. } => *voice_id,
NoteEvent::VoiceTerminated { voice_id, .. } => *voice_id,
NoteEvent::PolyPressure { voice_id, .. } => *voice_id, NoteEvent::PolyPressure { voice_id, .. } => *voice_id,
NoteEvent::PolyVolume { voice_id, .. } => *voice_id, NoteEvent::PolyVolume { voice_id, .. } => *voice_id,
NoteEvent::PolyPan { voice_id, .. } => *voice_id, NoteEvent::PolyPan { voice_id, .. } => *voice_id,
@ -353,7 +371,8 @@ impl NoteEvent {
cc, cc,
(value * 127.0).round().clamp(0.0, 127.0) as u8, (value * 127.0).round().clamp(0.0, 127.0) as u8,
]), ]),
NoteEvent::PolyVolume { .. } NoteEvent::VoiceTerminated { .. }
| NoteEvent::PolyVolume { .. }
| NoteEvent::PolyPan { .. } | NoteEvent::PolyPan { .. }
| NoteEvent::PolyTuning { .. } | NoteEvent::PolyTuning { .. }
| NoteEvent::PolyVibrato { .. } | NoteEvent::PolyVibrato { .. }
@ -368,6 +387,7 @@ impl NoteEvent {
match self { match self {
NoteEvent::NoteOn { timing, .. } => *timing -= samples, NoteEvent::NoteOn { timing, .. } => *timing -= samples,
NoteEvent::NoteOff { timing, .. } => *timing -= samples, NoteEvent::NoteOff { timing, .. } => *timing -= samples,
NoteEvent::VoiceTerminated { timing, .. } => *timing -= samples,
NoteEvent::PolyPressure { timing, .. } => *timing -= samples, NoteEvent::PolyPressure { timing, .. } => *timing -= samples,
NoteEvent::PolyVolume { timing, .. } => *timing -= samples, NoteEvent::PolyVolume { timing, .. } => *timing -= samples,
NoteEvent::PolyPan { timing, .. } => *timing -= samples, NoteEvent::PolyPan { timing, .. } => *timing -= samples,

View file

@ -4,14 +4,15 @@ use clap_sys::events::{
clap_event_header, clap_event_midi, clap_event_note, clap_event_note_expression, 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_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_input_events, clap_output_events, CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_IS_LIVE,
CLAP_EVENT_MIDI, CLAP_EVENT_NOTE_EXPRESSION, CLAP_EVENT_NOTE_OFF, CLAP_EVENT_NOTE_ON, CLAP_EVENT_MIDI, CLAP_EVENT_NOTE_END, CLAP_EVENT_NOTE_EXPRESSION, CLAP_EVENT_NOTE_OFF,
CLAP_EVENT_PARAM_GESTURE_BEGIN, CLAP_EVENT_PARAM_GESTURE_END, CLAP_EVENT_PARAM_MOD, CLAP_EVENT_NOTE_ON, CLAP_EVENT_PARAM_GESTURE_BEGIN, CLAP_EVENT_PARAM_GESTURE_END,
CLAP_EVENT_PARAM_VALUE, CLAP_EVENT_TRANSPORT, CLAP_NOTE_EXPRESSION_BRIGHTNESS, CLAP_EVENT_PARAM_MOD, CLAP_EVENT_PARAM_VALUE, CLAP_EVENT_TRANSPORT,
CLAP_NOTE_EXPRESSION_EXPRESSION, CLAP_NOTE_EXPRESSION_PAN, CLAP_NOTE_EXPRESSION_PRESSURE, CLAP_NOTE_EXPRESSION_BRIGHTNESS, CLAP_NOTE_EXPRESSION_EXPRESSION, CLAP_NOTE_EXPRESSION_PAN,
CLAP_NOTE_EXPRESSION_TUNING, CLAP_NOTE_EXPRESSION_VIBRATO, CLAP_NOTE_EXPRESSION_VOLUME, CLAP_NOTE_EXPRESSION_PRESSURE, CLAP_NOTE_EXPRESSION_TUNING, CLAP_NOTE_EXPRESSION_VIBRATO,
CLAP_TRANSPORT_HAS_BEATS_TIMELINE, CLAP_TRANSPORT_HAS_SECONDS_TIMELINE, CLAP_NOTE_EXPRESSION_VOLUME, CLAP_TRANSPORT_HAS_BEATS_TIMELINE,
CLAP_TRANSPORT_HAS_TEMPO, CLAP_TRANSPORT_HAS_TIME_SIGNATURE, CLAP_TRANSPORT_IS_LOOP_ACTIVE, CLAP_TRANSPORT_HAS_SECONDS_TIMELINE, CLAP_TRANSPORT_HAS_TEMPO,
CLAP_TRANSPORT_IS_PLAYING, CLAP_TRANSPORT_IS_RECORDING, CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL, CLAP_TRANSPORT_HAS_TIME_SIGNATURE, CLAP_TRANSPORT_IS_LOOP_ACTIVE, CLAP_TRANSPORT_IS_PLAYING,
CLAP_TRANSPORT_IS_RECORDING, CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL,
}; };
use clap_sys::ext::audio_ports::{ use clap_sys::ext::audio_ports::{
clap_audio_port_info, clap_plugin_audio_ports, CLAP_AUDIO_PORT_IS_MAIN, CLAP_EXT_AUDIO_PORTS, clap_audio_port_info, clap_plugin_audio_ports, CLAP_AUDIO_PORT_IS_MAIN, CLAP_EXT_AUDIO_PORTS,
@ -956,6 +957,29 @@ impl<P: ClapPlugin> Wrapper<P> {
clap_call! { out=>try_push(out, &event.header) } clap_call! { out=>try_push(out, &event.header) }
} }
NoteEvent::VoiceTerminated {
timing: _,
voice_id,
channel,
note,
} if P::MIDI_OUTPUT >= MidiConfig::Basic => {
let event = clap_event_note {
header: clap_event_header {
size: mem::size_of::<clap_event_note>() as u32,
time,
space_id: CLAP_CORE_EVENT_SPACE_ID,
type_: CLAP_EVENT_NOTE_END,
flags: 0,
},
note_id: voice_id.unwrap_or(-1),
port_index: 0,
channel: channel as i16,
key: note as i16,
velocity: 0.0,
};
clap_call! { out=>try_push(out, &event.header) }
}
NoteEvent::PolyPressure { NoteEvent::PolyPressure {
timing: _, timing: _,
voice_id, voice_id,

View file

@ -1587,6 +1587,13 @@ impl<P: Vst3Plugin> IAudioProcessor for Wrapper<P> {
tuning: 0.0, tuning: 0.0,
}; };
} }
// VST3 does not support or need these events, but they should also not
// trigger a debug assertion failure in NIH-plug
NoteEvent::VoiceTerminated { .. }
if P::MIDI_OUTPUT >= MidiConfig::Basic =>
{
continue;
}
NoteEvent::PolyPressure { NoteEvent::PolyPressure {
timing: _, timing: _,
voice_id, voice_id,