summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio.rs37
-rw-r--r--src/gui.rs92
-rw-r--r--src/lib.rs8
-rw-r--r--src/params.rs20
4 files changed, 107 insertions, 50 deletions
diff --git a/src/audio.rs b/src/audio.rs
index 6ad1a86..4aaac9f 100644
--- a/src/audio.rs
+++ b/src/audio.rs
@@ -45,8 +45,8 @@ impl<'a> PluginAudioProcessor<'a, RetainPluginShared<'a>, RetainPluginMainThread
) -> Result<Self, PluginError> {
let params = Arc::clone(&shared.params);
- let fft_left = WindowedRealFft::new(params.get_window_size().into());
- let fft_right = WindowedRealFft::new(params.get_window_size().into());
+ let fft_left = WindowedRealFft::new(params.get_window_size());
+ let fft_right = WindowedRealFft::new(params.get_window_size());
// This is where we would allocate intermediate buffers and such if we needed them.
Ok(Self {
@@ -66,8 +66,7 @@ impl<'a> PluginAudioProcessor<'a, RetainPluginShared<'a>, RetainPluginMainThread
mut audio: Audio,
events: Events,
) -> Result<ProcessStatus, PluginError> {
- // First, we have to make a few sanity checks.
- // We want at least a single input/output port pair, which contains channels of `f32`
+ // Ensure at least a single input/output port pair, which contains channels of `f32`
// audio sample data.
let mut port_pair = audio
.port_pair(0)
@@ -78,11 +77,11 @@ impl<'a> PluginAudioProcessor<'a, RetainPluginShared<'a>, RetainPluginMainThread
.into_f32()
.ok_or(PluginError::Message("Expected f32 input/output"))?;
- let mut channel_buffers = [None, None];
-
// Extract the buffer slices that we need, while making sure they are paired correctly and
// check for either in-place or separate buffers.
- for (pair, buf) in output_channels.iter_mut().zip(&mut channel_buffers) {
+ let mut output_buffers = [None, None];
+
+ for (pair, buf) in output_channels.iter_mut().zip(&mut output_buffers) {
*buf = match pair {
ChannelPair::InputOnly(_) | ChannelPair::OutputOnly(_) => None,
ChannelPair::InPlace(b) => Some(b),
@@ -101,12 +100,16 @@ impl<'a> PluginAudioProcessor<'a, RetainPluginShared<'a>, RetainPluginMainThread
self.fft_right.window_size(window_size.clone());
if let Some(latency) = self.shared.host.get_extension::<HostLatency>() {
- // should be safe
+ // SAFETY: should be safe as self.shared.host is a shared
+ // version of the main thread handle
+ // no reason to propagate it through all these layers
let mut main = unsafe { self.shared.host.as_main_thread_unchecked() };
latency.changed(&mut main);
// self.shared.host.request_restart();
}
+
+ self.prev_window_size = Some(window_size);
}
// update change in window type if needed
@@ -114,23 +117,26 @@ impl<'a> PluginAudioProcessor<'a, RetainPluginShared<'a>, RetainPluginMainThread
if self.prev_window_type != Some(window_type.clone()) {
self.fft_left.window_function(&window_type);
self.fft_right.window_function(&window_type);
+ self.prev_window_type = Some(window_type);
}
- // Now let's process the audio, while splitting the processing in batches between each
- // sample-accurate event.
+ // process the audio
for event_batch in events.input.batch() {
- // Process all param events in this batch
for event in event_batch.events() {
self.params.handle_event(event);
}
// Get the parameters after all changes have been handled.
let order = self.params.get_order();
+
+ // this parameter is read-only and can only be changed in the UI
let complement = self.params.get_complement();
// process samples in place here
- if let [Some(left), Some(right)] = &mut channel_buffers {
- for sample in left.iter_mut() {
+ if let [Some(left), Some(right)] = &mut output_buffers {
+ let range = event_batch.sample_bounds();
+
+ for sample in &mut left[range] {
if self.fft_left.push_back_input(*sample) {
self.fft_left.forward();
@@ -143,7 +149,7 @@ impl<'a> PluginAudioProcessor<'a, RetainPluginShared<'a>, RetainPluginMainThread
*sample = self.fft_left.pop_front_output();
}
- for sample in right.iter_mut() {
+ for sample in &mut right[range] {
if self.fft_right.push_back_input(*sample) {
self.fft_right.forward();
@@ -158,9 +164,6 @@ impl<'a> PluginAudioProcessor<'a, RetainPluginShared<'a>, RetainPluginMainThread
}
}
- self.prev_window_size = Some(window_size);
- self.prev_window_type = Some(window_type);
-
// Publish any parameter changes we may have received back to the GUI.
// Request the on-main-thread callback, which we use to refresh the UI if it is open
self.host.request_callback();
diff --git a/src/gui.rs b/src/gui.rs
index 5780f50..2c70798 100644
--- a/src/gui.rs
+++ b/src/gui.rs
@@ -4,26 +4,75 @@ use crate::{
RetainPluginMainThread, RetainPluginShared, params::RetainParams, window_size::WindowSize,
window_type::WindowType,
};
+use atomic_float::AtomicF32;
use baseview::{Size, WindowHandle, WindowOpenOptions, WindowScalePolicy, gl::GlConfig};
use clack_extensions::gui::{GuiApiType, GuiConfiguration, GuiSize, PluginGuiImpl, Window};
use clack_plugin::prelude::*;
use egui_baseview::{
EguiWindow, GraphicsConfig, Queue,
- egui::{self, Align, ComboBox, Context, Layout, Slider},
+ egui::{Align, CentralPanel, ComboBox, Context, Layout, Slider, Vec2, ViewportCommand},
};
-use std::sync::Arc;
+use std::sync::{Arc, atomic::Ordering};
+
+const DEFAULT_SIZE_X: f32 = 600.0;
+const DEFAULT_SIZE_Y: f32 = 350.0;
+
+pub struct AtomicVec2 {
+ x: AtomicF32,
+ y: AtomicF32,
+}
+
+impl Default for AtomicVec2 {
+ fn default() -> Self {
+ Self {
+ x: AtomicF32::new(DEFAULT_SIZE_X),
+ y: AtomicF32::new(DEFAULT_SIZE_Y),
+ }
+ }
+}
+
+impl AtomicVec2 {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn get_x(&self) -> f32 {
+ self.x.load(Ordering::SeqCst)
+ }
+
+ pub fn set_x(&self, x: f32) {
+ self.x.store(x, Ordering::SeqCst);
+ }
+
+ pub fn get_y(&self) -> f32 {
+ self.y.load(Ordering::SeqCst)
+ }
+
+ pub fn set_y(&self, x: f32) {
+ self.y.store(x, Ordering::SeqCst);
+ }
+
+ pub fn as_vec2(&self) -> Vec2 {
+ Vec2 {
+ x: self.get_x(),
+ y: self.get_y(),
+ }
+ }
+}
/// The EGUI application state
struct AppState {
/// A handle to the shared params state
params: Arc<RetainParams>,
+ gui_size: Arc<AtomicVec2>,
}
impl AppState {
/// Initializes a new [`AppState`] from the given shared params state handle
- pub fn new(params: &Arc<RetainParams>) -> Self {
+ pub fn new(params: &Arc<RetainParams>, gui_size: &Arc<AtomicVec2>) -> Self {
Self {
params: Arc::clone(params),
+ gui_size: Arc::clone(gui_size),
}
}
}
@@ -41,7 +90,10 @@ impl RetainPluginGui {
pub fn new(parent: Window<'_>, plugin_state: &RetainPluginShared) -> Self {
let settings = WindowOpenOptions {
title: "Retain".to_string(),
- size: Size::new(600.0, 350.0),
+ size: Size::new(
+ plugin_state.gui_size.get_x().into(),
+ plugin_state.gui_size.get_y().into(),
+ ),
scale: WindowScalePolicy::SystemScaleFactor,
gl_config: Some(GlConfig::default()),
};
@@ -52,15 +104,15 @@ impl RetainPluginGui {
&parent,
settings,
GraphicsConfig::default(),
- AppState::new(&plugin_state.params),
- move |egui_ctx: &Context, _queue: &mut Queue, _state: &mut AppState| {
- tx.send(egui_ctx.clone()).unwrap();
+ AppState::new(&plugin_state.params, &plugin_state.gui_size),
+ move |ctx: &Context, _queue: &mut Queue, _state: &mut AppState| {
+ tx.send(ctx.clone()).unwrap();
},
- |egui_ctx: &Context, _queue: &mut Queue, state: &mut AppState| {
- egui::CentralPanel::default().show(egui_ctx, |ui| {
- ui.with_layout(Layout::top_down(Align::Center), |ui| {
- ui.heading("Retain");
- });
+ |ctx: &Context, _queue: &mut Queue, state: &mut AppState| {
+ ctx.send_viewport_cmd(ViewportCommand::InnerSize(state.gui_size.as_vec2()));
+
+ CentralPanel::default().show(ctx, |ui| {
+ ui.heading("Retain");
ui.horizontal(|ui| {
ui.with_layout(Layout::top_down(Align::LEFT), |ui| {
@@ -195,21 +247,29 @@ impl PluginGuiImpl for RetainPluginMainThread<'_> {
fn get_size(&mut self) -> Option<GuiSize> {
Some(GuiSize {
- width: 600,
- height: 350,
+ width: self.shared.gui_size.get_x() as u32,
+ height: self.shared.gui_size.get_y() as u32,
})
}
fn can_resize(&mut self) -> bool {
- false
+ true
}
- fn set_size(&mut self, _size: GuiSize) -> Result<(), PluginError> {
+ fn set_size(&mut self, size: GuiSize) -> Result<(), PluginError> {
+ self.shared.gui_size.set_x(size.width as f32);
+ self.shared.gui_size.set_y(size.height as f32);
+
+ if let Some(gui) = &self.gui {
+ gui.request_repaint();
+ }
+
Ok(())
}
fn set_parent(&mut self, window: Window) -> Result<(), PluginError> {
self.gui = Some(RetainPluginGui::new(window, self.shared));
+
Ok(())
}
diff --git a/src/lib.rs b/src/lib.rs
index 2878e4f..c391e98 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,8 @@
-use crate::{audio::RetainPluginAudioProcessor, gui::RetainPluginGui, params::RetainParams};
+use crate::{
+ audio::RetainPluginAudioProcessor,
+ gui::{AtomicVec2, RetainPluginGui},
+ params::RetainParams,
+};
use clack_extensions::{
audio_ports::PluginAudioPorts,
gui::PluginGui,
@@ -69,6 +73,7 @@ impl DefaultPluginFactory for RetainPlugin {
pub struct RetainPluginShared<'a> {
/// The plugin's parameter values.
params: Arc<RetainParams>,
+ gui_size: Arc<AtomicVec2>,
host: HostSharedHandle<'a>,
}
@@ -76,6 +81,7 @@ impl<'a> RetainPluginShared<'a> {
fn new(host: HostSharedHandle<'a>) -> Self {
RetainPluginShared {
params: Arc::new(RetainParams::new()),
+ gui_size: Arc::new(AtomicVec2::new()),
host,
}
}
diff --git a/src/params.rs b/src/params.rs
index 4b57dd6..fd928e0 100644
--- a/src/params.rs
+++ b/src/params.rs
@@ -110,22 +110,10 @@ impl RetainParams {
/// If the given event is a matching parameter change event, the order parameter will be
/// updated accordingly.
pub fn handle_event(&self, event: &UnknownEvent) {
- if let Some(CoreEventSpace::ParamValue(event)) = event.as_core_event() {
- if event.param_id() == RetainParams::PARAM_ORDER_ID {
- self.set_order(event.value() as usize);
- }
-
- if event.param_id() == RetainParams::PARAM_WINDOW_SIZE_ID {
- self.set_window_size((event.value() as usize).into());
- }
-
- if event.param_id() == RetainParams::PARAM_WINDOW_TYPE_ID {
- self.set_window_type((event.value() as u8).into());
- }
-
- if event.param_id() == RetainParams::PARAM_COMPLEMENT_ID {
- self.set_complement(event.value() != 0.0);
- }
+ if let Some(CoreEventSpace::ParamValue(event)) = event.as_core_event()
+ && event.param_id() == RetainParams::PARAM_ORDER_ID
+ {
+ self.set_order(event.value() as usize);
}
}
}