Skip to content
Draft
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
128 changes: 96 additions & 32 deletions presets/High_Gain.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,127 @@
"description": null,
"author": null,
"stages": [
{
"NoiseGate": {
"threshold_db": -40.0,
"ratio": 10.0,
"attack_ms": 0.5,
"hold_ms": 80.0,
"release_ms": 150.0,
"bypassed": false
}
},
{
"Preamp": {
"gain": 6.7000003,
"bias": 0.0,
"clipper_type": "ClassA"
"gain": 3.5,
"bias": 0.05,
"clipper_type": "Triode",
"bypassed": false
}
},
{
"ToneStack": {
"model": "British",
"bass": 0.75,
"mid": 1.0,
"treble": 0.95,
"presence": 0.65000004
"Preamp": {
"gain": 5.2000003,
"bias": 0.40000004,
"clipper_type": "Triode",
"bypassed": false
}
},
{
"Compressor": {
"attack_ms": 1.0,
"release_ms": 80.0,
"threshold_db": -16.0,
"ratio": 4.0,
"makeup_db": 16.7
"Preamp": {
"gain": 4.2,
"bias": -0.1,
"clipper_type": "Triode",
"bypassed": false
}
},
{
"Preamp": {
"gain": 6.8,
"bias": 1.4901161e-08,
"clipper_type": "Hard"
"gain": 5.0,
"bias": 0.15,
"clipper_type": "Triode",
"bypassed": false
}
},
{
"Level": {
"gain": 0.5
"Preamp": {
"gain": 5.8,
"bias": -0.05,
"clipper_type": "Triode",
"bypassed": false
}
},
{
"NoiseGate": {
"threshold_db": -13.0,
"ratio": 10.0,
"attack_ms": 3.7,
"hold_ms": 10.0,
"release_ms": 100.0
"ToneStack": {
"model": "American",
"bass": 0.90000004,
"mid": 1.0,
"treble": 1.0,
"presence": 0.65000004,
"bypassed": false
}
},
{
"PowerAmp": {
"drive": 0.15,
"amp_type": "ClassAB",
"sag": 0.3,
"sag_release": 120.0,
"bypassed": false
}
},
{
"MultibandSaturator": {
"low_drive": 0.06,
"mid_drive": 0.16,
"high_drive": 0.099999994,
"low_level": 0.72999996,
"mid_level": 0.87,
"high_level": 0.84,
"low_freq": 347.0,
"high_freq": 2810.0,
"bypassed": false
}
},
{
"Eq": {
"gains": [
-7.7999997,
-5.9,
-3.1999998,
-3.4999998,
-1.9999999,
-1.0999998,
-0.09999982,
0.5000002,
0.0,
-1.5999999,
1.7881393e-7,
-0.79999983,
0.0,
-1.5999999,
-2.6999998,
-3.4999998
],
"bypassed": false
}
},
{
"Level": {
"gain": 1.0
"Reverb": {
"room_size": 0.22,
"damping": 0.72999996,
"mix": 0.2,
"bypassed": false
}
}
],
"ir_name": "Jesterdyne/Engl/sc450-left-01.wav",
"ir_name": "Science Amplification/4x12/G12H-150/SM57 Darker.wav",
"ir_gain": 0.21,
"pitch_shift_semitones": 0,
"input_filters": {
"hp_enabled": true,
"hp_cutoff": 187.0,
"hp_cutoff": 80.0,
"lp_enabled": true,
"lp_cutoff": 9913.0
"lp_cutoff": 9000.0
}
}
}
37 changes: 28 additions & 9 deletions rustortion-plugin/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ pub struct PluginBackend {
shared_state: Arc<SharedState>,
capabilities: Capabilities,
sample_rate: f32,
oversampling_factor: u32,
}

impl PluginBackend {
Expand All @@ -31,7 +30,6 @@ impl PluginBackend {
ir_loader: Option<Arc<IrLoader>>,
shared_state: Arc<SharedState>,
sample_rate: f32,
oversampling_factor: u32,
) -> Self {
Self {
engine_handle,
Expand All @@ -41,7 +39,6 @@ impl PluginBackend {
shared_state,
capabilities: Capabilities::plugin(),
sample_rate,
oversampling_factor,
}
}

Expand All @@ -50,9 +47,16 @@ impl PluginBackend {
self.params.chain_state.lock().ok()?.clone()
}

/// Effective sample rate using the *active* (applied) oversampling factor,
/// not the requested one. This ensures chain rebuilds match the current
/// sampler state.
#[allow(clippy::cast_precision_loss)]
fn effective_sample_rate(&self) -> f32 {
self.sample_rate * self.oversampling_factor as f32
let active = self
.shared_state
.active_oversampling
.load(std::sync::atomic::Ordering::Relaxed);
self.sample_rate * active as f32
}

/// Notify the host that a parameter value changed from the GUI.
Expand Down Expand Up @@ -187,10 +191,23 @@ impl ParamBackend for PluginBackend {
self.notify_host_param_changed(param.as_ptr(), param.preview_normalized(idx));
}

fn set_oversampling(&self, _factor: u32) {
// Oversampling changes in plugin mode are handled through the
// nih-plug parameter system and the process() loop. This trait
// method is a no-op for the plugin backend.
fn set_oversampling(&self, factor: u32) {
debug_assert!(
factor.is_power_of_two() && factor > 0 && factor <= 16,
"oversampling factor must be a power of two in [1, 16], got {factor}"
);
self.shared_state
.requested_oversampling
.store(factor, std::sync::atomic::Ordering::Relaxed);
// Sync to params for DAW project persistence
self.params
.oversampling_factor
.store(factor, std::sync::atomic::Ordering::Relaxed);
// Mark DAW session dirty so the new value is saved with the project.
// #[persist] fields are serialized passively and don't trigger a save.
let param = &self.params.preset_idx;
let current = param.modulated_normalized_value();
self.notify_host_param_changed(param.as_ptr(), current);
}

fn sample_rate(&self) -> u32 {
Expand All @@ -200,7 +217,9 @@ impl ParamBackend for PluginBackend {
}

fn oversampling_factor(&self) -> u32 {
self.oversampling_factor
self.shared_state
.requested_oversampling
.load(std::sync::atomic::Ordering::Relaxed)
}

fn capabilities(&self) -> &Capabilities {
Expand Down
11 changes: 2 additions & 9 deletions rustortion-plugin/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,6 @@ impl Editor for PluginEditor {
.sample_rate
.load(std::sync::atomic::Ordering::Relaxed),
);
let os_idx = self
.shared_state
.oversampling_idx
.load(std::sync::atomic::Ordering::Relaxed);
let oversampling_factor = 2_u32.pow(u32::from(os_idx));

let restored_preset_idx = self.params.preset_idx.value();

let flags = PluginAppFlags {
Expand All @@ -94,7 +88,6 @@ impl Editor for PluginEditor {
engine_handle,
ir_loader,
sample_rate,
oversampling_factor,
restored_preset_idx,
};

Expand Down Expand Up @@ -145,7 +138,6 @@ struct PluginAppFlags {
engine_handle: Option<rustortion_core::audio::engine::EngineHandle>,
ir_loader: Option<Arc<rustortion_core::ir::loader::IrLoader>>,
sample_rate: f32,
oversampling_factor: u32,
restored_preset_idx: i32,
}

Expand All @@ -172,7 +164,6 @@ impl iced_baseview::Application for PluginApp {
flags.ir_loader,
flags.shared_state.clone(),
flags.sample_rate,
flags.oversampling_factor,
);

let available_irs = backend.get_available_irs();
Expand Down Expand Up @@ -209,6 +200,7 @@ impl iced_baseview::Application for PluginApp {
preset_handler.load_preset_by_name(name);
}

let oversampling_factor = backend.oversampling_factor();
let shared = SharedApp {
backend,
stages: Vec::new(),
Expand All @@ -222,6 +214,7 @@ impl iced_baseview::Application for PluginApp {
peak_meter_display: PeakMeterDisplay::default(),
hotkey_handler: HotkeyHandler::new(HotkeySettings::default()),
input_filter_config: rustortion_core::preset::InputFilterConfig::default(),
oversampling_factor,
is_recording: false,
};

Expand Down
Loading