Initialize the standalone target
This commit is contained in:
parent
cf3745a4e1
commit
261594a478
|
@ -200,6 +200,7 @@ pub struct ParamSetter<'a> {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum PluginApi {
|
pub enum PluginApi {
|
||||||
Clap,
|
Clap,
|
||||||
|
Standalone,
|
||||||
Vst3,
|
Vst3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,6 +208,7 @@ impl Display for PluginApi {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
PluginApi::Clap => write!(f, "CLAP"),
|
PluginApi::Clap => write!(f, "CLAP"),
|
||||||
|
PluginApi::Standalone => write!(f, "standalone"),
|
||||||
PluginApi::Vst3 => write!(f, "VST3"),
|
PluginApi::Vst3 => write!(f, "VST3"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
//! A standalone plugin target that directly connects to the system's audio and MIDI ports instead
|
//! A standalone plugin target that directly connects to the system's audio and MIDI ports instead
|
||||||
//! of relying on a plugin host. This is mostly useful for quickly testing GUI changes.
|
//! of relying on a plugin host. This is mostly useful for quickly testing GUI changes.
|
||||||
|
|
||||||
use self::wrapper::Wrapper;
|
use self::wrapper::{Wrapper, WrapperConfig};
|
||||||
use crate::plugin::Plugin;
|
use crate::plugin::Plugin;
|
||||||
|
|
||||||
|
mod context;
|
||||||
mod wrapper;
|
mod wrapper;
|
||||||
|
|
||||||
/// Open an NIH-plug plugin as a standalone application. If the plugin has an editor, this will open
|
/// Open an NIH-plug plugin as a standalone application. If the plugin has an editor, this will open
|
||||||
|
@ -50,7 +51,19 @@ pub fn nih_export_standalone<P: Plugin>() {
|
||||||
pub fn nih_export_standalone_with_args<P: Plugin, Args: IntoIterator<Item = String>>(args: Args) {
|
pub fn nih_export_standalone_with_args<P: Plugin, Args: IntoIterator<Item = String>>(args: Args) {
|
||||||
// TODO: Do something with the arguments
|
// TODO: Do something with the arguments
|
||||||
|
|
||||||
Wrapper::<P>::new();
|
// FIXME: The configuration should be set based on the command line arguments
|
||||||
|
let config = WrapperConfig {
|
||||||
|
input_channels: 2,
|
||||||
|
output_channels: 2,
|
||||||
|
sample_rate: 44100.0,
|
||||||
|
period_size: 512,
|
||||||
|
|
||||||
|
tempo: 120.0,
|
||||||
|
timesig_num: 4,
|
||||||
|
timesig_denom: 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
Wrapper::<P>::new(config);
|
||||||
|
|
||||||
// TODO: Open the editor if available, do IO things
|
// TODO: Open the editor if available, do IO things
|
||||||
// TODO: If the plugin has an editor, block until the editor is closed. Otherwise block
|
// TODO: If the plugin has an editor, block until the editor is closed. Otherwise block
|
||||||
|
|
42
src/wrapper/standalone/context.rs
Normal file
42
src/wrapper/standalone/context.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
use super::wrapper::Wrapper;
|
||||||
|
use crate::context::{PluginApi, ProcessContext, Transport};
|
||||||
|
use crate::midi::NoteEvent;
|
||||||
|
use crate::plugin::Plugin;
|
||||||
|
|
||||||
|
/// A [`ProcessContext`] implementation for the standalone wrapper. This is a separate object so it
|
||||||
|
/// can hold on to lock guards for event queues. Otherwise reading these events would require
|
||||||
|
/// constant unnecessary atomic operations to lock the uncontested RwLocks.
|
||||||
|
pub(crate) struct WrapperProcessContext<'a, P: Plugin> {
|
||||||
|
pub(super) wrapper: &'a Wrapper<P>,
|
||||||
|
// TODO: Events
|
||||||
|
// pub(super) input_events_guard: AtomicRefMut<'a, VecDeque<NoteEvent>>,
|
||||||
|
// pub(super) output_events_guard: AtomicRefMut<'a, VecDeque<NoteEvent>>,
|
||||||
|
pub(super) transport: Transport,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: Plugin> ProcessContext for WrapperProcessContext<'_, P> {
|
||||||
|
fn plugin_api(&self) -> PluginApi {
|
||||||
|
PluginApi::Standalone
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transport(&self) -> &Transport {
|
||||||
|
&self.transport
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_event(&mut self) -> Option<NoteEvent> {
|
||||||
|
nih_debug_assert_failure!("TODO: WrapperProcessContext::next_event()");
|
||||||
|
|
||||||
|
// self.input_events_guard.pop_front()
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_event(&mut self, event: NoteEvent) {
|
||||||
|
nih_debug_assert_failure!("TODO: WrapperProcessContext::send_event()");
|
||||||
|
|
||||||
|
// self.output_events_guard.push_back(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_latency_samples(&self, samples: u32) {
|
||||||
|
nih_debug_assert_failure!("TODO: WrapperProcessContext::set_latency_samples()");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,89 @@
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::plugin::Plugin;
|
use super::context::WrapperProcessContext;
|
||||||
|
use crate::context::Transport;
|
||||||
|
use crate::plugin::{BufferConfig, BusConfig, Plugin};
|
||||||
|
|
||||||
|
/// Configuration for a standalone plugin that would normally be provided by the DAW.
|
||||||
|
pub struct WrapperConfig {
|
||||||
|
/// The number of input channels.
|
||||||
|
pub input_channels: u32,
|
||||||
|
/// The number of output channels.
|
||||||
|
pub output_channels: u32,
|
||||||
|
/// The audio backend's sample rate.
|
||||||
|
pub sample_rate: f32,
|
||||||
|
/// The audio backend's period size.
|
||||||
|
pub period_size: u32,
|
||||||
|
|
||||||
|
/// The current tempo.
|
||||||
|
pub tempo: f32,
|
||||||
|
/// The time signature's numerator.
|
||||||
|
pub timesig_num: u32,
|
||||||
|
/// The time signature's denominator.
|
||||||
|
pub timesig_denom: u32,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Wrapper<P: Plugin> {
|
pub struct Wrapper<P: Plugin> {
|
||||||
/// The wrapped plugin instance.
|
/// The wrapped plugin instance.
|
||||||
plugin: RwLock<P>,
|
plugin: RwLock<P>,
|
||||||
|
|
||||||
|
config: WrapperConfig,
|
||||||
|
|
||||||
|
/// The bus and buffer configurations are static for the standalone target.
|
||||||
|
bus_config: BusConfig,
|
||||||
|
buffer_config: BufferConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Errors that may arise while initializing the wrapped plugins.
|
||||||
|
pub enum WrapperError {
|
||||||
|
/// The plugin does not accept the IO configuration from the config.
|
||||||
|
IncompatibleConfig,
|
||||||
|
/// The plugin returned `false` during initialization.
|
||||||
|
InitializationFailed,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Plugin> Wrapper<P> {
|
impl<P: Plugin> Wrapper<P> {
|
||||||
/// Instantiate a new instance of the standalone wrapper.
|
/// Instantiate a new instance of the standalone wrapper. Returns an error if the plugin does
|
||||||
//
|
/// not accept the IO configuration from the wrapper config.
|
||||||
// TODO: This should take some arguments for the audio and MIDI IO.
|
pub fn new(config: WrapperConfig) -> Result<Arc<Self>, WrapperError> {
|
||||||
pub fn new() -> Self {
|
let wrapper = Arc::new(Wrapper {
|
||||||
Wrapper {
|
|
||||||
plugin: RwLock::new(P::default()),
|
plugin: RwLock::new(P::default()),
|
||||||
|
bus_config: BusConfig {
|
||||||
|
num_input_channels: config.input_channels,
|
||||||
|
num_output_channels: config.output_channels,
|
||||||
|
},
|
||||||
|
buffer_config: BufferConfig {
|
||||||
|
sample_rate: config.sample_rate,
|
||||||
|
max_buffer_size: config.period_size,
|
||||||
|
},
|
||||||
|
config,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Right now the IO configuration is fixed in the standalone target, so if the plugin cannot
|
||||||
|
// work with this then we cannot initialize the plugin at all.
|
||||||
|
{
|
||||||
|
let mut plugin = wrapper.plugin.write();
|
||||||
|
if !plugin.accepts_bus_config(&wrapper.bus_config) {
|
||||||
|
return Err(WrapperError::IncompatibleConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !plugin.initialize(
|
||||||
|
&wrapper.bus_config,
|
||||||
|
&wrapper.buffer_config,
|
||||||
|
&mut wrapper.make_process_context(Transport::new(wrapper.config.sample_rate)),
|
||||||
|
) {
|
||||||
|
return Err(WrapperError::InitializationFailed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(wrapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_process_context(&self, transport: Transport) -> WrapperProcessContext<'_, P> {
|
||||||
|
WrapperProcessContext {
|
||||||
|
wrapper: self,
|
||||||
|
transport,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue