Implement CLAP audio ports config extension
This commit is contained in:
parent
78b815fede
commit
70c81642a5
|
@ -8,6 +8,10 @@ use clap_sys::events::{
|
||||||
CLAP_EVENT_NOTE_EXPRESSION, CLAP_EVENT_NOTE_OFF, CLAP_EVENT_NOTE_ON, CLAP_EVENT_PARAM_MOD,
|
CLAP_EVENT_NOTE_EXPRESSION, CLAP_EVENT_NOTE_OFF, CLAP_EVENT_NOTE_ON, CLAP_EVENT_PARAM_MOD,
|
||||||
CLAP_EVENT_PARAM_VALUE,
|
CLAP_EVENT_PARAM_VALUE,
|
||||||
};
|
};
|
||||||
|
use clap_sys::ext::audio_ports::{CLAP_PORT_MONO, CLAP_PORT_STEREO};
|
||||||
|
use clap_sys::ext::audio_ports_config::{
|
||||||
|
clap_audio_ports_config, clap_plugin_audio_ports_config, CLAP_EXT_AUDIO_PORTS_CONFIG,
|
||||||
|
};
|
||||||
use clap_sys::ext::params::{
|
use clap_sys::ext::params::{
|
||||||
clap_param_info, clap_plugin_params, CLAP_EXT_PARAMS, CLAP_PARAM_IS_BYPASS,
|
clap_param_info, clap_plugin_params, CLAP_EXT_PARAMS, CLAP_PARAM_IS_BYPASS,
|
||||||
CLAP_PARAM_IS_STEPPED,
|
CLAP_PARAM_IS_STEPPED,
|
||||||
|
@ -40,6 +44,7 @@ use crate::event_loop::{EventLoop, MainThreadExecutor, TASK_QUEUE_CAPACITY};
|
||||||
use crate::param::internals::ParamPtr;
|
use crate::param::internals::ParamPtr;
|
||||||
use crate::plugin::{BufferConfig, BusConfig, ClapPlugin, NoteEvent, ProcessStatus};
|
use crate::plugin::{BufferConfig, BusConfig, ClapPlugin, NoteEvent, ProcessStatus};
|
||||||
use crate::wrapper::util::{hash_param_id, process_wrapper, strlcpy};
|
use crate::wrapper::util::{hash_param_id, process_wrapper, strlcpy};
|
||||||
|
use crate::Plugin;
|
||||||
|
|
||||||
/// Right now the wrapper adds its own bypass parameter.
|
/// Right now the wrapper adds its own bypass parameter.
|
||||||
///
|
///
|
||||||
|
@ -90,6 +95,16 @@ pub struct Wrapper<P: ClapPlugin> {
|
||||||
/// this.
|
/// this.
|
||||||
plugin_descriptor: Box<PluginDescriptor<P>>,
|
plugin_descriptor: Box<PluginDescriptor<P>>,
|
||||||
|
|
||||||
|
clap_plugin_audio_ports_config: clap_plugin_audio_ports_config,
|
||||||
|
/// During initialization we'll ask `P` which bus configurations it supports. The host can then
|
||||||
|
/// use the audio ports config extension to choose a configuration. Right now we only query mono
|
||||||
|
/// and stereo configurations, with and without inputs, as well as the plugin's default input
|
||||||
|
/// and output channel counts if that does not match one of those configurations (to do the
|
||||||
|
/// least surprising thing).
|
||||||
|
///
|
||||||
|
/// TODO: Support surround setups once a plugin needs taht
|
||||||
|
supported_bus_configs: Vec<BusConfig>,
|
||||||
|
|
||||||
clap_plugin_params: clap_plugin_params,
|
clap_plugin_params: clap_plugin_params,
|
||||||
// These fiels are exactly the same as their VST3 wrapper counterparts.
|
// These fiels are exactly the same as their VST3 wrapper counterparts.
|
||||||
//
|
//
|
||||||
|
@ -227,6 +242,13 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
|
|
||||||
plugin_descriptor,
|
plugin_descriptor,
|
||||||
|
|
||||||
|
clap_plugin_audio_ports_config: clap_plugin_audio_ports_config {
|
||||||
|
count: Self::ext_audio_ports_config_count,
|
||||||
|
get: Self::ext_audio_ports_config_get,
|
||||||
|
select: Self::ext_audio_ports_config_select,
|
||||||
|
},
|
||||||
|
supported_bus_configs: Vec::new(),
|
||||||
|
|
||||||
clap_plugin_params: clap_plugin_params {
|
clap_plugin_params: clap_plugin_params {
|
||||||
count: Self::ext_params_count,
|
count: Self::ext_params_count,
|
||||||
get_info: Self::ext_params_get_info,
|
get_info: Self::ext_params_get_info,
|
||||||
|
@ -245,6 +267,35 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
main_thread_id: thread::current().id(),
|
main_thread_id: thread::current().id(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Query all sensible bus configurations supported by the plugin. We don't do surround or
|
||||||
|
// anything beyond stereo right now.
|
||||||
|
for num_output_channels in [1, 2] {
|
||||||
|
for num_input_channels in [0, num_output_channels] {
|
||||||
|
let bus_config = BusConfig {
|
||||||
|
num_input_channels,
|
||||||
|
num_output_channels,
|
||||||
|
};
|
||||||
|
if wrapper.plugin.read().accepts_bus_config(&bus_config) {
|
||||||
|
wrapper.supported_bus_configs.push(bus_config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the off chance that the default config specified by the plugin is not in the above
|
||||||
|
// list, we'll try that as well.
|
||||||
|
let default_bus_config = BusConfig {
|
||||||
|
num_input_channels: P::DEFAULT_NUM_INPUTS,
|
||||||
|
num_output_channels: P::DEFAULT_NUM_OUTPUTS,
|
||||||
|
};
|
||||||
|
if !wrapper.supported_bus_configs.contains(&default_bus_config)
|
||||||
|
&& wrapper
|
||||||
|
.plugin
|
||||||
|
.read()
|
||||||
|
.accepts_bus_config(&default_bus_config)
|
||||||
|
{
|
||||||
|
wrapper.supported_bus_configs.push(default_bus_config);
|
||||||
|
}
|
||||||
|
|
||||||
// This is a mapping from the parameter IDs specified by the plugin to pointers to thsoe
|
// This is a mapping from the parameter IDs specified by the plugin to pointers to thsoe
|
||||||
// parameters. Since the object returned by `params()` is pinned, these pointers are safe to
|
// parameters. Since the object returned by `params()` is pinned, these pointers are safe to
|
||||||
// dereference as long as `wrapper.plugin` is alive
|
// dereference as long as `wrapper.plugin` is alive
|
||||||
|
@ -437,6 +488,7 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
_min_frames_count: u32,
|
_min_frames_count: u32,
|
||||||
max_frames_count: u32,
|
max_frames_count: u32,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
check_null_ptr!(false, plugin);
|
||||||
let wrapper = &*(plugin as *const Self);
|
let wrapper = &*(plugin as *const Self);
|
||||||
|
|
||||||
let bus_config = wrapper.current_bus_config.load();
|
let bus_config = wrapper.current_bus_config.load();
|
||||||
|
@ -605,16 +657,25 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
check_null_ptr!(ptr::null(), plugin, id);
|
check_null_ptr!(ptr::null(), plugin, id);
|
||||||
let wrapper = &*(plugin as *const Self);
|
let wrapper = &*(plugin as *const Self);
|
||||||
|
|
||||||
// TODO: Implement the other useful extensions. Like uh audio inputs.
|
// TODO: Implement the following extensions:
|
||||||
|
// - audio ports (for naming audio ports)
|
||||||
|
// - gui
|
||||||
|
// - the non-freestanding GUI extensions depending on the platform
|
||||||
|
// - latency
|
||||||
|
// - state
|
||||||
let id = CStr::from_ptr(id);
|
let id = CStr::from_ptr(id);
|
||||||
if id == CStr::from_ptr(CLAP_EXT_PARAMS) {
|
if id == CStr::from_ptr(CLAP_EXT_AUDIO_PORTS_CONFIG) {
|
||||||
|
&wrapper.clap_plugin_audio_ports_config as *const _ as *const c_void
|
||||||
|
} else if id == CStr::from_ptr(CLAP_EXT_PARAMS) {
|
||||||
&wrapper.clap_plugin_params as *const _ as *const c_void
|
&wrapper.clap_plugin_params as *const _ as *const c_void
|
||||||
} else {
|
} else {
|
||||||
|
nih_log!("Host tried to query unknown extension '{:?}'", id);
|
||||||
ptr::null()
|
ptr::null()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn on_main_thread(plugin: *const clap_plugin) {
|
unsafe extern "C" fn on_main_thread(plugin: *const clap_plugin) {
|
||||||
|
check_null_ptr!((), plugin);
|
||||||
let wrapper = &*(plugin as *const Self);
|
let wrapper = &*(plugin as *const Self);
|
||||||
|
|
||||||
// [Self::do_maybe_async] posts a task to the queue and asks the host to call this function
|
// [Self::do_maybe_async] posts a task to the queue and asks the host to call this function
|
||||||
|
@ -624,6 +685,96 @@ impl<P: ClapPlugin> Wrapper<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn ext_audio_ports_config_count(plugin: *const clap_plugin) -> u32 {
|
||||||
|
// TODO: Implement sidechain nputs and auxiliary outputs
|
||||||
|
check_null_ptr!(0, plugin);
|
||||||
|
let wrapper = &*(plugin as *const Self);
|
||||||
|
|
||||||
|
wrapper.supported_bus_configs.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn ext_audio_ports_config_get(
|
||||||
|
plugin: *const clap_plugin,
|
||||||
|
index: u32,
|
||||||
|
config: *mut clap_audio_ports_config,
|
||||||
|
) -> bool {
|
||||||
|
check_null_ptr!(false, plugin, config);
|
||||||
|
let wrapper = &*(plugin as *const Self);
|
||||||
|
|
||||||
|
match wrapper.supported_bus_configs.get(index as usize) {
|
||||||
|
Some(bus_config) => {
|
||||||
|
let name = match bus_config {
|
||||||
|
BusConfig {
|
||||||
|
num_input_channels: _,
|
||||||
|
num_output_channels: 1,
|
||||||
|
} => String::from("Mono"),
|
||||||
|
BusConfig {
|
||||||
|
num_input_channels: _,
|
||||||
|
num_output_channels: 2,
|
||||||
|
} => String::from("Stereo"),
|
||||||
|
BusConfig {
|
||||||
|
num_input_channels,
|
||||||
|
num_output_channels,
|
||||||
|
} => format!("{num_input_channels} inputs, {num_output_channels} outputs"),
|
||||||
|
};
|
||||||
|
let input_port_type = match bus_config.num_input_channels {
|
||||||
|
1 => CLAP_PORT_MONO,
|
||||||
|
2 => CLAP_PORT_STEREO,
|
||||||
|
_ => ptr::null(),
|
||||||
|
};
|
||||||
|
let output_port_type = match bus_config.num_output_channels {
|
||||||
|
1 => CLAP_PORT_MONO,
|
||||||
|
2 => CLAP_PORT_STEREO,
|
||||||
|
_ => ptr::null(),
|
||||||
|
};
|
||||||
|
|
||||||
|
*config = std::mem::zeroed();
|
||||||
|
let config = &mut *config;
|
||||||
|
config.id = index;
|
||||||
|
strlcpy(&mut config.name, &name);
|
||||||
|
config.input_channel_count = bus_config.num_input_channels;
|
||||||
|
config.input_port_type = input_port_type;
|
||||||
|
config.output_channel_count = bus_config.num_output_channels;
|
||||||
|
config.output_port_type = output_port_type;
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
nih_debug_assert_failure!(
|
||||||
|
"Host tried to query out of bounds audio port config {}",
|
||||||
|
index
|
||||||
|
);
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn ext_audio_ports_config_select(
|
||||||
|
plugin: *const clap_plugin,
|
||||||
|
config_id: clap_id,
|
||||||
|
) -> bool {
|
||||||
|
check_null_ptr!(false, plugin);
|
||||||
|
let wrapper = &*(plugin as *const Self);
|
||||||
|
|
||||||
|
// We use the vector indices for the config ID
|
||||||
|
match wrapper.supported_bus_configs.get(config_id as usize) {
|
||||||
|
Some(bus_config) => {
|
||||||
|
wrapper.current_bus_config.store(*bus_config);
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
nih_debug_assert_failure!(
|
||||||
|
"Host tried to select out of bounds audio port config {}",
|
||||||
|
config_id
|
||||||
|
);
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn ext_params_count(plugin: *const clap_plugin) -> u32 {
|
unsafe extern "C" fn ext_params_count(plugin: *const clap_plugin) -> u32 {
|
||||||
check_null_ptr!(0, plugin);
|
check_null_ptr!(0, plugin);
|
||||||
let wrapper = &*(plugin as *const Self);
|
let wrapper = &*(plugin as *const Self);
|
||||||
|
|
Loading…
Reference in a new issue