Allow the plugin to change current voice capacity
This commit is contained in:
parent
f828761677
commit
cdee721e9c
6 changed files with 100 additions and 7 deletions
|
@ -21,6 +21,14 @@ pub trait InitContext {
|
||||||
/// Update the current latency of the plugin. If the plugin is currently processing audio, then
|
/// Update the current latency of the plugin. If the plugin is currently processing audio, then
|
||||||
/// this may cause audio playback to be restarted.
|
/// this may cause audio playback to be restarted.
|
||||||
fn set_latency_samples(&self, samples: u32);
|
fn set_latency_samples(&self, samples: u32);
|
||||||
|
|
||||||
|
/// Set the current voice **capacity** for this plugin (so not the number of currently active
|
||||||
|
/// voices). This may only be called if
|
||||||
|
/// [`ClapPlugin::CLAP_POLY_MODULATION_CONFIG`][crate::prelude::ClapPlugin::CLAP_POLY_MODULATION_CONFIG]
|
||||||
|
/// is set. `capacity` must be between 1 and the configured maximum capacity. Changing this at
|
||||||
|
/// runtime allows the host to better optimize polyphonic modulation, or to switch to strictly
|
||||||
|
/// monophonic modulation when dropping the capacity down to 1.
|
||||||
|
fn set_current_voice_capacity(&self, capacity: u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains both context data and callbacks the plugin can use during processing. Most notably this
|
/// Contains both context data and callbacks the plugin can use during processing. Most notably this
|
||||||
|
@ -82,6 +90,14 @@ pub trait ProcessContext {
|
||||||
/// this may cause audio playback to be restarted.
|
/// this may cause audio playback to be restarted.
|
||||||
fn set_latency_samples(&self, samples: u32);
|
fn set_latency_samples(&self, samples: u32);
|
||||||
|
|
||||||
|
/// Set the current voice **capacity** for this plugin (so not the number of currently active
|
||||||
|
/// voices). This may only be called if
|
||||||
|
/// [`ClapPlugin::CLAP_POLY_MODULATION_CONFIG`][crate::prelude::ClapPlugin::CLAP_POLY_MODULATION_CONFIG]
|
||||||
|
/// is set. `capacity` must be between 1 and the configured maximum capacity. Changing this at
|
||||||
|
/// runtime allows the host to better optimize polyphonic modulation, or to switch to strictly
|
||||||
|
/// monophonic modulation when dropping the capacity down to 1.
|
||||||
|
fn set_current_voice_capacity(&self, capacity: u32);
|
||||||
|
|
||||||
// TODO: Add this, this works similar to [GuiContext::set_parameter] but it adds the parameter
|
// TODO: Add this, this works similar to [GuiContext::set_parameter] but it adds the parameter
|
||||||
// change to a queue (or directly to the VST3 plugin's parameter output queues) instead of
|
// change to a queue (or directly to the VST3 plugin's parameter output queues) instead of
|
||||||
// using main thread host automation (and all the locks involved there).
|
// using main thread host automation (and all the locks involved there).
|
||||||
|
|
|
@ -426,9 +426,9 @@ pub enum ProcessMode {
|
||||||
/// Configuration for the plugin's polyphonic modulation options, if it supports .
|
/// Configuration for the plugin's polyphonic modulation options, if it supports .
|
||||||
pub struct PolyModulationConfig {
|
pub struct PolyModulationConfig {
|
||||||
/// The maximum number of voices this plugin will ever use. Call the context's
|
/// The maximum number of voices this plugin will ever use. Call the context's
|
||||||
/// `set_current_voices()` method during initialization or audio processing to set the number of
|
/// `set_current_voice_capacity()` method during initialization or audio processing to set the
|
||||||
/// currently active voices.
|
/// polyphony limit.
|
||||||
pub max_voices: u32,
|
pub max_voice_capacity: u32,
|
||||||
/// If set to `true`, then the host may send note events for the same channel and key, but using
|
/// If set to `true`, then the host may send note events for the same channel and key, but using
|
||||||
/// different voice IDs. Bitwig Studio, for instance, can use this to do voice stacking. After
|
/// different voice IDs. Bitwig Studio, for instance, can use this to do voice stacking. After
|
||||||
/// enabling this, you should always prioritize using voice IDs to map note events to voices.
|
/// enabling this, you should always prioritize using voice IDs to map note events to voices.
|
||||||
|
|
|
@ -120,6 +120,10 @@ impl<P: ClapPlugin> InitContext for WrapperInitContext<'_, P> {
|
||||||
fn set_latency_samples(&self, samples: u32) {
|
fn set_latency_samples(&self, samples: u32) {
|
||||||
self.wrapper.set_latency_samples(samples)
|
self.wrapper.set_latency_samples(samples)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_current_voice_capacity(&self, capacity: u32) {
|
||||||
|
self.wrapper.set_current_voice_capacity(capacity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: ClapPlugin> ProcessContext for WrapperProcessContext<'_, P> {
|
impl<P: ClapPlugin> ProcessContext for WrapperProcessContext<'_, P> {
|
||||||
|
@ -142,4 +146,8 @@ impl<P: ClapPlugin> ProcessContext for WrapperProcessContext<'_, P> {
|
||||||
fn set_latency_samples(&self, samples: u32) {
|
fn set_latency_samples(&self, samples: u32) {
|
||||||
self.wrapper.set_latency_samples(samples)
|
self.wrapper.set_latency_samples(samples)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_current_voice_capacity(&self, capacity: u32) {
|
||||||
|
self.wrapper.set_current_voice_capacity(capacity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ use clap_sys::ext::audio_ports_config::{
|
||||||
clap_audio_ports_config, clap_plugin_audio_ports_config, CLAP_EXT_AUDIO_PORTS_CONFIG,
|
clap_audio_ports_config, clap_plugin_audio_ports_config, CLAP_EXT_AUDIO_PORTS_CONFIG,
|
||||||
};
|
};
|
||||||
use clap_sys::ext::draft::voice_info::{
|
use clap_sys::ext::draft::voice_info::{
|
||||||
clap_plugin_voice_info, clap_voice_info, CLAP_EXT_VOICE_INFO,
|
clap_host_voice_info, clap_plugin_voice_info, clap_voice_info, CLAP_EXT_VOICE_INFO,
|
||||||
CLAP_VOICE_INFO_SUPPORTS_OVERLAPPING_NOTES,
|
CLAP_VOICE_INFO_SUPPORTS_OVERLAPPING_NOTES,
|
||||||
};
|
};
|
||||||
use clap_sys::ext::gui::{
|
use clap_sys::ext::gui::{
|
||||||
|
@ -240,6 +240,11 @@ pub struct Wrapper<P: ClapPlugin> {
|
||||||
clap_plugin_tail: clap_plugin_tail,
|
clap_plugin_tail: clap_plugin_tail,
|
||||||
|
|
||||||
clap_plugin_voice_info: clap_plugin_voice_info,
|
clap_plugin_voice_info: clap_plugin_voice_info,
|
||||||
|
host_voice_info: AtomicRefCell<Option<ClapPtr<clap_host_voice_info>>>,
|
||||||
|
/// If `P::CLAP_POLY_MODULATION_CONFIG` is set, then the plugin can configure the current number
|
||||||
|
/// of active voices using a context method called from the initialization or processing
|
||||||
|
/// context. This defaults to the maximum number of voices.
|
||||||
|
current_voice_capacity: AtomicU32,
|
||||||
|
|
||||||
/// A queue of tasks that still need to be performed. Because CLAP lets the plugin request a
|
/// A queue of tasks that still need to be performed. Because CLAP lets the plugin request a
|
||||||
/// host callback directly, we don't need to use the OsEventLoop we use in our other plugin
|
/// host callback directly, we don't need to use the OsEventLoop we use in our other plugin
|
||||||
|
@ -261,6 +266,8 @@ pub struct Wrapper<P: ClapPlugin> {
|
||||||
pub enum Task {
|
pub enum Task {
|
||||||
/// Inform the host that the latency has changed.
|
/// Inform the host that the latency has changed.
|
||||||
LatencyChanged,
|
LatencyChanged,
|
||||||
|
/// Inform the host that the voice info has changed.
|
||||||
|
VoiceInfoChanged,
|
||||||
/// Tell the host that it should rescan the current parameter values.
|
/// Tell the host that it should rescan the current parameter values.
|
||||||
RescanParamValues,
|
RescanParamValues,
|
||||||
}
|
}
|
||||||
|
@ -351,6 +358,12 @@ impl<P: ClapPlugin> MainThreadExecutor<Task> for Wrapper<P> {
|
||||||
}
|
}
|
||||||
None => nih_debug_assert_failure!("Host does not support the latency extension"),
|
None => nih_debug_assert_failure!("Host does not support the latency extension"),
|
||||||
},
|
},
|
||||||
|
Task::VoiceInfoChanged => match &*self.host_voice_info.borrow() {
|
||||||
|
Some(host_voice_info) => {
|
||||||
|
clap_call! { host_voice_info=>changed(&*self.host_callback) };
|
||||||
|
}
|
||||||
|
None => nih_debug_assert_failure!("Host does not support the voice-info extension"),
|
||||||
|
},
|
||||||
Task::RescanParamValues => match &*self.host_params.borrow() {
|
Task::RescanParamValues => match &*self.host_params.borrow() {
|
||||||
Some(host_params) => {
|
Some(host_params) => {
|
||||||
clap_call! { host_params=>rescan(&*self.host_callback, CLAP_PARAM_RESCAN_VALUES) };
|
clap_call! { host_params=>rescan(&*self.host_callback, CLAP_PARAM_RESCAN_VALUES) };
|
||||||
|
@ -616,6 +629,18 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
clap_plugin_voice_info: clap_plugin_voice_info {
|
clap_plugin_voice_info: clap_plugin_voice_info {
|
||||||
get: Some(Self::ext_voice_info_get),
|
get: Some(Self::ext_voice_info_get),
|
||||||
},
|
},
|
||||||
|
host_voice_info: AtomicRefCell::new(None),
|
||||||
|
current_voice_capacity: AtomicU32::new(
|
||||||
|
P::CLAP_POLY_MODULATION_CONFIG
|
||||||
|
.map(|c| {
|
||||||
|
nih_debug_assert!(
|
||||||
|
c.max_voice_capacity >= 1,
|
||||||
|
"The maximum voice capacity cannot be zero"
|
||||||
|
);
|
||||||
|
c.max_voice_capacity
|
||||||
|
})
|
||||||
|
.unwrap_or(1),
|
||||||
|
),
|
||||||
|
|
||||||
tasks: ArrayQueue::new(TASK_QUEUE_CAPACITY),
|
tasks: ArrayQueue::new(TASK_QUEUE_CAPACITY),
|
||||||
main_thread_id: thread::current().id(),
|
main_thread_id: thread::current().id(),
|
||||||
|
@ -1577,6 +1602,30 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_current_voice_capacity(&self, capacity: u32) {
|
||||||
|
match P::CLAP_POLY_MODULATION_CONFIG {
|
||||||
|
Some(config) => {
|
||||||
|
let clamped_capacity = capacity.clamp(1, config.max_voice_capacity);
|
||||||
|
nih_debug_assert_eq!(
|
||||||
|
capacity,
|
||||||
|
clamped_capacity,
|
||||||
|
"The current voice capacity must be between 1 and the maximum capacity"
|
||||||
|
);
|
||||||
|
|
||||||
|
if clamped_capacity != self.current_voice_capacity.load(Ordering::Relaxed) {
|
||||||
|
self.current_voice_capacity
|
||||||
|
.store(clamped_capacity, Ordering::Relaxed);
|
||||||
|
let task_posted = self.do_maybe_async(Task::VoiceInfoChanged);
|
||||||
|
nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => nih_debug_assert_failure!(
|
||||||
|
"Configuring the current voice capacity is only possible when \
|
||||||
|
'ClapPlugin::CLAP_POLY_MODULATION_CONFIG' is set"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn init(plugin: *const clap_plugin) -> bool {
|
unsafe extern "C" fn init(plugin: *const clap_plugin) -> bool {
|
||||||
check_null_ptr!(false, plugin);
|
check_null_ptr!(false, plugin);
|
||||||
let wrapper = &*(plugin as *const Self);
|
let wrapper = &*(plugin as *const Self);
|
||||||
|
@ -1588,6 +1637,10 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
query_host_extension::<clap_host_latency>(&wrapper.host_callback, CLAP_EXT_LATENCY);
|
query_host_extension::<clap_host_latency>(&wrapper.host_callback, CLAP_EXT_LATENCY);
|
||||||
*wrapper.host_params.borrow_mut() =
|
*wrapper.host_params.borrow_mut() =
|
||||||
query_host_extension::<clap_host_params>(&wrapper.host_callback, CLAP_EXT_PARAMS);
|
query_host_extension::<clap_host_params>(&wrapper.host_callback, CLAP_EXT_PARAMS);
|
||||||
|
*wrapper.host_voice_info.borrow_mut() = query_host_extension::<clap_host_voice_info>(
|
||||||
|
&wrapper.host_callback,
|
||||||
|
CLAP_EXT_VOICE_INFO,
|
||||||
|
);
|
||||||
*wrapper.host_thread_check.borrow_mut() = query_host_extension::<clap_host_thread_check>(
|
*wrapper.host_thread_check.borrow_mut() = query_host_extension::<clap_host_thread_check>(
|
||||||
&wrapper.host_callback,
|
&wrapper.host_callback,
|
||||||
CLAP_EXT_THREAD_CHECK,
|
CLAP_EXT_THREAD_CHECK,
|
||||||
|
@ -3081,13 +3134,13 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
info: *mut clap_voice_info,
|
info: *mut clap_voice_info,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
check_null_ptr!(false, plugin, info);
|
check_null_ptr!(false, plugin, info);
|
||||||
|
let wrapper = &*(plugin as *const Self);
|
||||||
|
|
||||||
// TODO: Allow the plugin to set the active voice count
|
|
||||||
match P::CLAP_POLY_MODULATION_CONFIG {
|
match P::CLAP_POLY_MODULATION_CONFIG {
|
||||||
Some(config) => {
|
Some(config) => {
|
||||||
*info = clap_voice_info {
|
*info = clap_voice_info {
|
||||||
voice_count: config.max_voices,
|
voice_count: wrapper.current_voice_capacity.load(Ordering::Relaxed),
|
||||||
voice_capacity: config.max_voices,
|
voice_capacity: config.max_voice_capacity,
|
||||||
flags: if config.supports_overlapping_voices {
|
flags: if config.supports_overlapping_voices {
|
||||||
CLAP_VOICE_INFO_SUPPORTS_OVERLAPPING_NOTES
|
CLAP_VOICE_INFO_SUPPORTS_OVERLAPPING_NOTES
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -90,6 +90,10 @@ impl<P: Plugin, B: Backend> InitContext for WrapperInitContext<'_, P, B> {
|
||||||
fn set_latency_samples(&self, _samples: u32) {
|
fn set_latency_samples(&self, _samples: u32) {
|
||||||
nih_debug_assert_failure!("TODO: WrapperInitContext::set_latency_samples()");
|
nih_debug_assert_failure!("TODO: WrapperInitContext::set_latency_samples()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_current_voice_capacity(&self, _capacity: u32) {
|
||||||
|
// This is only supported by CLAP
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Plugin, B: Backend> ProcessContext for WrapperProcessContext<'_, P, B> {
|
impl<P: Plugin, B: Backend> ProcessContext for WrapperProcessContext<'_, P, B> {
|
||||||
|
@ -120,4 +124,8 @@ impl<P: Plugin, B: Backend> ProcessContext for WrapperProcessContext<'_, P, B> {
|
||||||
fn set_latency_samples(&self, _samples: u32) {
|
fn set_latency_samples(&self, _samples: u32) {
|
||||||
nih_debug_assert_failure!("TODO: WrapperProcessContext::set_latency_samples()");
|
nih_debug_assert_failure!("TODO: WrapperProcessContext::set_latency_samples()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_current_voice_capacity(&self, _capacity: u32) {
|
||||||
|
// This is only supported by CLAP
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,6 +124,10 @@ impl<P: Vst3Plugin> InitContext for WrapperInitContext<'_, P> {
|
||||||
fn set_latency_samples(&self, samples: u32) {
|
fn set_latency_samples(&self, samples: u32) {
|
||||||
self.inner.set_latency_samples(samples)
|
self.inner.set_latency_samples(samples)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_current_voice_capacity(&self, _capacity: u32) {
|
||||||
|
// This is only supported by CLAP
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Vst3Plugin> ProcessContext for WrapperProcessContext<'_, P> {
|
impl<P: Vst3Plugin> ProcessContext for WrapperProcessContext<'_, P> {
|
||||||
|
@ -146,4 +150,8 @@ impl<P: Vst3Plugin> ProcessContext for WrapperProcessContext<'_, P> {
|
||||||
fn set_latency_samples(&self, samples: u32) {
|
fn set_latency_samples(&self, samples: u32) {
|
||||||
self.inner.set_latency_samples(samples)
|
self.inner.set_latency_samples(samples)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_current_voice_capacity(&self, _capacity: u32) {
|
||||||
|
// This is only supported by CLAP
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue