NeuralAmpModeler-rs 3.0.1

An opinionated, high-performance Neural Amp Modeler (NAM) client and core implementation in Rust for Linux/PipeWire and CLAP plugins.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2026 Fábio Henrique de Lima Silva (fhl.bsb@gmail.com) All rights reserved.

//! Lock-free shared state between the audio thread and the main thread.

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};

/// Main -> RT communication payload for the CLAP plugin.
pub enum ClapParamPayload {
    /// Parameter update (gain, gate, bypass).
    Params(RtPluginParams),
    /// Loading of a new model pair (transferred/constructed outside RT) and its resampler.
    LoadModel {
        /// The encapsulated model for neural inference (Left Channel)
        model_l: Option<Box<StaticModel>>,
        /// Polyphase sinc resampler
        new_resampler: Box<NamResampler>,
        /// Model input gain calibration multiplier (from input_level_dbu metadata).
        input_mult_adj: f32,
        /// Model output gain calibration multiplier (from loudness metadata).
        output_mult_adj: f32,
    },
    /// Loading of a new cab-sim convolution engine via SPSC.
    /// Follows the same pattern as `LoadModel`: the engine is constructed
    /// outside the RT thread and swapped atomically in the audio thread.
    #[cfg(any(feature = "standalone", feature = "clap-plugin", test))]
    LoadCabIr {
        /// Pre-built convolution engine (None = bypass cabsim).
        engine: Option<Box<crate::dsp::cabsim::conv::ConvEngine>>,
    },
    /// Oversampling factor change: carries new pre-built engines for the audio thread.
    SetOversample {
        /// Pre-built left-channel oversample engine.
        os_l: Box<crate::dsp::oversample::OversampleEngine>,
        /// Pre-built right-channel oversample engine.
        os_r: Box<crate::dsp::oversample::OversampleEngine>,
    },
}

/// Model metadata for display in the GUI.
#[derive(Clone, Debug, Default)]
pub struct NamModelMetadata {
    /// Model architecture (e.g. "LSTM", "WaveNet").
    pub architecture: String,
    /// Model topology (e.g. "Standard", "1x64").
    pub topology: String,
    /// Model native sample rate (Hz).
    pub sample_rate: u32,
    /// Author / Modeled by.
    pub modeled_by: Option<String>,
    /// Original equipment manufacturer.
    pub gear_make: Option<String>,
    /// Original equipment model.
    pub gear_model: Option<String>,
    /// Equipment type.
    pub gear_type: Option<String>,
    /// Style/Tone type of the equipment.
    pub tone_type: Option<String>,
    /// Date formatted as YYYY-MM-DD.
    pub date: Option<String>,
}

/// Safe wrapper for a NamClapShared pointer passed to the GUI thread.
///
/// Encapsulates a `NonNull<NamClapShared>` privately so the pointer is never
/// exposed directly. Access is mediated through `as_ptr` and `as_ref`,
/// with the SAFETY contract clearly documented at each call site.
#[derive(Clone, Copy)]
pub struct NamClapSharedRef(std::ptr::NonNull<NamClapShared>);

// SAFETY: the pointer is obtained from a leaked `Arc` — the pointee is
// never deallocated while the process runs. All interior mutation uses
// `Atomic*`/`Mutex`, so concurrent access is sound. `Send` is safe because
// the pointee is Sync+Send; `Sync` is safe because `&NamClapSharedRef` only
// allows reading the pointer itself (not dereferencing the pointee).
unsafe impl Send for NamClapSharedRef {}
unsafe impl Sync for NamClapSharedRef {}

impl NamClapSharedRef {
    /// Creates a new `NamClapSharedRef` from a raw pointer.
    ///
    /// # Safety
    ///
    /// The pointer must be valid, non-null, and obtained from a leaked `Arc<NamClapShared>`.
    /// The pointee must outlive all uses of this wrapper.
    #[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)
    }

    /// Returns the raw pointer without dereferencing it.
    ///
    /// Use this only for FFI or type-erased storage; use [`as_ref`](Self::as_ref)
    /// for safe access to the shared state.
    #[inline]
    pub fn as_ptr(&self) -> *const NamClapShared {
        self.0.as_ptr()
    }

    /// Returns a static reference to the shared state.
    ///
    /// # Safety
    ///
    /// The caller must ensure the `alive_fence` is active (see
    /// `NamPluginWindow::safe_shared`) — the returned reference can
    /// outlive the plugin if the fence is not checked.
    #[inline]
    pub unsafe fn as_ref(&self) -> &'static NamClapShared {
        unsafe { self.0.as_ref() }
    }
}

// ---------------------------------------------------------------------------
// Cache-line-isolated sub-structs grouped by access pattern
// ---------------------------------------------------------------------------

/// Fields written every block by the RT thread, read by the UI thread.
///
/// All fields in this struct are written exclusively by the audio thread
/// and read lock-free by the UI/main thread via `Ordering::Relaxed` (or
/// stronger when the field is part of a synchronization pair).
/// The UI thread must never write to any field in this struct.
#[repr(align(128))]
pub struct RtToUi {
    /// True Peak L level set by the audio thread (f32 bits via f32::to_bits()). Read by the UI thread.
    pub ui_peak_l: AtomicU32,
    /// True Peak R level set by the audio thread (f32 bits via f32::to_bits()). Read by the UI thread.
    pub ui_peak_r: AtomicU32,
    /// Flag indicating whether clipping has occurred since the last UI frame. Read/reset by the UI thread.
    pub ui_clipped: AtomicBool,
    /// Current latency reported to the host (in samples).
    pub current_latency: AtomicU32,
    /// CabSim tail length in samples (= num_partitions × partition_size).
    /// Written by the audio thread after each IR swap; read by the main thread
    /// via `clap.tail`. Zero when no IR is loaded (passthrough mode).
    pub cabsim_tail_samples: AtomicU32,
    /// Number of active channels: 1 = mono, 2 = stereo.
    pub active_channel_count: AtomicU32,
}

/// Fields written by the UI/Main thread, read every block by the RT thread.
#[repr(align(128))]
pub struct UiToRt {
    /// Latest Input Gain parameter value (f32 as bits).
    pub param_input_gain: AtomicU32,
    /// Latest Output Gain parameter value (f32 as bits).
    pub param_output_gain: AtomicU32,
    /// Latest Gate Threshold parameter value (f32 as bits).
    pub param_gate_thresh: AtomicU32,
    /// Latest Bypass parameter value (0 = false, 1 = true).
    pub param_bypass: AtomicU32,
    /// Latest Adaptive Compute mode parameter value (0=Off, 1=Conservative, 2=Aggressive).
    pub param_adaptive_compute: AtomicU32,
    /// Latest Slim Override parameter value (0=Auto, 1=ForceFull, 2=ForceLite).
    pub param_slim_override: AtomicU32,
    /// Latest Oversampling factor parameter value (0=Off, 1=X2, 2=X4).
    pub param_oversample: AtomicU32,
    /// Latest Activation Precision parameter value (0=Fast, 1=Standard).
    pub param_activation: AtomicU32,
    /// Gesture and modification flag bitmap per parameter (GUI -> Host/Processor).
    pub gesture_flags: AtomicU32,
    /// Monotonic generation counter bumped (Release) by GUI on any param write.
    pub gui_param_generation: AtomicU32,
    /// True if the host has deactivated the R channel via audio-ports-activation.
    /// Written by Main Thread (or Audio Thread when processing), read every block by RT.
    pub host_r_deactivated: AtomicBool,
}

/// Fields accessed at low frequency by both threads (init, shutdown, rare events).
#[repr(align(128))]
pub struct ColdShared {
    /// SPSC channel: Main Thread -> Audio Thread (New parameters/models).
    pub param_tx: Mutex<Option<Producer<ClapParamPayload>>>,
    /// SPSC channel: Main Thread -> Audio Thread (Consumer).
    pub param_rx: Mutex<Option<Consumer<ClapParamPayload>>>,
    /// GC channel: Audio Thread -> Main Thread (Obsolete models for disposal).
    pub gc_tx: Mutex<Option<Producer<GcItem>>>,
    /// GC channel: Audio Thread -> Main Thread (Consumer).
    pub gc_rx: Mutex<Option<Consumer<GcItem>>>,
    /// Fallback buffer for GC overflow (overwrite).
    pub gc_overflow: Arc<GcOverflowBuffer>,
    /// Atomic status flags (RT->Main telemetry).
    pub rt_status: Arc<RtStatusFlags>,
    /// Native sample rate required by the actively loaded model.
    pub model_sample_rate: AtomicU32,
    /// Detected host sample rate.
    pub sample_rate: AtomicU32,
    /// Host buffer size.
    pub buffer_size: AtomicU32,
    /// Dynamic accent color based on DAW track color (packed ARGB).
    pub track_accent_color: AtomicU32,
    /// Parameter indication (mapping, automation, and override) for the 9 parameters.
    /// Bit 0: Mapped, Bit 1: Automating, Bit 2: Override.
    pub param_indication: [AtomicU8; 9],
    /// Indicated/mapped parameter colors (packed ARGB).
    pub param_indication_color: [AtomicU32; 9],
    /// Model load counter (incremented on each successful model load).
    pub model_load_counter: AtomicU32,
    /// Loaded model name (path basename). Written by the main thread, read by the UI thread.
    pub ui_model_name: Mutex<String>,
    /// Loaded model metadata for UI display.
    pub ui_model_metadata: Mutex<Option<NamModelMetadata>>,
    /// Pending model path to be loaded by the Main Thread. Written by the UI thread.
    pub ui_pending_model: Mutex<Option<PathBuf>>,
    /// Indicates whether the GUI is in the middle of an asynchronous model load.
    pub ui_loading: AtomicBool,
    /// Flag signaling that a model loading error occurred.
    pub ui_load_error: AtomicBool,
    /// Detailed error message for the GUI.
    pub ui_load_error_msg: Mutex<String>,
    /// Dynamic model info for diagnostics.
    pub ui_model_info: Mutex<Option<crate::common::diagnostics::ModelInfo>>,
    /// Lifetime fence: true while the plugin exists. Checked by the File Picker thread.
    pub alive_fence: Arc<AtomicBool>,
    /// Render mode as set by the host via `clap.render`: 0 = Realtime, 1 = Offline.
    /// Written by the Main Thread, read by the RT thread at low frequency (transitions only).
    pub render_mode: AtomicU32,
    /// Current GUI scale factor (f32 bits). Written by `gui.set_scale` on the Main Thread,
    /// read by the window handler for HiDPI rendering before the first Resized event.
    pub gui_scale_factor: AtomicU32,
    /// Active cab-sim IR file path (for state save/load and GUI display).
    pub ir_path: Mutex<Option<String>>,
    /// Pending IR path to be loaded by the Main Thread. Written by the UI thread.
    pub ui_pending_ir: Mutex<Option<PathBuf>>,
    /// Indicates whether the GUI is in the middle of an asynchronous IR load.
    pub ui_ir_loading: AtomicBool,
    /// Flag signaling that an IR loading error occurred.
    pub ui_ir_load_error: AtomicBool,
    /// Detailed error message for the GUI.
    pub ui_ir_load_error_msg: Mutex<String>,
    /// Flag signaling that the IR should be cleared (bypass cabsim).
    pub ui_clear_ir: AtomicBool,
    /// Raw IR samples stored for adaptive partition rebuild without WAV reload.
    pub ir_raw_samples: Mutex<Option<Vec<f32>>>,
    /// SPSC channel: Main Thread -> Audio Thread (Slimmable model producer).
    pub slimmable_tx: Mutex<Option<Producer<Option<Box<StaticModel>>>>>,
    /// SPSC channel: Main Thread -> Audio Thread (Slimmable model consumer).
    pub slimmable_rx: Mutex<Option<Consumer<Option<Box<StaticModel>>>>>,
    /// Full WaveNet model weights stored for main-thread slimmable rebuild.
    /// When a WaveNet model is loaded, a clone is stored here so the main thread
    /// can create slimmed variants without touching the audio thread.
    pub full_wavenet_model: Mutex<Option<Box<StaticModel>>>,
    /// Model loaded before `activate()` (state restore while `buffer_size == 0`),
    /// deferred to avoid heap allocation on the audio thread (F3 fix).
    /// See `flush_pending_model()` in load.rs and housekeeping.rs.
    pub pending_model: Mutex<Option<PendingModel>>,
    /// Dialog state for model file-dialog (Arc-backed, UAF-safe). Initialized on main thread.
    pub(crate) dialog_state:
        Option<Arc<crate::clap::gui::ui::zones::dialog_state::DialogSharedState>>,
    /// Dialog state for IR file-dialog (Arc-backed, UAF-safe). Initialized on main thread.
    pub(crate) ir_dialog_state:
        Option<Arc<crate::clap::gui::ui::zones::dialog_state::IrDialogSharedState>>,
    /// Sink for model dialog thread handle (written by UI, read by main thread for join).
    pub(crate) dialog_handle_sink: Mutex<Option<std::thread::JoinHandle<()>>>,
    /// Sink for IR dialog thread handle (written by UI, read by main thread for join).
    pub(crate) ir_dialog_handle_sink: Mutex<Option<std::thread::JoinHandle<()>>>,
    /// Host-log bridge sink for forwarding `log::*` macros to the DAW host.
    /// Each CLAP instance registers a `Weak<HostLogFn>` with the global `NamLogger`.
    /// This `Arc` is stored here (owned by the plugin) so the `Weak` stays alive
    /// for the plugin's lifetime.
    #[cfg(feature = "clap-plugin")]
    pub(crate) host_log_sink: Mutex<Option<Arc<crate::common::diagnostics::logger::HostLogFn>>>,
}

/// Model payload deferred from the main thread until `buffer_size` is known
/// (state-restore-before-activate scenario). Carries all fields of a
/// `ClapParamPayload::LoadModel` for deferred SPSC delivery.
pub struct PendingModel {
    /// The encapsulated model for neural inference (Left Channel).
    pub model: Option<Box<StaticModel>>,
    /// Polyphase sinc resampler.
    pub resampler: Box<NamResampler>,
    /// Model input gain calibration multiplier.
    pub input_mult_adj: f32,
    /// Model output gain calibration multiplier.
    pub output_mult_adj: f32,
}

// ---------------------------------------------------------------------------
// Outer shared struct
// ---------------------------------------------------------------------------

/// Lock-free shared state between the audio thread and the main thread.
///
/// Fields are segregated into cache-line-isolated sub-structs grouped by
/// access pattern to eliminate False Sharing.  Each sub-struct has its own
/// `#[repr(align(128))]` so that no two sub-structs share a 128-byte cache
/// line, preventing cache-line bouncing between RT↔UI hotpath writes/reads.
///
/// SPSC channels are wrapped in Mutex<Option<...>> only to allow
/// them to be "extracted" by their respective threads during initialization,
/// satisfying the `Sync` requirement of the `PluginShared` trait.
///
/// - `rt_to_ui`: written every block by RT, read by UI.
/// - `ui_to_rt`: written by UI/Main, read every block by RT.
/// - `cold`: low-frequency access by both threads.
pub struct NamClapShared {
    /// Cache-line-isolated sub-struct: written every block by RT, read by UI.
    pub rt_to_ui: RtToUi,
    /// Cache-line-isolated sub-struct: written by UI/Main, read every block by RT.
    pub ui_to_rt: UiToRt,
    /// Cache-line-isolated sub-struct: low-frequency access by both threads.
    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); // pairs with Acquire load em gui/window/state.rs:190
        crate::common::panic_hook::set_shutdown_in_progress();
    }
}

/// Render mode constants for `ColdShared::render_mode`.
/// Render mode: realtime (normal processing).
pub const RENDER_MODE_REALTIME: u32 = 0;
/// Render mode: offline (export/bounce, max quality, no soft-degrade).
pub const RENDER_MODE_OFFLINE: u32 = 1;

impl NamClapShared {
    /// Bitmask for the parameter in the `gesture_flags` field.
    /// 3 flags per parameter: Changed, GestureBegin, GestureEnd.
    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;

    /// Maps a CLAP param_id (0..8) to internal index 0..8.
    const fn param_index(param_id: u32) -> usize {
        param_id as usize
    }

    /// Sets a gesture flag for the parameter (store = true).
    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);
    }

    /// Reads and clears a gesture flag (swap to false), returns the previous value.
    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
    }

    /// Zeros out all gesture flags.
    pub fn clear_gestures(&self) {
        self.ui_to_rt.gesture_flags.store(0, Ordering::Relaxed);
    }

    /// Bumps the GUI parameter generation counter (Release ordering).
    /// Signals to the RT thread that GUI-initiated parameter values may have changed.
    pub fn bump_generation(&self) {
        self.ui_to_rt
            .gui_param_generation
            .fetch_add(1, Ordering::Release); // pairs with Acquire loads em processor/events.rs:94, extensions/params/audio.rs:70
    }

    /// Flushes gestures and parameter updates initiated by the GUI
    /// into the host's output event queue.
    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 &params {
            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;