Add stubs for editor handling
This commit is contained in:
parent
bbc190c67f
commit
7ac1df0d8d
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -100,6 +100,12 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cty"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gain"
|
name = "gain"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -154,6 +160,7 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"nih_plug_derive",
|
"nih_plug_derive",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
"raw-window-handle",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"vst3-sys",
|
"vst3-sys",
|
||||||
|
@ -209,6 +216,15 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "raw-window-handle"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fba75eee94a9d5273a68c9e1e105d9cffe1ef700532325788389e5a83e2522b7"
|
||||||
|
dependencies = [
|
||||||
|
"cty",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.10"
|
version = "0.2.10"
|
||||||
|
|
|
@ -17,10 +17,11 @@ members = [
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nih_plug_derive = { path = "nih_plug_derive" }
|
nih_plug_derive = { path = "nih_plug_derive" }
|
||||||
|
|
||||||
crossbeam = "0.8"
|
|
||||||
cfg-if = "1.0"
|
cfg-if = "1.0"
|
||||||
|
crossbeam = "0.8"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
|
raw-window-handle = "0.4"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
vst3-sys = { git = "https://github.com/robbert-vdh/vst3-sys.git", branch = "fix/atomic-reference-count" }
|
vst3-sys = { git = "https://github.com/robbert-vdh/vst3-sys.git", branch = "fix/atomic-reference-count" }
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
extern crate nih_plug;
|
extern crate nih_plug;
|
||||||
|
|
||||||
use nih_plug::{
|
use nih_plug::{
|
||||||
formatters, util, Buffer, BufferConfig, BusConfig, Plugin, ProcessContext, ProcessStatus,
|
formatters, util, Buffer, BufferConfig, BusConfig, NoEditor, Plugin, ProcessContext,
|
||||||
Vst3Plugin,
|
ProcessStatus, Vst3Plugin,
|
||||||
};
|
};
|
||||||
use nih_plug::{BoolParam, FloatParam, Param, Params, Range, Smoother, SmoothingStyle};
|
use nih_plug::{BoolParam, FloatParam, Param, Params, Range, Smoother, SmoothingStyle};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
@ -86,6 +86,8 @@ impl Default for GainParams {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Plugin for Gain {
|
impl Plugin for Gain {
|
||||||
|
type Editor = NoEditor;
|
||||||
|
|
||||||
const NAME: &'static str = "Gain";
|
const NAME: &'static str = "Gain";
|
||||||
const VENDOR: &'static str = "Moist Plugins GmbH";
|
const VENDOR: &'static str = "Moist Plugins GmbH";
|
||||||
const URL: &'static str = "https://youtu.be/dQw4w9WgXcQ";
|
const URL: &'static str = "https://youtu.be/dQw4w9WgXcQ";
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
extern crate nih_plug;
|
extern crate nih_plug;
|
||||||
|
|
||||||
use nih_plug::{
|
use nih_plug::{
|
||||||
formatters, util, Buffer, BufferConfig, BusConfig, Plugin, ProcessContext, ProcessStatus,
|
formatters, util, Buffer, BufferConfig, BusConfig, NoEditor, Plugin, ProcessContext,
|
||||||
Vst3Plugin,
|
ProcessStatus, Vst3Plugin,
|
||||||
};
|
};
|
||||||
use nih_plug::{BoolParam, FloatParam, Param, Params, Range, Smoother, SmoothingStyle};
|
use nih_plug::{BoolParam, FloatParam, Param, Params, Range, Smoother, SmoothingStyle};
|
||||||
use std::f32::consts;
|
use std::f32::consts;
|
||||||
|
@ -34,6 +34,8 @@ struct Sine {
|
||||||
/// The current phase of the sine wave, always kept between in `[0, 1]`.
|
/// The current phase of the sine wave, always kept between in `[0, 1]`.
|
||||||
phase: f32,
|
phase: f32,
|
||||||
|
|
||||||
|
samples: f32,
|
||||||
|
|
||||||
/// The frequency if the active note, if triggered by MIDI.
|
/// The frequency if the active note, if triggered by MIDI.
|
||||||
midi_note_freq: f32,
|
midi_note_freq: f32,
|
||||||
/// A simple attack and release envelope to avoid clicks.
|
/// A simple attack and release envelope to avoid clicks.
|
||||||
|
@ -62,6 +64,7 @@ impl Default for Sine {
|
||||||
sample_rate: 1.0,
|
sample_rate: 1.0,
|
||||||
|
|
||||||
phase: 0.0,
|
phase: 0.0,
|
||||||
|
samples: 0.0,
|
||||||
midi_note_freq: 1.0,
|
midi_note_freq: 1.0,
|
||||||
midi_note_gain: Smoother::new(SmoothingStyle::Linear(5.0)),
|
midi_note_gain: Smoother::new(SmoothingStyle::Linear(5.0)),
|
||||||
}
|
}
|
||||||
|
@ -120,6 +123,8 @@ impl Sine {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Plugin for Sine {
|
impl Plugin for Sine {
|
||||||
|
type Editor = NoEditor;
|
||||||
|
|
||||||
const NAME: &'static str = "Sine Test Tone";
|
const NAME: &'static str = "Sine Test Tone";
|
||||||
const VENDOR: &'static str = "Moist Plugins GmbH";
|
const VENDOR: &'static str = "Moist Plugins GmbH";
|
||||||
const URL: &'static str = "https://youtu.be/dQw4w9WgXcQ";
|
const URL: &'static str = "https://youtu.be/dQw4w9WgXcQ";
|
||||||
|
@ -158,6 +163,13 @@ impl Plugin for Sine {
|
||||||
// Smoothing is optionally built into the parameters themselves
|
// Smoothing is optionally built into the parameters themselves
|
||||||
let gain = self.params.gain.smoothed.next();
|
let gain = self.params.gain.smoothed.next();
|
||||||
|
|
||||||
|
let offset =
|
||||||
|
((self.samples / 100.0) + 1.0).log2() / (100.0f32 * 500.0 + 1.0).log2() * 500.0;
|
||||||
|
self.samples += 1.0;
|
||||||
|
if self.samples / 100.0 > 500.0 {
|
||||||
|
self.samples = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
// This plugin can be either triggered by MIDI or controleld by a parameter
|
// This plugin can be either triggered by MIDI or controleld by a parameter
|
||||||
let sine = if self.params.use_midi.value {
|
let sine = if self.params.use_midi.value {
|
||||||
// Act on the next MIDI event
|
// Act on the next MIDI event
|
||||||
|
@ -184,7 +196,7 @@ impl Plugin for Sine {
|
||||||
self.calculate_sine(self.midi_note_freq) * self.midi_note_gain.next()
|
self.calculate_sine(self.midi_note_freq) * self.midi_note_gain.next()
|
||||||
} else {
|
} else {
|
||||||
let frequency = self.params.frequency.smoothed.next();
|
let frequency = self.params.frequency.smoothed.next();
|
||||||
self.calculate_sine(frequency)
|
self.calculate_sine(frequency + offset)
|
||||||
};
|
};
|
||||||
|
|
||||||
for sample in samples {
|
for sample in samples {
|
||||||
|
|
|
@ -24,15 +24,15 @@ mod linux;
|
||||||
#[cfg(all(target_family = "unix", not(target_os = "macos")))]
|
#[cfg(all(target_family = "unix", not(target_os = "macos")))]
|
||||||
pub(crate) use linux::LinuxEventLoop as OsEventLoop;
|
pub(crate) use linux::LinuxEventLoop as OsEventLoop;
|
||||||
|
|
||||||
|
use crate::param::Param;
|
||||||
use crate::plugin::NoteEvent;
|
use crate::plugin::NoteEvent;
|
||||||
|
|
||||||
pub(crate) const TASK_QUEUE_CAPACITY: usize = 512;
|
pub(crate) const TASK_QUEUE_CAPACITY: usize = 512;
|
||||||
|
|
||||||
// TODO: ProcessContext for parameter automation and sending events
|
// TODO: ProcessContext for parameter automation and sending events
|
||||||
// TODO: GuiContext for GUI parameter automation and resizing
|
|
||||||
|
|
||||||
/// General callbacks the plugin can make during its lifetime. This is passed to the plugin during
|
/// General callbacks the plugin can make during its lifetime. This is passed to the plugin during
|
||||||
/// [crate::plugin::Plugin::initialize].
|
/// [crate::plugin::Plugin::initialize] and as part of [crate::plugin::Plugin::process].
|
||||||
//
|
//
|
||||||
// # Safety
|
// # Safety
|
||||||
//
|
//
|
||||||
|
@ -50,10 +50,37 @@ pub trait ProcessContext {
|
||||||
/// here)
|
/// here)
|
||||||
fn next_midi_event(&mut self) -> Option<NoteEvent>;
|
fn next_midi_event(&mut self) -> Option<NoteEvent>;
|
||||||
|
|
||||||
// // TODO: Add this next
|
// TODO: Add this, this works similar to [GuiContext::set_parameter] but it adds the parameter
|
||||||
// fn set_parameter<P>(&self, param: &P, value: P::Plain)
|
// change to a queue (or directly to the VST3 plugin's parameter output queues) instead of
|
||||||
// where
|
// using main thread host automation (and all the locks involved there).
|
||||||
// P: Param;
|
// fn set_parameter<P: Param>(&self, param: &P, value: P::Plain);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Callbacks the plugin can make while handling its GUI, such as updating parameter values. This is
|
||||||
|
/// passed to the plugin during [crate::plugin::Plugin::create_editor].
|
||||||
|
//
|
||||||
|
// # Safety
|
||||||
|
//
|
||||||
|
// The implementing wrapper needs to be able to handle concurrent requests, and it should perform
|
||||||
|
// the actual callback within [MainThreadQueue::do_maybe_async].
|
||||||
|
pub trait GuiContext {
|
||||||
|
/// Inform the host that you will start automating a parmater. This needs to be called before
|
||||||
|
/// calling [set_parameter] for the specified parameter.
|
||||||
|
fn begin_set_parameter<P: Param>(&self, param: &P);
|
||||||
|
|
||||||
|
/// Set a parameter to the specified parameter value. You will need to call
|
||||||
|
/// [begin_set_parameter] before and [end_set_parameter] after calling this so the host can
|
||||||
|
/// properly record automation for the parameter. This can be called multiple times in a row
|
||||||
|
/// before calling [end_set_parameter], for instance when moving a slider around.
|
||||||
|
///
|
||||||
|
/// This function assumes you're already calling this from a GUI thread. Calling any of these
|
||||||
|
/// functions from any other thread may result in unexpected behavior.
|
||||||
|
fn set_parameter<P: Param>(&self, param: &P, value: P::Plain);
|
||||||
|
|
||||||
|
/// Inform the host that you are done automating a parameter. This needs to be called after one
|
||||||
|
/// or more [set_parameter] calls for a parameter so the host knows the automation gesture has
|
||||||
|
/// finished.
|
||||||
|
fn end_set_parameter<P: Param>(&self, param: &P);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait describing the functionality of the platform-specific event loop that can execute tasks
|
/// A trait describing the functionality of the platform-specific event loop that can execute tasks
|
||||||
|
|
|
@ -32,7 +32,9 @@ pub use param::internals::Params;
|
||||||
pub use param::range::Range;
|
pub use param::range::Range;
|
||||||
pub use param::smoothing::{Smoother, SmoothingStyle};
|
pub use param::smoothing::{Smoother, SmoothingStyle};
|
||||||
pub use param::{BoolParam, FloatParam, IntParam, Param};
|
pub use param::{BoolParam, FloatParam, IntParam, Param};
|
||||||
pub use plugin::{BufferConfig, BusConfig, NoteEvent, Plugin, ProcessStatus, Vst3Plugin};
|
pub use plugin::{
|
||||||
|
BufferConfig, BusConfig, Editor, NoEditor, NoteEvent, Plugin, ProcessStatus, Vst3Plugin,
|
||||||
|
};
|
||||||
|
|
||||||
// The rest is either internal or already re-exported
|
// The rest is either internal or already re-exported
|
||||||
mod buffer;
|
mod buffer;
|
||||||
|
|
|
@ -14,10 +14,11 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use raw_window_handle::RawWindowHandle;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use crate::buffer::Buffer;
|
use crate::buffer::Buffer;
|
||||||
use crate::context::ProcessContext;
|
use crate::context::{GuiContext, ProcessContext};
|
||||||
use crate::param::internals::Params;
|
use crate::param::internals::Params;
|
||||||
|
|
||||||
/// Basic functionality that needs to be implemented by a plugin. The wrappers will use this to
|
/// Basic functionality that needs to be implemented by a plugin. The wrappers will use this to
|
||||||
|
@ -39,6 +40,11 @@ use crate::param::internals::Params;
|
||||||
/// - Outputting MIDI events
|
/// - Outputting MIDI events
|
||||||
/// - GUIs
|
/// - GUIs
|
||||||
pub trait Plugin: Default + Send + Sync {
|
pub trait Plugin: Default + Send + Sync {
|
||||||
|
/// The type of the GUI editor instance belonging to this plugin. Use [NoEditor] when you don't
|
||||||
|
/// need an editor. Make sure to implement both the [create_editor] and [editor_size] functions
|
||||||
|
/// when you do add an editor.
|
||||||
|
type Editor: Editor;
|
||||||
|
|
||||||
const NAME: &'static str;
|
const NAME: &'static str;
|
||||||
const VENDOR: &'static str;
|
const VENDOR: &'static str;
|
||||||
const URL: &'static str;
|
const URL: &'static str;
|
||||||
|
@ -64,6 +70,31 @@ pub trait Plugin: Default + Send + Sync {
|
||||||
/// plugin receives an update.
|
/// plugin receives an update.
|
||||||
fn params(&self) -> Pin<&dyn Params>;
|
fn params(&self) -> Pin<&dyn Params>;
|
||||||
|
|
||||||
|
/// Create an editor for this plugin and embed it in the parent window. The idea is that you
|
||||||
|
/// take a reference to your [Params] in your editor to be able to read the current values. Then
|
||||||
|
/// whenever you need to change any of those values, you can use the methods on the [GuiContext]
|
||||||
|
/// that's passed to this function. When you change a parameter value there it will be
|
||||||
|
/// broadcasted to the host and also updated in your [Params] struct.
|
||||||
|
//
|
||||||
|
// TODO: Think of how this would work with the event loop. On Linux the wrapper must provide a
|
||||||
|
// timer using VST3's `IRunLoop` interface, but on Window and macOS the window would
|
||||||
|
// normally register its own timer. Right now we just ignore this because it would
|
||||||
|
// otherwise be basically impossible to have this still be GUI-framework agnostic. Any
|
||||||
|
// callback that deos involve actual GUI operations will still be spooled to the IRunLoop
|
||||||
|
// instance.
|
||||||
|
fn create_editor(
|
||||||
|
&self,
|
||||||
|
_parent: RawWindowHandle,
|
||||||
|
_context: &impl GuiContext,
|
||||||
|
) -> Option<Self::Editor> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the current size of the plugin's editor, if it has one.
|
||||||
|
fn editor_size(&self) -> Option<(u32, u32)> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// The following functions follow the lifetime of the plugin.
|
// The following functions follow the lifetime of the plugin.
|
||||||
//
|
//
|
||||||
|
@ -117,6 +148,29 @@ pub trait Vst3Plugin: Plugin {
|
||||||
const VST3_CATEGORIES: &'static str;
|
const VST3_CATEGORIES: &'static str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An editor for a [Plugin]. If you don't have or need an editor, then you can use the [NoEditor]
|
||||||
|
/// struct as a placeholder.
|
||||||
|
pub trait Editor {
|
||||||
|
/// Called just before the window gets closed.
|
||||||
|
fn close(&self);
|
||||||
|
|
||||||
|
/// Return the (currnent) size of the editor in pixels as a `(width, height)` pair.
|
||||||
|
fn size(&self) -> (u32, u32);
|
||||||
|
|
||||||
|
// TODO: Add the things needed for DPI scaling
|
||||||
|
// TODO: Resizing
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NoEditor;
|
||||||
|
|
||||||
|
impl Editor for NoEditor {
|
||||||
|
fn close(&self) {}
|
||||||
|
|
||||||
|
fn size(&self) -> (u32, u32) {
|
||||||
|
(0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// We only support a single main input and output bus at the moment.
|
/// We only support a single main input and output bus at the moment.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct BusConfig {
|
pub struct BusConfig {
|
||||||
|
|
Loading…
Reference in a new issue