use crate::common::params::RtPluginParams;
use crate::common::spsc::{GcItem, GcOverflowBuffer, RtStatusFlags};
use crate::dsp::resampler::NamResampler;
use crate::models::StaticModel;
use clack_plugin::prelude::*;
use rtrb::{Consumer, Producer};
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, Ordering};
use std::sync::{Arc, Mutex};
pub enum ClapParamPayload {
Params(RtPluginParams),
LoadModel {
model_l: Option<Box<StaticModel>>,
new_resampler: Box<NamResampler>,
input_mult_adj: f32,
output_mult_adj: f32,
},
#[cfg(any(feature = "standalone", feature = "clap-plugin", test))]
LoadCabIr {
engine: Option<Box<crate::dsp::cabsim::conv::ConvEngine>>,
},
SetOversample {
os_l: Box<crate::dsp::oversample::OversampleEngine>,
os_r: Box<crate::dsp::oversample::OversampleEngine>,
},
}
#[derive(Clone, Debug, Default)]
pub struct NamModelMetadata {
pub architecture: String,
pub topology: String,
pub sample_rate: u32,
pub modeled_by: Option<String>,
pub gear_make: Option<String>,
pub gear_model: Option<String>,
pub gear_type: Option<String>,
pub tone_type: Option<String>,
pub date: Option<String>,
}
#[derive(Clone, Copy)]
pub struct NamClapSharedRef(std::ptr::NonNull<NamClapShared>);
unsafe impl Send for NamClapSharedRef {}
unsafe impl Sync for NamClapSharedRef {}
impl NamClapSharedRef {
#[inline]
pub unsafe fn new(ptr: *const NamClapShared) -> Self {
let nn = std::ptr::NonNull::new(ptr as *mut NamClapShared)
.expect("NamClapSharedRef::new: pointer must not be null");
Self(nn)
}
#[inline]
pub fn as_ptr(&self) -> *const NamClapShared {
self.0.as_ptr()
}
#[inline]
pub unsafe fn as_ref(&self) -> &'static NamClapShared {
unsafe { self.0.as_ref() }
}
}
#[repr(align(128))]
pub struct RtToUi {
pub ui_peak_l: AtomicU32,
pub ui_peak_r: AtomicU32,
pub ui_clipped: AtomicBool,
pub current_latency: AtomicU32,
pub cabsim_tail_samples: AtomicU32,
pub active_channel_count: AtomicU32,
}
#[repr(align(128))]
pub struct UiToRt {
pub param_input_gain: AtomicU32,
pub param_output_gain: AtomicU32,
pub param_gate_thresh: AtomicU32,
pub param_bypass: AtomicU32,
pub param_adaptive_compute: AtomicU32,
pub param_slim_override: AtomicU32,
pub param_oversample: AtomicU32,
pub param_activation: AtomicU32,
pub gesture_flags: AtomicU32,
pub gui_param_generation: AtomicU32,
pub host_r_deactivated: AtomicBool,
}
#[repr(align(128))]
pub struct ColdShared {
pub param_tx: Mutex<Option<Producer<ClapParamPayload>>>,
pub param_rx: Mutex<Option<Consumer<ClapParamPayload>>>,
pub gc_tx: Mutex<Option<Producer<GcItem>>>,
pub gc_rx: Mutex<Option<Consumer<GcItem>>>,
pub gc_overflow: Arc<GcOverflowBuffer>,
pub rt_status: Arc<RtStatusFlags>,
pub model_sample_rate: AtomicU32,
pub sample_rate: AtomicU32,
pub buffer_size: AtomicU32,
pub track_accent_color: AtomicU32,
pub param_indication: [AtomicU8; 9],
pub param_indication_color: [AtomicU32; 9],
pub model_load_counter: AtomicU32,
pub ui_model_name: Mutex<String>,
pub ui_model_metadata: Mutex<Option<NamModelMetadata>>,
pub ui_pending_model: Mutex<Option<PathBuf>>,
pub ui_loading: AtomicBool,
pub ui_load_error: AtomicBool,
pub ui_load_error_msg: Mutex<String>,
pub ui_model_info: Mutex<Option<crate::common::diagnostics::ModelInfo>>,
pub alive_fence: Arc<AtomicBool>,
pub render_mode: AtomicU32,
pub gui_scale_factor: AtomicU32,
pub ir_path: Mutex<Option<String>>,
pub ui_pending_ir: Mutex<Option<PathBuf>>,
pub ui_ir_loading: AtomicBool,
pub ui_ir_load_error: AtomicBool,
pub ui_ir_load_error_msg: Mutex<String>,
pub ui_clear_ir: AtomicBool,
pub ir_raw_samples: Mutex<Option<Vec<f32>>>,
pub slimmable_tx: Mutex<Option<Producer<Option<Box<StaticModel>>>>>,
pub slimmable_rx: Mutex<Option<Consumer<Option<Box<StaticModel>>>>>,
pub full_wavenet_model: Mutex<Option<Box<StaticModel>>>,
pub pending_model: Mutex<Option<PendingModel>>,
pub(crate) dialog_state:
Option<Arc<crate::clap::gui::ui::zones::dialog_state::DialogSharedState>>,
pub(crate) ir_dialog_state:
Option<Arc<crate::clap::gui::ui::zones::dialog_state::IrDialogSharedState>>,
pub(crate) dialog_handle_sink: Mutex<Option<std::thread::JoinHandle<()>>>,
pub(crate) ir_dialog_handle_sink: Mutex<Option<std::thread::JoinHandle<()>>>,
#[cfg(feature = "clap-plugin")]
pub(crate) host_log_sink: Mutex<Option<Arc<crate::common::diagnostics::logger::HostLogFn>>>,
}
pub struct PendingModel {
pub model: Option<Box<StaticModel>>,
pub resampler: Box<NamResampler>,
pub input_mult_adj: f32,
pub output_mult_adj: f32,
}
pub struct NamClapShared {
pub rt_to_ui: RtToUi,
pub ui_to_rt: UiToRt,
pub cold: ColdShared,
}
impl<'a> PluginShared<'a> for NamClapShared {}
impl Drop for NamClapShared {
fn drop(&mut self) {
log::debug!("NAM-rs: NamClapShared dropped.");
self.cold.alive_fence.store(false, Ordering::Release); crate::common::panic_hook::set_shutdown_in_progress();
}
}
pub const RENDER_MODE_REALTIME: u32 = 0;
pub const RENDER_MODE_OFFLINE: u32 = 1;
impl NamClapShared {
const GESTURE_CHANGED_SHIFT: u32 = 0;
const GESTURE_BEGIN_SHIFT: u32 = 1;
const GESTURE_END_SHIFT: u32 = 2;
const GESTURE_BITS_PER_PARAM: u32 = 3;
const fn param_index(param_id: u32) -> usize {
param_id as usize
}
pub fn set_gesture(&self, param_index: usize, flag_shift: u32) {
let bit = 1u32 << (param_index as u32 * Self::GESTURE_BITS_PER_PARAM + flag_shift);
self.ui_to_rt.gesture_flags.fetch_or(bit, Ordering::Relaxed);
}
pub fn take_gesture(&self, param_index: usize, flag_shift: u32) -> bool {
let bit = 1u32 << (param_index as u32 * Self::GESTURE_BITS_PER_PARAM + flag_shift);
(self
.ui_to_rt
.gesture_flags
.fetch_and(!bit, Ordering::Relaxed)
& bit)
!= 0
}
pub fn clear_gestures(&self) {
self.ui_to_rt.gesture_flags.store(0, Ordering::Relaxed);
}
pub fn bump_generation(&self) {
self.ui_to_rt
.gui_param_generation
.fetch_add(1, Ordering::Release); }
pub fn write_gui_events(&self, output: &mut OutputEvents) {
use crate::clap::extensions::params::{
PARAM_ACTIVATION, PARAM_ADAPTIVE_COMPUTE, PARAM_BYPASS, PARAM_GATE_THRESH,
PARAM_INPUT_GAIN, PARAM_OUTPUT_GAIN, PARAM_OVERSAMPLE, PARAM_SLIM_OVERRIDE,
};
use clack_plugin::events::event_types::{
ParamGestureBeginEvent, ParamGestureEndEvent, ParamValueEvent,
};
let params: [(u32, u32, &AtomicU32); 8] = [
(
PARAM_INPUT_GAIN,
Self::param_index(PARAM_INPUT_GAIN) as u32,
&self.ui_to_rt.param_input_gain,
),
(
PARAM_OUTPUT_GAIN,
Self::param_index(PARAM_OUTPUT_GAIN) as u32,
&self.ui_to_rt.param_output_gain,
),
(
PARAM_GATE_THRESH,
Self::param_index(PARAM_GATE_THRESH) as u32,
&self.ui_to_rt.param_gate_thresh,
),
(
PARAM_BYPASS,
Self::param_index(PARAM_BYPASS) as u32,
&self.ui_to_rt.param_bypass,
),
(
PARAM_ADAPTIVE_COMPUTE,
Self::param_index(PARAM_ADAPTIVE_COMPUTE) as u32,
&self.ui_to_rt.param_adaptive_compute,
),
(
PARAM_SLIM_OVERRIDE,
Self::param_index(PARAM_SLIM_OVERRIDE) as u32,
&self.ui_to_rt.param_slim_override,
),
(
PARAM_OVERSAMPLE,
Self::param_index(PARAM_OVERSAMPLE) as u32,
&self.ui_to_rt.param_oversample,
),
(
PARAM_ACTIVATION,
Self::param_index(PARAM_ACTIVATION) as u32,
&self.ui_to_rt.param_activation,
),
];
for (param_id, param_idx, value_atomic) in ¶ms {
let pi = *param_idx as usize;
if self.take_gesture(pi, Self::GESTURE_BEGIN_SHIFT) {
let ev = ParamGestureBeginEvent::new(0, ClapId::new(*param_id));
let _ = output.try_push(ev);
}
if self.take_gesture(pi, Self::GESTURE_CHANGED_SHIFT) {
let val = f32::from_bits(value_atomic.load(Ordering::Relaxed)) as f64;
let ev = ParamValueEvent::new(
0,
ClapId::new(*param_id),
clack_plugin::events::Pckn::new(0u8, 0u8, 0u8, 0u8),
val,
clack_plugin::utils::Cookie::empty(),
);
let _ = output.try_push(ev);
}
if self.take_gesture(pi, Self::GESTURE_END_SHIFT) {
let ev = ParamGestureEndEvent::new(0, ClapId::new(*param_id));
let _ = output.try_push(ev);
}
}
}
}
impl crate::common::diagnostics::HasRuntimeSnapshot for NamClapShared {
fn model_info(&self) -> Option<crate::common::diagnostics::ModelInfo> {
if let Ok(info_guard) = self.cold.ui_model_info.lock() {
info_guard.clone()
} else {
None
}
}
fn audio_info(&self) -> crate::common::diagnostics::AudioInfo {
let sr = self.cold.sample_rate.load(Ordering::Relaxed);
let buffer_size = self.cold.buffer_size.load(Ordering::Relaxed) as usize;
let channel_count = self.rt_to_ui.active_channel_count.load(Ordering::Relaxed) as usize;
crate::common::diagnostics::AudioInfo {
sample_rate: sr,
buffer_size,
channel_count,
host_name: "CLAP".to_string(),
}
}
fn rt_info(&self) -> crate::common::diagnostics::RtInfo {
self.cold.rt_status.rt_info()
}
fn telemetry_snapshot(&self) -> crate::common::diagnostics::TelemetrySnapshot {
self.cold.rt_status.telemetry_snapshot()
}
fn flags_seen(&self) -> u64 {
self.cold.rt_status.flags_seen()
}
}
#[cfg(test)]
#[path = "shared_test.rs"]
mod shared_test;
#[cfg(test)]
pub(crate) use shared_test::make_test_shared;