Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::fmt::Display;
pub mod gui;
pub mod init;
pub mod process;
pub mod track_info;

// Contexts for more plugin-API specific features
pub mod remote_controls;
Expand Down
6 changes: 6 additions & 0 deletions src/context/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use std::sync::Arc;

use super::track_info::TrackInfo;
use super::PluginApi;
use crate::prelude::{Param, ParamPtr, Plugin, PluginState};

Expand Down Expand Up @@ -63,6 +64,11 @@ pub trait GuiContext: Send + Sync + 'static {
/// host. If the plugin is currently processing audio, then the parameter values will be
/// restored at the end of the current processing cycle.
fn set_state(&self, state: PluginState);

/// Get information about the track the plugin is on. Not all hosts support this, and the
/// information may not be available until the host provides it. Returns `None` if the host
/// does not provide track information.
fn track_info(&self) -> Option<TrackInfo>;
}

/// An way to run background tasks from the plugin's GUI, equivalent to the
Expand Down
6 changes: 6 additions & 0 deletions src/context/init.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! A context passed during plugin initialization.

use super::track_info::TrackInfo;
use super::PluginApi;
use crate::prelude::Plugin;

Expand Down Expand Up @@ -34,4 +35,9 @@ pub trait InitContext<P: Plugin> {
/// 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);

/// Get information about the track the plugin is on. Not all hosts support this, and the
/// information may not be available until the host provides it. Returns `None` if the host
/// does not provide track information.
fn track_info(&self) -> Option<TrackInfo>;
}
6 changes: 6 additions & 0 deletions src/context/process.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! A context passed during the process function.

use super::track_info::TrackInfo;
use super::PluginApi;
use crate::prelude::{Plugin, PluginNoteEvent};

Expand Down Expand Up @@ -40,6 +41,11 @@ pub trait ProcessContext<P: Plugin> {
/// Get information about the current transport position and status.
fn transport(&self) -> &Transport;

/// Get information about the track the plugin is on. Not all hosts support this, and the
/// information may not be available until the host provides it. Returns `None` if the host
/// does not provide track information.
fn track_info(&self) -> Option<TrackInfo>;

/// Returns the next note event, if there is one. Use
/// [`NoteEvent::timing()`][crate::prelude::NoteEvent::timing()] to get the event's timing
/// within the buffer. Only available when
Expand Down
43 changes: 43 additions & 0 deletions src/context/track_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//! Information about the track/channel the plugin is inserted on.

/// Information about the track the plugin is on. Not all hosts and plugin APIs support all fields,
/// so most of them are optional.
///
/// This is queried from the host and may change at any time. It can be accessed from
/// [`InitContext`][super::init::InitContext],
/// [`ProcessContext`][super::process::ProcessContext], and
/// [`GuiContext`][super::gui::GuiContext].
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct TrackInfo {
/// The name of the track, if available.
pub name: Option<String>,
/// The color assigned to the track, if available.
pub color: Option<TrackColor>,
/// The number of audio channels on the track, if available.
pub audio_channel_count: Option<u32>,
/// The type of track the plugin is on.
pub track_type: TrackType,
}

/// An RGBA color value.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TrackColor {
pub red: u8,
pub green: u8,
pub blue: u8,
pub alpha: u8,
}

/// The type of track the plugin is inserted on.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum TrackType {
/// A regular track.
#[default]
Regular,
/// A return/FX track.
Return,
/// A bus/group track.
Bus,
/// The master/main output track.
Master,
}
1 change: 1 addition & 0 deletions src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub use crate::context::process::{ProcessContext, Transport};
pub use crate::context::remote_controls::{
RemoteControlsContext, RemoteControlsPage, RemoteControlsSection,
};
pub use crate::context::track_info::{TrackColor, TrackInfo, TrackType};
pub use crate::context::PluginApi;
// This also includes the derive macro
pub use crate::editor::{Editor, ParentWindowHandle};
Expand Down
17 changes: 14 additions & 3 deletions src/wrapper/clap/context.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use atomic_refcell::AtomicRefMut;
use clap_sys::ext::remote_controls::{
clap_remote_controls_page, CLAP_REMOTE_CONTROLS_COUNT,
};
use clap_sys::ext::remote_controls::{clap_remote_controls_page, CLAP_REMOTE_CONTROLS_COUNT};
use clap_sys::id::{clap_id, CLAP_INVALID_ID};
use clap_sys::string_sizes::CLAP_NAME_SIZE;
use std::cell::Cell;
use std::collections::{HashMap, VecDeque};
use std::sync::Arc;

use super::wrapper::{OutputParamEvent, Task, Wrapper};
use crate::context::track_info::TrackInfo;
use crate::event_loop::EventLoop;
use crate::prelude::{
ClapPlugin, GuiContext, InitContext, ParamPtr, PluginApi, PluginNoteEvent, ProcessContext,
Expand Down Expand Up @@ -90,6 +89,10 @@ impl<P: ClapPlugin> InitContext<P> for WrapperInitContext<'_, P> {
fn set_current_voice_capacity(&self, capacity: u32) {
self.wrapper.set_current_voice_capacity(capacity)
}

fn track_info(&self) -> Option<TrackInfo> {
self.wrapper.get_track_info()
}
}

impl<P: ClapPlugin> ProcessContext<P> for WrapperProcessContext<'_, P> {
Expand All @@ -112,6 +115,10 @@ impl<P: ClapPlugin> ProcessContext<P> for WrapperProcessContext<'_, P> {
&self.transport
}

fn track_info(&self) -> Option<TrackInfo> {
self.wrapper.get_track_info()
}

fn next_event(&mut self) -> Option<PluginNoteEvent<P>> {
self.input_events_guard.pop_front()
}
Expand Down Expand Up @@ -240,6 +247,10 @@ impl<P: ClapPlugin> GuiContext for WrapperGuiContext<P> {
fn set_state(&self, state: crate::wrapper::state::PluginState) {
self.wrapper.set_state_object_from_gui(state)
}

fn track_info(&self) -> Option<TrackInfo> {
self.wrapper.get_track_info()
}
}

/// A remote control section. The plugin can fill this with information for one or more pages.
Expand Down
113 changes: 109 additions & 4 deletions src/wrapper/clap/wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ use clap_sys::ext::audio_ports::{
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::remote_controls::{
clap_plugin_remote_controls, clap_remote_controls_page, CLAP_EXT_REMOTE_CONTROLS,
};
use clap_sys::ext::gui::{
clap_gui_resize_hints, clap_host_gui, clap_plugin_gui, clap_window, CLAP_EXT_GUI,
CLAP_WINDOW_API_COCOA, CLAP_WINDOW_API_WIN32, CLAP_WINDOW_API_X11,
Expand All @@ -40,13 +37,22 @@ use clap_sys::ext::params::{
CLAP_PARAM_IS_MODULATABLE, CLAP_PARAM_IS_MODULATABLE_PER_NOTE_ID, CLAP_PARAM_IS_READONLY,
CLAP_PARAM_IS_STEPPED, CLAP_PARAM_RESCAN_VALUES,
};
use clap_sys::ext::remote_controls::{
clap_plugin_remote_controls, clap_remote_controls_page, CLAP_EXT_REMOTE_CONTROLS,
};
use clap_sys::ext::render::{
clap_plugin_render, clap_plugin_render_mode, CLAP_EXT_RENDER, CLAP_RENDER_OFFLINE,
CLAP_RENDER_REALTIME,
};
use clap_sys::ext::state::{clap_plugin_state, CLAP_EXT_STATE};
use clap_sys::ext::tail::{clap_plugin_tail, CLAP_EXT_TAIL};
use clap_sys::ext::thread_check::{clap_host_thread_check, CLAP_EXT_THREAD_CHECK};
use clap_sys::ext::track_info::{
clap_host_track_info, clap_plugin_track_info, CLAP_EXT_TRACK_INFO, CLAP_EXT_TRACK_INFO_COMPAT,
CLAP_TRACK_INFO_HAS_AUDIO_CHANNEL, CLAP_TRACK_INFO_HAS_TRACK_COLOR,
CLAP_TRACK_INFO_HAS_TRACK_NAME, CLAP_TRACK_INFO_IS_FOR_BUS, CLAP_TRACK_INFO_IS_FOR_MASTER,
CLAP_TRACK_INFO_IS_FOR_RETURN_TRACK,
};
use clap_sys::ext::voice_info::{
clap_host_voice_info, clap_plugin_voice_info, clap_voice_info, CLAP_EXT_VOICE_INFO,
CLAP_VOICE_INFO_SUPPORTS_OVERLAPPING_NOTES,
Expand All @@ -63,7 +69,7 @@ use clap_sys::stream::{clap_istream, clap_ostream};
use crossbeam::atomic::AtomicCell;
use crossbeam::channel::{self, SendTimeoutError};
use crossbeam::queue::ArrayQueue;
use parking_lot::Mutex;
use parking_lot::{Mutex, RwLock};
use std::any::Any;
use std::borrow::Borrow;
use std::collections::{HashMap, HashSet, VecDeque};
Expand All @@ -80,6 +86,7 @@ use std::time::Duration;
use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext};
use super::descriptor::PluginDescriptor;
use super::util::ClapPtr;
use crate::context::track_info::{TrackColor, TrackInfo, TrackType};
use crate::event_loop::{BackgroundThread, EventLoop, MainThreadExecutor, TASK_QUEUE_CAPACITY};
use crate::midi::MidiResult;
use crate::prelude::{
Expand Down Expand Up @@ -242,6 +249,11 @@ pub struct Wrapper<P: ClapPlugin> {
/// context. This defaults to the maximum number of voices.
current_voice_capacity: AtomicU32,

clap_plugin_track_info: clap_plugin_track_info,
host_track_info: AtomicRefCell<Option<ClapPtr<clap_host_track_info>>>,
/// The current track information, as reported by the host through the track-info extension.
track_info: RwLock<Option<TrackInfo>>,

/// 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
/// implementations. Instead, we'll post tasks to this queue, ask the host to call
Expand Down Expand Up @@ -681,6 +693,12 @@ impl<P: ClapPlugin> Wrapper<P> {
.unwrap_or(1),
),

clap_plugin_track_info: clap_plugin_track_info {
changed: Some(Self::ext_track_info_changed),
},
host_track_info: AtomicRefCell::new(None),
track_info: RwLock::new(None),

tasks: ArrayQueue::new(TASK_QUEUE_CAPACITY),
main_thread_id: thread::current().id(),
// Initialized later as it needs a reference to the wrapper for the executor
Expand Down Expand Up @@ -1855,6 +1873,18 @@ impl<P: ClapPlugin> Wrapper<P> {
&wrapper.host_callback,
CLAP_EXT_THREAD_CHECK,
);
*wrapper.host_track_info.borrow_mut() = query_host_extension::<clap_host_track_info>(
&wrapper.host_callback,
CLAP_EXT_TRACK_INFO,
)
.or_else(|| {
query_host_extension::<clap_host_track_info>(
&wrapper.host_callback,
CLAP_EXT_TRACK_INFO_COMPAT,
)
});
// Fetch initial track info if available
wrapper.update_track_info();

true
}
Expand Down Expand Up @@ -2337,6 +2367,8 @@ impl<P: ClapPlugin> Wrapper<P> {
&wrapper.clap_plugin_tail as *const _ as *const c_void
} else if id == CLAP_EXT_VOICE_INFO && P::CLAP_POLY_MODULATION_CONFIG.is_some() {
&wrapper.clap_plugin_voice_info as *const _ as *const c_void
} else if id == CLAP_EXT_TRACK_INFO || id == CLAP_EXT_TRACK_INFO_COMPAT {
&wrapper.clap_plugin_track_info as *const _ as *const c_void
} else {
nih_trace!("Host tried to query unknown extension {:?}", id);
std::ptr::null()
Expand Down Expand Up @@ -3210,6 +3242,79 @@ impl<P: ClapPlugin> Wrapper<P> {
None => false,
}
}

unsafe extern "C" fn ext_track_info_changed(plugin: *const clap_plugin) {
check_null_ptr!((), plugin, (*plugin).plugin_data);
let wrapper = &*((*plugin).plugin_data as *const Self);

wrapper.update_track_info();
}

/// Query the host for the current track information and store it.
fn update_track_info(&self) {
let host_track_info = self.host_track_info.borrow();
let host_track_info = match host_track_info.as_ref() {
Some(h) => h,
None => return,
};

let mut info = std::mem::MaybeUninit::uninit();
let success = unsafe {
clap_call! { host_track_info=>get(&*self.host_callback, info.as_mut_ptr()) }
};

if success {
let info = unsafe { info.assume_init() };

let name = if info.flags & CLAP_TRACK_INFO_HAS_TRACK_NAME != 0 {
let name_cstr = unsafe { CStr::from_ptr(info.name.as_ptr()) };
name_cstr.to_str().ok().map(|s| s.to_string())
} else {
None
};

let color = if info.flags & CLAP_TRACK_INFO_HAS_TRACK_COLOR != 0 {
Some(TrackColor {
red: info.color.red,
green: info.color.green,
blue: info.color.blue,
alpha: info.color.alpha,
})
} else {
None
};

let audio_channel_count = if info.flags & CLAP_TRACK_INFO_HAS_AUDIO_CHANNEL != 0 {
u32::try_from(info.audio_channel_count).ok()
} else {
None
};

let track_type = if info.flags & CLAP_TRACK_INFO_IS_FOR_MASTER != 0 {
TrackType::Master
} else if info.flags & CLAP_TRACK_INFO_IS_FOR_RETURN_TRACK != 0 {
TrackType::Return
} else if info.flags & CLAP_TRACK_INFO_IS_FOR_BUS != 0 {
TrackType::Bus
} else {
TrackType::Regular
};

*self.track_info.write() = Some(TrackInfo {
name,
color,
audio_channel_count,
track_type,
});
} else {
*self.track_info.write() = None;
}
}

/// Get the current track info, if available.
pub fn get_track_info(&self) -> Option<TrackInfo> {
self.track_info.read().clone()
}
}

/// Convenience function to query an extension from the host.
Expand Down
13 changes: 13 additions & 0 deletions src/wrapper/standalone/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::sync::Arc;

use super::backend::Backend;
use super::wrapper::{Task, Wrapper};
use crate::context::track_info::TrackInfo;
use crate::prelude::{
GuiContext, InitContext, ParamPtr, Plugin, PluginApi, PluginNoteEvent, ProcessContext,
Transport,
Expand Down Expand Up @@ -52,6 +53,10 @@ impl<P: Plugin, B: Backend<P>> InitContext<P> for WrapperInitContext<'_, P, B> {
fn set_current_voice_capacity(&self, _capacity: u32) {
// This is only supported by CLAP
}

fn track_info(&self) -> Option<TrackInfo> {
None
}
}

impl<P: Plugin, B: Backend<P>> ProcessContext<P> for WrapperProcessContext<'_, P, B> {
Expand Down Expand Up @@ -97,6 +102,10 @@ impl<P: Plugin, B: Backend<P>> ProcessContext<P> for WrapperProcessContext<'_, P
fn set_current_voice_capacity(&self, _capacity: u32) {
// This is only supported by CLAP
}

fn track_info(&self) -> Option<TrackInfo> {
None
}
}

impl<P: Plugin, B: Backend<P>> GuiContext for WrapperGuiContext<P, B> {
Expand Down Expand Up @@ -159,4 +168,8 @@ impl<P: Plugin, B: Backend<P>> GuiContext for WrapperGuiContext<P, B> {
fn set_state(&self, state: crate::wrapper::state::PluginState) {
self.wrapper.set_state_object_from_gui(state)
}

fn track_info(&self) -> Option<TrackInfo> {
None
}
}
Loading