/*
Copyright (C) 2023 Ardura
This program is free software:
you can redistribute it and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation,either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program.
If not, see https://www.gnu.org/licenses/.
#####################################
Actuate - Synthesizer + Sampler/Granulizer by Ardura
Version 1.1
#####################################
This is the first synth I've ever written and first large Rust project. Thanks for checking it out and have fun!
#####################################
*/
#![allow(non_snake_case)]
use flate2::read::GzDecoder;
use flate2::write::GzEncoder;
use flate2::Compression;
use nih_plug::prelude::*;
use nih_plug_egui::{
create_egui_editor,
egui::{self, Align2, Color32, FontId, Pos2, Rect, RichText, Rounding, ScrollArea, Vec2},
widgets::ParamSlider,
EguiState,
};
use rfd::FileDialog;
use serde::{Deserialize, Serialize};
use std::{fmt, io::Read};
use std::{
fs::File,
io::Write,
ops::RangeInclusive,
sync::{
atomic::{AtomicBool, AtomicU32, Ordering},
Arc, Mutex,
},
};
use tinyfiledialogs;
// My Files/crates
use audio_module::{
AudioModule, AudioModuleType,
Oscillator::{self, OscState, RetriggerStyle, SmoothStyle, VoiceType},
};
use fx::{
abass::a_bass_saturation,
biquad_filters::{self, FilterType},
buffermodulator::BufferModulator,
compressor::Compressor,
delay::{Delay, DelaySnapValues, DelayType},
flanger::StereoFlanger,
limiter::StereoLimiter,
phaser::StereoPhaser,
reverb::StereoReverb,
saturation::{Saturation, SaturationType},
ArduraFilter::{self, ResponseType},
StateVariableFilter::{ResonanceType, StateVariableFilter},
VCFilter::ResponseType as VCResponseType,
};
use old_preset_structs::{load_unserialized_old, load_unserialized_v114};
use CustomWidgets::{
toggle_switch, ui_knob, BoolButton, CustomParamSlider,
CustomParamSlider::ParamSlider as HorizontalParamSlider,
CustomVerticalSlider::ParamSlider as VerticalParamSlider,
};
mod CustomWidgets;
mod LFOController;
mod audio_module;
mod fx;
mod old_preset_structs;
// This holds our current sample/granulizer sample (L,R) per sample
pub struct LoadedSample(Vec<Vec<f32>>);
// Plugin sizing
const WIDTH: u32 = 920;
const HEIGHT: u32 = 656;
// Until we have a real preset editor and browser it's better to keep the preset bank smaller
const PRESET_BANK_SIZE: usize = 32;
// File Open Buffer Timer - fixes sync issues from load/save to the gui
const FILE_OPEN_BUFFER_MAX: u32 = 1;
// GUI values to refer to
pub const A_KNOB_OUTSIDE_COLOR: Color32 = Color32::from_rgb(67, 157, 148);
pub const DARK_GREY_UI_COLOR: Color32 = Color32::from_rgb(49, 53, 71);
pub const LIGHT_GREY_UI_COLOR: Color32 = Color32::from_rgb(99, 103, 121);
pub const LIGHTER_GREY_UI_COLOR: Color32 = Color32::from_rgb(149, 153, 171);
pub const SYNTH_SOFT_BLUE: Color32 = Color32::from_rgb(142, 166, 201);
pub const SYNTH_SOFT_BLUE2: Color32 = Color32::from_rgb(102, 126, 181);
pub const A_BACKGROUND_COLOR_TOP: Color32 = Color32::from_rgb(185, 186, 198);
pub const SYNTH_BARS_PURPLE: Color32 = Color32::from_rgb(45, 41, 99);
pub const LIGHTER_PURPLE: Color32 = Color32::from_rgb(85, 81, 139);
pub const SYNTH_MIDDLE_BLUE: Color32 = Color32::from_rgb(98, 145, 204);
pub const FONT_COLOR: Color32 = Color32::from_rgb(10, 103, 210);
// Gui for which filter to display on bottom
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
enum UIBottomSelection {
Filter1,
Filter2,
Pitch1,
Pitch2,
}
// Gui for which panel to display in bottom right
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
enum LFOSelect {
INFO,
LFO1,
LFO2,
LFO3,
Modulation,
Misc,
FX,
}
// Sources that can modulate a value
#[derive(Debug, PartialEq, Enum, Clone, Copy, Serialize, Deserialize)]
pub enum ModulationSource {
None,
Velocity,
LFO1,
LFO2,
LFO3,
UnsetModulation,
}
// Destinations modulations can go
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Enum, Clone, Copy, Serialize, Deserialize)]
pub enum ModulationDestination {
None,
Cutoff_1,
Cutoff_2,
Resonance_1,
Resonance_2,
All_Gain,
Osc1_Gain,
Osc2_Gain,
Osc3_Gain,
All_Detune,
Osc1Detune,
Osc2Detune,
Osc3Detune,
All_UniDetune,
Osc1UniDetune,
Osc2UniDetune,
Osc3UniDetune,
UnsetModulation,
}
// Values for Audio Module Routing to filters
#[derive(Enum, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum AMFilterRouting {
Bypass,
Filter1,
Filter2,
Both,
}
// Filter implementations
#[derive(Enum, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum FilterAlgorithms {
SVF,
TILT,
VCF,
}
// Preset categories in dropdown
#[derive(Debug, Enum, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
pub enum PresetType {
Select,
Atmosphere,
Bass,
FX,
Keys,
Lead,
Pad,
Percussion,
Pluck,
Synth,
Other,
}
// These let us output ToString for the ComboBox stuff + Nih-Plug
impl fmt::Display for PresetType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
// or, alternatively:
// fmt::Debug::fmt(self, f)
}
}
// These let us output ToString for the ComboBox stuff + Nih-Plug
impl fmt::Display for ModulationSource {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
// or, alternatively:
// fmt::Debug::fmt(self, f)
}
}
// These let us output ToString for the ComboBox stuff + Nih-Plug
impl fmt::Display for ModulationDestination {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
// or, alternatively:
// fmt::Debug::fmt(self, f)
}
}
// Filter order routing
#[derive(Enum, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum FilterRouting {
Parallel,
Series12,
Series21,
}
// Pitch Envelope routing
#[allow(non_camel_case_types)]
#[derive(Enum, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum PitchRouting {
All,
Osc1,
Osc2,
Osc3,
Osc1_Osc2,
Osc1_Osc3,
Osc2_Osc3,
}
// Fonts
const FONT: nih_plug_egui::egui::FontId = FontId::proportional(14.0);
const LOADING_FONT: nih_plug_egui::egui::FontId = FontId::proportional(20.0);
const SMALLER_FONT: nih_plug_egui::egui::FontId = FontId::proportional(11.0);
/// Modulation struct for passing mods to audio modules
#[derive(Serialize, Deserialize, Clone)]
pub struct ModulationStruct {
temp_mod_cutoff_1: f32,
temp_mod_cutoff_2: f32,
temp_mod_resonance_1: f32,
temp_mod_resonance_2: f32,
temp_mod_detune_1: f32,
temp_mod_detune_2: f32,
temp_mod_detune_3: f32,
temp_mod_uni_detune_1: f32,
temp_mod_uni_detune_2: f32,
temp_mod_uni_detune_3: f32,
temp_mod_vel_sum: f32,
}
/// This is the structure that represents a storable preset value
#[derive(Serialize, Deserialize, Clone)]
pub struct ActuatePreset {
// Information
preset_name: String,
preset_info: String,
preset_category: PresetType,
// Preset tag information - made into bools to make my life easier
tag_acid: bool,
tag_analog: bool,
tag_bright: bool,
tag_chord: bool,
tag_crisp: bool,
tag_deep: bool,
tag_delicate: bool,
tag_hard: bool,
tag_harsh: bool,
tag_lush: bool,
tag_mellow: bool,
tag_resonant: bool,
tag_rich: bool,
tag_sharp: bool,
tag_silky: bool,
tag_smooth: bool,
tag_soft: bool,
tag_stab: bool,
tag_warm: bool,
// Modules 1
///////////////////////////////////////////////////////////
mod1_audio_module_type: AudioModuleType,
mod1_audio_module_level: f32,
// Granulizer/Sampler
mod1_loaded_sample: Vec<Vec<f32>>,
mod1_sample_lib: Vec<Vec<Vec<f32>>>,
mod1_loop_wavetable: bool,
mod1_single_cycle: bool,
mod1_restretch: bool,
mod1_prev_restretch: bool,
mod1_grain_hold: i32,
mod1_grain_gap: i32,
mod1_start_position: f32,
mod1_end_position: f32,
mod1_grain_crossfade: i32,
// Osc module knob storage
mod1_osc_type: VoiceType,
mod1_osc_octave: i32,
mod1_osc_semitones: i32,
mod1_osc_detune: f32,
mod1_osc_attack: f32,
mod1_osc_decay: f32,
mod1_osc_sustain: f32,
mod1_osc_release: f32,
mod1_osc_retrigger: RetriggerStyle,
mod1_osc_atk_curve: SmoothStyle,
mod1_osc_dec_curve: SmoothStyle,
mod1_osc_rel_curve: SmoothStyle,
mod1_osc_unison: i32,
mod1_osc_unison_detune: f32,
mod1_osc_stereo: f32,
// Modules 2
///////////////////////////////////////////////////////////
mod2_audio_module_type: AudioModuleType,
mod2_audio_module_level: f32,
// Granulizer/Sampler
mod2_loaded_sample: Vec<Vec<f32>>,
mod2_sample_lib: Vec<Vec<Vec<f32>>>,
mod2_loop_wavetable: bool,
mod2_single_cycle: bool,
mod2_restretch: bool,
mod2_prev_restretch: bool,
mod2_grain_hold: i32,
mod2_grain_gap: i32,
mod2_start_position: f32,
mod2_end_position: f32,
mod2_grain_crossfade: i32,
// Osc module knob storage
mod2_osc_type: VoiceType,
mod2_osc_octave: i32,
mod2_osc_semitones: i32,
mod2_osc_detune: f32,
mod2_osc_attack: f32,
mod2_osc_decay: f32,
mod2_osc_sustain: f32,
mod2_osc_release: f32,
mod2_osc_retrigger: RetriggerStyle,
mod2_osc_atk_curve: SmoothStyle,
mod2_osc_dec_curve: SmoothStyle,
mod2_osc_rel_curve: SmoothStyle,
mod2_osc_unison: i32,
mod2_osc_unison_detune: f32,
mod2_osc_stereo: f32,
// Modules 3
///////////////////////////////////////////////////////////
mod3_audio_module_type: AudioModuleType,
mod3_audio_module_level: f32,
// Granulizer/Sampler
mod3_loaded_sample: Vec<Vec<f32>>,
mod3_sample_lib: Vec<Vec<Vec<f32>>>,
mod3_loop_wavetable: bool,
mod3_single_cycle: bool,
mod3_restretch: bool,
mod3_prev_restretch: bool,
mod3_grain_hold: i32,
mod3_grain_gap: i32,
mod3_start_position: f32,
mod3_end_position: f32,
mod3_grain_crossfade: i32,
// Osc module knob storage
mod3_osc_type: VoiceType,
mod3_osc_octave: i32,
mod3_osc_semitones: i32,
mod3_osc_detune: f32,
mod3_osc_attack: f32,
mod3_osc_decay: f32,
mod3_osc_sustain: f32,
mod3_osc_release: f32,
mod3_osc_retrigger: RetriggerStyle,
mod3_osc_atk_curve: SmoothStyle,
mod3_osc_dec_curve: SmoothStyle,
mod3_osc_rel_curve: SmoothStyle,
mod3_osc_unison: i32,
mod3_osc_unison_detune: f32,
mod3_osc_stereo: f32,
// Filters
filter_wet: f32,
filter_cutoff: f32,
filter_resonance: f32,
filter_res_type: ResonanceType,
filter_lp_amount: f32,
filter_hp_amount: f32,
filter_bp_amount: f32,
filter_env_peak: f32,
filter_env_attack: f32,
filter_env_decay: f32,
filter_env_sustain: f32,
filter_env_release: f32,
filter_env_atk_curve: Oscillator::SmoothStyle,
filter_env_dec_curve: Oscillator::SmoothStyle,
filter_env_rel_curve: Oscillator::SmoothStyle,
filter_alg_type: FilterAlgorithms,
tilt_filter_type: ArduraFilter::ResponseType,
filter_wet_2: f32,
filter_cutoff_2: f32,
filter_resonance_2: f32,
filter_res_type_2: ResonanceType,
filter_lp_amount_2: f32,
filter_hp_amount_2: f32,
filter_bp_amount_2: f32,
filter_env_peak_2: f32,
filter_env_attack_2: f32,
filter_env_decay_2: f32,
filter_env_sustain_2: f32,
filter_env_release_2: f32,
filter_env_atk_curve_2: Oscillator::SmoothStyle,
filter_env_dec_curve_2: Oscillator::SmoothStyle,
filter_env_rel_curve_2: Oscillator::SmoothStyle,
filter_alg_type_2: FilterAlgorithms,
tilt_filter_type_2: ArduraFilter::ResponseType,
filter_routing: FilterRouting,
filter_cutoff_link: bool,
// Pitch Env
pitch_enable: bool,
pitch_routing: PitchRouting,
pitch_env_peak: f32,
pitch_env_attack: f32,
pitch_env_decay: f32,
pitch_env_sustain: f32,
pitch_env_release: f32,
pitch_env_atk_curve: Oscillator::SmoothStyle,
pitch_env_dec_curve: Oscillator::SmoothStyle,
pitch_env_rel_curve: Oscillator::SmoothStyle,
pitch_enable_2: bool,
pitch_routing_2: PitchRouting,
pitch_env_peak_2: f32,
pitch_env_attack_2: f32,
pitch_env_decay_2: f32,
pitch_env_sustain_2: f32,
pitch_env_release_2: f32,
pitch_env_atk_curve_2: Oscillator::SmoothStyle,
pitch_env_dec_curve_2: Oscillator::SmoothStyle,
pitch_env_rel_curve_2: Oscillator::SmoothStyle,
// LFOs
lfo1_enable: bool,
lfo2_enable: bool,
lfo3_enable: bool,
lfo1_freq: f32,
lfo1_retrigger: LFOController::LFORetrigger,
lfo1_sync: bool,
lfo1_snap: LFOController::LFOSnapValues,
lfo1_waveform: LFOController::Waveform,
lfo1_phase: f32,
lfo2_freq: f32,
lfo2_retrigger: LFOController::LFORetrigger,
lfo2_sync: bool,
lfo2_snap: LFOController::LFOSnapValues,
lfo2_waveform: LFOController::Waveform,
lfo2_phase: f32,
lfo3_freq: f32,
lfo3_retrigger: LFOController::LFORetrigger,
lfo3_sync: bool,
lfo3_snap: LFOController::LFOSnapValues,
lfo3_waveform: LFOController::Waveform,
lfo3_phase: f32,
// Modulation
mod_source_1: ModulationSource,
mod_source_2: ModulationSource,
mod_source_3: ModulationSource,
mod_source_4: ModulationSource,
mod_dest_1: ModulationDestination,
mod_dest_2: ModulationDestination,
mod_dest_3: ModulationDestination,
mod_dest_4: ModulationDestination,
mod_amount_1: f32,
mod_amount_2: f32,
mod_amount_3: f32,
mod_amount_4: f32,
// EQ
pre_use_eq: bool,
pre_low_freq: f32,
pre_mid_freq: f32,
pre_high_freq: f32,
pre_low_gain: f32,
pre_mid_gain: f32,
pre_high_gain: f32,
// FX
use_fx: bool,
use_compressor: bool,
comp_amt: f32,
comp_atk: f32,
comp_rel: f32,
comp_drive: f32,
use_abass: bool,
abass_amount: f32,
use_saturation: bool,
sat_amount: f32,
sat_type: SaturationType,
use_delay: bool,
delay_amount: f32,
delay_time: DelaySnapValues,
delay_decay: f32,
delay_type: DelayType,
use_reverb: bool,
reverb_amount: f32,
reverb_size: f32,
reverb_feedback: f32,
use_phaser: bool,
phaser_amount: f32,
phaser_depth: f32,
phaser_rate: f32,
phaser_feedback: f32,
use_buffermod: bool,
buffermod_amount: f32,
buffermod_depth: f32,
buffermod_rate: f32,
buffermod_spread: f32,
buffermod_timing: f32,
use_flanger: bool,
flanger_amount: f32,
flanger_depth: f32,
flanger_rate: f32,
flanger_feedback: f32,
use_limiter: bool,
limiter_threshold: f32,
limiter_knee: f32,
}
// This is the struct of the actual plugin object that tracks everything
//#[derive(Clone)]
pub struct Actuate {
pub params: Arc<ActuateParams>,
pub sample_rate: f32,
// Plugin control Arcs
update_something: Arc<AtomicBool>,
clear_voices: Arc<AtomicBool>,
reload_entire_preset: Arc<Mutex<bool>>,
file_dialog: Arc<AtomicBool>,
file_open_buffer_timer: Arc<AtomicU32>,
// Using this like a state: 0 = Closed, 1 = Opening, 2 = Open
current_preset: Arc<AtomicU32>,
update_current_preset: Arc<AtomicBool>,
load_bank: Arc<Mutex<bool>>,
save_bank: Arc<Mutex<bool>>,
import_preset: Arc<Mutex<bool>>,
export_preset: Arc<Mutex<bool>>,
current_note_on_velocity: Arc<AtomicF32>,
// Modules
audio_module_1: Arc<Mutex<AudioModule>>,
_audio_module_1_type: AudioModuleType,
audio_module_2: Arc<Mutex<AudioModule>>,
_audio_module_2_type: AudioModuleType,
audio_module_3: Arc<Mutex<AudioModule>>,
_audio_module_3_type: AudioModuleType,
// SVF Filters
filter_l_1: StateVariableFilter,
filter_r_1: StateVariableFilter,
// TILT Filters
tilt_filter_l_1: ArduraFilter::ArduraFilter,
tilt_filter_r_1: ArduraFilter::ArduraFilter,
// VCF Filters
vcf_filter_l_1: fx::VCFilter::VCFilter,
vcf_filter_r_1: fx::VCFilter::VCFilter,
// Filter state variables
filter_state_1: OscState,
filter_atk_smoother_1: Smoother<f32>,
filter_dec_smoother_1: Smoother<f32>,
filter_rel_smoother_1: Smoother<f32>,
// SVF Filters
filter_l_2: StateVariableFilter,
filter_r_2: StateVariableFilter,
// TILT Filters
tilt_filter_l_2: ArduraFilter::ArduraFilter,
tilt_filter_r_2: ArduraFilter::ArduraFilter,
// VCF Filters
vcf_filter_l_2: fx::VCFilter::VCFilter,
vcf_filter_r_2: fx::VCFilter::VCFilter,
// Filter state variables
filter_state_2: OscState,
filter_atk_smoother_2: Smoother<f32>,
filter_dec_smoother_2: Smoother<f32>,
filter_rel_smoother_2: Smoother<f32>,
// LFOs!
lfo_1: LFOController::LFOController,
lfo_2: LFOController::LFOController,
lfo_3: LFOController::LFOController,
// Modulation overrides for preset loading
mod_override_source_1: Arc<Mutex<ModulationSource>>,
mod_override_source_2: Arc<Mutex<ModulationSource>>,
mod_override_source_3: Arc<Mutex<ModulationSource>>,
mod_override_source_4: Arc<Mutex<ModulationSource>>,
mod_override_dest_1: Arc<Mutex<ModulationDestination>>,
mod_override_dest_2: Arc<Mutex<ModulationDestination>>,
mod_override_dest_3: Arc<Mutex<ModulationDestination>>,
mod_override_dest_4: Arc<Mutex<ModulationDestination>>,
preset_category_override: Arc<Mutex<PresetType>>,
// Preset Lib Default
preset_lib_name: String,
preset_name: Arc<Mutex<String>>,
preset_info: Arc<Mutex<String>>,
preset_category: Arc<Mutex<PresetType>>,
preset_lib: Arc<Mutex<Vec<ActuatePreset>>>,
// Used for DC Offset calculations
dc_filter_l: StateVariableFilter,
dc_filter_r: StateVariableFilter,
// EQ Structs
// I'm not using the Interleaved ones since in Interleaf
// People thought the quirks of interleaving were bugs
bands: Arc<Mutex<[biquad_filters::Biquad; 3]>>,
// Compressor
compressor: Compressor,
// Saturation
saturator: Saturation,
// Delay
delay: Delay,
// Reverb
reverb: [StereoReverb; 8],
// Phaser
phaser: StereoPhaser,
// Buffer Modulation
buffermod: BufferModulator,
// Flanger
flanger: StereoFlanger,
// Limiter
limiter: StereoLimiter,
}
impl Default for Actuate {
fn default() -> Self {
// These are persistent fields to trigger updates like Diopser
let update_something = Arc::new(AtomicBool::new(false));
let clear_voices = Arc::new(AtomicBool::new(false));
let reload_entire_preset = Arc::new(Mutex::new(false));
let file_dialog = Arc::new(AtomicBool::new(false));
let file_open_buffer_timer = Arc::new(AtomicU32::new(0));
let current_preset = Arc::new(AtomicU32::new(0));
let load_bank = Arc::new(Mutex::new(false));
let save_bank = Arc::new(Mutex::new(false));
let import_preset = Arc::new(Mutex::new(false));
let export_preset = Arc::new(Mutex::new(false));
let update_current_preset = Arc::new(AtomicBool::new(false));
Self {
params: Arc::new(ActuateParams::new(
update_something.clone(),
clear_voices.clone(),
file_dialog.clone(),
update_current_preset.clone(),
load_bank.clone(),
save_bank.clone(),
import_preset.clone(),
export_preset.clone(),
)),
sample_rate: 44100.0,
// Plugin control ARCs
update_something: update_something,
clear_voices: clear_voices,
reload_entire_preset: reload_entire_preset,
file_dialog: file_dialog,
file_open_buffer_timer: file_open_buffer_timer,
current_preset: current_preset,
load_bank: load_bank,
save_bank: save_bank,
import_preset: import_preset,
export_preset: export_preset,
update_current_preset: update_current_preset,
current_note_on_velocity: Arc::new(AtomicF32::new(0.0)),
// Module 1
audio_module_1: Arc::new(Mutex::new(AudioModule::default())),
_audio_module_1_type: AudioModuleType::Osc,
audio_module_2: Arc::new(Mutex::new(AudioModule::default())),
_audio_module_2_type: AudioModuleType::Off,
audio_module_3: Arc::new(Mutex::new(AudioModule::default())),
_audio_module_3_type: AudioModuleType::Off,
// SVF Filters
filter_l_2: StateVariableFilter::default().set_oversample(4),
filter_r_2: StateVariableFilter::default().set_oversample(4),
// TILT Filters
tilt_filter_l_2: ArduraFilter::ArduraFilter::new(
44100.0,
20000.0,
1.0,
ResponseType::Lowpass,
),
tilt_filter_r_2: ArduraFilter::ArduraFilter::new(
44100.0,
20000.0,
1.0,
ResponseType::Lowpass,
),
// VCF Filters
vcf_filter_l_1: fx::VCFilter::VCFilter::new(),
vcf_filter_r_1: fx::VCFilter::VCFilter::new(),
filter_state_2: OscState::Off,
filter_atk_smoother_2: Smoother::new(SmoothingStyle::Linear(300.0)),
filter_dec_smoother_2: Smoother::new(SmoothingStyle::Linear(300.0)),
filter_rel_smoother_2: Smoother::new(SmoothingStyle::Linear(300.0)),
// SVF Filters
filter_l_1: StateVariableFilter::default().set_oversample(4),
filter_r_1: StateVariableFilter::default().set_oversample(4),
// TILT Filters
tilt_filter_l_1: ArduraFilter::ArduraFilter::new(
44100.0,
20000.0,
1.0,
ResponseType::Lowpass,
),
tilt_filter_r_1: ArduraFilter::ArduraFilter::new(
44100.0,
20000.0,
1.0,
ResponseType::Lowpass,
),
// VCF Filters
vcf_filter_l_2: fx::VCFilter::VCFilter::new(),
vcf_filter_r_2: fx::VCFilter::VCFilter::new(),
filter_state_1: OscState::Off,
filter_atk_smoother_1: Smoother::new(SmoothingStyle::Linear(300.0)),
filter_dec_smoother_1: Smoother::new(SmoothingStyle::Linear(300.0)),
filter_rel_smoother_1: Smoother::new(SmoothingStyle::Linear(300.0)),
//LFOs
lfo_1: LFOController::LFOController::new(2.0, 1.0, LFOController::Waveform::Sine, 0.0),
lfo_2: LFOController::LFOController::new(2.0, 1.0, LFOController::Waveform::Sine, 0.0),
lfo_3: LFOController::LFOController::new(2.0, 1.0, LFOController::Waveform::Sine, 0.0),
// Modulation Overrides
mod_override_source_1: Arc::new(Mutex::new(ModulationSource::UnsetModulation)),
mod_override_source_2: Arc::new(Mutex::new(ModulationSource::UnsetModulation)),
mod_override_source_3: Arc::new(Mutex::new(ModulationSource::UnsetModulation)),
mod_override_source_4: Arc::new(Mutex::new(ModulationSource::UnsetModulation)),
mod_override_dest_1: Arc::new(Mutex::new(ModulationDestination::UnsetModulation)),
mod_override_dest_2: Arc::new(Mutex::new(ModulationDestination::UnsetModulation)),
mod_override_dest_3: Arc::new(Mutex::new(ModulationDestination::UnsetModulation)),
mod_override_dest_4: Arc::new(Mutex::new(ModulationDestination::UnsetModulation)),
preset_category_override: Arc::new(Mutex::new(PresetType::Select)),
// Preset Library DEFAULT
preset_lib_name: String::from("Default"),
preset_name: Arc::new(Mutex::new(String::new())),
preset_info: Arc::new(Mutex::new(String::new())),
preset_category: Arc::new(Mutex::new(PresetType::Select)),
preset_lib: Arc::new(Mutex::new(vec![
ActuatePreset {
preset_name: "Default".to_string(),
preset_info: "Info".to_string(),
preset_category: PresetType::Select,
tag_acid: false,
tag_analog: false,
tag_bright: false,
tag_chord: false,
tag_crisp: false,
tag_deep: false,
tag_delicate: false,
tag_hard: false,
tag_harsh: false,
tag_lush: false,
tag_mellow: false,
tag_resonant: false,
tag_rich: false,
tag_sharp: false,
tag_silky: false,
tag_smooth: false,
tag_soft: false,
tag_stab: false,
tag_warm: false,
mod1_audio_module_type: AudioModuleType::Osc,
mod1_audio_module_level: 1.0,
mod1_loaded_sample: vec![vec![0.0, 0.0]],
mod1_sample_lib: vec![vec![vec![0.0, 0.0]]],
mod1_loop_wavetable: false,
mod1_single_cycle: false,
mod1_restretch: true,
mod1_prev_restretch: false,
mod1_grain_hold: 200,
mod1_grain_gap: 200,
mod1_start_position: 0.0,
mod1_end_position: 1.0,
mod1_grain_crossfade: 50,
mod1_osc_type: VoiceType::Sine,
mod1_osc_octave: 0,
mod1_osc_semitones: 0,
mod1_osc_detune: 0.0,
mod1_osc_attack: 0.0001,
mod1_osc_decay: 0.0001,
mod1_osc_sustain: 999.9,
mod1_osc_release: 5.0,
mod1_osc_retrigger: RetriggerStyle::Retrigger,
mod1_osc_atk_curve: SmoothStyle::Linear,
mod1_osc_dec_curve: SmoothStyle::Linear,
mod1_osc_rel_curve: SmoothStyle::Linear,
mod1_osc_unison: 1,
mod1_osc_unison_detune: 0.0,
mod1_osc_stereo: 0.0,
mod2_audio_module_type: AudioModuleType::Off,
mod2_audio_module_level: 1.0,
mod2_loaded_sample: vec![vec![0.0, 0.0]],
mod2_sample_lib: vec![vec![vec![0.0, 0.0]]],
mod2_loop_wavetable: false,
mod2_single_cycle: false,
mod2_restretch: true,
mod2_prev_restretch: false,
mod2_grain_hold: 200,
mod2_grain_gap: 200,
mod2_start_position: 0.0,
mod2_end_position: 1.0,
mod2_grain_crossfade: 50,
mod2_osc_type: VoiceType::Sine,
mod2_osc_octave: 0,
mod2_osc_semitones: 0,
mod2_osc_detune: 0.0,
mod2_osc_attack: 0.0001,
mod2_osc_decay: 0.0001,
mod2_osc_sustain: 999.9,
mod2_osc_release: 5.0,
mod2_osc_retrigger: RetriggerStyle::Retrigger,
mod2_osc_atk_curve: SmoothStyle::Linear,
mod2_osc_dec_curve: SmoothStyle::Linear,
mod2_osc_rel_curve: SmoothStyle::Linear,
mod2_osc_unison: 1,
mod2_osc_unison_detune: 0.0,
mod2_osc_stereo: 0.0,
mod3_audio_module_type: AudioModuleType::Off,
mod3_audio_module_level: 1.0,
mod3_loaded_sample: vec![vec![0.0, 0.0]],
mod3_sample_lib: vec![vec![vec![0.0, 0.0]]],
mod3_loop_wavetable: false,
mod3_single_cycle: false,
mod3_restretch: true,
mod3_prev_restretch: false,
mod3_grain_hold: 200,
mod3_grain_gap: 200,
mod3_start_position: 0.0,
mod3_end_position: 1.0,
mod3_grain_crossfade: 50,
mod3_osc_type: VoiceType::Sine,
mod3_osc_octave: 0,
mod3_osc_semitones: 0,
mod3_osc_detune: 0.0,
mod3_osc_attack: 0.0001,
mod3_osc_decay: 0.0001,
mod3_osc_sustain: 999.9,
mod3_osc_release: 5.0,
mod3_osc_retrigger: RetriggerStyle::Retrigger,
mod3_osc_atk_curve: SmoothStyle::Linear,
mod3_osc_dec_curve: SmoothStyle::Linear,
mod3_osc_rel_curve: SmoothStyle::Linear,
mod3_osc_unison: 1,
mod3_osc_unison_detune: 0.0,
mod3_osc_stereo: 0.0,
filter_wet: 1.0,
filter_cutoff: 20000.0,
filter_resonance: 1.0,
filter_res_type: ResonanceType::Default,
filter_lp_amount: 1.0,
filter_hp_amount: 0.0,
filter_bp_amount: 0.0,
filter_env_peak: 0.0,
filter_env_attack: 0.0,
filter_env_decay: 0.0001,
filter_env_sustain: 999.9,
filter_env_release: 5.0,
filter_env_atk_curve: SmoothStyle::Linear,
filter_env_dec_curve: SmoothStyle::Linear,
filter_env_rel_curve: SmoothStyle::Linear,
filter_alg_type: FilterAlgorithms::SVF,
tilt_filter_type: ArduraFilter::ResponseType::Lowpass,
filter_wet_2: 1.0,
filter_cutoff_2: 20000.0,
filter_resonance_2: 1.0,
filter_res_type_2: ResonanceType::Default,
filter_lp_amount_2: 1.0,
filter_hp_amount_2: 0.0,
filter_bp_amount_2: 0.0,
filter_env_peak_2: 0.0,
filter_env_attack_2: 0.0,
filter_env_decay_2: 0.0001,
filter_env_sustain_2: 999.9,
filter_env_release_2: 5.0,
filter_env_atk_curve_2: SmoothStyle::Linear,
filter_env_dec_curve_2: SmoothStyle::Linear,
filter_env_rel_curve_2: SmoothStyle::Linear,
filter_alg_type_2: FilterAlgorithms::SVF,
tilt_filter_type_2: ArduraFilter::ResponseType::Lowpass,
filter_routing: FilterRouting::Parallel,
filter_cutoff_link: false,
// Pitch Routing
pitch_enable: false,
pitch_routing: PitchRouting::Osc1,
pitch_env_peak: 0.0,
pitch_env_attack: 0.0,
pitch_env_decay: 300.0,
pitch_env_sustain: 0.0,
pitch_env_release: 0.0,
pitch_env_atk_curve: SmoothStyle::Linear,
pitch_env_dec_curve: SmoothStyle::Linear,
pitch_env_rel_curve: SmoothStyle::Linear,
pitch_enable_2: false,
pitch_routing_2: PitchRouting::Osc1,
pitch_env_peak_2: 0.0,
pitch_env_attack_2: 0.0,
pitch_env_decay_2: 300.0,
pitch_env_sustain_2: 0.0,
pitch_env_release_2: 0.0,
pitch_env_atk_curve_2: SmoothStyle::Linear,
pitch_env_dec_curve_2: SmoothStyle::Linear,
pitch_env_rel_curve_2: SmoothStyle::Linear,
// LFOs
lfo1_enable: false,
lfo2_enable: false,
lfo3_enable: false,
lfo1_freq: 2.0,
lfo1_retrigger: LFOController::LFORetrigger::None,
lfo1_sync: true,
lfo1_snap: LFOController::LFOSnapValues::Half,
lfo1_waveform: LFOController::Waveform::Sine,
lfo1_phase: 0.0,
lfo2_freq: 2.0,
lfo2_retrigger: LFOController::LFORetrigger::None,
lfo2_sync: true,
lfo2_snap: LFOController::LFOSnapValues::Half,
lfo2_waveform: LFOController::Waveform::Sine,
lfo2_phase: 0.0,
lfo3_freq: 2.0,
lfo3_retrigger: LFOController::LFORetrigger::None,
lfo3_sync: true,
lfo3_snap: LFOController::LFOSnapValues::Half,
lfo3_waveform: LFOController::Waveform::Sine,
lfo3_phase: 0.0,
// Modulations
mod_source_1: ModulationSource::None,
mod_source_2: ModulationSource::None,
mod_source_3: ModulationSource::None,
mod_source_4: ModulationSource::None,
mod_dest_1: ModulationDestination::None,
mod_dest_2: ModulationDestination::None,
mod_dest_3: ModulationDestination::None,
mod_dest_4: ModulationDestination::None,
mod_amount_1: 0.0,
mod_amount_2: 0.0,
mod_amount_3: 0.0,
mod_amount_4: 0.0,
// EQ
pre_use_eq: false,
pre_low_freq: 800.0,
pre_mid_freq: 3000.0,
pre_high_freq: 10000.0,
pre_low_gain: 0.0,
pre_mid_gain: 0.0,
pre_high_gain: 0.0,
//FX
use_fx: true,
use_compressor: false,
comp_amt: 0.5,
comp_atk: 0.5,
comp_rel: 0.5,
comp_drive: 0.5,
use_abass: false,
abass_amount: 0.0011,
use_saturation: false,
sat_amount: 0.0,
sat_type: SaturationType::Tape,
use_delay: false,
delay_amount: 0.0,
delay_time: DelaySnapValues::Quarter,
delay_decay: 0.0,
delay_type: DelayType::Stereo,
use_reverb: false,
reverb_amount: 0.5,
reverb_size: 0.5,
reverb_feedback: 0.5,
use_phaser: false,
phaser_amount: 0.5,
phaser_depth: 0.5,
phaser_rate: 0.5,
phaser_feedback: 0.5,
use_buffermod: false,
buffermod_amount: 0.5,
buffermod_depth: 0.5,
buffermod_rate: 0.5,
buffermod_spread: 0.0,
buffermod_timing: 620.0,
use_flanger: false,
flanger_amount: 0.5,
flanger_depth: 0.5,
flanger_rate: 0.5,
flanger_feedback: 0.5,
use_limiter: false,
limiter_threshold: 0.5,
limiter_knee: 0.5,
};
PRESET_BANK_SIZE
])),
dc_filter_l: StateVariableFilter::default().set_oversample(2),
dc_filter_r: StateVariableFilter::default().set_oversample(2),
// EQ Structs
bands: Arc::new(Mutex::new([
biquad_filters::Biquad::new(44100.0, 800.0, 0.0, 0.93, FilterType::LowShelf),
biquad_filters::Biquad::new(44100.0, 3000.0, 0.0, 0.93, FilterType::Peak),
biquad_filters::Biquad::new(44100.0, 10000.0, 0.0, 0.93, FilterType::HighShelf),
])),
// Compressor
compressor: Compressor::new(44100.0, 0.5, 0.5, 0.5, 0.5),
// Saturation
saturator: Saturation::new(),
// Delay
delay: Delay::new(44100.0, 138.0, DelaySnapValues::Quarter, 0.5),
// Reverb
reverb: [
StereoReverb::new(44100.0, 0.5, 0.5),
StereoReverb::new(44100.0, 0.5, 0.5),
StereoReverb::new(44100.0, 0.5, 0.5),
StereoReverb::new(44100.0, 0.5, 0.5),
StereoReverb::new(44100.0, 0.5, 0.5),
StereoReverb::new(44100.0, 0.5, 0.5),
StereoReverb::new(44100.0, 0.5, 0.5),
StereoReverb::new(44100.0, 0.5, 0.5),
],
// Buffer Modulator
buffermod: BufferModulator::new(44100.0, 0.5, 10.0),
// Flanger initialized to use delay range of 50, for 100 samples
flanger: StereoFlanger::new(44100.0, 0.5, 0.5, 10.0, 0.5, 20),
// Phaser
phaser: StereoPhaser::new(),
// Limiter
limiter: StereoLimiter::new(0.5, 0.5),
}
}
}
/// Plugin parameters struct
#[derive(Params)]
pub struct ActuateParams {
#[persist = "editor-state"]
editor_state: Arc<EguiState>,
// Synth-level settings
#[id = "Master Level"]
pub master_level: FloatParam,
#[id = "Max Voices"]
pub voice_limit: IntParam,
// This audio module is what switches between functions for generators in the synth
#[id = "audio_module_1_type"]
pub _audio_module_1_type: EnumParam<AudioModuleType>,
#[id = "audio_module_2_type"]
pub _audio_module_2_type: EnumParam<AudioModuleType>,
#[id = "audio_module_3_type"]
pub _audio_module_3_type: EnumParam<AudioModuleType>,
// Audio Module Gains
#[id = "audio_module_1_level"]
pub audio_module_1_level: FloatParam,
#[id = "audio_module_2_level"]
pub audio_module_2_level: FloatParam,
#[id = "audio_module_3_level"]
pub audio_module_3_level: FloatParam,
// Audio Module Filter Routing
#[id = "audio_module_1_routing"]
pub audio_module_1_routing: EnumParam<AMFilterRouting>,
#[id = "audio_module_2_routing"]
pub audio_module_2_routing: EnumParam<AMFilterRouting>,
#[id = "audio_module_3_routing"]
pub audio_module_3_routing: EnumParam<AMFilterRouting>,
// Filter routing
#[id = "filter_routing"]
pub filter_routing: EnumParam<FilterRouting>,
#[id = "filter_cutoff_link"]
pub filter_cutoff_link: BoolParam,
// Controls for when audio_module_1_type is Osc
#[id = "osc_1_type"]
pub osc_1_type: EnumParam<VoiceType>,
#[id = "osc_1_octave"]
pub osc_1_octave: IntParam,
#[id = "osc_1_semitones"]
pub osc_1_semitones: IntParam,
#[id = "osc_1_detune"]
pub osc_1_detune: FloatParam,
#[id = "osc_1_attack"]
pub osc_1_attack: FloatParam,
#[id = "osc_1_decay"]
pub osc_1_decay: FloatParam,
#[id = "osc_1_sustain"]
pub osc_1_sustain: FloatParam,
#[id = "osc_1_release"]
pub osc_1_release: FloatParam,
#[id = "osc_1_retrigger"]
pub osc_1_retrigger: EnumParam<RetriggerStyle>,
#[id = "osc_1_atk_curve"]
pub osc_1_atk_curve: EnumParam<Oscillator::SmoothStyle>,
#[id = "osc_1_dec_curve"]
pub osc_1_dec_curve: EnumParam<Oscillator::SmoothStyle>,
#[id = "osc_1_rel_curve"]
pub osc_1_rel_curve: EnumParam<Oscillator::SmoothStyle>,
#[id = "osc_1_unison"]
pub osc_1_unison: IntParam,
#[id = "osc_1_unison_detune"]
pub osc_1_unison_detune: FloatParam,
#[id = "osc_1_stereo"]
pub osc_1_stereo: FloatParam,
// Controls for when audio_module_2_type is Osc
#[id = "osc_2_type"]
pub osc_2_type: EnumParam<VoiceType>,
#[id = "osc_2_octave"]
pub osc_2_octave: IntParam,
#[id = "osc_2_semitones"]
pub osc_2_semitones: IntParam,
#[id = "osc_2_detune"]
pub osc_2_detune: FloatParam,
#[id = "osc_2_attack"]
pub osc_2_attack: FloatParam,
#[id = "osc_2_decay"]
pub osc_2_decay: FloatParam,
#[id = "osc_2_sustain"]
pub osc_2_sustain: FloatParam,
#[id = "osc_2_release"]
pub osc_2_release: FloatParam,
#[id = "osc_2_retrigger"]
pub osc_2_retrigger: EnumParam<RetriggerStyle>,
#[id = "osc_2_atk_curve"]
pub osc_2_atk_curve: EnumParam<Oscillator::SmoothStyle>,
#[id = "osc_2_dec_curve"]
pub osc_2_dec_curve: EnumParam<Oscillator::SmoothStyle>,
#[id = "osc_2_rel_curve"]
pub osc_2_rel_curve: EnumParam<Oscillator::SmoothStyle>,
#[id = "osc_2_unison"]
pub osc_2_unison: IntParam,
#[id = "osc_2_unison_detune"]
pub osc_2_unison_detune: FloatParam,
#[id = "osc_2_stereo"]
pub osc_2_stereo: FloatParam,
// Controls for when audio_module_3_type is Osc
#[id = "osc_3_type"]
pub osc_3_type: EnumParam<VoiceType>,
#[id = "osc_3_octave"]
pub osc_3_octave: IntParam,
#[id = "osc_3_semitones"]
pub osc_3_semitones: IntParam,
#[id = "osc_3_detune"]
pub osc_3_detune: FloatParam,
#[id = "osc_3_attack"]
pub osc_3_attack: FloatParam,
#[id = "osc_3_decay"]
pub osc_3_decay: FloatParam,
#[id = "osc_3_sustain"]
pub osc_3_sustain: FloatParam,
#[id = "osc_3_release"]
pub osc_3_release: FloatParam,
#[id = "osc_3_retrigger"]
pub osc_3_retrigger: EnumParam<RetriggerStyle>,
#[id = "osc_3_atk_curve"]
pub osc_3_atk_curve: EnumParam<Oscillator::SmoothStyle>,
#[id = "osc_3_dec_curve"]
pub osc_3_dec_curve: EnumParam<Oscillator::SmoothStyle>,
#[id = "osc_3_rel_curve"]
pub osc_3_rel_curve: EnumParam<Oscillator::SmoothStyle>,
#[id = "osc_3_unison"]
pub osc_3_unison: IntParam,
#[id = "osc_3_unison_detune"]
pub osc_3_unison_detune: FloatParam,
#[id = "osc_3_stereo"]
pub osc_3_stereo: FloatParam,
// Controls for when audio_module_1_type is Sampler/Granulizer
#[id = "load_sample_1"]
pub load_sample_1: BoolParam,
#[id = "loop_sample_1"]
pub loop_sample_1: BoolParam,
#[id = "single_cycle_1"]
pub single_cycle_1: BoolParam,
#[id = "restretch_1"]
pub restretch_1: BoolParam,
#[id = "grain_hold_1"]
grain_hold_1: IntParam,
#[id = "grain_gap_1"]
grain_gap_1: IntParam,
#[id = "start_position_1"]
start_position_1: FloatParam,
#[id = "end_position_1"]
end_position_1: FloatParam,
#[id = "grain_crossfade_1"]
grain_crossfade_1: IntParam,
// Controls for when audio_module_2_type is Sampler/Granulizer
#[id = "load_sample_2"]
pub load_sample_2: BoolParam,
#[id = "loop_sample_2"]
pub loop_sample_2: BoolParam,
#[id = "single_cycle_2"]
pub single_cycle_2: BoolParam,
#[id = "restretch_2"]
pub restretch_2: BoolParam,
#[id = "grain_hold_2"]
grain_hold_2: IntParam,
#[id = "grain_gap_2"]
grain_gap_2: IntParam,
#[id = "start_position_2"]
start_position_2: FloatParam,
#[id = "end_position_2"]
end_position_2: FloatParam,
#[id = "grain_crossfade_2"]
grain_crossfade_2: IntParam,
// Controls for when audio_module_3_type is Sampler/Granulizer
#[id = "load_sample_3"]
pub load_sample_3: BoolParam,
#[id = "loop_sample_3"]
pub loop_sample_3: BoolParam,
#[id = "single_cycle_3"]
pub single_cycle_3: BoolParam,
#[id = "restretch_3"]
pub restretch_3: BoolParam,
#[id = "grain_hold_3"]
grain_hold_3: IntParam,
#[id = "grain_gap_3"]
grain_gap_3: IntParam,
#[id = "start_position_3"]
start_position_3: FloatParam,
#[id = "end_position_3"]
end_position_3: FloatParam,
#[id = "grain_crossfade_3"]
grain_crossfade_3: IntParam,
// Filters
#[id = "filter_wet"]
pub filter_wet: FloatParam,
#[id = "filter_cutoff"]
pub filter_cutoff: FloatParam,
#[id = "filter_resonance"]
pub filter_resonance: FloatParam,
#[id = "filter_res_type"]
pub filter_res_type: EnumParam<ResonanceType>,
#[id = "filter_lp_amount"]
pub filter_lp_amount: FloatParam,
#[id = "filter_hp_amount"]
pub filter_hp_amount: FloatParam,
#[id = "filter_bp_amount"]
pub filter_bp_amount: FloatParam,
#[id = "filter_env_peak"]
pub filter_env_peak: FloatParam,
#[id = "filter_env_attack"]
pub filter_env_attack: FloatParam,
#[id = "filter_env_decay"]
pub filter_env_decay: FloatParam,
#[id = "filter_env_sustain"]
pub filter_env_sustain: FloatParam,
#[id = "filter_env_release"]
pub filter_env_release: FloatParam,
#[id = "filter_env_atk_curve"]
pub filter_env_atk_curve: EnumParam<Oscillator::SmoothStyle>,
#[id = "filter_env_dec_curve"]
pub filter_env_dec_curve: EnumParam<Oscillator::SmoothStyle>,
#[id = "filter_env_rel_curve"]
pub filter_env_rel_curve: EnumParam<Oscillator::SmoothStyle>,
#[id = "filter_alg_type"]
pub filter_alg_type: EnumParam<FilterAlgorithms>,
#[id = "tilt_filter_type"]
pub tilt_filter_type: EnumParam<ResponseType>,
#[id = "vcf_filter_type"]
pub vcf_filter_type: EnumParam<VCResponseType>,
#[id = "filter_wet_2"]
pub filter_wet_2: FloatParam,
#[id = "filter_cutoff_2"]
pub filter_cutoff_2: FloatParam,
#[id = "filter_resonance_2"]
pub filter_resonance_2: FloatParam,
#[id = "filter_res_type_2"]
pub filter_res_type_2: EnumParam<ResonanceType>,
#[id = "filter_lp_amount_2"]
pub filter_lp_amount_2: FloatParam,
#[id = "filter_hp_amount_2"]
pub filter_hp_amount_2: FloatParam,
#[id = "filter_bp_amount_2"]
pub filter_bp_amount_2: FloatParam,
#[id = "filter_env_peak_2"]
pub filter_env_peak_2: FloatParam,
#[id = "filter_env_attack_2"]
pub filter_env_attack_2: FloatParam,
#[id = "filter_env_decay_2"]
pub filter_env_decay_2: FloatParam,
#[id = "filter_env_sustain_2"]
pub filter_env_sustain_2: FloatParam,
#[id = "filter_env_release_2"]
pub filter_env_release_2: FloatParam,
#[id = "filter_env_atk_curve_2"]
pub filter_env_atk_curve_2: EnumParam<Oscillator::SmoothStyle>,
#[id = "filter_env_dec_curve_2"]
pub filter_env_dec_curve_2: EnumParam<Oscillator::SmoothStyle>,
#[id = "filter_env_rel_curve_2"]
pub filter_env_rel_curve_2: EnumParam<Oscillator::SmoothStyle>,
#[id = "filter_alg_type_2"]
pub filter_alg_type_2: EnumParam<FilterAlgorithms>,
#[id = "tilt_filter_type_2"]
pub tilt_filter_type_2: EnumParam<ResponseType>,
#[id = "vcf_filter_type_2"]
pub vcf_filter_type_2: EnumParam<VCResponseType>,
// Pitch Envelope
#[id = "pitch_enable"]
pub pitch_enable: BoolParam,
#[id = "pitch_routing"]
pub pitch_routing: EnumParam<PitchRouting>,
#[id = "pitch_env_peak"]
pub pitch_env_peak: FloatParam,
#[id = "pitch_env_attack"]
pub pitch_env_attack: FloatParam,
#[id = "pitch_env_decay"]
pub pitch_env_decay: FloatParam,
#[id = "pitch_env_sustain"]
pub pitch_env_sustain: FloatParam,
#[id = "pitch_env_release"]
pub pitch_env_release: FloatParam,
#[id = "pitch_env_atk_curve"]
pub pitch_env_atk_curve: EnumParam<Oscillator::SmoothStyle>,
#[id = "pitch_env_dec_curve"]
pub pitch_env_dec_curve: EnumParam<Oscillator::SmoothStyle>,
#[id = "pitch_env_rel_curve"]
pub pitch_env_rel_curve: EnumParam<Oscillator::SmoothStyle>,
#[id = "pitch_enable_2"]
pub pitch_enable_2: BoolParam,
#[id = "pitch_routing_2"]
pub pitch_routing_2: EnumParam<PitchRouting>,
#[id = "pitch_env_peak_2"]
pub pitch_env_peak_2: FloatParam,
#[id = "pitch_env_attack_2"]
pub pitch_env_attack_2: FloatParam,
#[id = "pitch_env_decay_2"]
pub pitch_env_decay_2: FloatParam,
#[id = "pitch_env_sustain_2"]
pub pitch_env_sustain_2: FloatParam,
#[id = "pitch_env_release_2"]
pub pitch_env_release_2: FloatParam,
#[id = "pitch_env_atk_curve_2"]
pub pitch_env_atk_curve_2: EnumParam<Oscillator::SmoothStyle>,
#[id = "pitch_env_dec_curve_2"]
pub pitch_env_dec_curve_2: EnumParam<Oscillator::SmoothStyle>,
#[id = "pitch_env_rel_curve_2"]
pub pitch_env_rel_curve_2: EnumParam<Oscillator::SmoothStyle>,
// LFOS
#[id = "lfo1_enable"]
pub lfo1_enable: BoolParam,
#[id = "lfo2_enable"]
pub lfo2_enable: BoolParam,
#[id = "lfo3_enable"]
pub lfo3_enable: BoolParam,
#[id = "lfo1_Retrigger"]
pub lfo1_retrigger: EnumParam<LFOController::LFORetrigger>,
#[id = "lfo2_Retrigger"]
pub lfo2_retrigger: EnumParam<LFOController::LFORetrigger>,
#[id = "lfo3_Retrigger"]
pub lfo3_retrigger: EnumParam<LFOController::LFORetrigger>,
#[id = "lfo1_sync"]
pub lfo1_sync: BoolParam,
#[id = "lfo2_sync"]
pub lfo2_sync: BoolParam,
#[id = "lfo3_sync"]
pub lfo3_sync: BoolParam,
#[id = "lfo1_freq"]
pub lfo1_freq: FloatParam,
#[id = "lfo2_freq"]
pub lfo2_freq: FloatParam,
#[id = "lfo3_freq"]
pub lfo3_freq: FloatParam,
#[id = "lfo1_snap"]
pub lfo1_snap: EnumParam<LFOController::LFOSnapValues>,
#[id = "lfo2_snap"]
pub lfo2_snap: EnumParam<LFOController::LFOSnapValues>,
#[id = "lfo3_snap"]
pub lfo3_snap: EnumParam<LFOController::LFOSnapValues>,
#[id = "lfo1_waveform"]
pub lfo1_waveform: EnumParam<LFOController::Waveform>,
#[id = "lfo2_waveform"]
pub lfo2_waveform: EnumParam<LFOController::Waveform>,
#[id = "lfo3_waveform"]
pub lfo3_waveform: EnumParam<LFOController::Waveform>,
#[id = "lfo1_phase"]
pub lfo1_phase: FloatParam,
#[id = "lfo2_phase"]
pub lfo2_phase: FloatParam,
#[id = "lfo3_phase"]
pub lfo3_phase: FloatParam,
// Mod knobs
#[id = "mod_amount_knob_1"]
pub mod_amount_knob_1: FloatParam,
#[id = "mod_amount_knob_2"]
pub mod_amount_knob_2: FloatParam,
#[id = "mod_amount_knob_3"]
pub mod_amount_knob_3: FloatParam,
#[id = "mod_amount_knob_4"]
pub mod_amount_knob_4: FloatParam,
#[id = "mod_source_1"]
pub mod_source_1: EnumParam<ModulationSource>,
#[id = "mod_source_2"]
pub mod_source_2: EnumParam<ModulationSource>,
#[id = "mod_source_3"]
pub mod_source_3: EnumParam<ModulationSource>,
#[id = "mod_source_4"]
pub mod_source_4: EnumParam<ModulationSource>,
#[id = "mod_destination_1"]
pub mod_destination_1: EnumParam<ModulationDestination>,
#[id = "mod_destination_2"]
pub mod_destination_2: EnumParam<ModulationDestination>,
#[id = "mod_destination_3"]
pub mod_destination_3: EnumParam<ModulationDestination>,
#[id = "mod_destination_4"]
pub mod_destination_4: EnumParam<ModulationDestination>,
// EQ Params
#[id = "pre_use_eq"]
pub pre_use_eq: BoolParam,
#[id = "pre_low_freq"]
pub pre_low_freq: FloatParam,
#[id = "pre_mid_freq"]
pub pre_mid_freq: FloatParam,
#[id = "pre_high_freq"]
pub pre_high_freq: FloatParam,
#[id = "pre_low_gain"]
pub pre_low_gain: FloatParam,
#[id = "pre_mid_gain"]
pub pre_mid_gain: FloatParam,
#[id = "pre_high_gain"]
pub pre_high_gain: FloatParam,
// FX
#[id = "use_fx"]
pub use_fx: BoolParam,
#[id = "use_compressor"]
pub use_compressor: BoolParam,
#[id = "comp_amt"]
pub comp_amt: FloatParam,
#[id = "comp_atk"]
pub comp_atk: FloatParam,
#[id = "comp_rel"]
pub comp_rel: FloatParam,
#[id = "comp_drive"]
pub comp_drive: FloatParam,
#[id = "use_abass"]
pub use_abass: BoolParam,
#[id = "abass_amount"]
pub abass_amount: FloatParam,
#[id = "use_saturation"]
pub use_saturation: BoolParam,
#[id = "sat_amt"]
pub sat_amt: FloatParam,
#[id = "sat_type"]
pub sat_type: EnumParam<SaturationType>,
#[id = "use_delay"]
pub use_delay: BoolParam,
#[id = "delay_amount"]
pub delay_amount: FloatParam,
#[id = "delay_time"]
pub delay_time: EnumParam<DelaySnapValues>,
#[id = "delay_decay"]
pub delay_decay: FloatParam,
#[id = "delay_type"]
pub delay_type: EnumParam<DelayType>,
#[id = "use_reverb"]
pub use_reverb: BoolParam,
#[id = "reverb_amount"]
pub reverb_amount: FloatParam,
#[id = "reverb_size"]
pub reverb_size: FloatParam,
#[id = "reverb_feedback"]
pub reverb_feedback: FloatParam,
#[id = "use_phaser"]
pub use_phaser: BoolParam,
#[id = "phaser_amount"]
pub phaser_amount: FloatParam,
#[id = "phaser_depth"]
pub phaser_depth: FloatParam,
#[id = "phaser_rate"]
pub phaser_rate: FloatParam,
#[id = "phaser_feedback"]
pub phaser_feedback: FloatParam,
#[id = "use_buffermod"]
pub use_buffermod: BoolParam,
#[id = "buffermod_amount"]
pub buffermod_amount: FloatParam,
#[id = "buffermod_depth"]
pub buffermod_depth: FloatParam,
#[id = "buffermod_rate"]
pub buffermod_rate: FloatParam,
#[id = "buffermod_spread"]
pub buffermod_spread: FloatParam,
#[id = "buffermod_timing"]
pub buffermod_timing: FloatParam,
#[id = "use_flanger"]
pub use_flanger: BoolParam,
#[id = "flanger_amount"]
pub flanger_amount: FloatParam,
#[id = "flanger_depth"]
pub flanger_depth: FloatParam,
#[id = "flanger_rate"]
pub flanger_rate: FloatParam,
#[id = "flanger_feedback"]
pub flanger_feedback: FloatParam,
#[id = "use_limiter"]
pub use_limiter: BoolParam,
#[id = "limiter_threshold"]
pub limiter_threshold: FloatParam,
#[id = "limiter_knee"]
pub limiter_knee: FloatParam,
// UI Non-param Params
#[id = "param_load_bank"]
pub param_load_bank: BoolParam,
#[id = "param_save_bank"]
pub param_save_bank: BoolParam,
#[id = "param_import_preset"]
pub param_import_preset: BoolParam,
#[id = "param_export_preset"]
pub param_export_preset: BoolParam,
#[id = "param_next_preset"]
pub param_next_preset: BoolParam,
#[id = "param_prev_preset"]
pub param_prev_preset: BoolParam,
#[id = "param_update_current_preset"]
pub param_update_current_preset: BoolParam,
#[id = "preset_category"]
pub preset_category: EnumParam<PresetType>,
#[id = "tag_acid"]
pub tag_acid: BoolParam,
#[id = "tag_analog"]
pub tag_analog: BoolParam,
#[id = "tag_bright"]
pub tag_bright: BoolParam,
#[id = "tag_chord"]
pub tag_chord: BoolParam,
#[id = "tag_crisp"]
pub tag_crisp: BoolParam,
#[id = "tag_deep"]
pub tag_deep: BoolParam,
#[id = "tag_delicate"]
pub tag_delicate: BoolParam,
#[id = "tag_hard"]
pub tag_hard: BoolParam,
#[id = "tag_harsh"]
pub tag_harsh: BoolParam,
#[id = "tag_lush"]
pub tag_lush: BoolParam,
#[id = "tag_mellow"]
pub tag_mellow: BoolParam,
#[id = "tag_resonant"]
pub tag_resonant: BoolParam,
#[id = "tag_rich"]
pub tag_rich: BoolParam,
#[id = "tag_sharp"]
pub tag_sharp: BoolParam,
#[id = "tag_silky"]
pub tag_silky: BoolParam,
#[id = "tag_smooth"]
pub tag_smooth: BoolParam,
#[id = "tag_soft"]
pub tag_soft: BoolParam,
#[id = "tag_stab"]
pub tag_stab: BoolParam,
#[id = "tag_warm"]
pub tag_warm: BoolParam,
// Not a param
#[id = "loading"]
pub loading: BoolParam,
}
// This is where parameters are established and defined as well as the callbacks to share gui/audio process info
impl ActuateParams {
fn new(
update_something: Arc<AtomicBool>,
clear_voices: Arc<AtomicBool>,
file_dialog: Arc<AtomicBool>,
update_current_preset: Arc<AtomicBool>,
load_bank: Arc<Mutex<bool>>,
save_bank: Arc<Mutex<bool>>,
import_preset: Arc<Mutex<bool>>,
export_preset: Arc<Mutex<bool>>,
) -> Self {
Self {
editor_state: EguiState::from_size(WIDTH, HEIGHT),
// Top Level objects
////////////////////////////////////////////////////////////////////////////////////
master_level: FloatParam::new("Master", 1.0, FloatRange::Linear { min: 0.0, max: 2.0 })
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_unit("%"),
voice_limit: IntParam::new("Max Voices", 64, IntRange::Linear { min: 1, max: 512 }),
_audio_module_1_type: EnumParam::new("Type", AudioModuleType::Osc).with_callback({
let clear_voices = clear_voices.clone();
Arc::new(move |_| clear_voices.store(true, Ordering::Relaxed))
}),
_audio_module_2_type: EnumParam::new("Type", AudioModuleType::Off).with_callback({
let clear_voices = clear_voices.clone();
Arc::new(move |_| clear_voices.store(true, Ordering::Relaxed))
}),
_audio_module_3_type: EnumParam::new("Type", AudioModuleType::Off).with_callback({
let clear_voices = clear_voices.clone();
Arc::new(move |_| clear_voices.store(true, Ordering::Relaxed))
}),
audio_module_1_level: FloatParam::new(
"Level",
0.5,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_unit("%"),
audio_module_2_level: FloatParam::new(
"Level",
0.5,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_unit("%"),
audio_module_3_level: FloatParam::new(
"Level",
0.5,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_unit("%"),
audio_module_1_routing: EnumParam::new("Routing", AMFilterRouting::Filter1),
audio_module_2_routing: EnumParam::new("Routing", AMFilterRouting::Filter1),
audio_module_3_routing: EnumParam::new("Routing", AMFilterRouting::Filter1),
filter_routing: EnumParam::new("Filter Routing", FilterRouting::Parallel),
// Oscillators
////////////////////////////////////////////////////////////////////////////////////
osc_1_type: EnumParam::new("Wave", VoiceType::Sine).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_1_octave: IntParam::new("Octave", 0, IntRange::Linear { min: -2, max: 2 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_1_semitones: IntParam::new("Semi", 0, IntRange::Linear { min: -11, max: 11 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_1_detune: FloatParam::new(
"Fine",
0.0,
FloatRange::Linear {
min: -0.999,
max: 0.999,
},
)
.with_step_size(0.0001)
.with_value_to_string(formatters::v2s_f32_rounded(4))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_1_attack: FloatParam::new(
"Attack",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.5,
},
)
.with_step_size(0.0001)
.with_value_to_string(format_nothing())
.with_unit("A")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_1_decay: FloatParam::new(
"Decay",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.5,
},
)
.with_step_size(0.0001)
.with_value_to_string(format_nothing())
.with_unit("D")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_1_sustain: FloatParam::new(
"Sustain",
999.9,
FloatRange::Linear {
min: 0.0001,
max: 999.9,
},
)
.with_step_size(0.0001)
.with_value_to_string(format_nothing())
.with_unit("S")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_1_release: FloatParam::new(
"Release",
5.0,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.5,
},
)
.with_step_size(0.0001)
.with_value_to_string(format_nothing())
.with_unit("R")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_1_retrigger: EnumParam::new("Retrig", RetriggerStyle::Retrigger).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_1_atk_curve: EnumParam::new("Atk Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_1_dec_curve: EnumParam::new("Dec Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_1_rel_curve: EnumParam::new("Rel Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_1_unison: IntParam::new("Unison", 1, IntRange::Linear { min: 1, max: 9 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_1_unison_detune: FloatParam::new(
"UDetune",
0.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_step_size(0.0001)
.with_value_to_string(formatters::v2s_f32_rounded(4))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_1_stereo: FloatParam::new("Stereo", 1.0, FloatRange::Linear { min: 0.0, max: 2.0 })
.with_value_to_string(formatters::v2s_f32_rounded(2))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_2_type: EnumParam::new("Wave", VoiceType::Sine).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_2_octave: IntParam::new("Octave", 0, IntRange::Linear { min: -2, max: 2 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_2_semitones: IntParam::new("Semi", 0, IntRange::Linear { min: -11, max: 11 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_2_detune: FloatParam::new(
"Fine",
0.0,
FloatRange::Linear {
min: -0.999,
max: 0.999,
},
)
.with_step_size(0.0001)
.with_value_to_string(formatters::v2s_f32_rounded(4))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_2_attack: FloatParam::new(
"Attack",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.5,
},
)
.with_step_size(0.0001)
.with_value_to_string(format_nothing())
.with_unit("A")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_2_decay: FloatParam::new(
"Decay",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.5,
},
)
.with_step_size(0.0001)
.with_value_to_string(format_nothing())
.with_unit("D")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_2_sustain: FloatParam::new(
"Sustain",
999.9,
FloatRange::Linear {
min: 0.0001,
max: 999.9,
},
)
.with_step_size(0.0001)
.with_value_to_string(format_nothing())
.with_unit("S")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_2_release: FloatParam::new(
"Release",
5.0,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.5,
},
)
.with_step_size(0.0001)
.with_value_to_string(format_nothing())
.with_unit("R")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_2_retrigger: EnumParam::new("Retrig", RetriggerStyle::Retrigger).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_2_atk_curve: EnumParam::new("Atk Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_2_dec_curve: EnumParam::new("Dec Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_2_rel_curve: EnumParam::new("Rel Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_2_unison: IntParam::new("Unison", 1, IntRange::Linear { min: 1, max: 9 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_2_unison_detune: FloatParam::new(
"UDetune",
0.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_step_size(0.0001)
.with_value_to_string(formatters::v2s_f32_rounded(4))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_2_stereo: FloatParam::new("Stereo", 1.0, FloatRange::Linear { min: 0.0, max: 2.0 })
.with_value_to_string(formatters::v2s_f32_rounded(2))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_3_type: EnumParam::new("Wave", VoiceType::Sine).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_3_octave: IntParam::new("Octave", 0, IntRange::Linear { min: -2, max: 2 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_3_semitones: IntParam::new("Semi", 0, IntRange::Linear { min: -11, max: 11 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_3_detune: FloatParam::new(
"Fine",
0.0,
FloatRange::Linear {
min: -0.999,
max: 0.999,
},
)
.with_step_size(0.0001)
.with_value_to_string(formatters::v2s_f32_rounded(4))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_3_attack: FloatParam::new(
"Attack",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.5,
},
)
.with_step_size(0.0001)
.with_value_to_string(format_nothing())
.with_unit("A")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_3_decay: FloatParam::new(
"Decay",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.5,
},
)
.with_step_size(0.0001)
.with_value_to_string(format_nothing())
.with_unit("D")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_3_sustain: FloatParam::new(
"Sustain",
999.9,
FloatRange::Linear {
min: 0.0001,
max: 999.9,
},
)
.with_step_size(0.0001)
.with_value_to_string(format_nothing())
.with_unit("S")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_3_release: FloatParam::new(
"Release",
5.0,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.5,
},
)
.with_step_size(0.0001)
.with_value_to_string(format_nothing())
.with_unit("R")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_3_retrigger: EnumParam::new("Retrig", RetriggerStyle::Retrigger).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_3_atk_curve: EnumParam::new("Atk Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_3_dec_curve: EnumParam::new("Dec Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_3_rel_curve: EnumParam::new("Rel Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_3_unison: IntParam::new("Unison", 1, IntRange::Linear { min: 1, max: 9 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_3_unison_detune: FloatParam::new(
"UDetune",
0.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_step_size(0.0001)
.with_value_to_string(formatters::v2s_f32_rounded(4))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
osc_3_stereo: FloatParam::new("Stereo", 1.0, FloatRange::Linear { min: 0.0, max: 2.0 })
.with_value_to_string(formatters::v2s_f32_rounded(2))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
// Granulizer/Sampler
////////////////////////////////////////////////////////////////////////////////////
load_sample_1: BoolParam::new("Load Sample", false)
.with_callback({
let file_dialog = file_dialog.clone();
Arc::new(move |_| file_dialog.store(true, Ordering::Relaxed))
})
.hide(),
load_sample_2: BoolParam::new("Load Sample", false)
.with_callback({
let file_dialog = file_dialog.clone();
Arc::new(move |_| file_dialog.store(true, Ordering::Relaxed))
})
.hide(),
load_sample_3: BoolParam::new("Load Sample", false)
.with_callback({
let file_dialog = file_dialog.clone();
Arc::new(move |_| file_dialog.store(true, Ordering::Relaxed))
})
.hide(),
// To loop the sampler/granulizer
loop_sample_1: BoolParam::new("Loop Sample", false).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
loop_sample_2: BoolParam::new("Loop Sample", false).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
loop_sample_3: BoolParam::new("Loop Sample", false).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
// Sampler only - toggle single cycle mode
single_cycle_1: BoolParam::new("Single Cycle", false).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
single_cycle_2: BoolParam::new("Single Cycle", false).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
single_cycle_3: BoolParam::new("Single Cycle", false).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
// Always true for granulizer/ can be off for sampler
restretch_1: BoolParam::new("Load Stretch", true).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
restretch_2: BoolParam::new("Load Stretch", true).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
restretch_3: BoolParam::new("Load Stretch", true).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
// This is from 0 to 2000 samples
grain_hold_1: IntParam::new("Hold", 200, IntRange::Linear { min: 5, max: 22050 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
grain_hold_2: IntParam::new("Hold", 200, IntRange::Linear { min: 5, max: 22050 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
grain_hold_3: IntParam::new("Hold", 200, IntRange::Linear { min: 5, max: 22050 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
grain_gap_1: IntParam::new("Gap", 200, IntRange::Linear { min: 0, max: 22050 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
grain_gap_2: IntParam::new("Gap", 200, IntRange::Linear { min: 0, max: 22050 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
grain_gap_3: IntParam::new("Gap", 200, IntRange::Linear { min: 0, max: 22050 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
// This is going to be in % since sample can be any size
start_position_1: FloatParam::new(
"Start",
0.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_unit("%")
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
start_position_2: FloatParam::new(
"Start",
0.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_unit("%")
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
start_position_3: FloatParam::new(
"Start",
0.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_unit("%")
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
end_position_1: FloatParam::new("End", 1.0, FloatRange::Linear { min: 0.0, max: 1.0 })
.with_unit("%")
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
end_position_2: FloatParam::new("End", 1.0, FloatRange::Linear { min: 0.0, max: 1.0 })
.with_unit("%")
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
end_position_3: FloatParam::new("End", 1.0, FloatRange::Linear { min: 0.0, max: 1.0 })
.with_unit("%")
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
// Grain Crossfade
grain_crossfade_1: IntParam::new("Shape", 50, IntRange::Linear { min: 2, max: 2000 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
grain_crossfade_2: IntParam::new("Shape", 50, IntRange::Linear { min: 2, max: 2000 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
grain_crossfade_3: IntParam::new("Shape", 50, IntRange::Linear { min: 2, max: 2000 })
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
// Filters
////////////////////////////////////////////////////////////////////////////////////
filter_lp_amount: FloatParam::new(
"LPF",
1.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_hp_amount: FloatParam::new(
"HPF",
0.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_bp_amount: FloatParam::new(
"BPF",
0.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_wet: FloatParam::new("Filter", 1.0, FloatRange::Linear { min: 0.0, max: 1.0 })
.with_unit("%")
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_resonance: FloatParam::new(
"Res",
1.0,
FloatRange::Reversed(&FloatRange::Linear { min: 0.1, max: 1.0 }),
)
.with_unit("%")
.with_value_to_string(formatters::v2s_f32_percentage(0)),
filter_res_type: EnumParam::new("Res Type", ResonanceType::Default).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_cutoff: FloatParam::new(
"Cutoff",
20000.0,
FloatRange::Skewed {
min: 20.0,
max: 20000.0,
factor: 0.5,
},
)
.with_step_size(1.0)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_alg_type: EnumParam::new("Filter 1 Alg", FilterAlgorithms::SVF),
tilt_filter_type: EnumParam::new("Filter Type", ResponseType::Lowpass),
vcf_filter_type: EnumParam::new("Filter Type", VCResponseType::Lowpass),
filter_env_peak: FloatParam::new(
"Env Mod",
0.0,
FloatRange::Linear {
min: -14980.0,
max: 14980.0,
},
)
.with_step_size(1.0)
.with_value_to_string(format_nothing())
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_env_attack: FloatParam::new(
"Env Attack",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("A")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_env_decay: FloatParam::new(
"Env Decay",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("D")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_env_sustain: FloatParam::new(
"Env Sustain",
999.9,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("S")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_env_release: FloatParam::new(
"Env Release",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("R")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_env_atk_curve: EnumParam::new("Atk Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_env_dec_curve: EnumParam::new("Dec Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_env_rel_curve: EnumParam::new("Rel Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_lp_amount_2: FloatParam::new(
"LPF",
1.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_hp_amount_2: FloatParam::new(
"HPF",
0.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_bp_amount_2: FloatParam::new(
"BPF",
0.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_wet_2: FloatParam::new("Filter", 1.0, FloatRange::Linear { min: 0.0, max: 1.0 })
.with_unit("%")
.with_value_to_string(formatters::v2s_f32_percentage(0))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_resonance_2: FloatParam::new(
"Res",
1.0,
FloatRange::Reversed(&FloatRange::Linear { min: 0.1, max: 1.0 }),
)
.with_unit("%")
.with_value_to_string(formatters::v2s_f32_percentage(0)),
filter_res_type_2: EnumParam::new("Res Type", ResonanceType::Default).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_cutoff_2: FloatParam::new(
"Cutoff",
20000.0,
FloatRange::Skewed {
min: 20.0,
max: 20000.0,
factor: 0.5,
},
)
.with_step_size(1.0)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_alg_type_2: EnumParam::new("Filter 2 Alg", FilterAlgorithms::SVF),
tilt_filter_type_2: EnumParam::new("Filter Type", ResponseType::Lowpass),
vcf_filter_type_2: EnumParam::new("Filter Type", VCResponseType::Lowpass),
filter_env_peak_2: FloatParam::new(
"Env Mod",
0.0,
FloatRange::Linear {
min: -14980.0,
max: 14980.0,
},
)
.with_step_size(1.0)
.with_value_to_string(format_nothing())
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_env_attack_2: FloatParam::new(
"Env Attack",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("A")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_env_decay_2: FloatParam::new(
"Env Decay",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("D")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_env_sustain_2: FloatParam::new(
"Env Sustain",
999.9,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("S")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_env_release_2: FloatParam::new(
"Env Release",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("R")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_env_atk_curve_2: EnumParam::new("Atk Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_env_dec_curve_2: EnumParam::new("Dec Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_env_rel_curve_2: EnumParam::new("Rel Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
filter_cutoff_link: BoolParam::new("Filter Cutoffs Linked", false),
// Pitch Envelope
////////////////////////////////////////////////////////////////////////////////////
pitch_env_peak: FloatParam::new(
"Pitch Env",
0.0,
FloatRange::Linear {
min: -144.0,
max: 144.0,
},
)
//.with_step_size(1.0)
//.with_value_to_string(format_nothing())
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_env_attack: FloatParam::new(
"Env Attack",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("A")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_env_decay: FloatParam::new(
"Env Decay",
300.0,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("D")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_env_sustain: FloatParam::new(
"Env Sustain",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("S")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_env_release: FloatParam::new(
"Env Release",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("R")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_env_atk_curve: EnumParam::new("Atk Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_env_dec_curve: EnumParam::new("Dec Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_env_rel_curve: EnumParam::new("Rel Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_enable: BoolParam::new("Pitch Enable", false),
pitch_routing: EnumParam::new("Routing", PitchRouting::Osc1).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_env_peak_2: FloatParam::new(
"Pitch Env",
0.0,
FloatRange::Linear {
min: -144.0,
max: 144.0,
},
)
//.with_step_size(1.0)
//.with_value_to_string(format_nothing())
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_env_attack_2: FloatParam::new(
"Env Attack",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("A")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_env_decay_2: FloatParam::new(
"Env Decay",
300.0,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("D")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_env_sustain_2: FloatParam::new(
"Env Sustain",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("S")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_env_release_2: FloatParam::new(
"Env Release",
0.0001,
FloatRange::Skewed {
min: 0.0001,
max: 999.9,
factor: 0.2,
},
)
.with_value_to_string(format_nothing())
.with_unit("R")
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_env_atk_curve_2: EnumParam::new("Atk Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_env_dec_curve_2: EnumParam::new("Dec Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_env_rel_curve_2: EnumParam::new("Rel Curve", Oscillator::SmoothStyle::Linear)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
pitch_enable_2: BoolParam::new("Pitch Enable", false),
pitch_routing_2: EnumParam::new("Routing", PitchRouting::Osc1).with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
// LFOs
////////////////////////////////////////////////////////////////////////////////////
lfo1_enable: BoolParam::new("LFO 1 Enable", false),
lfo2_enable: BoolParam::new("LFO 2 Enable", false),
lfo3_enable: BoolParam::new("LFO 3 Enable", false),
lfo1_retrigger: EnumParam::new("LFO Retrigger", LFOController::LFORetrigger::None)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
lfo2_retrigger: EnumParam::new("LFO Retrigger", LFOController::LFORetrigger::None)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
lfo3_retrigger: EnumParam::new("LFO Retrigger", LFOController::LFORetrigger::None)
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
lfo1_freq: FloatParam::new(
"LFO1 Freq",
4.62, // Defualt is half note at 138 bpm
FloatRange::Skewed {
min: 1.0,
max: 9600.0,
factor: 0.3,
}, // Based on max bpm of 300 w/ 32nd notes
)
.with_value_to_string(formatters::v2s_f32_hz_then_khz(2))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
lfo2_freq: FloatParam::new(
"LFO2 Freq",
4.62, // Defualt is half note at 138 bpm
FloatRange::Skewed {
min: 1.0,
max: 9600.0,
factor: 0.3,
}, // Based on max bpm of 300 w/ 32nd notes
)
.with_value_to_string(formatters::v2s_f32_hz_then_khz(2))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
lfo3_freq: FloatParam::new(
"LFO3 Freq",
4.62, // Defualt is half note at 138 bpm
FloatRange::Skewed {
min: 1.0,
max: 9600.0,
factor: 0.3,
}, // Based on max bpm of 300 w/ 32nd notes
)
.with_value_to_string(formatters::v2s_f32_hz_then_khz(2))
.with_callback({
let update_something = update_something.clone();
Arc::new(move |_| update_something.store(true, Ordering::Relaxed))
}),
lfo1_snap: EnumParam::new("LFO1 Snap", LFOController::LFOSnapValues::Half),
lfo2_snap: EnumParam::new("LFO2 Snap", LFOController::LFOSnapValues::Half),
lfo3_snap: EnumParam::new("LFO3 Snap", LFOController::LFOSnapValues::Half),
lfo1_sync: BoolParam::new("LFO1 Sync", true),
lfo2_sync: BoolParam::new("LFO2 Sync", true),
lfo3_sync: BoolParam::new("LFO3 Sync", true),
lfo1_waveform: EnumParam::new("LFO1 Waveform", LFOController::Waveform::Sine),
lfo2_waveform: EnumParam::new("LFO2 Waveform", LFOController::Waveform::Sine),
lfo3_waveform: EnumParam::new("LFO3 Waveform", LFOController::Waveform::Sine),
lfo1_phase: FloatParam::new(
"LFO1 Phase",
0.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
),
lfo2_phase: FloatParam::new(
"LFO2 Phase",
0.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
),
lfo3_phase: FloatParam::new(
"LFO3 Phase",
0.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
),
// Modulators
////////////////////////////////////////////////////////////////////////////////////
mod_amount_knob_1: FloatParam::new(
"Mod Amt 1",
0.0,
FloatRange::Linear {
min: -1.0,
max: 1.0,
},
)
.with_value_to_string(format_nothing()),
mod_amount_knob_2: FloatParam::new(
"Mod Amt 2",
0.0,
FloatRange::Linear {
min: -1.0,
max: 1.0,
},
)
.with_value_to_string(format_nothing()),
mod_amount_knob_3: FloatParam::new(
"Mod Amt 3",
0.0,
FloatRange::Linear {
min: -1.0,
max: 1.0,
},
)
.with_value_to_string(format_nothing()),
mod_amount_knob_4: FloatParam::new(
"Mod Amt 4",
0.0,
FloatRange::Linear {
min: -1.0,
max: 1.0,
},
)
.with_value_to_string(format_nothing()),
mod_source_1: EnumParam::new("Source 1", ModulationSource::None),
mod_source_2: EnumParam::new("Source 2", ModulationSource::None),
mod_source_3: EnumParam::new("Source 3", ModulationSource::None),
mod_source_4: EnumParam::new("Source 4", ModulationSource::None),
mod_destination_1: EnumParam::new("Dest 1", ModulationDestination::None),
mod_destination_2: EnumParam::new("Dest 2", ModulationDestination::None),
mod_destination_3: EnumParam::new("Dest 3", ModulationDestination::None),
mod_destination_4: EnumParam::new("Dest 4", ModulationDestination::None),
// EQ
pre_use_eq: BoolParam::new("EQ", false),
pre_low_freq: FloatParam::new(
"Low",
800.0,
FloatRange::Linear {
min: 100.0,
max: 2000.0,
},
)
.with_step_size(1.0)
.with_smoother(SmoothingStyle::Linear(5.0))
.with_value_to_string(formatters::v2s_f32_hz_then_khz(0)),
pre_mid_freq: FloatParam::new(
"Mid",
3000.0,
FloatRange::Linear {
min: 1000.0,
max: 8000.0,
},
)
.with_step_size(1.0)
.with_smoother(SmoothingStyle::Linear(5.0))
.with_value_to_string(formatters::v2s_f32_hz_then_khz(0)),
pre_high_freq: FloatParam::new(
"High",
10000.0,
FloatRange::Linear {
min: 3000.0,
max: 20000.0,
},
)
.with_step_size(1.0)
.with_smoother(SmoothingStyle::Linear(5.0))
.with_value_to_string(formatters::v2s_f32_hz_then_khz(0)),
pre_low_gain: FloatParam::new(
"Low Gain",
0.0,
FloatRange::Linear {
min: -12.0,
max: 12.0,
},
)
.with_value_to_string(formatters::v2s_f32_rounded(1)),
pre_mid_gain: FloatParam::new(
"Mid Gain",
0.0,
FloatRange::Linear {
min: -12.0,
max: 12.0,
},
)
.with_value_to_string(formatters::v2s_f32_rounded(1)),
pre_high_gain: FloatParam::new(
"High Gain",
0.0,
FloatRange::Linear {
min: -12.0,
max: 12.0,
},
)
.with_value_to_string(formatters::v2s_f32_rounded(1)),
// fx
use_fx: BoolParam::new("FX", true),
use_compressor: BoolParam::new("Compressor", false),
comp_amt: FloatParam::new("Amount", 0.3, FloatRange::Linear { min: 0.0, max: 1.0 })
.with_value_to_string(formatters::v2s_f32_rounded(2)),
comp_atk: FloatParam::new("Attack", 0.8, FloatRange::Linear { min: 0.0, max: 1.0 })
.with_value_to_string(formatters::v2s_f32_rounded(2)),
comp_rel: FloatParam::new("Release", 0.3, FloatRange::Linear { min: 0.0, max: 1.0 })
.with_value_to_string(formatters::v2s_f32_rounded(2)),
comp_drive: FloatParam::new("Drive", 0.3, FloatRange::Linear { min: 0.0, max: 1.0 })
.with_value_to_string(formatters::v2s_f32_rounded(2)),
use_abass: BoolParam::new("ABass", false),
abass_amount: FloatParam::new(
"Amount",
0.0011,
FloatRange::Skewed {
min: 0.0,
max: 1.0,
factor: 0.3,
},
)
.with_value_to_string(formatters::v2s_f32_rounded(4)),
use_saturation: BoolParam::new("Saturation", false),
sat_amt: FloatParam::new("Amount", 0.0, FloatRange::Linear { min: 0.0, max: 1.0 })
.with_value_to_string(formatters::v2s_f32_rounded(2)),
sat_type: EnumParam::new("Type", SaturationType::Tape),
use_delay: BoolParam::new("Delay", false),
delay_amount: FloatParam::new("Amount", 0.5, FloatRange::Linear { min: 0.0, max: 1.0 })
.with_value_to_string(formatters::v2s_f32_rounded(2)),
delay_time: EnumParam::new("Time", DelaySnapValues::Quarter),
delay_decay: FloatParam::new("Decay", 0.5, FloatRange::Linear { min: 0.001, max: 1.0 })
.with_value_to_string(formatters::v2s_f32_rounded(2)),
delay_type: EnumParam::new("Type", DelayType::Stereo),
use_reverb: BoolParam::new("Reverb", false),
reverb_amount: FloatParam::new(
"Amount",
0.85,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_rounded(2)),
reverb_size: FloatParam::new("Size", 1.0, FloatRange::Linear { min: 0.001, max: 2.0 })
.with_value_to_string(formatters::v2s_f32_rounded(2)),
reverb_feedback: FloatParam::new(
"Feedback",
0.28,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_rounded(2)),
use_phaser: BoolParam::new("Phaser", false),
phaser_amount: FloatParam::new(
"Amount",
0.5,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_rounded(2)),
phaser_depth: FloatParam::new("Depth", 1.0, FloatRange::Linear { min: 0.0, max: 1.0 })
.with_value_to_string(formatters::v2s_f32_rounded(2)),
phaser_rate: FloatParam::new(
"Rate",
1.0,
FloatRange::Linear {
min: 0.1,
max: 100.0,
},
)
.with_step_size(0.1)
.with_value_to_string(formatters::v2s_f32_rounded(2)),
phaser_feedback: FloatParam::new(
"Feedback",
0.5,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_rounded(2)),
use_buffermod: BoolParam::new("Buffer Modulator", false),
buffermod_amount: FloatParam::new(
"Amount",
0.5,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_rounded(2)),
buffermod_depth: FloatParam::new(
"Depth",
1.0,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_rounded(2)),
buffermod_spread: FloatParam::new(
"Spread",
0.0,
FloatRange::Skewed {
min: 0.0,
max: 1.0,
factor: 0.5,
},
)
.with_step_size(0.001)
.with_value_to_string(formatters::v2s_f32_rounded(3)),
buffermod_rate: FloatParam::new(
"Rate",
0.01,
FloatRange::Skewed {
min: 0.01,
max: 3.0,
factor: 0.5,
},
)
.with_step_size(0.001)
.with_value_to_string(formatters::v2s_f32_rounded(3)),
buffermod_timing: FloatParam::new(
"Buffer",
620.0,
FloatRange::Skewed {
min: 1.0,
max: 2700.0,
factor: 0.5,
},
)
.with_step_size(1.0)
.with_value_to_string(formatters::v2s_f32_rounded(0)),
use_flanger: BoolParam::new("Flanger", false),
flanger_amount: FloatParam::new(
"Amount",
0.5,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_rounded(2)),
flanger_depth: FloatParam::new("Depth", 0.5, FloatRange::Linear { min: 0.0, max: 1.0 })
.with_value_to_string(formatters::v2s_f32_rounded(2)),
flanger_rate: FloatParam::new(
"Rate",
5.0,
FloatRange::Linear {
min: 0.01,
max: 24.0,
},
)
.with_step_size(0.01)
.with_value_to_string(formatters::v2s_f32_rounded(2)),
flanger_feedback: FloatParam::new(
"Feedback",
0.5,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_rounded(2)),
use_limiter: BoolParam::new("Limiter", false),
limiter_threshold: FloatParam::new(
"Threshold",
0.5,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_value_to_string(formatters::v2s_f32_rounded(2)),
limiter_knee: FloatParam::new("Knee", 0.5, FloatRange::Linear { min: 0.0, max: 1.0 })
.with_value_to_string(formatters::v2s_f32_rounded(2)),
// UI Non-Param Params
////////////////////////////////////////////////////////////////////////////////////
param_load_bank: BoolParam::new("Load Bank", false)
.with_callback({
let load_bank = load_bank.clone();
Arc::new(move |_| *load_bank.lock().unwrap() = true)
})
.hide(),
param_save_bank: BoolParam::new("Save Bank", false)
.with_callback({
let save_bank = save_bank.clone();
Arc::new(move |_| *save_bank.lock().unwrap() = true)
})
.hide(),
param_import_preset: BoolParam::new("Import Preset", false)
.with_callback({
let import_preset = import_preset.clone();
Arc::new(move |_| *import_preset.lock().unwrap() = true)
})
.hide(),
param_export_preset: BoolParam::new("Export Preset", false)
.with_callback({
let export_preset = export_preset.clone();
Arc::new(move |_| *export_preset.lock().unwrap() = true)
})
.hide(),
preset_category: EnumParam::new("Type", PresetType::Select),
tag_acid: BoolParam::new("Acid", false).hide(),
tag_analog: BoolParam::new("Analog", false).hide(),
tag_bright: BoolParam::new("Bright", false).hide(),
tag_chord: BoolParam::new("Chord", false).hide(),
tag_crisp: BoolParam::new("Crisp", false).hide(),
tag_deep: BoolParam::new("Deep", false).hide(),
tag_delicate: BoolParam::new("Delicate", false).hide(),
tag_hard: BoolParam::new("Hard", false).hide(),
tag_harsh: BoolParam::new("Harsh", false).hide(),
tag_lush: BoolParam::new("Lush", false).hide(),
tag_mellow: BoolParam::new("Mellow", false).hide(),
tag_resonant: BoolParam::new("Resonant", false).hide(),
tag_rich: BoolParam::new("Rich", false).hide(),
tag_sharp: BoolParam::new("Sharp", false).hide(),
tag_silky: BoolParam::new("Silky", false).hide(),
tag_smooth: BoolParam::new("Smooth", false).hide(),
tag_soft: BoolParam::new("Soft", false).hide(),
tag_stab: BoolParam::new("Stab", false).hide(),
tag_warm: BoolParam::new("Warm", false).hide(),
// For some reason the callback doesn't work right here so I went by validating params for previous and next
param_next_preset: BoolParam::new("->", false).hide(),
param_prev_preset: BoolParam::new("<-", false).hide(),
param_update_current_preset: BoolParam::new("Update Preset", false)
.with_callback({
let update_current_preset = update_current_preset.clone();
Arc::new(move |_| update_current_preset.store(true, Ordering::Relaxed))
})
.hide(),
// Not a param
loading: BoolParam::new("loading_mod", false).hide(),
}
}
}
impl Plugin for Actuate {
const NAME: &'static str = "Actuate";
const VENDOR: &'static str = "Ardura";
const URL: &'static str = "https://github.com/ardura";
const EMAIL: &'static str = "azviscarra@gmail.com";
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const SAMPLE_ACCURATE_AUTOMATION: bool = true;
const MIDI_INPUT: MidiConfig = MidiConfig::Basic;
const MIDI_OUTPUT: MidiConfig = MidiConfig::Basic;
type SysExMessage = ();
type BackgroundTask = ();
const AUDIO_IO_LAYOUTS: &'static [AudioIOLayout] = &[AudioIOLayout {
main_input_channels: None,
main_output_channels: NonZeroU32::new(2),
..AudioIOLayout::const_default()
}];
fn params(&self) -> Arc<dyn Params> {
self.params.clone()
}
// This draws our GUI with egui library
fn editor(&mut self, _async_executor: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
let params: Arc<ActuateParams> = self.params.clone();
let arc_preset: Arc<Mutex<Vec<ActuatePreset>>> = Arc::clone(&self.preset_lib); //Arc<Mutex<Vec<ActuatePreset>>>
let arc_preset_name: Arc<Mutex<String>> = Arc::clone(&self.preset_name);
let arc_preset_info: Arc<Mutex<String>> = Arc::clone(&self.preset_info);
let arc_preset_category: Arc<Mutex<PresetType>> = Arc::clone(&self.preset_category);
let clear_voices: Arc<AtomicBool> = Arc::clone(&self.clear_voices);
let reload_entire_preset: Arc<Mutex<bool>> = Arc::clone(&self.reload_entire_preset);
let current_preset: Arc<AtomicU32> = Arc::clone(&self.current_preset);
let AM1: Arc<Mutex<AudioModule>> = Arc::clone(&self.audio_module_1);
let AM2: Arc<Mutex<AudioModule>> = Arc::clone(&self.audio_module_2);
let AM3: Arc<Mutex<AudioModule>> = Arc::clone(&self.audio_module_3);
let update_current_preset: Arc<AtomicBool> = Arc::clone(&self.update_current_preset);
let load_bank: Arc<Mutex<bool>> = Arc::clone(&self.load_bank);
let save_bank: Arc<Mutex<bool>> = Arc::clone(&self.save_bank);
let import_preset: Arc<Mutex<bool>> = Arc::clone(&self.import_preset);
let export_preset: Arc<Mutex<bool>> = Arc::clone(&self.export_preset);
let loading: Arc<AtomicBool> = Arc::clone(&self.file_dialog);
let filter_select_outside: Arc<Mutex<UIBottomSelection>> =
Arc::new(Mutex::new(UIBottomSelection::Filter1));
let lfo_select_outside: Arc<Mutex<LFOSelect>> = Arc::new(Mutex::new(LFOSelect::INFO));
let mod_source_1_tracker_outside: Arc<Mutex<ModulationSource>> =
Arc::new(Mutex::new(ModulationSource::None));
let mod_source_2_tracker_outside: Arc<Mutex<ModulationSource>> =
Arc::new(Mutex::new(ModulationSource::None));
let mod_source_3_tracker_outside: Arc<Mutex<ModulationSource>> =
Arc::new(Mutex::new(ModulationSource::None));
let mod_source_4_tracker_outside: Arc<Mutex<ModulationSource>> =
Arc::new(Mutex::new(ModulationSource::None));
let mod_dest_1_tracker_outside: Arc<Mutex<ModulationDestination>> =
Arc::new(Mutex::new(ModulationDestination::None));
let mod_dest_2_tracker_outside: Arc<Mutex<ModulationDestination>> =
Arc::new(Mutex::new(ModulationDestination::None));
let mod_dest_3_tracker_outside: Arc<Mutex<ModulationDestination>> =
Arc::new(Mutex::new(ModulationDestination::None));
let mod_dest_4_tracker_outside: Arc<Mutex<ModulationDestination>> =
Arc::new(Mutex::new(ModulationDestination::None));
let preset_category_tracker_outside: Arc<Mutex<PresetType>> =
Arc::new(Mutex::new(PresetType::Select));
let mod_source_override_1 = self.mod_override_source_1.clone();
let mod_source_override_2 = self.mod_override_source_2.clone();
let mod_source_override_3 = self.mod_override_source_3.clone();
let mod_source_override_4 = self.mod_override_source_4.clone();
let mod_dest_override_1 = self.mod_override_dest_1.clone();
let mod_dest_override_2 = self.mod_override_dest_2.clone();
let mod_dest_override_3 = self.mod_override_dest_3.clone();
let mod_dest_override_4 = self.mod_override_dest_4.clone();
let preset_category_override = self.preset_category_override.clone();
// Do our GUI stuff. Store this to later get parent window handle from it
create_egui_editor(
self.params.editor_state.clone(),
(),
|_, _| {},
move |egui_ctx, setter, _state| {
egui::CentralPanel::default()
.show(egui_ctx, |ui| {
let current_preset_index = current_preset.load(Ordering::Relaxed);
let filter_select = filter_select_outside.clone();
let lfo_select = lfo_select_outside.clone();
let mod_source_1_tracker = mod_source_1_tracker_outside.clone();
let mod_source_2_tracker = mod_source_2_tracker_outside.clone();
let mod_source_3_tracker = mod_source_3_tracker_outside.clone();
let mod_source_4_tracker = mod_source_4_tracker_outside.clone();
let mod_dest_1_tracker = mod_dest_1_tracker_outside.clone();
let mod_dest_2_tracker = mod_dest_2_tracker_outside.clone();
let mod_dest_3_tracker = mod_dest_3_tracker_outside.clone();
let mod_dest_4_tracker = mod_dest_4_tracker_outside.clone();
let preset_category_tracker = preset_category_tracker_outside.clone();
// Reset our buttons
if params.param_next_preset.value() {
if current_preset_index < (PRESET_BANK_SIZE - 1) as u32 {
loading.store(true, Ordering::Relaxed);
setter.set_parameter(¶ms.loading, true);
current_preset.store(current_preset_index + 1, Ordering::Relaxed);
setter.set_parameter(¶ms.param_next_preset, false);
clear_voices.store(true, Ordering::Relaxed);
// Move to info tab on preset change
*lfo_select.lock().unwrap() = LFOSelect::INFO;
// Update our displayed info
let temp_current_preset = arc_preset.lock().unwrap()[current_preset_index as usize + 1].clone();
*arc_preset_name.lock().unwrap() = temp_current_preset.preset_name;
*arc_preset_info.lock().unwrap() = temp_current_preset.preset_info;
// This is manually here to make sure it appears for long loads from different threads
// Create the loading popup here.
let screen_size = Rect::from_x_y_ranges(
RangeInclusive::new(0.0, WIDTH as f32),
RangeInclusive::new(0.0, HEIGHT as f32));
let popup_size = Vec2::new(400.0, 200.0);
let popup_pos = screen_size.center();
// Draw the loading popup content here.
ui.painter().rect_filled(Rect::from_center_size(Pos2 { x: popup_pos.x, y: popup_pos.y }, popup_size), 10.0, Color32::GRAY);
ui.painter().text(popup_pos, Align2::CENTER_CENTER, "Loading...", LOADING_FONT, Color32::BLACK);
// GUI thread misses this without this call here for some reason
(
*mod_source_override_1.lock().unwrap(),
*mod_source_override_2.lock().unwrap(),
*mod_source_override_3.lock().unwrap(),
*mod_source_override_4.lock().unwrap(),
*mod_dest_override_1.lock().unwrap(),
*mod_dest_override_2.lock().unwrap(),
*mod_dest_override_3.lock().unwrap(),
*mod_dest_override_4.lock().unwrap(),
*preset_category_override.lock().unwrap(),
) = Actuate::reload_entire_preset(
setter,
params.clone(),
(current_preset_index + 1) as usize,
arc_preset.clone(),
AM1.clone(),
AM2.clone(),
AM3.clone(),);
// This is the gui value only - the preset type itself is loaded in the preset already
*arc_preset_category.lock().unwrap() = *preset_category_override.lock().unwrap();
// This is set for the process thread
*reload_entire_preset.lock().unwrap() = true;
}
setter.set_parameter(¶ms.loading, false);
}
if params.param_prev_preset.value() {
if current_preset_index > 0 {
loading.store(true, Ordering::Relaxed);
setter.set_parameter(¶ms.loading, true);
current_preset.store(current_preset_index - 1, Ordering::Relaxed);
setter.set_parameter(¶ms.param_prev_preset, false);
clear_voices.store(true, Ordering::Relaxed);
// Move to info tab on preset change
*lfo_select.lock().unwrap() = LFOSelect::INFO;
// Update our displayed info
let temp_current_preset = arc_preset.lock().unwrap()[current_preset_index as usize - 1].clone();
*arc_preset_name.lock().unwrap() = temp_current_preset.preset_name;
*arc_preset_info.lock().unwrap() = temp_current_preset.preset_info;
// This is manually here to make sure it appears for long loads from different threads
// Create the loading popup here.
let screen_size = Rect::from_x_y_ranges(
RangeInclusive::new(0.0, WIDTH as f32),
RangeInclusive::new(0.0, HEIGHT as f32));
let popup_size = Vec2::new(400.0, 200.0);
let popup_pos = screen_size.center();
// Draw the loading popup content here.
ui.painter().rect_filled(Rect::from_center_size(Pos2 { x: popup_pos.x, y: popup_pos.y }, popup_size), 10.0, Color32::GRAY);
ui.painter().text(popup_pos, Align2::CENTER_CENTER, "Loading...", LOADING_FONT, Color32::BLACK);
// GUI thread misses this without this call here for some reason
(
*mod_source_override_1.lock().unwrap(),
*mod_source_override_2.lock().unwrap(),
*mod_source_override_3.lock().unwrap(),
*mod_source_override_4.lock().unwrap(),
*mod_dest_override_1.lock().unwrap(),
*mod_dest_override_2.lock().unwrap(),
*mod_dest_override_3.lock().unwrap(),
*mod_dest_override_4.lock().unwrap(),
*preset_category_override.lock().unwrap(),
) = Actuate::reload_entire_preset(
setter,
params.clone(),
(current_preset_index - 1) as usize,
arc_preset.clone(),
AM1.clone(),
AM2.clone(),
AM3.clone(),);
// This is the gui value only - the preset type itself is loaded in the preset already
*arc_preset_category.lock().unwrap() = *preset_category_override.lock().unwrap();
// This is set for the process thread
*reload_entire_preset.lock().unwrap() = true;
}
setter.set_parameter(¶ms.loading, false);
}
if *load_bank.lock().unwrap() {
setter.set_parameter(¶ms.loading, true);
*reload_entire_preset.lock().unwrap() = true;
loading.store(true, Ordering::Relaxed);
// Move to info tab on preset change
*lfo_select.lock().unwrap() = LFOSelect::INFO;
// This is manually here to make sure it appears for long loads from different threads
// Create the loading popup here.
let screen_size = Rect::from_x_y_ranges(
RangeInclusive::new(0.0, WIDTH as f32),
RangeInclusive::new(0.0, HEIGHT as f32));
let popup_size = Vec2::new(400.0, 200.0);
let popup_pos = screen_size.center();
// Draw the loading popup content here.
ui.painter().rect_filled(Rect::from_center_size(Pos2 { x: popup_pos.x, y: popup_pos.y }, popup_size), 10.0, Color32::GRAY);
ui.painter().text(popup_pos, Align2::CENTER_CENTER, "Loading...", LOADING_FONT, Color32::BLACK);
(
*mod_source_override_1.lock().unwrap(),
*mod_source_override_2.lock().unwrap(),
*mod_source_override_3.lock().unwrap(),
*mod_source_override_4.lock().unwrap(),
*mod_dest_override_1.lock().unwrap(),
*mod_dest_override_2.lock().unwrap(),
*mod_dest_override_3.lock().unwrap(),
*mod_dest_override_4.lock().unwrap(),
*preset_category_override.lock().unwrap(),
) = Actuate::reload_entire_preset(
setter,
params.clone(),
current_preset_index as usize,
arc_preset.clone(),
AM1.clone(),
AM2.clone(),
AM3.clone(),);
setter.set_parameter(¶ms.param_load_bank, false);
*load_bank.lock().unwrap() = false;
*reload_entire_preset.lock().unwrap() = false;
setter.set_parameter(¶ms.loading, false);
}
if *save_bank.lock().unwrap() {
setter.set_parameter(¶ms.param_save_bank, false);
*save_bank.lock().unwrap() = false;
}
if *export_preset.lock().unwrap() {
setter.set_parameter(¶ms.param_export_preset, false);
*export_preset.lock().unwrap() = false;
}
if *import_preset.lock().unwrap() {
setter.set_parameter(¶ms.loading, true);
*reload_entire_preset.lock().unwrap() = true;
loading.store(true, Ordering::Relaxed);
setter.set_parameter(¶ms.param_import_preset, false);
*import_preset.lock().unwrap() = false;
// This is manually here to make sure it appears for long loads from different threads
// Create the loading popup here.
let screen_size = Rect::from_x_y_ranges(
RangeInclusive::new(0.0, WIDTH as f32),
RangeInclusive::new(0.0, HEIGHT as f32));
let popup_size = Vec2::new(400.0, 200.0);
let popup_pos = screen_size.center();
// Draw the loading popup content here.
ui.painter().rect_filled(Rect::from_center_size(Pos2 { x: popup_pos.x, y: popup_pos.y }, popup_size), 10.0, Color32::GRAY);
ui.painter().text(popup_pos, Align2::CENTER_CENTER, "Loading...", LOADING_FONT, Color32::BLACK);
(
*mod_source_override_1.lock().unwrap(),
*mod_source_override_2.lock().unwrap(),
*mod_source_override_3.lock().unwrap(),
*mod_source_override_4.lock().unwrap(),
*mod_dest_override_1.lock().unwrap(),
*mod_dest_override_2.lock().unwrap(),
*mod_dest_override_3.lock().unwrap(),
*mod_dest_override_4.lock().unwrap(),
*preset_category_override.lock().unwrap(),
) = Actuate::reload_entire_preset(
setter,
params.clone(),
current_preset_index as usize,
arc_preset.clone(),
AM1.clone(),
AM2.clone(),
AM3.clone(),);
setter.set_parameter(¶ms.param_load_bank, false);
*load_bank.lock().unwrap() = false;
*reload_entire_preset.lock().unwrap() = false;
setter.set_parameter(¶ms.loading, false);
}
if params.param_export_preset.value() {
setter.set_parameter(¶ms.param_export_preset, false);
*export_preset.lock().unwrap() = false;
}
if params.param_import_preset.value() {
setter.set_parameter(¶ms.param_import_preset, false);
*import_preset.lock().unwrap() = false;
}
// Extra checks for sanity
if params.param_load_bank.value() {
setter.set_parameter(¶ms.param_load_bank, false);
}
if params.param_save_bank.value() {
setter.set_parameter(¶ms.param_save_bank, false);
}
if update_current_preset.load(Ordering::Relaxed) || params.param_update_current_preset.value() {
setter.set_parameter(¶ms.param_update_current_preset, false);
update_current_preset.store(false, Ordering::Relaxed);
}
if params.filter_cutoff_link.value() {
setter.set_parameter(¶ms.filter_cutoff_2, params.filter_cutoff.value());
}
// Assign default colors
ui.style_mut().visuals.widgets.inactive.bg_stroke.color = A_KNOB_OUTSIDE_COLOR;
ui.style_mut().visuals.widgets.inactive.bg_fill = DARK_GREY_UI_COLOR;
ui.style_mut().visuals.widgets.active.fg_stroke.color = A_KNOB_OUTSIDE_COLOR;
ui.style_mut().visuals.widgets.active.bg_stroke.color = A_KNOB_OUTSIDE_COLOR;
ui.style_mut().visuals.widgets.open.fg_stroke.color = A_KNOB_OUTSIDE_COLOR;
ui.style_mut().visuals.widgets.open.bg_fill = DARK_GREY_UI_COLOR;
// Lettering on param sliders
ui.style_mut().visuals.widgets.inactive.fg_stroke.color = A_KNOB_OUTSIDE_COLOR;
// Background of the bar in param sliders
ui.style_mut().visuals.selection.bg_fill = A_KNOB_OUTSIDE_COLOR;
ui.style_mut().visuals.selection.stroke.color = A_KNOB_OUTSIDE_COLOR;
// Unfilled background of the bar
ui.style_mut().visuals.widgets.noninteractive.bg_fill = DARK_GREY_UI_COLOR;
// egui 0.20 to 0.22 changed this styling then I later decided proportional looks nice
//ui.style_mut().drag_value_text_style = egui::TextStyle::Monospace;
// Trying to draw background colors as rects
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new(0.0, WIDTH as f32),
RangeInclusive::new(0.0, (HEIGHT as f32)*0.72)),
Rounding::from(16.0), A_BACKGROUND_COLOR_TOP);
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new(0.0, WIDTH as f32),
RangeInclusive::new((HEIGHT as f32)*0.72, HEIGHT as f32)),
Rounding::from(16.0), DARK_GREY_UI_COLOR);
//ui.set_style(ui.style_mut());
ui.horizontal(|ui| {
// Synth Bars on left and right
let synth_bar_space = 32.0;
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new(0.0, synth_bar_space),
RangeInclusive::new(0.0, HEIGHT as f32)),
Rounding::none(),
SYNTH_BARS_PURPLE
);
// Spacers for primary generator knobs
let generator_separator_length: f32 = 170.0;
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new(synth_bar_space + 4.0, synth_bar_space + generator_separator_length),
RangeInclusive::new(192.0, 194.0)),
Rounding::none(),
LIGHTER_GREY_UI_COLOR
);
// Spacers for primary generator knobs
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new(synth_bar_space + 4.0, synth_bar_space + generator_separator_length),
RangeInclusive::new(328.0, 330.0)),
Rounding::none(),
LIGHTER_GREY_UI_COLOR
);
ui.add_space(synth_bar_space);
// GUI Structure
ui.vertical(|ui| {
ui.horizontal(|ui|{
ui.label(RichText::new("Actuate")
.font(FONT)
.color(FONT_COLOR))
.on_hover_text("by Ardura!");
ui.separator();
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.master_level, setter)
.slimmer(0.5)
.set_left_sided_label(true)
.set_label_width(70.0)
.with_width(30.0));
ui.separator();
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.voice_limit, setter)
.slimmer(0.5)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(30.0));
ui.separator();
ui.label(RichText::new("FX")
.font(FONT)
.background_color(A_BACKGROUND_COLOR_TOP)
.color(FONT_COLOR)
)
.on_hover_text("Process FX");
let use_fx_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.use_fx, setter);
ui.add(use_fx_toggle);
ui.separator();
let load_bank_button = BoolButton::BoolButton::for_param(¶ms.param_load_bank, setter, 3.5, 0.9, SMALLER_FONT)
.with_background_color(DARK_GREY_UI_COLOR);
ui.add(load_bank_button);
let save_bank_button = BoolButton::BoolButton::for_param(¶ms.param_save_bank, setter, 3.5, 0.9, SMALLER_FONT)
.with_background_color(DARK_GREY_UI_COLOR);
ui.add(save_bank_button);
let prev_preset_button = BoolButton::BoolButton::for_param(¶ms.param_prev_preset, setter, 1.5, 0.9, FONT)
.with_background_color(DARK_GREY_UI_COLOR);
ui.add(prev_preset_button);
ui.label(RichText::new("Preset")
.background_color(A_BACKGROUND_COLOR_TOP)
.color(FONT_COLOR)
.size(16.0));
ui.label(RichText::new(current_preset_index.to_string())
.background_color(A_BACKGROUND_COLOR_TOP)
.color(FONT_COLOR)
.size(16.0));
let next_preset_button = BoolButton::BoolButton::for_param(¶ms.param_next_preset, setter, 1.5, 0.9, FONT)
.with_background_color(DARK_GREY_UI_COLOR);
ui.add(next_preset_button);
ui.separator();
ui.button(RichText::new("Browse Presets")
.font(SMALLER_FONT)
.background_color(DARK_GREY_UI_COLOR)
.color(FONT_COLOR)
).on_hover_text("Coming soon!");
});
ui.separator();
const KNOB_SIZE: f32 = 32.0;
const TEXT_SIZE: f32 = 11.0;
ui.horizontal(|ui|{
ui.vertical(|ui|{
ui.label(RichText::new("Generators")
.font(FONT)
.color(FONT_COLOR))
.on_hover_text("These are the audio modules that create sound on midi events");
// Side knobs for types
ui.horizontal(|ui|{
let audio_module_1_knob = ui_knob::ArcKnob::for_param(
¶ms._audio_module_1_type,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(DARK_GREY_UI_COLOR)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(audio_module_1_knob);
let audio_module_1_level_knob = ui_knob::ArcKnob::for_param(
¶ms.audio_module_1_level,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(DARK_GREY_UI_COLOR)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(audio_module_1_level_knob);
});
ui.horizontal(|ui|{
let audio_module_1_filter_routing = ui_knob::ArcKnob::for_param(
¶ms.audio_module_1_routing,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(DARK_GREY_UI_COLOR)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(audio_module_1_filter_routing);
});
ui.horizontal(|ui|{
let audio_module_2_knob = ui_knob::ArcKnob::for_param(
¶ms._audio_module_2_type,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(DARK_GREY_UI_COLOR)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(audio_module_2_knob);
let audio_module_2_level_knob = ui_knob::ArcKnob::for_param(
¶ms.audio_module_2_level,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(DARK_GREY_UI_COLOR)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(audio_module_2_level_knob);
});
ui.horizontal(|ui|{
let audio_module_2_filter_routing = ui_knob::ArcKnob::for_param(
¶ms.audio_module_2_routing,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(DARK_GREY_UI_COLOR)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(audio_module_2_filter_routing);
});
ui.horizontal(|ui| {
let audio_module_3_knob = ui_knob::ArcKnob::for_param(
¶ms._audio_module_3_type,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(DARK_GREY_UI_COLOR)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(audio_module_3_knob);
let audio_module_3_level_knob = ui_knob::ArcKnob::for_param(
¶ms.audio_module_3_level,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(DARK_GREY_UI_COLOR)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(audio_module_3_level_knob);
});
ui.horizontal(|ui|{
let audio_module_3_filter_routing = ui_knob::ArcKnob::for_param(
¶ms.audio_module_3_routing,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(DARK_GREY_UI_COLOR)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(audio_module_3_filter_routing);
});
});
ui.separator();
ui.vertical(|ui|{
ui.label(RichText::new("Generator Controls")
.font(SMALLER_FONT)
.color(FONT_COLOR))
.on_hover_text("These are the controls for the active/selected generators");
audio_module::AudioModule::draw_modules(ui, params.clone(), setter);
});
});
//ui.add_space(32.0);
ui.label("Filters and Modulations");
// Filter section
ui.horizontal(|ui| {
ui.vertical(|ui|{
ui.horizontal(|ui|{
match *filter_select.lock().unwrap() {
UIBottomSelection::Filter1 => {
match params.filter_alg_type.value() {
FilterAlgorithms::SVF => {
ui.vertical(|ui|{
let filter_wet_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_wet,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(filter_wet_knob);
let filter_resonance_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_resonance,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(filter_resonance_knob);
});
ui.vertical(|ui|{
let filter_cutoff_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_cutoff,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(filter_cutoff_knob);
let filter_res_type_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_res_type,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(filter_res_type_knob);
});
ui.vertical(|ui|{
let filter_hp_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_hp_amount,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(filter_hp_knob);
let filter_env_peak = ui_knob::ArcKnob::for_param(
¶ms.filter_env_peak,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_readable_box(false)
.set_text_size(TEXT_SIZE);
ui.add(filter_env_peak);
});
ui.vertical(|ui| {
let filter_lp_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_lp_amount,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(filter_lp_knob);
let filter_bp_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_bp_amount,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(filter_bp_knob);
});
},
FilterAlgorithms::TILT => {
ui.vertical(|ui|{
let filter_wet_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_wet,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(filter_wet_knob);
let filter_resonance_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_resonance,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(filter_resonance_knob);
});
ui.vertical(|ui|{
let filter_cutoff_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_cutoff,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(filter_cutoff_knob);
let filter_tilt_type_knob = ui_knob::ArcKnob::for_param(
¶ms.tilt_filter_type,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(filter_tilt_type_knob);
});
ui.vertical(|ui|{
let filter_env_peak = ui_knob::ArcKnob::for_param(
¶ms.filter_env_peak,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_readable_box(false)
.set_text_size(TEXT_SIZE);
ui.add(filter_env_peak);
});
ui.add_space(KNOB_SIZE*2.0);
},
FilterAlgorithms::VCF => {
ui.vertical(|ui|{
let filter_wet_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_wet,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(filter_wet_knob);
let filter_resonance_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_resonance,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(filter_resonance_knob);
});
ui.vertical(|ui|{
let filter_cutoff_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_cutoff,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(filter_cutoff_knob);
let vcf_filter_type_knob = ui_knob::ArcKnob::for_param(
¶ms.vcf_filter_type,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_text_size(TEXT_SIZE);
ui.add(vcf_filter_type_knob);
});
ui.vertical(|ui|{
let filter_env_peak = ui_knob::ArcKnob::for_param(
¶ms.filter_env_peak,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(SYNTH_MIDDLE_BLUE)
.set_readable_box(false)
.set_text_size(TEXT_SIZE);
ui.add(filter_env_peak);
});
ui.add_space(KNOB_SIZE*2.0);
},
}
// Middle bottom light section
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new((WIDTH as f32)*0.35, (WIDTH as f32)*0.64),
RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)),
Rounding::from(16.0),
SYNTH_SOFT_BLUE
);
// Middle Bottom Filter select background
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new((WIDTH as f32)*0.38, (WIDTH as f32)*0.62),
RangeInclusive::new((HEIGHT as f32) - 26.0, (HEIGHT as f32) - 2.0)),
Rounding::from(16.0),
A_BACKGROUND_COLOR_TOP
);
},
UIBottomSelection::Filter2 => {
match params.filter_alg_type_2.value() {
FilterAlgorithms::SVF => {
ui.vertical(|ui|{
let filter_wet_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_wet_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE);
ui.add(filter_wet_knob);
let filter_resonance_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_resonance_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE);
ui.add(filter_resonance_knob);
});
ui.vertical(|ui|{
let filter_cutoff_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_cutoff_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE);
ui.add(filter_cutoff_knob);
let filter_res_type_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_res_type_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE);
ui.add(filter_res_type_knob);
});
ui.vertical(|ui|{
let filter_hp_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_hp_amount_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE);
ui.add(filter_hp_knob);
let filter_env_peak = ui_knob::ArcKnob::for_param(
¶ms.filter_env_peak_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_readable_box(false)
.set_text_size(TEXT_SIZE);
ui.add(filter_env_peak);
});
ui.vertical(|ui| {
let filter_lp_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_lp_amount_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE);
ui.add(filter_lp_knob);
let filter_bp_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_bp_amount_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE);
ui.add(filter_bp_knob);
});
},
FilterAlgorithms::TILT => {
ui.vertical(|ui|{
let filter_wet_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_wet_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE);
ui.add(filter_wet_knob);
let filter_resonance_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_resonance_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE);
ui.add(filter_resonance_knob);
});
ui.vertical(|ui|{
let filter_cutoff_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_cutoff_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE);
ui.add(filter_cutoff_knob);
let filter_tilt_type_knob = ui_knob::ArcKnob::for_param(
¶ms.tilt_filter_type_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE);
ui.add(filter_tilt_type_knob);
});
ui.vertical(|ui|{
let filter_env_peak = ui_knob::ArcKnob::for_param(
¶ms.filter_env_peak_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_readable_box(false)
.set_text_size(TEXT_SIZE);
ui.add(filter_env_peak);
});
ui.add_space(KNOB_SIZE*2.0);
},
FilterAlgorithms::VCF => {
ui.vertical(|ui|{
let filter_wet_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_wet_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE);
ui.add(filter_wet_knob);
let filter_resonance_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_resonance_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE);
ui.add(filter_resonance_knob);
});
ui.vertical(|ui|{
let filter_cutoff_knob = ui_knob::ArcKnob::for_param(
¶ms.filter_cutoff_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE);
ui.add(filter_cutoff_knob);
let vcf_filter_type_knob = ui_knob::ArcKnob::for_param(
¶ms.vcf_filter_type_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE);
ui.add(vcf_filter_type_knob);
});
ui.vertical(|ui|{
let filter_env_peak = ui_knob::ArcKnob::for_param(
¶ms.filter_env_peak_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_readable_box(false)
.set_text_size(TEXT_SIZE);
ui.add(filter_env_peak);
});
ui.add_space(KNOB_SIZE*2.0);
},
}
// Middle bottom light section
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new((WIDTH as f32)*0.35, (WIDTH as f32)*0.64),
RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)),
Rounding::from(16.0),
SYNTH_SOFT_BLUE2
);
// Middle Bottom Filter select background
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new((WIDTH as f32)*0.38, (WIDTH as f32)*0.62),
RangeInclusive::new((HEIGHT as f32) - 26.0, (HEIGHT as f32) - 2.0)),
Rounding::from(16.0),
A_BACKGROUND_COLOR_TOP
);
},
UIBottomSelection::Pitch1 => {
ui.vertical(|ui|{
ui.horizontal(|ui|{
let pitch_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.pitch_enable, setter);
ui.add(pitch_toggle);
ui.label(RichText::new("Enable Pitch Envelope")
.font(SMALLER_FONT)
.color(A_BACKGROUND_COLOR_TOP)
);
});
ui.horizontal(|ui|{
let pitch_env_peak_knob = ui_knob::ArcKnob::for_param(
¶ms.pitch_env_peak,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_readable_box(false)
.set_text_size(TEXT_SIZE);
ui.add(pitch_env_peak_knob);
let pitch_routing_knob = ui_knob::ArcKnob::for_param(
¶ms.pitch_routing,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_readable_box(false)
.set_text_size(TEXT_SIZE);
ui.add(pitch_routing_knob);
});
});
ui.add_space(KNOB_SIZE*3.5);
ui.add_space(8.0);
// Middle bottom light section
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new((WIDTH as f32)*0.35, (WIDTH as f32)*0.64),
RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)),
Rounding::from(16.0),
SYNTH_MIDDLE_BLUE
);
// Middle Bottom Filter select background
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new((WIDTH as f32)*0.38, (WIDTH as f32)*0.62),
RangeInclusive::new((HEIGHT as f32) - 26.0, (HEIGHT as f32) - 2.0)),
Rounding::from(16.0),
A_BACKGROUND_COLOR_TOP
);
},
UIBottomSelection::Pitch2 => {
ui.vertical(|ui|{
ui.horizontal(|ui|{
let pitch_toggle_2 = toggle_switch::ToggleSwitch::for_param(¶ms.pitch_enable_2, setter);
ui.add(pitch_toggle_2);
ui.label(RichText::new("Enable Pitch Envelope")
.font(SMALLER_FONT)
.color(A_BACKGROUND_COLOR_TOP)
);
});
ui.horizontal(|ui|{
let pitch_env_peak_knob_2 = ui_knob::ArcKnob::for_param(
¶ms.pitch_env_peak_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_readable_box(false)
.set_text_size(TEXT_SIZE);
ui.add(pitch_env_peak_knob_2);
let pitch_routing_knob_2 = ui_knob::ArcKnob::for_param(
¶ms.pitch_routing_2,
setter,
KNOB_SIZE)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_readable_box(false)
.set_text_size(TEXT_SIZE);
ui.add(pitch_routing_knob_2);
});
});
ui.add_space(KNOB_SIZE*3.5);
ui.add_space(8.0);
// Middle bottom light section
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new((WIDTH as f32)*0.35, (WIDTH as f32)*0.64),
RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)),
Rounding::from(16.0),
SYNTH_MIDDLE_BLUE.linear_multiply(0.8)
);
// Middle Bottom Filter select background
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new((WIDTH as f32)*0.38, (WIDTH as f32)*0.62),
RangeInclusive::new((HEIGHT as f32) - 26.0, (HEIGHT as f32) - 2.0)),
Rounding::from(16.0),
A_BACKGROUND_COLOR_TOP
);
}
}
});
});
////////////////////////////////////////////////////////////
// ADSR FOR FILTER
const VERT_BAR_HEIGHT: f32 = 106.0;
const VERT_BAR_WIDTH: f32 = 14.0;
const HCURVE_WIDTH: f32 = 120.0;
const HCURVE_BWIDTH: f32 = 28.0;
ui.vertical(|ui|{
ui.horizontal(|ui|{
match *filter_select.lock().unwrap() {
UIBottomSelection::Filter1 => {
// ADSR
ui.add(
VerticalParamSlider::for_param(¶ms.filter_env_attack, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
SYNTH_MIDDLE_BLUE,
),
);
ui.add(
VerticalParamSlider::for_param(¶ms.filter_env_decay, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
SYNTH_MIDDLE_BLUE,
),
);
ui.add(
VerticalParamSlider::for_param(¶ms.filter_env_sustain, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
SYNTH_MIDDLE_BLUE,
),
);
ui.add(
VerticalParamSlider::for_param(¶ms.filter_env_release, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
SYNTH_MIDDLE_BLUE,
),
);
},
UIBottomSelection::Filter2 => {
// ADSR
ui.add(
VerticalParamSlider::for_param(¶ms.filter_env_attack_2, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
A_KNOB_OUTSIDE_COLOR,
),
);
ui.add(
VerticalParamSlider::for_param(¶ms.filter_env_decay_2, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
A_KNOB_OUTSIDE_COLOR,
),
);
ui.add(
VerticalParamSlider::for_param(¶ms.filter_env_sustain_2, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
A_KNOB_OUTSIDE_COLOR,
),
);
ui.add(
VerticalParamSlider::for_param(¶ms.filter_env_release_2, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
A_KNOB_OUTSIDE_COLOR,
),
);
},
UIBottomSelection::Pitch1 => {
// ADSR
ui.add(
VerticalParamSlider::for_param(¶ms.pitch_env_attack, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
SYNTH_SOFT_BLUE,
),
);
ui.add(
VerticalParamSlider::for_param(¶ms.pitch_env_decay, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
SYNTH_SOFT_BLUE,
),
);
ui.add(
VerticalParamSlider::for_param(¶ms.pitch_env_sustain, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
SYNTH_SOFT_BLUE,
),
);
ui.add(
VerticalParamSlider::for_param(¶ms.pitch_env_release, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
SYNTH_SOFT_BLUE,
),
);
},
UIBottomSelection::Pitch2 => {
// ADSR
ui.add(
VerticalParamSlider::for_param(¶ms.pitch_env_attack_2, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
SYNTH_SOFT_BLUE2,
),
);
ui.add(
VerticalParamSlider::for_param(¶ms.pitch_env_decay_2, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
SYNTH_SOFT_BLUE2,
),
);
ui.add(
VerticalParamSlider::for_param(¶ms.pitch_env_sustain_2, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
SYNTH_SOFT_BLUE2,
),
);
ui.add(
VerticalParamSlider::for_param(¶ms.pitch_env_release_2, setter)
.with_width(VERT_BAR_WIDTH)
.with_height(VERT_BAR_HEIGHT)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
SYNTH_SOFT_BLUE2,
),
);
}
}
// Curve sliders
ui.vertical(|ui| {
match *filter_select.lock().unwrap() {
UIBottomSelection::Filter1 => {
ui.add(
HorizontalParamSlider::for_param(¶ms.filter_env_atk_curve, setter)
.with_width(HCURVE_BWIDTH)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(HCURVE_WIDTH)
.override_colors(
DARK_GREY_UI_COLOR,
SYNTH_MIDDLE_BLUE),
);
ui.add(
HorizontalParamSlider::for_param(¶ms.filter_env_dec_curve, setter)
.with_width(HCURVE_BWIDTH)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(HCURVE_WIDTH)
.override_colors(
DARK_GREY_UI_COLOR,
SYNTH_MIDDLE_BLUE),
);
ui.add(
HorizontalParamSlider::for_param(¶ms.filter_env_rel_curve, setter)
.with_width(HCURVE_BWIDTH)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(HCURVE_WIDTH)
.override_colors(
DARK_GREY_UI_COLOR,
SYNTH_MIDDLE_BLUE),
);
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.filter_alg_type, setter)
.slimmer(0.4)
.set_left_sided_label(true)
.set_label_width(120.0)
.override_colors(Color32::WHITE, Color32::BLACK)
.with_width(30.0)
);
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.filter_alg_type_2, setter)
.slimmer(0.4)
.set_left_sided_label(true)
.set_label_width(120.0)
.override_colors(Color32::WHITE, Color32::BLACK)
.with_width(30.0)
);
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.filter_routing, setter)
.slimmer(0.4)
.set_left_sided_label(true)
.set_label_width(120.0)
.override_colors(Color32::WHITE, Color32::BLACK)
.with_width(30.0)
);
},
UIBottomSelection::Filter2 => {
ui.add(
HorizontalParamSlider::for_param(¶ms.filter_env_atk_curve_2, setter)
.with_width(HCURVE_BWIDTH)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(HCURVE_WIDTH)
.override_colors(
SYNTH_BARS_PURPLE,
A_KNOB_OUTSIDE_COLOR),
);
ui.add(
HorizontalParamSlider::for_param(¶ms.filter_env_dec_curve_2, setter)
.with_width(HCURVE_BWIDTH)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(HCURVE_WIDTH)
.override_colors(
SYNTH_BARS_PURPLE,
A_KNOB_OUTSIDE_COLOR),
);
ui.add(
HorizontalParamSlider::for_param(¶ms.filter_env_rel_curve_2, setter)
.with_width(HCURVE_BWIDTH)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(HCURVE_WIDTH)
.override_colors(
SYNTH_BARS_PURPLE,
A_KNOB_OUTSIDE_COLOR),
);
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.filter_alg_type, setter)
.slimmer(0.4)
.set_left_sided_label(true)
.set_label_width(120.0)
.override_colors(Color32::WHITE, Color32::BLACK)
.with_width(30.0)
);
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.filter_alg_type_2, setter)
.slimmer(0.4)
.set_left_sided_label(true)
.set_label_width(120.0)
.override_colors(Color32::WHITE, Color32::BLACK)
.with_width(30.0)
);
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.filter_routing, setter)
.slimmer(0.4)
.set_left_sided_label(true)
.set_label_width(120.0)
.override_colors(Color32::WHITE, Color32::BLACK)
.with_width(30.0)
);
},
UIBottomSelection::Pitch1 => {
ui.add(
HorizontalParamSlider::for_param(¶ms.pitch_env_atk_curve, setter)
.with_width(HCURVE_BWIDTH)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(HCURVE_WIDTH)
.override_colors(
DARK_GREY_UI_COLOR,
SYNTH_SOFT_BLUE2)
.with_width(30.0),
);
ui.add(
HorizontalParamSlider::for_param(¶ms.pitch_env_dec_curve, setter)
.with_width(HCURVE_BWIDTH)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(HCURVE_WIDTH)
.override_colors(
DARK_GREY_UI_COLOR,
SYNTH_SOFT_BLUE2)
.with_width(30.0),
);
ui.add(
HorizontalParamSlider::for_param(¶ms.pitch_env_rel_curve, setter)
.with_width(HCURVE_BWIDTH)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(HCURVE_WIDTH)
.override_colors(
DARK_GREY_UI_COLOR,
SYNTH_SOFT_BLUE2)
.with_width(30.0),
);
ui.add_space(67.0);
},
UIBottomSelection::Pitch2 => {
ui.add(
HorizontalParamSlider::for_param(¶ms.pitch_env_atk_curve_2, setter)
.with_width(HCURVE_BWIDTH)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(HCURVE_WIDTH)
.override_colors(
DARK_GREY_UI_COLOR,
SYNTH_SOFT_BLUE)
.with_width(30.0),
);
ui.add(
HorizontalParamSlider::for_param(¶ms.pitch_env_dec_curve_2, setter)
.with_width(HCURVE_BWIDTH)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(HCURVE_WIDTH)
.override_colors(
DARK_GREY_UI_COLOR,
SYNTH_SOFT_BLUE)
.with_width(30.0),
);
ui.add(
HorizontalParamSlider::for_param(¶ms.pitch_env_rel_curve_2, setter)
.with_width(HCURVE_BWIDTH)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(HCURVE_WIDTH)
.override_colors(
DARK_GREY_UI_COLOR,
SYNTH_SOFT_BLUE)
.with_width(30.0),
);
ui.add_space(67.0);
}
}
});
});
ui.horizontal(|ui|{
ui.add_space(25.0);
ui.selectable_value(&mut *filter_select.lock().unwrap(), UIBottomSelection::Filter1, RichText::new("Filter 1").color(Color32::BLACK));
ui.selectable_value(&mut *filter_select.lock().unwrap(), UIBottomSelection::Filter2, RichText::new("Filter 2").color(Color32::BLACK));
ui.selectable_value(&mut *filter_select.lock().unwrap(), UIBottomSelection::Pitch1, RichText::new("Pitch 1").color(Color32::BLACK));
ui.selectable_value(&mut *filter_select.lock().unwrap(), UIBottomSelection::Pitch2, RichText::new("Pitch 2").color(Color32::BLACK));
});
});
// Move Presets over!
ui.add_space(8.0);
match *lfo_select.lock().unwrap() {
LFOSelect::LFO1 => {
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new((WIDTH as f32)*0.64, (WIDTH as f32) - (synth_bar_space + 4.0)),
RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)),
Rounding::from(16.0),
A_BACKGROUND_COLOR_TOP
);
}
LFOSelect::LFO2 => {
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new((WIDTH as f32)*0.64, (WIDTH as f32) - (synth_bar_space + 4.0)),
RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)),
Rounding::from(16.0),
SYNTH_SOFT_BLUE
);
}
LFOSelect::LFO3 => {
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new((WIDTH as f32)*0.64, (WIDTH as f32) - (synth_bar_space + 4.0)),
RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)),
Rounding::from(16.0),
SYNTH_MIDDLE_BLUE
);
}
LFOSelect::Misc => {
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new((WIDTH as f32)*0.64, (WIDTH as f32) - (synth_bar_space + 4.0)),
RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)),
Rounding::from(16.0),
SYNTH_SOFT_BLUE2
);
}
LFOSelect::Modulation => {
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new((WIDTH as f32)*0.64, (WIDTH as f32) - (synth_bar_space + 4.0)),
RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)),
Rounding::from(16.0),
LIGHTER_PURPLE
);
}
LFOSelect::INFO => {
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new((WIDTH as f32)*0.64, (WIDTH as f32) - (synth_bar_space + 4.0)),
RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)),
Rounding::from(16.0),
SYNTH_SOFT_BLUE2
);
}
LFOSelect::FX => {
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new((WIDTH as f32)*0.64, (WIDTH as f32) - (synth_bar_space + 4.0)),
RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)),
Rounding::from(16.0),
FONT_COLOR
);
}
}
// LFO Box
ui.vertical(|ui|{
ui.horizontal(|ui| {
ui.selectable_value(&mut *lfo_select.lock().unwrap(), LFOSelect::INFO, RichText::new("INFO").color(Color32::BLACK).font(SMALLER_FONT));
ui.selectable_value(&mut *lfo_select.lock().unwrap(), LFOSelect::LFO1, RichText::new("LFO 1").color(Color32::BLACK).font(SMALLER_FONT));
ui.selectable_value(&mut *lfo_select.lock().unwrap(), LFOSelect::LFO2, RichText::new("LFO 2").color(Color32::BLACK).font(SMALLER_FONT));
ui.selectable_value(&mut *lfo_select.lock().unwrap(), LFOSelect::LFO3, RichText::new("LFO 3").color(Color32::BLACK).font(SMALLER_FONT));
ui.selectable_value(&mut *lfo_select.lock().unwrap(), LFOSelect::Misc, RichText::new("Misc").color(Color32::BLACK).font(SMALLER_FONT));
ui.selectable_value(&mut *lfo_select.lock().unwrap(), LFOSelect::Modulation, RichText::new("Mods").color(Color32::BLACK).font(SMALLER_FONT));
ui.selectable_value(&mut *lfo_select.lock().unwrap(), LFOSelect::FX, RichText::new("FX").color(Color32::BLACK).font(SMALLER_FONT));
});
ui.separator();
match *lfo_select.lock().unwrap() {
LFOSelect::LFO1 => {
ui.vertical(|ui|{
ui.horizontal(|ui|{
ui.label(RichText::new("LFO Enabled")
.font(SMALLER_FONT)
.color(Color32::BLACK)
);
let lfo1_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.lfo1_enable, setter);
ui.add(lfo1_toggle);
});
ui.horizontal(|ui|{
ui.label(RichText::new("Sync")
.font(SMALLER_FONT)
.color(Color32::BLACK)
)
.on_hover_text("Sync LFO values to your DAW");
let lfosync1 = toggle_switch::ToggleSwitch::for_param(¶ms.lfo1_sync, setter);
ui.add(lfosync1);
ui.separator();
ui.label(RichText::new("Retrig")
.font(SMALLER_FONT)
.color(Color32::BLACK)
);
ui.add(ParamSlider::for_param(¶ms.lfo1_retrigger, setter).with_width(80.0));
});
ui.separator();
ui.horizontal(|ui|{
ui.label(RichText::new("Rate ")
.font(SMALLER_FONT)
.color(Color32::BLACK)
);
if params.lfo1_sync.value() {
ui.add(ParamSlider::for_param(¶ms.lfo1_snap, setter).with_width(180.0));
} else {
ui.add(ParamSlider::for_param(¶ms.lfo1_freq, setter).with_width(180.0));
}
});
ui.horizontal(|ui|{
ui.label(RichText::new("Shape")
.font(SMALLER_FONT)
.color(Color32::BLACK)
);
ui.add(ParamSlider::for_param(¶ms.lfo1_waveform, setter).with_width(180.0));
});
ui.horizontal(|ui|{
ui.label(RichText::new("Phase")
.font(SMALLER_FONT)
.color(Color32::BLACK)
);
ui.add(ParamSlider::for_param(¶ms.lfo1_phase, setter).with_width(180.0));
});
});
},
LFOSelect::LFO2 => {
ui.vertical(|ui|{
ui.horizontal(|ui|{
ui.label(RichText::new("LFO Enabled")
.font(SMALLER_FONT)
.color(Color32::BLACK)
);
let lfo2_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.lfo2_enable, setter);
ui.add(lfo2_toggle);
});
ui.horizontal(|ui|{
ui.label(RichText::new("Sync")
.font(SMALLER_FONT)
.color(Color32::BLACK)
)
.on_hover_text("Sync LFO values to your DAW");
let lfosync2 = toggle_switch::ToggleSwitch::for_param(¶ms.lfo2_sync, setter);
ui.add(lfosync2);
ui.separator();
ui.label(RichText::new("Retrig")
.font(SMALLER_FONT)
.color(Color32::BLACK)
);
ui.add(ParamSlider::for_param(¶ms.lfo2_retrigger, setter).with_width(80.0));
});
ui.separator();
ui.horizontal(|ui|{
ui.label(RichText::new("Rate ")
.font(SMALLER_FONT)
.color(Color32::BLACK)
);
if params.lfo2_sync.value() {
ui.add(ParamSlider::for_param(¶ms.lfo2_snap, setter).with_width(180.0));
} else {
ui.add(ParamSlider::for_param(¶ms.lfo2_freq, setter).with_width(180.0));
}
});
ui.horizontal(|ui|{
ui.label(RichText::new("Shape")
.font(SMALLER_FONT)
.color(Color32::BLACK)
);
ui.add(ParamSlider::for_param(¶ms.lfo2_waveform, setter).with_width(180.0));
});
ui.horizontal(|ui|{
ui.label(RichText::new("Phase")
.font(SMALLER_FONT)
.color(Color32::BLACK)
);
ui.add(ParamSlider::for_param(¶ms.lfo2_phase, setter).with_width(180.0));
});
});
},
LFOSelect::LFO3 => {
ui.vertical(|ui|{
ui.horizontal(|ui|{
ui.label(RichText::new("LFO Enabled")
.font(SMALLER_FONT)
.color(Color32::BLACK)
);
let lfo3_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.lfo3_enable, setter);
ui.add(lfo3_toggle);
});
ui.horizontal(|ui|{
ui.label(RichText::new("Sync")
.font(SMALLER_FONT)
.color(Color32::BLACK)
)
.on_hover_text("Sync LFO values to your DAW");
let lfosync3 = toggle_switch::ToggleSwitch::for_param(¶ms.lfo3_sync, setter);
ui.add(lfosync3);
ui.separator();
ui.label(RichText::new("Retrig")
.font(SMALLER_FONT)
.color(Color32::BLACK)
);
ui.add(ParamSlider::for_param(¶ms.lfo3_retrigger, setter).with_width(80.0));
});
ui.separator();
ui.horizontal(|ui|{
ui.label(RichText::new("Rate ")
.font(SMALLER_FONT)
.color(Color32::BLACK)
);
if params.lfo3_sync.value() {
ui.add(ParamSlider::for_param(¶ms.lfo3_snap, setter).with_width(180.0));
} else {
ui.add(ParamSlider::for_param(¶ms.lfo3_freq, setter).with_width(180.0));
}
});
ui.horizontal(|ui|{
ui.label(RichText::new("Shape")
.font(SMALLER_FONT)
.color(Color32::BLACK)
);
ui.add(ParamSlider::for_param(¶ms.lfo3_waveform, setter).with_width(180.0));
});
ui.horizontal(|ui|{
ui.label(RichText::new("Phase")
.font(SMALLER_FONT)
.color(Color32::BLACK)
);
ui.add(ParamSlider::for_param(¶ms.lfo3_phase, setter).with_width(180.0));
});
});
},
LFOSelect::Misc => {
ui.horizontal(|ui|{
ui.label(RichText::new("Link Cutoff 2 to Cutoff 1")
.font(SMALLER_FONT)
.color(Color32::BLACK)
)
.on_hover_text("Filter 1 will control both filter cutoff values");
let filter_cutoff_link = toggle_switch::ToggleSwitch::for_param(¶ms.filter_cutoff_link, setter);
ui.add(filter_cutoff_link);
});
}
LFOSelect::Modulation => {
// This is my creative "combobox" to use an enumparam with Nih-Plug
ui.vertical(|ui|{
// Modulator section 1
//////////////////////////////////////////////////////////////////////////////////
ui.horizontal(|ui|{
let mod_1_knob = ui_knob::ArcKnob::for_param(
¶ms.mod_amount_knob_1,
setter,
12.0)
.preset_style(ui_knob::KnobStyle::NewPresets2)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_show_label(false);
ui.add(mod_1_knob);
ui.separator();
egui::ComboBox::new("mod_source_supported", "")
.selected_text(format!("{:?}", *mod_source_1_tracker.lock().unwrap()))
.width(70.0)
.show_ui(ui, |ui| {
ui.selectable_value(&mut *mod_source_1_tracker.lock().unwrap(), ModulationSource::None, "None");
ui.selectable_value(&mut *mod_source_1_tracker.lock().unwrap(), ModulationSource::Velocity, "Velocity");
ui.selectable_value(&mut *mod_source_1_tracker.lock().unwrap(), ModulationSource::LFO1, "LFO 1");
ui.selectable_value(&mut *mod_source_1_tracker.lock().unwrap(), ModulationSource::LFO2, "LFO 2");
ui.selectable_value(&mut *mod_source_1_tracker.lock().unwrap(), ModulationSource::LFO3, "LFO 3");
});
/*
CustomComboBox::ComboBox::new("mod_source_1_ID",params.mod_source_1.value().to_string(), true, 5)
.selected_text(format!("{:?}", *mod_source_1_tracker.lock().unwrap()))
.width(70.0)
.show_ui(ui, |ui|{
ui.selectable_value(&mut *mod_source_1_tracker.lock().unwrap(), ModulationSource::None, "None");
ui.selectable_value(&mut *mod_source_1_tracker.lock().unwrap(), ModulationSource::Velocity, "Velocity");
ui.selectable_value(&mut *mod_source_1_tracker.lock().unwrap(), ModulationSource::LFO1, "LFO 1");
ui.selectable_value(&mut *mod_source_1_tracker.lock().unwrap(), ModulationSource::LFO2, "LFO 2");
ui.selectable_value(&mut *mod_source_1_tracker.lock().unwrap(), ModulationSource::LFO3, "LFO 3");
});
*/
// This was a workaround for updating combobox on preset load but otherwise updating preset through combobox selection
if *mod_source_override_1.lock().unwrap() != ModulationSource::UnsetModulation {
// This happens on plugin preset load
*mod_source_1_tracker.lock().unwrap() = *mod_source_override_1.lock().unwrap();
setter.set_parameter( ¶ms.mod_source_1, mod_source_1_tracker.lock().unwrap().clone());
*mod_source_override_1.lock().unwrap() = ModulationSource::UnsetModulation;
} else {
if *mod_source_1_tracker.lock().unwrap() != params.mod_source_1.value() {
setter.set_parameter( ¶ms.mod_source_1, mod_source_1_tracker.lock().unwrap().clone());
}
}
ui.label(RichText::new("Mods")
.font(SMALLER_FONT)
.color(Color32::BLACK));
egui::ComboBox::new("mod_dest_1_ID", "")
.selected_text(format!("{:?}", *mod_dest_1_tracker.lock().unwrap()))
.width(100.0)
.show_ui(ui, |ui|{
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::None, "None");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::Cutoff_1, "Cutoff 1");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::Cutoff_2, "Cutoff 2");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::Resonance_1, "Resonance 1");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::Resonance_2, "Resonance 2");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::All_Gain, "All Gain");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::Osc1_Gain, "Osc1 Gain");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::Osc2_Gain, "Osc2 Gain");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::Osc3_Gain, "Osc3 Gain");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::All_Detune, "All Detune");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::Osc1Detune, "Osc1 Detune");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::Osc2Detune, "Osc2 Detune");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::Osc3Detune, "Osc3 Detune");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::All_UniDetune, "All UniDetune");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::Osc1UniDetune, "Osc1 UniDetune");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::Osc2UniDetune, "Osc2 UniDetune");
ui.selectable_value(&mut *mod_dest_1_tracker.lock().unwrap(), ModulationDestination::Osc3UniDetune, "Osc3 UniDetune");
});
// This was a workaround for updating combobox on preset load but otherwise updating preset through combobox selection
if *mod_dest_override_1.lock().unwrap() != ModulationDestination::UnsetModulation {
// This happens on plugin preset load
*mod_dest_1_tracker.lock().unwrap() = *mod_dest_override_1.lock().unwrap();
setter.set_parameter( ¶ms.mod_destination_1, mod_dest_1_tracker.lock().unwrap().clone());
*mod_dest_override_1.lock().unwrap() = ModulationDestination::UnsetModulation;
} else {
if *mod_dest_1_tracker.lock().unwrap() != params.mod_destination_1.value() {
setter.set_parameter( ¶ms.mod_destination_1, mod_dest_1_tracker.lock().unwrap().clone());
}
}
});
ui.separator();
// Modulator section 2
//////////////////////////////////////////////////////////////////////////////////
ui.horizontal(|ui|{
let mod_2_knob = ui_knob::ArcKnob::for_param(
¶ms.mod_amount_knob_2,
setter,
12.0)
.preset_style(ui_knob::KnobStyle::NewPresets2)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_show_label(false);
ui.add(mod_2_knob);
ui.separator();
egui::ComboBox::new("mod_source_2_ID","")
.selected_text(format!("{:?}", *mod_source_2_tracker.lock().unwrap()))
.width(70.0)
.show_ui(ui, |ui|{
ui.selectable_value(&mut *mod_source_2_tracker.lock().unwrap(), ModulationSource::None, "None");
ui.selectable_value(&mut *mod_source_2_tracker.lock().unwrap(), ModulationSource::Velocity, "Velocity");
ui.selectable_value(&mut *mod_source_2_tracker.lock().unwrap(), ModulationSource::LFO1, "LFO 1");
ui.selectable_value(&mut *mod_source_2_tracker.lock().unwrap(), ModulationSource::LFO2, "LFO 2");
ui.selectable_value(&mut *mod_source_2_tracker.lock().unwrap(), ModulationSource::LFO3, "LFO 3");
});
// This was a workaround for updating combobox on preset load but otherwise updating preset through combobox selection
if *mod_source_override_2.lock().unwrap() != ModulationSource::UnsetModulation {
// This happens on plugin preset load
*mod_source_2_tracker.lock().unwrap() = *mod_source_override_2.lock().unwrap();
setter.set_parameter( ¶ms.mod_source_2, mod_source_2_tracker.lock().unwrap().clone());
*mod_source_override_2.lock().unwrap() = ModulationSource::UnsetModulation;
} else {
if *mod_source_2_tracker.lock().unwrap() != params.mod_source_2.value() {
setter.set_parameter( ¶ms.mod_source_2, mod_source_2_tracker.lock().unwrap().clone());
}
}
ui.label(RichText::new("Mods")
.font(SMALLER_FONT)
.color(Color32::BLACK));
egui::ComboBox::new("mod_dest_2_ID", "")
.selected_text(format!("{:?}", *mod_dest_2_tracker.lock().unwrap()))
.width(100.0)
.show_ui(ui, |ui|{
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::None, "None");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::Cutoff_1, "Cutoff 1");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::Cutoff_2, "Cutoff 2");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::Resonance_1, "Resonance 1");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::Resonance_2, "Resonance 2");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::All_Gain, "All Gain");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::Osc1_Gain, "Osc1 Gain");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::Osc2_Gain, "Osc2 Gain");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::Osc3_Gain, "Osc3 Gain");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::All_Detune, "All Detune");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::Osc1Detune, "Osc1 Detune");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::Osc2Detune, "Osc2 Detune");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::Osc3Detune, "Osc3 Detune");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::All_UniDetune, "All UniDetune");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::Osc1UniDetune, "Osc1 UniDetune");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::Osc2UniDetune, "Osc2 UniDetune");
ui.selectable_value(&mut *mod_dest_2_tracker.lock().unwrap(), ModulationDestination::Osc3UniDetune, "Osc3 UniDetune");
});
// This was a workaround for updating combobox on preset load but otherwise updating preset through combobox selection
if *mod_dest_override_2.lock().unwrap() != ModulationDestination::UnsetModulation {
// This happens on plugin preset load
*mod_dest_2_tracker.lock().unwrap() = *mod_dest_override_2.lock().unwrap();
setter.set_parameter( ¶ms.mod_destination_2, mod_dest_2_tracker.lock().unwrap().clone());
*mod_dest_override_2.lock().unwrap() = ModulationDestination::UnsetModulation;
} else {
if *mod_dest_2_tracker.lock().unwrap() != params.mod_destination_2.value() {
setter.set_parameter( ¶ms.mod_destination_2, mod_dest_2_tracker.lock().unwrap().clone());
}
}
});
ui.separator();
// Modulator section 3
//////////////////////////////////////////////////////////////////////////////////
ui.horizontal(|ui|{
let mod_3_knob = ui_knob::ArcKnob::for_param(
¶ms.mod_amount_knob_3,
setter,
12.0)
.preset_style(ui_knob::KnobStyle::NewPresets2)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_show_label(false);
ui.add(mod_3_knob);
ui.separator();
egui::ComboBox::new("mod_source_3_ID","")
.selected_text(format!("{:?}", *mod_source_3_tracker.lock().unwrap()))
.width(70.0)
.show_ui(ui, |ui|{
ui.selectable_value(&mut *mod_source_3_tracker.lock().unwrap(), ModulationSource::None, "None");
ui.selectable_value(&mut *mod_source_3_tracker.lock().unwrap(), ModulationSource::Velocity, "Velocity");
ui.selectable_value(&mut *mod_source_3_tracker.lock().unwrap(), ModulationSource::LFO1, "LFO 1");
ui.selectable_value(&mut *mod_source_3_tracker.lock().unwrap(), ModulationSource::LFO2, "LFO 2");
ui.selectable_value(&mut *mod_source_3_tracker.lock().unwrap(), ModulationSource::LFO3, "LFO 3");
});
// This was a workaround for updating combobox on preset load but otherwise updating preset through combobox selection
if *mod_source_override_3.lock().unwrap() != ModulationSource::UnsetModulation {
// This happens on plugin preset load
*mod_source_3_tracker.lock().unwrap() = *mod_source_override_3.lock().unwrap();
setter.set_parameter( ¶ms.mod_source_3, mod_source_3_tracker.lock().unwrap().clone());
*mod_source_override_3.lock().unwrap() = ModulationSource::UnsetModulation;
} else {
if *mod_source_3_tracker.lock().unwrap() != params.mod_source_3.value() {
setter.set_parameter( ¶ms.mod_source_3, mod_source_3_tracker.lock().unwrap().clone());
}
}
ui.label(RichText::new("Mods")
.font(SMALLER_FONT)
.color(Color32::BLACK));
egui::ComboBox::new("mod_dest_3_ID", "")
.selected_text(format!("{:?}", *mod_dest_3_tracker.lock().unwrap()))
.width(100.0)
.show_ui(ui, |ui|{
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::None, "None");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::Cutoff_1, "Cutoff 1");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::Cutoff_2, "Cutoff 2");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::Resonance_1, "Resonance 1");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::Resonance_2, "Resonance 2");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::All_Gain, "All Gain");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::Osc1_Gain, "Osc1 Gain");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::Osc2_Gain, "Osc2 Gain");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::Osc3_Gain, "Osc3 Gain");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::All_Detune, "All Detune");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::Osc1Detune, "Osc1 Detune");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::Osc2Detune, "Osc2 Detune");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::Osc3Detune, "Osc3 Detune");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::All_UniDetune, "All UniDetune");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::Osc1UniDetune, "Osc1 UniDetune");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::Osc2UniDetune, "Osc2 UniDetune");
ui.selectable_value(&mut *mod_dest_3_tracker.lock().unwrap(), ModulationDestination::Osc3UniDetune, "Osc3 UniDetune");
});
// This was a workaround for updating combobox on preset load but otherwise updating preset through combobox selection
if *mod_dest_override_3.lock().unwrap() != ModulationDestination::UnsetModulation {
// This happens on plugin preset load
*mod_dest_3_tracker.lock().unwrap() = *mod_dest_override_3.lock().unwrap();
setter.set_parameter( ¶ms.mod_destination_3, mod_dest_3_tracker.lock().unwrap().clone());
*mod_dest_override_3.lock().unwrap() = ModulationDestination::UnsetModulation;
} else {
if *mod_dest_3_tracker.lock().unwrap() != params.mod_destination_3.value() {
setter.set_parameter( ¶ms.mod_destination_3, mod_dest_3_tracker.lock().unwrap().clone());
}
}
});
ui.separator();
// Modulator section 4
//////////////////////////////////////////////////////////////////////////////////
ui.horizontal(|ui|{
let mod_4_knob = ui_knob::ArcKnob::for_param(
¶ms.mod_amount_knob_4,
setter,
12.0)
.preset_style(ui_knob::KnobStyle::NewPresets2)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_show_label(false);
ui.add(mod_4_knob);
ui.separator();
egui::ComboBox::new("mod_source_4_ID","")
.selected_text(format!("{:?}", *mod_source_4_tracker.lock().unwrap()))
.width(70.0)
.show_ui(ui, |ui|{
ui.selectable_value(&mut *mod_source_4_tracker.lock().unwrap(), ModulationSource::None, "None");
ui.selectable_value(&mut *mod_source_4_tracker.lock().unwrap(), ModulationSource::Velocity, "Velocity");
ui.selectable_value(&mut *mod_source_4_tracker.lock().unwrap(), ModulationSource::LFO1, "LFO 1");
ui.selectable_value(&mut *mod_source_4_tracker.lock().unwrap(), ModulationSource::LFO2, "LFO 2");
ui.selectable_value(&mut *mod_source_4_tracker.lock().unwrap(), ModulationSource::LFO3, "LFO 3");
});
// This was a workaround for updating combobox on preset load but otherwise updating preset through combobox selection
if *mod_source_override_4.lock().unwrap() != ModulationSource::UnsetModulation {
// This happens on plugin preset load
*mod_source_4_tracker.lock().unwrap() = *mod_source_override_4.lock().unwrap();
setter.set_parameter( ¶ms.mod_source_4, mod_source_4_tracker.lock().unwrap().clone());
*mod_source_override_4.lock().unwrap() = ModulationSource::UnsetModulation;
} else {
if *mod_source_4_tracker.lock().unwrap() != params.mod_source_4.value() {
setter.set_parameter( ¶ms.mod_source_4, mod_source_4_tracker.lock().unwrap().clone());
}
}
ui.label(RichText::new("Mods")
.font(SMALLER_FONT)
.color(Color32::BLACK));
egui::ComboBox::new("mod_dest_4_ID", "")
.selected_text(format!("{:?}", *mod_dest_4_tracker.lock().unwrap()))
.width(100.0)
.show_ui(ui, |ui|{
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::None, "None");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::Cutoff_1, "Cutoff 1");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::Cutoff_2, "Cutoff 2");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::Resonance_1, "Resonance 1");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::Resonance_2, "Resonance 2");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::All_Gain, "All Gain");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::Osc1_Gain, "Osc1 Gain");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::Osc2_Gain, "Osc2 Gain");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::Osc3_Gain, "Osc3 Gain");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::All_Detune, "All Detune");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::Osc1Detune, "Osc1 Detune");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::Osc2Detune, "Osc2 Detune");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::Osc3Detune, "Osc3 Detune");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::All_UniDetune, "All UniDetune");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::Osc1UniDetune, "Osc1 UniDetune");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::Osc2UniDetune, "Osc2 UniDetune");
ui.selectable_value(&mut *mod_dest_4_tracker.lock().unwrap(), ModulationDestination::Osc3UniDetune, "Osc3 UniDetune");
});
// This was a workaround for updating combobox on preset load but otherwise updating preset through combobox selection
if *mod_dest_override_4.lock().unwrap() != ModulationDestination::UnsetModulation {
// This happens on plugin preset load
*mod_dest_4_tracker.lock().unwrap() = *mod_dest_override_4.lock().unwrap();
setter.set_parameter( ¶ms.mod_destination_4, mod_dest_4_tracker.lock().unwrap().clone());
*mod_dest_override_4.lock().unwrap() = ModulationDestination::UnsetModulation;
} else {
if *mod_dest_4_tracker.lock().unwrap() != params.mod_destination_4.value() {
setter.set_parameter( ¶ms.mod_destination_4, mod_dest_4_tracker.lock().unwrap().clone());
}
}
});
ui.separator();
});
},
LFOSelect::INFO => {
ui.horizontal(|ui| {
let text_response = ui.add(
egui::TextEdit::singleline(&mut *arc_preset_name.lock().unwrap())
.interactive(true)
.hint_text("Preset Name")
.desired_width(120.0));
if text_response.clicked() {
let mut temp_lock = arc_preset_name.lock().unwrap();
//TFD
match tinyfiledialogs::input_box("Rename preset", "Preset name:", &*temp_lock) {
Some(input) => *temp_lock = input,
None => {},
}
}
ui.label(RichText::new("Type")
.font(FONT)
.size(12.0)
.color(Color32::BLACK));
egui::ComboBox::new("preset_category", "")
.selected_text(format!("{:?}", *preset_category_tracker.lock().unwrap()))
.width(100.0)
.show_ui(ui, |ui|{
ui.selectable_value(&mut *preset_category_tracker.lock().unwrap(), PresetType::Select, "Select");
ui.selectable_value(&mut *preset_category_tracker.lock().unwrap(), PresetType::Atmosphere, "Atmosphere");
ui.selectable_value(&mut *preset_category_tracker.lock().unwrap(), PresetType::Bass, "Bass");
ui.selectable_value(&mut *preset_category_tracker.lock().unwrap(), PresetType::FX, "FX");
ui.selectable_value(&mut *preset_category_tracker.lock().unwrap(), PresetType::Keys, "Keys");
ui.selectable_value(&mut *preset_category_tracker.lock().unwrap(), PresetType::Lead, "Lead");
ui.selectable_value(&mut *preset_category_tracker.lock().unwrap(), PresetType::Pad, "Pad");
ui.selectable_value(&mut *preset_category_tracker.lock().unwrap(), PresetType::Percussion, "Percussion");
ui.selectable_value(&mut *preset_category_tracker.lock().unwrap(), PresetType::Pluck, "Pluck");
ui.selectable_value(&mut *preset_category_tracker.lock().unwrap(), PresetType::Synth, "Synth");
ui.selectable_value(&mut *preset_category_tracker.lock().unwrap(), PresetType::Other, "Other");
});
// This was a workaround for updating combobox on preset load but otherwise updating preset through combobox selection
if *preset_category_override.lock().unwrap() != PresetType::Select {
// This happens on plugin preset load
*preset_category_tracker.lock().unwrap() = *preset_category_override.lock().unwrap();
setter.set_parameter( ¶ms.preset_category, preset_category_tracker.lock().unwrap().clone());
*preset_category_override.lock().unwrap() = PresetType::Select;
} else {
if *preset_category_tracker.lock().unwrap() != params.preset_category.value() {
setter.set_parameter( ¶ms.preset_category, preset_category_tracker.lock().unwrap().clone());
}
}
});
ui.horizontal(|ui|{
let text_info_response = ui.add(
egui::TextEdit::multiline(&mut *arc_preset_info.lock().unwrap())
.interactive(true)
.hint_text("Preset Info")
.desired_width(120.0)
.desired_rows(5)
.lock_focus(true));
if text_info_response.clicked() {
let mut temp_lock = arc_preset_info.lock().unwrap();
//TFD
match tinyfiledialogs::input_box("Update preset description", "Preset description:", &*temp_lock) {
Some(input) => *temp_lock = input,
None => {},
}
}
ScrollArea::vertical()
.max_height(220.0)
.max_width(164.0)
//.always_show_scroll(true)
.show(ui, |ui|{
ui.vertical(|ui|{
ui.horizontal(|ui|{
let tag_acid = BoolButton::BoolButton::for_param(¶ms.tag_acid, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_acid);
let tag_analog = BoolButton::BoolButton::for_param(¶ms.tag_analog, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_analog);
let tag_bright = BoolButton::BoolButton::for_param(¶ms.tag_bright, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_bright);
});
ui.horizontal(|ui|{
let tag_chord = BoolButton::BoolButton::for_param(¶ms.tag_chord, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_chord);
let tag_crisp = BoolButton::BoolButton::for_param(¶ms.tag_crisp, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_crisp);
let tag_deep = BoolButton::BoolButton::for_param(¶ms.tag_deep, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_deep);
});
ui.horizontal(|ui|{
let tag_delicate = BoolButton::BoolButton::for_param(¶ms.tag_delicate, setter, 2.7, 0.9, SMALLER_FONT);
ui.add(tag_delicate);
let tag_hard = BoolButton::BoolButton::for_param(¶ms.tag_hard, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_hard);
});
ui.horizontal(|ui|{
let tag_harsh = BoolButton::BoolButton::for_param(¶ms.tag_harsh, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_harsh);
let tag_lush = BoolButton::BoolButton::for_param(¶ms.tag_lush, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_lush);
let tag_mellow = BoolButton::BoolButton::for_param(¶ms.tag_mellow, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_mellow);
});
ui.horizontal(|ui|{
let tag_resonant = BoolButton::BoolButton::for_param(¶ms.tag_resonant, setter, 2.7, 0.9, SMALLER_FONT);
ui.add(tag_resonant);
let tag_rich = BoolButton::BoolButton::for_param(¶ms.tag_rich, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_rich);
});
ui.horizontal(|ui|{
let tag_sharp = BoolButton::BoolButton::for_param(¶ms.tag_sharp, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_sharp);
let tag_silky = BoolButton::BoolButton::for_param(¶ms.tag_silky, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_silky);
let tag_smooth = BoolButton::BoolButton::for_param(¶ms.tag_smooth, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_smooth);
});
ui.horizontal(|ui|{
let tag_soft = BoolButton::BoolButton::for_param(¶ms.tag_soft, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_soft);
let tag_stab = BoolButton::BoolButton::for_param(¶ms.tag_stab, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_stab);
let tag_warm = BoolButton::BoolButton::for_param(¶ms.tag_warm, setter, 2.0, 0.9, SMALLER_FONT);
ui.add(tag_warm);
});
});
});
});
ui.horizontal(|ui| {
let update_current_preset = BoolButton::BoolButton::for_param(¶ms.param_update_current_preset, setter, 4.9, 0.9, SMALLER_FONT)
.with_background_color(DARK_GREY_UI_COLOR);
ui.add(update_current_preset);
let import_preset = BoolButton::BoolButton::for_param(¶ms.param_import_preset, setter, 4.9, 0.9, SMALLER_FONT)
.with_background_color(DARK_GREY_UI_COLOR);
ui.add(import_preset);
let export_preset = BoolButton::BoolButton::for_param(¶ms.param_export_preset, setter, 4.9, 0.9, SMALLER_FONT)
.with_background_color(DARK_GREY_UI_COLOR);
ui.add(export_preset);
});
},
LFOSelect::FX => {
ScrollArea::vertical()
.max_height(200.0)
.max_width(270.0)
//.always_show_scroll(true)
.show(ui, |ui|{
ui.vertical(|ui|{
// Equalizer
ui.horizontal(|ui|{
ui.vertical(|ui|{
ui.label(RichText::new("EQ")
.font(FONT)
.color(Color32::BLACK)
)
.on_hover_text("non interleaved EQ from Interleaf");
let use_eq_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.pre_use_eq, setter);
ui.add(use_eq_toggle);
});
ui.vertical(|ui|{
ui.add(
VerticalParamSlider::for_param(¶ms.pre_low_gain, setter)
.with_width(VERT_BAR_WIDTH * 2.2)
.with_height(VERT_BAR_HEIGHT * 0.6)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
A_KNOB_OUTSIDE_COLOR,
),
);
let low_freq_knob = ui_knob::ArcKnob::for_param(
¶ms.pre_low_freq,
setter,
KNOB_SIZE * 0.6)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE)
.override_text_color(Color32::DARK_GRAY);
ui.add(low_freq_knob);
});
ui.vertical(|ui|{
ui.add(
VerticalParamSlider::for_param(¶ms.pre_mid_gain, setter)
.with_width(VERT_BAR_WIDTH * 2.2)
.with_height(VERT_BAR_HEIGHT * 0.6)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
A_KNOB_OUTSIDE_COLOR,
),
);
let mid_freq_knob = ui_knob::ArcKnob::for_param(
¶ms.pre_mid_freq,
setter,
KNOB_SIZE * 0.6)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE)
.override_text_color(Color32::DARK_GRAY);
ui.add(mid_freq_knob);
});
ui.vertical(|ui|{
ui.add(
VerticalParamSlider::for_param(¶ms.pre_high_gain, setter)
.with_width(VERT_BAR_WIDTH * 2.2)
.with_height(VERT_BAR_HEIGHT * 0.6)
.set_reversed(true)
.override_colors(
SYNTH_BARS_PURPLE,
A_KNOB_OUTSIDE_COLOR,
),
);
let high_freq_knob = ui_knob::ArcKnob::for_param(
¶ms.pre_high_freq,
setter,
KNOB_SIZE * 0.6)
.preset_style(ui_knob::KnobStyle::NewPresets1)
.set_fill_color(SYNTH_BARS_PURPLE)
.set_line_color(A_KNOB_OUTSIDE_COLOR)
.set_text_size(TEXT_SIZE)
.override_text_color(Color32::DARK_GRAY);
ui.add(high_freq_knob);
});
ui.separator();
});
// Compressor
ui.horizontal(|ui|{
ui.label(RichText::new("Compressor")
.font(FONT)
.color(Color32::BLACK));
let use_comp_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.use_compressor, setter);
ui.add(use_comp_toggle);
});
ui.vertical(|ui|{
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.comp_amt, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.comp_atk, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.comp_rel, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.comp_drive, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
});
ui.separator();
// ABass
ui.horizontal(|ui|{
ui.label(RichText::new("ABass Algorithm")
.font(FONT)
.color(Color32::BLACK));
let use_abass_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.use_abass, setter);
ui.add(use_abass_toggle);
});
ui.vertical(|ui|{
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.abass_amount, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
});
ui.separator();
// Saturation
ui.horizontal(|ui|{
ui.label(RichText::new("Saturation")
.font(FONT)
.color(Color32::BLACK));
let use_sat_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.use_saturation, setter);
ui.add(use_sat_toggle);
});
ui.vertical(|ui|{
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.sat_type, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.sat_amt, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
});
ui.separator();
// Phaser
ui.horizontal(|ui|{
ui.label(RichText::new("Phaser")
.font(FONT)
.color(Color32::BLACK));
let use_phaser_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.use_phaser, setter);
ui.add(use_phaser_toggle);
});
ui.vertical(|ui|{
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.phaser_amount, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.phaser_depth, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.phaser_rate, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
});
ui.separator();
// Flanger
ui.horizontal(|ui|{
ui.label(RichText::new("Flanger")
.font(FONT)
.color(Color32::BLACK));
let use_flanger_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.use_flanger, setter);
ui.add(use_flanger_toggle);
});
ui.vertical(|ui|{
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.flanger_amount, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.flanger_depth, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.flanger_rate, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.flanger_feedback, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
});
ui.separator();
// Buffer Modulator
ui.horizontal(|ui|{
ui.label(RichText::new("Buffer Modulator")
.font(FONT)
.color(Color32::BLACK));
let use_buffermod_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.use_buffermod, setter);
ui.add(use_buffermod_toggle);
});
ui.vertical(|ui|{
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.buffermod_amount, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.buffermod_depth, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.buffermod_rate, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.buffermod_spread, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.buffermod_timing, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
});
ui.separator();
// Delay
ui.horizontal(|ui|{
ui.label(RichText::new("Delay")
.font(FONT)
.color(Color32::BLACK));
let use_delay_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.use_delay, setter);
ui.add(use_delay_toggle);
});
ui.vertical(|ui|{
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.delay_amount, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.delay_time, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.delay_decay, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.delay_type, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
});
ui.separator();
// Reverb
ui.horizontal(|ui|{
ui.label(RichText::new("Reverb")
.font(FONT)
.color(Color32::BLACK));
let use_reverb_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.use_reverb, setter);
ui.add(use_reverb_toggle);
});
ui.vertical(|ui|{
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.reverb_amount, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.reverb_size, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.reverb_feedback, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
});
ui.separator();
// Limiter
ui.horizontal(|ui|{
ui.label(RichText::new("Limiter")
.font(FONT)
.color(Color32::BLACK));
let use_limiter_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.use_limiter, setter);
ui.add(use_limiter_toggle);
});
ui.vertical(|ui|{
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.limiter_threshold, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.limiter_knee, setter)
.slimmer(0.7)
.set_left_sided_label(true)
.set_label_width(84.0)
.with_width(140.0));
});
});
})
.inner;
}
}
});
});
});
// Synth Bars on left and right
ui.painter().rect_filled(
Rect::from_x_y_ranges(
RangeInclusive::new(WIDTH as f32 - synth_bar_space, WIDTH as f32),
RangeInclusive::new(0.0, HEIGHT as f32)),
Rounding::none(),
SYNTH_BARS_PURPLE
);
// Screws for that vintage look
let screw_space = 16.0;
ui.painter().circle_filled(Pos2::new(screw_space,screw_space), 4.0, Color32::LIGHT_GRAY);
ui.painter().circle_filled(Pos2::new(screw_space,HEIGHT as f32 - screw_space), 4.0, Color32::LIGHT_GRAY);
ui.painter().circle_filled(Pos2::new(WIDTH as f32 - screw_space,screw_space), 4.0, Color32::LIGHT_GRAY);
ui.painter().circle_filled(Pos2::new(WIDTH as f32 - screw_space,HEIGHT as f32 - screw_space), 4.0, Color32::LIGHT_GRAY);
});
if params.loading.value() || loading.load(Ordering::Relaxed) {
// Create the loading popup here.
let screen_size = Rect::from_x_y_ranges(
RangeInclusive::new(0.0, WIDTH as f32),
RangeInclusive::new(0.0, HEIGHT as f32));
let popup_size = Vec2::new(400.0, 200.0);
let popup_pos = screen_size.center();
// Draw the loading popup content here.
ui.painter().rect_filled(Rect::from_center_size(Pos2 { x: popup_pos.x, y: popup_pos.y }, popup_size), 10.0, Color32::GRAY);
ui.painter().text(popup_pos, Align2::CENTER_CENTER, "Loading...", LOADING_FONT, Color32::BLACK);
}
});
},
// This is the end of create_egui_editor()
)
}
fn initialize(
&mut self,
_audio_io_layout: &AudioIOLayout,
buffer_config: &BufferConfig,
_context: &mut impl InitContext<Self>,
) -> bool {
self.sample_rate = buffer_config.sample_rate;
return true;
}
// Main processing thread that happens before the midi processing per-sample
fn process(
&mut self,
buffer: &mut Buffer,
_aux: &mut AuxiliaryBuffers,
context: &mut impl ProcessContext<Self>,
) -> ProcessStatus {
// Clear any voices on change of module type (especially during play)
// This fixes panics and other broken things attempting to play during preset change/load
if self.clear_voices.clone().load(Ordering::Relaxed) {
self.audio_module_1.lock().unwrap().clear_voices();
self.audio_module_2.lock().unwrap().clear_voices();
self.audio_module_3.lock().unwrap().clear_voices();
self.clear_voices.store(false, Ordering::Relaxed);
self.update_something.store(true, Ordering::Relaxed);
}
self.process_midi(context, buffer);
ProcessStatus::Normal
}
const HARD_REALTIME_ONLY: bool = false;
fn task_executor(&mut self) -> TaskExecutor<Self> {
// In the default implementation we can simply ignore the value
Box::new(|_| ())
}
fn filter_state(_state: &mut PluginState) {}
fn reset(&mut self) {}
fn deactivate(&mut self) {}
}
impl Actuate {
// Send midi events to the audio modules and let them process them - also send params so they can access
fn process_midi(&mut self, context: &mut impl ProcessContext<Self>, buffer: &mut Buffer) {
let mut lfo_1_current: f32 = -2.0;
let mut lfo_2_current: f32 = -2.0;
let mut lfo_3_current: f32 = -2.0;
// Update our LFOs per each sample
/////////////////////////////////////////////////////////////////////////////////////////////
let bpm = context.transport().tempo.unwrap_or(1.0) as f32;
if bpm == 1.0 {
// This means we are not getting proper tempo so LFO can't sync
return;
}
if self.params.lfo1_enable.value() {
// Update LFO Frequency
if self.params.lfo1_sync.value() {
let divisor = match self.params.lfo1_snap.value() {
LFOController::LFOSnapValues::Quad => 16.0,
LFOController::LFOSnapValues::QuadD => 16.0 * 1.5,
LFOController::LFOSnapValues::QuadT => 16.0 / 3.0,
LFOController::LFOSnapValues::Double => 8.0,
LFOController::LFOSnapValues::DoubleD => 8.0 * 1.5,
LFOController::LFOSnapValues::DoubleT => 8.0 / 3.0,
LFOController::LFOSnapValues::Whole => 4.0,
LFOController::LFOSnapValues::WholeD => 4.0 * 1.5,
LFOController::LFOSnapValues::WholeT => 4.0 / 3.0,
LFOController::LFOSnapValues::Half => 2.0,
LFOController::LFOSnapValues::HalfD => 2.0 * 1.5,
LFOController::LFOSnapValues::HalfT => 2.0 / 3.0,
LFOController::LFOSnapValues::Quarter => 1.0,
LFOController::LFOSnapValues::QuarterD => 1.0 * 1.5,
LFOController::LFOSnapValues::QuarterT => 1.0 / 3.0,
LFOController::LFOSnapValues::Eighth => 0.5,
LFOController::LFOSnapValues::EighthD => 0.5 * 1.5,
LFOController::LFOSnapValues::EighthT => 0.5 / 3.0,
LFOController::LFOSnapValues::Sixteen => 0.25,
LFOController::LFOSnapValues::SixteenD => 0.25 * 1.5,
LFOController::LFOSnapValues::SixteenT => 0.25 / 3.0,
LFOController::LFOSnapValues::ThirtySecond => 0.125,
LFOController::LFOSnapValues::ThirtySecondD => 0.125 * 1.5,
LFOController::LFOSnapValues::ThirtySecondT => 0.125 / 3.0,
};
let freq_snap = (bpm / divisor) / 60.0;
if self.params.lfo1_freq.value() != freq_snap {
self.lfo_1.set_frequency(freq_snap);
}
} else {
if self.params.lfo1_freq.value() != self.lfo_1.get_frequency() {
self.lfo_1.set_frequency(self.params.lfo1_freq.value());
}
}
// Update LFO Waveform
if self.params.lfo1_waveform.value() != self.lfo_1.get_waveform() {
self.lfo_1.set_waveform(self.params.lfo1_waveform.value());
}
}
if self.params.lfo2_enable.value() {
// Update LFO Frequency
if self.params.lfo2_sync.value() {
let divisor = match self.params.lfo2_snap.value() {
LFOController::LFOSnapValues::Quad => 16.0,
LFOController::LFOSnapValues::QuadD => 16.0 * 1.5,
LFOController::LFOSnapValues::QuadT => 16.0 / 3.0,
LFOController::LFOSnapValues::Double => 8.0,
LFOController::LFOSnapValues::DoubleD => 8.0 * 1.5,
LFOController::LFOSnapValues::DoubleT => 8.0 / 3.0,
LFOController::LFOSnapValues::Whole => 4.0,
LFOController::LFOSnapValues::WholeD => 4.0 * 1.5,
LFOController::LFOSnapValues::WholeT => 4.0 / 3.0,
LFOController::LFOSnapValues::Half => 2.0,
LFOController::LFOSnapValues::HalfD => 2.0 * 1.5,
LFOController::LFOSnapValues::HalfT => 2.0 / 3.0,
LFOController::LFOSnapValues::Quarter => 1.0,
LFOController::LFOSnapValues::QuarterD => 1.0 * 1.5,
LFOController::LFOSnapValues::QuarterT => 1.0 / 3.0,
LFOController::LFOSnapValues::Eighth => 0.5,
LFOController::LFOSnapValues::EighthD => 0.5 * 1.5,
LFOController::LFOSnapValues::EighthT => 0.5 / 3.0,
LFOController::LFOSnapValues::Sixteen => 0.25,
LFOController::LFOSnapValues::SixteenD => 0.25 * 1.5,
LFOController::LFOSnapValues::SixteenT => 0.25 / 3.0,
LFOController::LFOSnapValues::ThirtySecond => 0.125,
LFOController::LFOSnapValues::ThirtySecondD => 0.125 * 1.5,
LFOController::LFOSnapValues::ThirtySecondT => 0.125 / 3.0,
};
let freq_snap = (bpm / divisor) / 60.0;
if self.params.lfo2_freq.value() != freq_snap {
self.lfo_2.set_frequency(freq_snap);
}
} else {
if self.params.lfo2_freq.value() != self.lfo_2.get_frequency() {
self.lfo_2.set_frequency(self.params.lfo2_freq.value());
}
}
// Update LFO Waveform
if self.params.lfo2_waveform.value() != self.lfo_2.get_waveform() {
self.lfo_2.set_waveform(self.params.lfo2_waveform.value());
}
}
if self.params.lfo3_enable.value() {
// Update LFO Frequency
if self.params.lfo3_sync.value() {
let divisor = match self.params.lfo3_snap.value() {
LFOController::LFOSnapValues::Quad => 16.0,
LFOController::LFOSnapValues::QuadD => 16.0 * 1.5,
LFOController::LFOSnapValues::QuadT => 16.0 / 3.0,
LFOController::LFOSnapValues::Double => 8.0,
LFOController::LFOSnapValues::DoubleD => 8.0 * 1.5,
LFOController::LFOSnapValues::DoubleT => 8.0 / 3.0,
LFOController::LFOSnapValues::Whole => 4.0,
LFOController::LFOSnapValues::WholeD => 4.0 * 1.5,
LFOController::LFOSnapValues::WholeT => 4.0 / 3.0,
LFOController::LFOSnapValues::Half => 2.0,
LFOController::LFOSnapValues::HalfD => 2.0 * 1.5,
LFOController::LFOSnapValues::HalfT => 2.0 / 3.0,
LFOController::LFOSnapValues::Quarter => 1.0,
LFOController::LFOSnapValues::QuarterD => 1.0 * 1.5,
LFOController::LFOSnapValues::QuarterT => 1.0 / 3.0,
LFOController::LFOSnapValues::Eighth => 0.5,
LFOController::LFOSnapValues::EighthD => 0.5 * 1.5,
LFOController::LFOSnapValues::EighthT => 0.5 / 3.0,
LFOController::LFOSnapValues::Sixteen => 0.25,
LFOController::LFOSnapValues::SixteenD => 0.25 * 1.5,
LFOController::LFOSnapValues::SixteenT => 0.25 / 3.0,
LFOController::LFOSnapValues::ThirtySecond => 0.125,
LFOController::LFOSnapValues::ThirtySecondD => 0.125 * 1.5,
LFOController::LFOSnapValues::ThirtySecondT => 0.125 / 3.0,
};
let freq_snap = (bpm / divisor) / 60.0;
if self.params.lfo3_freq.value() != freq_snap {
self.lfo_3.set_frequency(freq_snap);
}
} else {
if self.params.lfo3_freq.value() != self.lfo_3.get_frequency() {
self.lfo_3.set_frequency(self.params.lfo3_freq.value());
}
}
// Update LFO Waveform
if self.params.lfo3_waveform.value() != self.lfo_3.get_waveform() {
self.lfo_3.set_waveform(self.params.lfo3_waveform.value());
}
}
// Check if we're loading a file before process happens
if self.params.load_sample_1.value() && self.file_dialog.load(Ordering::Relaxed) {
self.file_dialog.store(true, Ordering::Relaxed);
let sample_file = FileDialog::new()
.add_filter("wav", &["wav"])
//.set_directory("/")
.pick_file();
if Option::is_some(&sample_file) {
self.audio_module_1
.as_ref()
.lock()
.unwrap()
.load_new_sample(sample_file.unwrap());
}
} else if self.params.load_sample_2.value() && self.file_dialog.load(Ordering::Relaxed) {
self.file_dialog.store(true, Ordering::Relaxed);
let sample_file = FileDialog::new()
.add_filter("wav", &["wav"])
//.set_directory("/")
.pick_file();
if Option::is_some(&sample_file) {
self.audio_module_2
.as_ref()
.lock()
.unwrap()
.load_new_sample(sample_file.unwrap());
}
} else if self.params.load_sample_3.value() && self.file_dialog.load(Ordering::Relaxed) {
self.file_dialog.store(true, Ordering::Relaxed);
let sample_file = FileDialog::new()
.add_filter("wav", &["wav"])
//.set_directory("/")
.pick_file();
if Option::is_some(&sample_file) {
self.audio_module_3
.as_ref()
.lock()
.unwrap()
.load_new_sample(sample_file.unwrap());
}
}
for (sample_id, mut channel_samples) in buffer.iter_samples().enumerate() {
// Get around post file loading breaking things with an arbitrary buffer
if self.file_dialog.load(Ordering::Acquire) {
self.file_open_buffer_timer.store(
self.file_open_buffer_timer.load(Ordering::Relaxed) + 1,
Ordering::Relaxed,
);
if self.file_open_buffer_timer.load(Ordering::Relaxed) > FILE_OPEN_BUFFER_MAX {
self.file_open_buffer_timer.store(0, Ordering::Relaxed);
self.file_dialog.store(false, Ordering::Relaxed); //Changed from Release
}
}
// If Import Preset button was pressed
if *self.import_preset.lock().unwrap()
&& !self.file_dialog.load(Ordering::Relaxed)
&& self.file_open_buffer_timer.load(Ordering::Relaxed) == 0
&& !*self.reload_entire_preset.lock().unwrap()
{
// This is up here so it happens first - putting this down by
// (self.preset_lib_name, unserialized) = Actuate::load_preset_bank();
// Makes this block happen twice?
*self.import_preset.lock().unwrap() = false;
// This is here again purposefully
*self.reload_entire_preset.lock().unwrap() = true;
let unserialized: Option<ActuatePreset>;
(_, unserialized) = Actuate::import_preset();
if unserialized.is_some() {
let arc_lib: Arc<Mutex<Vec<ActuatePreset>>> = Arc::clone(&self.preset_lib);
let mut locked_lib = arc_lib.lock().unwrap();
locked_lib[self.current_preset.load(Ordering::Relaxed) as usize] =
unserialized.unwrap();
let temp_preset =
&locked_lib[self.current_preset.load(Ordering::Relaxed) as usize];
*self.preset_name.lock().unwrap() = temp_preset.preset_name.clone();
*self.preset_info.lock().unwrap() = temp_preset.preset_info.clone();
*self.preset_category.lock().unwrap() = temp_preset.preset_category.clone();
}
}
// If the Load Bank button was pressed
if *self.load_bank.lock().unwrap()
&& !self.file_dialog.load(Ordering::Relaxed)
&& self.file_open_buffer_timer.load(Ordering::Relaxed) == 0
&& !*self.reload_entire_preset.lock().unwrap()
{
// This is up here so it happens first - putting this down by
// (self.preset_lib_name, unserialized) = Actuate::load_preset_bank();
// Makes this block happen twice?
*self.load_bank.lock().unwrap() = false;
// This is here again purposefully
*self.reload_entire_preset.lock().unwrap() = true;
let AM1c = self.audio_module_1.clone();
let AM2c = self.audio_module_2.clone();
let AM3c = self.audio_module_3.clone();
let mut AM1 = AM1c.lock().unwrap();
let mut AM2 = AM2c.lock().unwrap();
let mut AM3 = AM3c.lock().unwrap();
// Load the preset bank
self.file_dialog.store(true, Ordering::Relaxed);
self.file_open_buffer_timer.store(1, Ordering::Relaxed);
let unserialized: Vec<ActuatePreset>;
(self.preset_lib_name, unserialized) = Actuate::load_preset_bank();
let arc_lib: Arc<Mutex<Vec<ActuatePreset>>> = Arc::clone(&self.preset_lib);
let mut locked_lib = arc_lib.lock().unwrap();
// Load our items into our library from the unserialized save file
for (item_index, item) in unserialized.iter().enumerate() {
// If our item exists then update it
if let Some(existing_item) = locked_lib.get_mut(item_index) {
*existing_item = item.clone();
} else {
// item_index is out of bounds in locked_lib
// These get dropped as the preset size should be the same all around
}
}
// Create missing samples on current preset
AM1.regenerate_samples();
AM2.regenerate_samples();
AM3.regenerate_samples();
let temp_current_preset =
locked_lib[self.current_preset.load(Ordering::Relaxed) as usize].clone();
*self.preset_name.lock().unwrap() = temp_current_preset.preset_name;
*self.preset_info.lock().unwrap() = temp_current_preset.preset_info;
*self.preset_category.lock().unwrap() = temp_current_preset.preset_category;
}
// If Export Preset button has been pressed
if *self.export_preset.lock().unwrap()
&& !self.file_dialog.load(Ordering::Relaxed)
&& self.file_open_buffer_timer.load(Ordering::Relaxed) == 0
&& !*self.reload_entire_preset.lock().unwrap()
{
// Move the mutex update as early as possible
*self.export_preset.lock().unwrap() = false;
// This is here again purposefully
*self.reload_entire_preset.lock().unwrap() = true;
self.file_dialog.store(true, Ordering::Relaxed);
self.file_open_buffer_timer.store(1, Ordering::Relaxed);
self.export_preset();
}
// If the save button has been pressed
if *self.save_bank.lock().unwrap()
&& !self.file_dialog.load(Ordering::Relaxed)
&& self.file_open_buffer_timer.load(Ordering::Relaxed) == 0
&& !*self.reload_entire_preset.lock().unwrap()
{
// Move the mutex update as early as possible
*self.save_bank.lock().unwrap() = false;
// This is here again purposefully
*self.reload_entire_preset.lock().unwrap() = true;
self.file_dialog.store(true, Ordering::Relaxed);
self.file_open_buffer_timer.store(1, Ordering::Relaxed);
self.save_preset_bank();
}
// If the Update Current Preset button has been pressed
if self.update_current_preset.load(Ordering::Relaxed)
&& !self.file_dialog.load(Ordering::Relaxed)
{
self.file_dialog.store(true, Ordering::Relaxed);
self.file_open_buffer_timer.store(1, Ordering::Relaxed);
self.update_current_preset();
self.update_current_preset.store(false, Ordering::Relaxed);
}
// Prevent processing if our file dialog is open!!!
if self.file_dialog.load(Ordering::Relaxed) {
return;
}
// Processing
/////////////////////////////////////////////////////////////////////////////////////////////////
// Reset our output buffer signal
*channel_samples.get_mut(0).unwrap() = 0.0;
*channel_samples.get_mut(1).unwrap() = 0.0;
// This weird bit is to stop playing when going from play to stop
// but also allowing playing of the synth while stopped
// midi choke doesn't seem to be working in FL
if !context.transport().playing
&& (self.audio_module_1.clone().lock().unwrap().get_playing()
|| self.audio_module_2.clone().lock().unwrap().get_playing()
|| self.audio_module_3.clone().lock().unwrap().get_playing())
{
// Create clones here
let AM1 = self.audio_module_1.clone();
let AM2 = self.audio_module_2.clone();
let AM3 = self.audio_module_3.clone();
// For some reason this format works vs doing lock and storing it earlier
AM1.lock().unwrap().set_playing(false);
AM2.lock().unwrap().set_playing(false);
AM3.lock().unwrap().set_playing(false);
AM1.lock().unwrap().clear_voices();
AM2.lock().unwrap().clear_voices();
AM3.lock().unwrap().clear_voices();
}
if context.transport().playing {
self.audio_module_1
.clone()
.lock()
.unwrap()
.set_playing(true);
self.audio_module_2
.clone()
.lock()
.unwrap()
.set_playing(true);
self.audio_module_3
.clone()
.lock()
.unwrap()
.set_playing(true);
}
let midi_event: Option<NoteEvent<()>> = context.next_event();
let sent_voice_max: usize = self.params.voice_limit.value() as usize;
let mut wave1_l: f32 = 0.0;
let mut wave2_l: f32 = 0.0;
let mut wave3_l: f32 = 0.0;
let mut wave1_r: f32 = 0.0;
let mut wave2_r: f32 = 0.0;
let mut wave3_r: f32 = 0.0;
// These track if a new note happens to re-open the filter
let mut reset_filter_controller1: bool = false;
let mut reset_filter_controller2: bool = false;
let mut reset_filter_controller3: bool = false;
// These track if a note just finished to allow filter closing
let mut note_off_filter_controller1: bool = false;
let mut note_off_filter_controller2: bool = false;
let mut note_off_filter_controller3: bool = false;
// Trigger passing variables to the audio modules when the GUI input changes
if self.update_something.load(Ordering::Relaxed) {
self.audio_module_1
.lock()
.unwrap()
.consume_params(self.params.clone(), 1);
self.audio_module_2
.lock()
.unwrap()
.consume_params(self.params.clone(), 2);
self.audio_module_3
.lock()
.unwrap()
.consume_params(self.params.clone(), 3);
self.update_something.store(false, Ordering::Relaxed);
}
// Modulations
/////////////////////////////////////////////////////////////////////////////////////////////////
let mod_value_1: f32;
let mod_value_2: f32;
let mod_value_3: f32;
let mod_value_4: f32;
// If no modulations this = -2.0
mod_value_1 = match self.params.mod_source_1.value() {
ModulationSource::None | ModulationSource::UnsetModulation => -2.0,
ModulationSource::LFO1 => lfo_1_current * self.params.mod_amount_knob_1.value(),
ModulationSource::LFO2 => lfo_2_current * self.params.mod_amount_knob_1.value(),
ModulationSource::LFO3 => lfo_3_current * self.params.mod_amount_knob_1.value(),
ModulationSource::Velocity => {
// This is to allow invalid midi events to not break this logic since we only want NoteOn
match midi_event.clone().unwrap_or(NoteEvent::Choke {
timing: 0_u32,
voice_id: Some(0_i32),
channel: 0_u8,
note: 0_u8,
}) {
NoteEvent::NoteOn {
velocity,
timing: _,
voice_id: _,
channel: _,
note: _,
} => {
// Store velocity on new note happening
let vel = (velocity * self.params.mod_amount_knob_1.value().abs())
.clamp(0.0, 1.0);
if velocity != -1.0 {
self.current_note_on_velocity.store(vel, Ordering::Relaxed);
}
vel
}
_ => -2.0,
}
}
};
mod_value_2 = match self.params.mod_source_2.value() {
ModulationSource::None | ModulationSource::UnsetModulation => -2.0,
ModulationSource::LFO1 => lfo_1_current * self.params.mod_amount_knob_2.value(),
ModulationSource::LFO2 => lfo_2_current * self.params.mod_amount_knob_2.value(),
ModulationSource::LFO3 => lfo_3_current * self.params.mod_amount_knob_2.value(),
ModulationSource::Velocity => {
match midi_event.clone().unwrap_or(NoteEvent::Choke {
timing: 0_u32,
voice_id: Some(0_i32),
channel: 0_u8,
note: 0_u8,
}) {
NoteEvent::NoteOn {
velocity,
timing: _,
voice_id: _,
channel: _,
note: _,
} => {
if velocity != -1.0 {
self.current_note_on_velocity
.store(velocity, Ordering::Relaxed);
}
(velocity * self.params.mod_amount_knob_2.value().abs()).clamp(0.0, 1.0)
}
_ => -2.0,
}
}
};
mod_value_3 = match self.params.mod_source_3.value() {
ModulationSource::None | ModulationSource::UnsetModulation => -2.0,
ModulationSource::LFO1 => lfo_1_current * self.params.mod_amount_knob_3.value(),
ModulationSource::LFO2 => lfo_2_current * self.params.mod_amount_knob_3.value(),
ModulationSource::LFO3 => lfo_3_current * self.params.mod_amount_knob_3.value(),
ModulationSource::Velocity => {
match midi_event.clone().unwrap_or(NoteEvent::Choke {
timing: 0_u32,
voice_id: Some(0_i32),
channel: 0_u8,
note: 0_u8,
}) {
NoteEvent::NoteOn {
velocity,
timing: _,
voice_id: _,
channel: _,
note: _,
} => {
if velocity != -1.0 {
self.current_note_on_velocity
.store(velocity, Ordering::Relaxed);
}
(velocity * self.params.mod_amount_knob_3.value().abs()).clamp(0.0, 1.0)
}
_ => -2.0,
}
}
};
mod_value_4 = match self.params.mod_source_4.value() {
ModulationSource::None | ModulationSource::UnsetModulation => -2.0,
ModulationSource::LFO1 => lfo_1_current * self.params.mod_amount_knob_4.value(),
ModulationSource::LFO2 => lfo_2_current * self.params.mod_amount_knob_4.value(),
ModulationSource::LFO3 => lfo_3_current * self.params.mod_amount_knob_4.value(),
ModulationSource::Velocity => {
match midi_event.clone().unwrap_or(NoteEvent::Choke {
timing: 0_u32,
voice_id: Some(0_i32),
channel: 0_u8,
note: 0_u8,
}) {
NoteEvent::NoteOn {
velocity,
timing: _,
voice_id: _,
channel: _,
note: _,
} => {
if velocity != -1.0 {
self.current_note_on_velocity
.store(velocity, Ordering::Relaxed);
}
(velocity * self.params.mod_amount_knob_4.value().abs()).clamp(0.0, 1.0)
}
_ => -2.0,
}
}
};
let mut temp_mod_cutoff_1_source_1: f32 = 0.0;
let mut temp_mod_cutoff_1_source_2: f32 = 0.0;
let mut temp_mod_cutoff_1_source_3: f32 = 0.0;
let mut temp_mod_cutoff_1_source_4: f32 = 0.0;
let mut temp_mod_cutoff_2_source_1: f32 = 0.0;
let mut temp_mod_cutoff_2_source_2: f32 = 0.0;
let mut temp_mod_cutoff_2_source_3: f32 = 0.0;
let mut temp_mod_cutoff_2_source_4: f32 = 0.0;
let mut temp_mod_resonance_1_source_1: f32 = 0.0;
let mut temp_mod_resonance_1_source_2: f32 = 0.0;
let mut temp_mod_resonance_1_source_3: f32 = 0.0;
let mut temp_mod_resonance_1_source_4: f32 = 0.0;
let mut temp_mod_resonance_2_source_1: f32 = 0.0;
let mut temp_mod_resonance_2_source_2: f32 = 0.0;
let mut temp_mod_resonance_2_source_3: f32 = 0.0;
let mut temp_mod_resonance_2_source_4: f32 = 0.0;
let mut temp_mod_detune_1: f32 = 0.0;
let mut temp_mod_detune_2: f32 = 0.0;
let mut temp_mod_detune_3: f32 = 0.0;
let mut temp_mod_uni_detune_1: f32 = 0.0;
let mut temp_mod_uni_detune_2: f32 = 0.0;
let mut temp_mod_uni_detune_3: f32 = 0.0;
// These are used for velocity to detune linkages
let mut temp_mod_vel_sum: f32 = 0.0;
let mut temp_mod_uni_vel_sum: f32 = 0.0;
let mut temp_mod_gain_1: f32 = -2.0;
let mut temp_mod_gain_2: f32 = -2.0;
let mut temp_mod_gain_3: f32 = -2.0;
let mut temp_mod_lfo_gain_1: f32 = 1.0;
let mut temp_mod_lfo_gain_2: f32 = 1.0;
let mut temp_mod_lfo_gain_3: f32 = 1.0;
// Modulation structs to pass things
let modulations_1: ModulationStruct;
let modulations_2: ModulationStruct;
let modulations_3: ModulationStruct;
let modulations_4: ModulationStruct;
// In this modulation section the velocity stuff is all weird since we need to pass velocity mod
// But this happens before we process the note values hence storing/passing it
// This is outside for held notes on specific source -> destinations
// This would happen when mod_value_X == 2.0 as a result - hence using the Atomic for velocity
if self.params.mod_source_1.value() == ModulationSource::Velocity {
match self.params.mod_destination_1.value() {
ModulationDestination::Cutoff_1 => {
temp_mod_cutoff_1_source_1 +=
8000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
}
ModulationDestination::Cutoff_2 => {
temp_mod_cutoff_2_source_1 +=
8000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
}
ModulationDestination::All_Gain => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
let vel = self.current_note_on_velocity.load(Ordering::Relaxed);
temp_mod_gain_1 = vel;
temp_mod_gain_2 = vel;
temp_mod_gain_3 = vel;
}
}
ModulationDestination::Osc1_Gain => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_gain_1 = self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Osc2_Gain => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_gain_2 = self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Osc3_Gain => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_gain_3 = self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Resonance_1 => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_resonance_1_source_1 +=
self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Resonance_2 => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_resonance_2_source_1 +=
self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
_ => {}
}
}
if self.params.mod_source_2.value() == ModulationSource::Velocity {
match self.params.mod_destination_2.value() {
ModulationDestination::Cutoff_1 => {
temp_mod_cutoff_1_source_2 +=
8000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
}
ModulationDestination::Cutoff_2 => {
temp_mod_cutoff_2_source_2 +=
8000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
}
ModulationDestination::All_Gain => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
let vel = self.current_note_on_velocity.load(Ordering::Relaxed);
temp_mod_gain_1 = vel;
temp_mod_gain_2 = vel;
temp_mod_gain_3 = vel;
}
}
ModulationDestination::Osc1_Gain => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_gain_1 = self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Osc2_Gain => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_gain_2 = self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Osc3_Gain => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_gain_3 = self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Resonance_1 => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_resonance_1_source_2 -=
self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Resonance_2 => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_resonance_2_source_2 -=
self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
_ => {}
}
}
if self.params.mod_source_3.value() == ModulationSource::Velocity {
match self.params.mod_destination_3.value() {
ModulationDestination::Cutoff_1 => {
temp_mod_cutoff_1_source_3 +=
8000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
}
ModulationDestination::Cutoff_2 => {
temp_mod_cutoff_2_source_3 +=
8000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
}
ModulationDestination::All_Gain => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
let vel = self.current_note_on_velocity.load(Ordering::Relaxed);
temp_mod_gain_1 = vel;
temp_mod_gain_2 = vel;
temp_mod_gain_3 = vel;
}
}
ModulationDestination::Osc1_Gain => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_gain_1 = self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Osc2_Gain => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_gain_2 = self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Osc3_Gain => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_gain_3 = self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Resonance_1 => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_resonance_1_source_3 +=
self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Resonance_2 => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_resonance_2_source_3 +=
self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
_ => {}
}
}
if self.params.mod_source_4.value() == ModulationSource::Velocity {
match self.params.mod_destination_4.value() {
ModulationDestination::Cutoff_1 => {
temp_mod_cutoff_1_source_4 +=
8000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
}
ModulationDestination::Cutoff_2 => {
temp_mod_cutoff_2_source_4 +=
8000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
}
ModulationDestination::All_Gain => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
let vel = self.current_note_on_velocity.load(Ordering::Relaxed);
temp_mod_gain_1 = vel;
temp_mod_gain_2 = vel;
temp_mod_gain_3 = vel;
}
}
ModulationDestination::Osc1_Gain => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_gain_1 = self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Osc2_Gain => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_gain_2 = self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Osc3_Gain => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_gain_3 = self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Resonance_1 => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_resonance_1_source_4 +=
self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
ModulationDestination::Resonance_2 => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_resonance_2_source_4 +=
self.current_note_on_velocity.load(Ordering::Relaxed);
}
}
_ => {}
}
}
///////////////////////////////////////////////////////////////
// If mod_value is not -2.0 we are in Note ON event or an LFO
if mod_value_1 != -2.0 {
match self.params.mod_destination_1.value() {
ModulationDestination::None | ModulationDestination::UnsetModulation => {}
ModulationDestination::Cutoff_1 => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
// I don't think this gets reached in Velocity case because of mod_value_X
temp_mod_cutoff_1_source_1 +=
20000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_cutoff_1_source_1 += 20000.0 * mod_value_1;
}
}
ModulationDestination::Cutoff_2 => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_cutoff_2_source_1 +=
20000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_cutoff_2_source_1 += 20000.0 * mod_value_1;
}
}
ModulationDestination::Resonance_1 => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_resonance_1_source_1 -=
self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_resonance_1_source_1 -= mod_value_1;
}
}
ModulationDestination::Resonance_2 => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_resonance_2_source_1 -=
self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_resonance_2_source_1 -= mod_value_1;
}
}
ModulationDestination::All_Detune => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_1;
}
temp_mod_detune_1 += mod_value_1;
temp_mod_detune_2 += mod_value_1;
temp_mod_detune_3 += mod_value_1;
}
ModulationDestination::Osc1Detune => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_1;
}
temp_mod_detune_1 += mod_value_1;
}
ModulationDestination::Osc2Detune => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_1;
}
temp_mod_detune_2 += mod_value_1;
}
ModulationDestination::Osc3Detune => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_1;
}
temp_mod_detune_3 += mod_value_1;
}
ModulationDestination::All_UniDetune => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_1;
}
temp_mod_uni_detune_1 += mod_value_1;
temp_mod_uni_detune_2 += mod_value_1;
temp_mod_uni_detune_3 += mod_value_1;
}
ModulationDestination::Osc1UniDetune => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_uni_vel_sum += mod_value_1;
}
temp_mod_uni_detune_1 += mod_value_1;
}
ModulationDestination::Osc2UniDetune => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_uni_vel_sum += mod_value_1;
}
temp_mod_uni_detune_2 += mod_value_1;
}
ModulationDestination::Osc3UniDetune => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_uni_vel_sum += mod_value_1;
}
temp_mod_uni_detune_3 += mod_value_1;
}
ModulationDestination::All_Gain => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
let vel = self.current_note_on_velocity.load(Ordering::Relaxed);
temp_mod_gain_1 = vel;
temp_mod_gain_2 = vel;
temp_mod_gain_3 = vel;
} else {
temp_mod_lfo_gain_1 = mod_value_1;
temp_mod_lfo_gain_2 = mod_value_1;
temp_mod_lfo_gain_3 = mod_value_1;
}
}
ModulationDestination::Osc1_Gain => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_gain_1 = self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_lfo_gain_1 = mod_value_1;
}
}
ModulationDestination::Osc2_Gain => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_gain_2 = self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_lfo_gain_2 = mod_value_1;
}
}
ModulationDestination::Osc3_Gain => {
if self.params.mod_source_1.value() == ModulationSource::Velocity {
temp_mod_gain_3 = self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_lfo_gain_3 = mod_value_1;
}
}
}
}
if mod_value_2 != -2.0 {
match self.params.mod_destination_2.value() {
ModulationDestination::None | ModulationDestination::UnsetModulation => {}
ModulationDestination::Cutoff_1 => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_cutoff_1_source_2 +=
20000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_cutoff_1_source_2 += 20000.0 * mod_value_2;
}
}
ModulationDestination::Cutoff_2 => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_cutoff_2_source_2 +=
20000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_cutoff_2_source_2 += 20000.0 * mod_value_2;
}
}
ModulationDestination::Resonance_1 => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_resonance_1_source_2 -=
self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_resonance_1_source_2 -= mod_value_2;
}
}
ModulationDestination::Resonance_2 => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_resonance_2_source_2 -=
self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_resonance_2_source_2 -= mod_value_2;
}
}
ModulationDestination::All_Detune => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_2;
}
temp_mod_detune_1 += mod_value_2;
temp_mod_detune_2 += mod_value_2;
temp_mod_detune_3 += mod_value_2;
}
ModulationDestination::Osc1Detune => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_2;
}
temp_mod_detune_1 += mod_value_2;
}
ModulationDestination::Osc2Detune => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_2;
}
temp_mod_detune_2 += mod_value_2;
}
ModulationDestination::Osc3Detune => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_2;
}
temp_mod_detune_3 += mod_value_2;
}
ModulationDestination::All_UniDetune => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_2;
}
temp_mod_uni_detune_1 += mod_value_2;
temp_mod_uni_detune_2 += mod_value_2;
temp_mod_uni_detune_3 += mod_value_2;
}
ModulationDestination::Osc1UniDetune => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_uni_vel_sum += mod_value_2;
}
temp_mod_uni_detune_1 += mod_value_2;
}
ModulationDestination::Osc2UniDetune => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_uni_vel_sum += mod_value_2;
}
temp_mod_uni_detune_2 += mod_value_2;
}
ModulationDestination::Osc3UniDetune => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_uni_vel_sum += mod_value_2;
}
temp_mod_uni_detune_3 += mod_value_2;
}
ModulationDestination::All_Gain => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
let vel = self.current_note_on_velocity.load(Ordering::Relaxed);
temp_mod_gain_1 = vel;
temp_mod_gain_2 = vel;
temp_mod_gain_3 = vel;
} else {
temp_mod_lfo_gain_1 = mod_value_2;
temp_mod_lfo_gain_2 = mod_value_2;
temp_mod_lfo_gain_3 = mod_value_2;
}
}
ModulationDestination::Osc1_Gain => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_gain_1 = self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_lfo_gain_1 = mod_value_2;
}
}
ModulationDestination::Osc2_Gain => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_gain_2 = self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_lfo_gain_2 = mod_value_2;
}
}
ModulationDestination::Osc3_Gain => {
if self.params.mod_source_2.value() == ModulationSource::Velocity {
temp_mod_gain_3 = self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_lfo_gain_3 = mod_value_2;
}
}
}
}
if mod_value_3 != -2.0 {
match self.params.mod_destination_3.value() {
ModulationDestination::None | ModulationDestination::UnsetModulation => {}
ModulationDestination::Cutoff_1 => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_cutoff_1_source_3 +=
20000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_cutoff_1_source_3 += 20000.0 * mod_value_3;
}
}
ModulationDestination::Cutoff_2 => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_cutoff_2_source_3 +=
20000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_cutoff_2_source_3 += 20000.0 * mod_value_3;
}
}
ModulationDestination::Resonance_1 => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_resonance_1_source_3 -=
self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_resonance_1_source_3 -= mod_value_3;
}
}
ModulationDestination::Resonance_2 => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_resonance_2_source_3 -=
self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_resonance_2_source_3 -= mod_value_3;
}
}
ModulationDestination::All_Detune => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_3;
}
temp_mod_detune_1 += mod_value_3;
temp_mod_detune_2 += mod_value_3;
temp_mod_detune_3 += mod_value_3;
}
ModulationDestination::Osc1Detune => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_3;
}
temp_mod_detune_1 += mod_value_3;
}
ModulationDestination::Osc2Detune => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_3;
}
temp_mod_detune_2 += mod_value_3;
}
ModulationDestination::Osc3Detune => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_3;
}
temp_mod_detune_3 += mod_value_3;
}
ModulationDestination::All_UniDetune => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_3;
}
temp_mod_uni_detune_1 += mod_value_3;
temp_mod_uni_detune_2 += mod_value_3;
temp_mod_uni_detune_3 += mod_value_3;
}
ModulationDestination::Osc1UniDetune => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_uni_vel_sum += mod_value_3;
}
temp_mod_uni_detune_1 += mod_value_3;
}
ModulationDestination::Osc2UniDetune => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_uni_vel_sum += mod_value_3;
}
temp_mod_uni_detune_2 += mod_value_3;
}
ModulationDestination::Osc3UniDetune => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_uni_vel_sum += mod_value_3;
}
temp_mod_uni_detune_3 += mod_value_3;
}
ModulationDestination::All_Gain => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
let vel = self.current_note_on_velocity.load(Ordering::Relaxed);
temp_mod_gain_1 = vel;
temp_mod_gain_2 = vel;
temp_mod_gain_3 = vel;
} else {
temp_mod_lfo_gain_1 = mod_value_3;
temp_mod_lfo_gain_2 = mod_value_3;
temp_mod_lfo_gain_3 = mod_value_3;
}
}
ModulationDestination::Osc1_Gain => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_gain_1 = self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_lfo_gain_1 = mod_value_3;
}
}
ModulationDestination::Osc2_Gain => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_gain_2 = self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_lfo_gain_2 = mod_value_3;
}
}
ModulationDestination::Osc3_Gain => {
if self.params.mod_source_3.value() == ModulationSource::Velocity {
temp_mod_gain_3 = self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_lfo_gain_3 = mod_value_3;
}
}
}
}
if mod_value_4 != -2.0 {
match self.params.mod_destination_4.value() {
ModulationDestination::None | ModulationDestination::UnsetModulation => {}
ModulationDestination::Cutoff_1 => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_cutoff_1_source_4 +=
20000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_cutoff_1_source_4 += 20000.0 * mod_value_4;
}
}
ModulationDestination::Cutoff_2 => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_cutoff_2_source_4 +=
20000.0 * self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_cutoff_2_source_4 += 20000.0 * mod_value_4;
}
}
ModulationDestination::Resonance_1 => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_resonance_1_source_4 -=
self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_resonance_1_source_4 -= mod_value_4;
}
}
ModulationDestination::Resonance_2 => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_resonance_2_source_4 -=
self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_resonance_2_source_4 -= mod_value_4;
}
}
ModulationDestination::All_Detune => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_4;
}
temp_mod_detune_1 += mod_value_4;
temp_mod_detune_2 += mod_value_4;
temp_mod_detune_3 += mod_value_4;
}
ModulationDestination::Osc1Detune => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_4;
}
temp_mod_detune_1 += mod_value_4;
}
ModulationDestination::Osc2Detune => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_4;
}
temp_mod_detune_2 += mod_value_4;
}
ModulationDestination::Osc3Detune => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_4;
}
temp_mod_detune_3 += mod_value_4;
}
ModulationDestination::All_UniDetune => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_vel_sum += mod_value_4;
}
temp_mod_uni_detune_1 += mod_value_4;
temp_mod_uni_detune_2 += mod_value_4;
temp_mod_uni_detune_3 += mod_value_4;
}
ModulationDestination::Osc1UniDetune => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_uni_vel_sum += mod_value_4;
}
temp_mod_uni_detune_1 += mod_value_4;
}
ModulationDestination::Osc2UniDetune => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_uni_vel_sum += mod_value_4;
}
temp_mod_uni_detune_2 += mod_value_4;
}
ModulationDestination::Osc3UniDetune => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_uni_vel_sum += mod_value_4;
}
temp_mod_uni_detune_3 += mod_value_4;
}
ModulationDestination::All_Gain => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
let vel = self.current_note_on_velocity.load(Ordering::Relaxed);
temp_mod_gain_1 = vel;
temp_mod_gain_2 = vel;
temp_mod_gain_3 = vel;
} else {
temp_mod_lfo_gain_1 = mod_value_4;
temp_mod_lfo_gain_2 = mod_value_4;
temp_mod_lfo_gain_3 = mod_value_4;
}
}
ModulationDestination::Osc1_Gain => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_gain_1 = self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_lfo_gain_1 = mod_value_4;
}
}
ModulationDestination::Osc2_Gain => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_gain_2 = self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_lfo_gain_2 = mod_value_4;
}
}
ModulationDestination::Osc3_Gain => {
if self.params.mod_source_4.value() == ModulationSource::Velocity {
temp_mod_gain_3 = self.current_note_on_velocity.load(Ordering::Relaxed);
} else {
temp_mod_lfo_gain_3 = mod_value_4;
}
}
}
}
// I think this makes sense to split into structs so each modulation path has its own easily debuggable chain
modulations_1 = ModulationStruct {
temp_mod_cutoff_1: temp_mod_cutoff_1_source_1,
temp_mod_cutoff_2: temp_mod_cutoff_2_source_1,
temp_mod_resonance_1: temp_mod_resonance_1_source_1,
temp_mod_resonance_2: temp_mod_resonance_2_source_1,
temp_mod_detune_1: temp_mod_detune_1,
temp_mod_detune_2: temp_mod_detune_2,
temp_mod_detune_3: temp_mod_detune_3,
temp_mod_uni_detune_1: temp_mod_uni_detune_1,
temp_mod_uni_detune_2: temp_mod_uni_detune_2,
temp_mod_uni_detune_3: temp_mod_uni_detune_3,
temp_mod_vel_sum: temp_mod_vel_sum,
};
modulations_2 = ModulationStruct {
temp_mod_cutoff_1: temp_mod_cutoff_1_source_2,
temp_mod_cutoff_2: temp_mod_cutoff_2_source_2,
temp_mod_resonance_1: temp_mod_resonance_1_source_2,
temp_mod_resonance_2: temp_mod_resonance_2_source_2,
temp_mod_detune_1: temp_mod_detune_1,
temp_mod_detune_2: temp_mod_detune_2,
temp_mod_detune_3: temp_mod_detune_3,
temp_mod_uni_detune_1: temp_mod_uni_detune_1,
temp_mod_uni_detune_2: temp_mod_uni_detune_2,
temp_mod_uni_detune_3: temp_mod_uni_detune_3,
temp_mod_vel_sum: temp_mod_vel_sum,
};
modulations_3 = ModulationStruct {
temp_mod_cutoff_1: temp_mod_cutoff_1_source_3,
temp_mod_cutoff_2: temp_mod_cutoff_2_source_3,
temp_mod_resonance_1: temp_mod_resonance_1_source_3,
temp_mod_resonance_2: temp_mod_resonance_2_source_3,
temp_mod_detune_1: temp_mod_detune_1,
temp_mod_detune_2: temp_mod_detune_2,
temp_mod_detune_3: temp_mod_detune_3,
temp_mod_uni_detune_1: temp_mod_uni_detune_1,
temp_mod_uni_detune_2: temp_mod_uni_detune_2,
temp_mod_uni_detune_3: temp_mod_uni_detune_3,
temp_mod_vel_sum: temp_mod_vel_sum,
};
modulations_4 = ModulationStruct {
temp_mod_cutoff_1: temp_mod_cutoff_1_source_4,
temp_mod_cutoff_2: temp_mod_cutoff_2_source_4,
temp_mod_resonance_1: temp_mod_resonance_1_source_4,
temp_mod_resonance_2: temp_mod_resonance_2_source_4,
temp_mod_detune_1: temp_mod_detune_1,
temp_mod_detune_2: temp_mod_detune_2,
temp_mod_detune_3: temp_mod_detune_3,
temp_mod_uni_detune_1: temp_mod_uni_detune_1,
temp_mod_uni_detune_2: temp_mod_uni_detune_2,
temp_mod_uni_detune_3: temp_mod_uni_detune_3,
temp_mod_vel_sum: temp_mod_vel_sum,
};
// Audio Module Processing of Audio kicks off here
/////////////////////////////////////////////////////////////////////////////////////////////////
// Since File Dialog can be set by any of these we need to check each time
if !self.file_dialog.load(Ordering::Relaxed)
&& self.params._audio_module_1_type.value() != AudioModuleType::Off
{
// We send our sample_id position, params, current midi event, module index, current voice max, and whether any params have changed
(
wave1_l,
wave1_r,
reset_filter_controller1,
note_off_filter_controller1,
) = self.audio_module_1.lock().unwrap().process(
sample_id,
midi_event.clone(),
sent_voice_max,
modulations_1.temp_mod_detune_1
+ modulations_2.temp_mod_detune_1
+ modulations_3.temp_mod_detune_1
+ modulations_4.temp_mod_detune_1,
modulations_1.temp_mod_uni_detune_1
+ modulations_2.temp_mod_uni_detune_1
+ modulations_3.temp_mod_uni_detune_1
+ modulations_4.temp_mod_uni_detune_1,
temp_mod_vel_sum,
temp_mod_uni_vel_sum,
temp_mod_gain_1,
temp_mod_lfo_gain_1,
);
// I know this isn't a perfect 3rd, but 0.01 is acceptable headroom
wave1_l *= self.params.audio_module_1_level.value() * 0.33;
wave1_r *= self.params.audio_module_1_level.value() * 0.33;
}
if !self.file_dialog.load(Ordering::Relaxed)
&& self.params._audio_module_2_type.value() != AudioModuleType::Off
{
(
wave2_l,
wave2_r,
reset_filter_controller2,
note_off_filter_controller2,
) = self.audio_module_2.lock().unwrap().process(
sample_id,
midi_event.clone(),
sent_voice_max,
modulations_1.temp_mod_detune_2
+ modulations_2.temp_mod_detune_2
+ modulations_3.temp_mod_detune_2
+ modulations_4.temp_mod_detune_2,
modulations_1.temp_mod_uni_detune_2
+ modulations_2.temp_mod_uni_detune_2
+ modulations_3.temp_mod_uni_detune_2
+ modulations_4.temp_mod_uni_detune_2,
temp_mod_vel_sum,
temp_mod_uni_vel_sum,
temp_mod_gain_2,
temp_mod_lfo_gain_2,
);
wave2_l *= self.params.audio_module_2_level.value() * 0.33;
wave2_r *= self.params.audio_module_2_level.value() * 0.33;
}
if !self.file_dialog.load(Ordering::Relaxed)
&& self.params._audio_module_3_type.value() != AudioModuleType::Off
{
(
wave3_l,
wave3_r,
reset_filter_controller3,
note_off_filter_controller3,
) = self.audio_module_3.lock().unwrap().process(
sample_id,
midi_event.clone(),
sent_voice_max,
modulations_1.temp_mod_detune_3
+ modulations_2.temp_mod_detune_3
+ modulations_3.temp_mod_detune_3
+ modulations_4.temp_mod_detune_3,
modulations_1.temp_mod_uni_detune_3
+ modulations_2.temp_mod_uni_detune_3
+ modulations_3.temp_mod_uni_detune_3
+ modulations_4.temp_mod_uni_detune_3,
temp_mod_vel_sum,
temp_mod_uni_vel_sum,
temp_mod_gain_3,
temp_mod_lfo_gain_3,
);
wave3_l *= self.params.audio_module_3_level.value() * 0.33;
wave3_r *= self.params.audio_module_3_level.value() * 0.33;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// Audio Module Processing over
// If a new note has happened we should reset the phase of our LFO if sync enabled
if reset_filter_controller1 || reset_filter_controller2 || reset_filter_controller3 {
if self.params.lfo1_sync.value() {
self.lfo_1.set_phase(self.params.lfo1_phase.value());
}
if self.params.lfo2_sync.value() {
self.lfo_2.set_phase(self.params.lfo2_phase.value());
}
if self.params.lfo3_sync.value() {
self.lfo_3.set_phase(self.params.lfo3_phase.value());
}
}
// Get our new LFO values
if self.params.lfo1_enable.value() {
lfo_1_current = self.lfo_1.next_sample(self.sample_rate);
}
if self.params.lfo2_enable.value() {
lfo_2_current = self.lfo_2.next_sample(self.sample_rate);
}
if self.params.lfo3_enable.value() {
lfo_3_current = self.lfo_3.next_sample(self.sample_rate);
}
// Define the outputs for filter routing or non-filter routing
let mut left_output_filter1: f32 = 0.0;
let mut right_output_filter1: f32 = 0.0;
let mut left_output_filter2: f32 = 0.0;
let mut right_output_filter2: f32 = 0.0;
let mut left_output: f32 = 0.0;
let mut right_output: f32 = 0.0;
match self.params.audio_module_1_routing.value() {
AMFilterRouting::Bypass => {
left_output += wave1_l;
right_output += wave1_r;
}
AMFilterRouting::Filter1 => {
left_output_filter1 += wave1_l;
right_output_filter1 += wave1_r;
}
AMFilterRouting::Filter2 => {
left_output_filter2 += wave1_l;
right_output_filter2 += wave1_r;
}
AMFilterRouting::Both => {
left_output_filter1 += wave1_l;
right_output_filter1 += wave1_r;
left_output_filter2 += wave1_l;
right_output_filter2 += wave1_r;
}
}
#[allow(unused_assignments)]
match self.params.audio_module_2_routing.value() {
AMFilterRouting::Bypass => {
left_output += wave2_l;
right_output += wave2_r;
}
AMFilterRouting::Filter1 => {
left_output_filter1 += wave2_l;
right_output_filter1 += wave2_r;
}
AMFilterRouting::Filter2 => {
left_output_filter2 += wave2_l;
right_output_filter2 += wave2_r;
}
AMFilterRouting::Both => {
left_output_filter1 += wave2_l;
right_output_filter1 += wave2_r;
left_output_filter2 += wave2_l;
right_output_filter2 += wave2_r;
}
}
#[allow(unused_assignments)]
match self.params.audio_module_3_routing.value() {
AMFilterRouting::Bypass => {
left_output += wave3_l;
right_output += wave3_r;
}
AMFilterRouting::Filter1 => {
left_output_filter1 += wave3_l;
right_output_filter1 += wave3_r;
}
AMFilterRouting::Filter2 => {
left_output_filter1 += wave3_l;
right_output_filter2 += wave3_r;
}
AMFilterRouting::Both => {
left_output_filter1 += wave3_l;
right_output_filter1 += wave3_r;
left_output_filter2 += wave3_l;
right_output_filter2 += wave3_r;
}
}
let mut filter1_processed_l: f32 = 0.0;
let mut filter1_processed_r: f32 = 0.0;
let mut filter2_processed_l: f32 = 0.0;
let mut filter2_processed_r: f32 = 0.0;
// I ended up doing a passthrough/sum of modulations so they can stack if that's what user desires
// without breaking things when they are unset
match self.params.filter_routing.value() {
FilterRouting::Parallel => {
self.filter_process_1(
note_off_filter_controller1,
note_off_filter_controller2,
note_off_filter_controller3,
reset_filter_controller1,
reset_filter_controller2,
reset_filter_controller3,
left_output_filter1,
right_output_filter1,
&mut filter1_processed_l,
&mut filter1_processed_r,
modulations_1.temp_mod_cutoff_1
+ modulations_2.temp_mod_cutoff_1
+ modulations_3.temp_mod_cutoff_1
+ modulations_4.temp_mod_cutoff_1,
//+ vel_cutoff_1,
modulations_1.temp_mod_resonance_1
+ modulations_2.temp_mod_resonance_1
+ modulations_3.temp_mod_resonance_1
+ modulations_4.temp_mod_resonance_1,
//+ vel_resonance_1,
);
self.filter_process_2(
note_off_filter_controller1,
note_off_filter_controller2,
note_off_filter_controller3,
reset_filter_controller1,
reset_filter_controller2,
reset_filter_controller3,
left_output_filter2,
right_output_filter2,
&mut filter2_processed_l,
&mut filter2_processed_r,
modulations_1.temp_mod_cutoff_2
+ modulations_2.temp_mod_cutoff_2
+ modulations_3.temp_mod_cutoff_2
+ modulations_4.temp_mod_cutoff_2,
//+ vel_cutoff_2,
modulations_1.temp_mod_resonance_2
+ modulations_2.temp_mod_resonance_2
+ modulations_3.temp_mod_resonance_2
+ modulations_4.temp_mod_resonance_2,
//+ vel_resonance_2,
);
left_output += filter1_processed_l + filter2_processed_l;
right_output += filter1_processed_r + filter2_processed_r;
}
FilterRouting::Series12 => {
self.filter_process_1(
note_off_filter_controller1,
note_off_filter_controller2,
note_off_filter_controller3,
reset_filter_controller1,
reset_filter_controller2,
reset_filter_controller3,
left_output_filter1,
right_output_filter1,
&mut filter1_processed_l,
&mut filter1_processed_r,
modulations_1.temp_mod_cutoff_1
+ modulations_2.temp_mod_cutoff_1
+ modulations_3.temp_mod_cutoff_1
+ modulations_4.temp_mod_cutoff_1,
//+ vel_cutoff_1,
modulations_1.temp_mod_resonance_1
+ modulations_2.temp_mod_resonance_1
+ modulations_3.temp_mod_resonance_1
+ modulations_4.temp_mod_resonance_1,
//+ vel_resonance_1,
);
self.filter_process_2(
note_off_filter_controller1,
note_off_filter_controller2,
note_off_filter_controller3,
reset_filter_controller1,
reset_filter_controller2,
reset_filter_controller3,
filter1_processed_l,
filter1_processed_r,
&mut filter2_processed_l,
&mut filter2_processed_r,
modulations_1.temp_mod_cutoff_2
+ modulations_2.temp_mod_cutoff_2
+ modulations_3.temp_mod_cutoff_2
+ modulations_4.temp_mod_cutoff_2,
//+ vel_cutoff_2,
modulations_1.temp_mod_resonance_2
+ modulations_2.temp_mod_resonance_2
+ modulations_3.temp_mod_resonance_2
+ modulations_4.temp_mod_resonance_2,
//+ vel_resonance_2,
);
left_output += filter2_processed_l;
right_output += filter2_processed_r;
}
FilterRouting::Series21 => {
self.filter_process_2(
note_off_filter_controller1,
note_off_filter_controller2,
note_off_filter_controller3,
reset_filter_controller1,
reset_filter_controller2,
reset_filter_controller3,
left_output_filter2,
right_output_filter2,
&mut filter2_processed_l,
&mut filter2_processed_r,
modulations_1.temp_mod_cutoff_2
+ modulations_2.temp_mod_cutoff_2
+ modulations_3.temp_mod_cutoff_2
+ modulations_4.temp_mod_cutoff_2,
//+ vel_cutoff_2,
modulations_1.temp_mod_resonance_2
+ modulations_2.temp_mod_resonance_2
+ modulations_3.temp_mod_resonance_2
+ modulations_4.temp_mod_resonance_2,
//+ vel_resonance_2,
);
self.filter_process_1(
note_off_filter_controller1,
note_off_filter_controller2,
note_off_filter_controller3,
reset_filter_controller1,
reset_filter_controller2,
reset_filter_controller3,
filter2_processed_l,
filter2_processed_r,
&mut filter1_processed_l,
&mut filter1_processed_r,
modulations_1.temp_mod_cutoff_1
+ modulations_2.temp_mod_cutoff_1
+ modulations_3.temp_mod_cutoff_1
+ modulations_4.temp_mod_cutoff_1,
//+ vel_cutoff_1,
modulations_1.temp_mod_resonance_1
+ modulations_2.temp_mod_resonance_1
+ modulations_3.temp_mod_resonance_1
+ modulations_4.temp_mod_resonance_1,
//+ vel_resonance_1,
);
left_output += filter1_processed_l;
right_output += filter1_processed_r;
}
}
// FX
////////////////////////////////////////////////////////////////////////////////////////
if self.params.use_fx.value() {
// Equalizer use
if self.params.pre_use_eq.value() {
let eq_ref = self.bands.clone();
let mut eq = eq_ref.lock().unwrap();
eq[0].set_type(FilterType::LowShelf);
eq[1].set_type(FilterType::Peak);
eq[2].set_type(FilterType::HighShelf);
let q_value: f32 = 0.93;
eq[0].update(
self.sample_rate,
self.params.pre_low_freq.value(),
self.params.pre_low_gain.value(),
q_value,
);
eq[1].update(
self.sample_rate,
self.params.pre_mid_freq.value(),
self.params.pre_mid_gain.value(),
q_value,
);
eq[2].update(
self.sample_rate,
self.params.pre_high_freq.value(),
self.params.pre_high_gain.value(),
q_value,
);
let mut temp_l: f32;
let mut temp_r: f32;
// This is the first time we run a filter at all
(temp_l, temp_r) = eq[0].process_sample(left_output, right_output);
(temp_l, temp_r) = eq[1].process_sample(temp_l, temp_r);
(temp_l, temp_r) = eq[2].process_sample(temp_l, temp_r);
// Reassign our new output
left_output = temp_l;
right_output = temp_r;
}
// Compressor
if self.params.use_compressor.value() {
self.compressor.update(
self.sample_rate,
self.params.comp_amt.value(),
self.params.comp_atk.value(),
self.params.comp_rel.value(),
self.params.comp_drive.value(),
);
(left_output, right_output) =
self.compressor.process(left_output, right_output);
}
// ABass Algorithm
if self.params.use_abass.value() {
left_output = a_bass_saturation(left_output, self.params.abass_amount.value());
right_output =
a_bass_saturation(right_output, self.params.abass_amount.value());
}
// Distortion
if self.params.use_saturation.value() {
self.saturator.set_type(self.params.sat_type.value());
(left_output, right_output) = self.saturator.process(
left_output,
right_output,
self.params.sat_amt.value(),
);
}
// Buffer Modulator
if self.params.use_buffermod.value() {
self.buffermod.update(
self.sample_rate,
self.params.buffermod_depth.value(),
self.params.buffermod_rate.value(),
self.params.buffermod_spread.value(),
self.params.buffermod_timing.value(),
);
(left_output, right_output) = self.buffermod.process(
left_output,
right_output,
self.params.buffermod_amount.value(),
);
}
// Phaser
if self.params.use_phaser.value() {
self.phaser.set_sample_rate(self.sample_rate);
self.phaser.set_depth(self.params.phaser_depth.value());
self.phaser.set_rate(self.params.phaser_rate.value());
self.phaser
.set_feedback(self.params.phaser_feedback.value());
(left_output, right_output) = self.phaser.process(
left_output,
right_output,
self.params.phaser_amount.value(),
);
}
// Flanger
if self.params.use_flanger.value() {
self.flanger.update(
self.sample_rate,
self.params.flanger_depth.value(),
self.params.flanger_rate.value(),
self.params.flanger_feedback.value(),
);
(left_output, right_output) = self.flanger.process(
left_output,
right_output,
self.params.flanger_amount.value(),
);
}
// Delay
if self.params.use_delay.value() {
self.delay.set_sample_rate(
self.sample_rate,
context.transport().tempo.unwrap_or(1.0) as f32,
);
self.delay.set_length(self.params.delay_time.value());
self.delay.set_feedback(self.params.delay_decay.value());
self.delay.set_type(self.params.delay_type.value());
(left_output, right_output) = self.delay.process(
left_output,
right_output,
self.params.delay_amount.value(),
);
}
// Reverb
if self.params.use_reverb.value() {
// Stacked TDLs to make reverb
self.reverb[0].set_size(self.params.reverb_size.value(), self.sample_rate);
self.reverb[1]
.set_size(self.params.reverb_size.value() * 0.546, self.sample_rate);
self.reverb[2]
.set_size(self.params.reverb_size.value() * 0.251, self.sample_rate);
self.reverb[3]
.set_size(self.params.reverb_size.value() * 0.735, self.sample_rate);
self.reverb[4]
.set_size(self.params.reverb_size.value() * 0.669, self.sample_rate);
self.reverb[5]
.set_size(self.params.reverb_size.value() * 0.374, self.sample_rate);
self.reverb[6]
.set_size(self.params.reverb_size.value() * 0.8, self.sample_rate);
self.reverb[7]
.set_size(self.params.reverb_size.value() * 0.4, self.sample_rate);
for verb in self.reverb.iter_mut() {
verb.set_feedback(self.params.reverb_feedback.value());
(left_output, right_output) = verb.process_tdl(
left_output,
right_output,
self.params.reverb_amount.value(),
);
}
}
// Limiter
if self.params.use_limiter.value() {
self.limiter.update(
self.params.limiter_knee.value(),
self.params.limiter_threshold.value(),
);
(left_output, right_output) = self.limiter.process(left_output, right_output);
}
}
// DC Offset Removal
////////////////////////////////////////////////////////////////////////////////////////
// There were several filter settings that caused massive DC spikes so I added this here
if !self.file_dialog.load(Ordering::Relaxed) {
// Remove DC Offsets with our SVF
self.dc_filter_l
.update(20.0, 0.8, self.sample_rate, ResonanceType::Default);
self.dc_filter_r
.update(20.0, 0.8, self.sample_rate, ResonanceType::Default);
(_, _, left_output) = self.dc_filter_l.process(left_output);
(_, _, right_output) = self.dc_filter_r.process(right_output);
}
// Final output to DAW
////////////////////////////////////////////////////////////////////////////////////////
// Reassign our output signal
*channel_samples.get_mut(0).unwrap() = left_output * self.params.master_level.value();
*channel_samples.get_mut(1).unwrap() = right_output * self.params.master_level.value();
// This is down here for the save function gui and export preset unsetting to work
if *self.reload_entire_preset.lock().unwrap() && self.params.param_save_bank.value() {
*self.reload_entire_preset.lock().unwrap() = false;
}
if *self.reload_entire_preset.lock().unwrap() && self.params.param_export_preset.value()
{
*self.reload_entire_preset.lock().unwrap() = false;
}
}
}
// import_preset() uses message packing with serde
fn import_preset() -> (String, Option<ActuatePreset>) {
let imported_preset = FileDialog::new()
.add_filter("actuate", &["actuate"])
.pick_file();
let return_name;
if let Some(imported_preset) = imported_preset {
return_name = imported_preset
.to_str()
.unwrap_or("Invalid Path")
.to_string();
// Read the compressed data from the file
let mut compressed_data = Vec::new();
if let Err(err) = std::fs::File::open(&return_name)
.and_then(|mut file| file.read_to_end(&mut compressed_data))
{
eprintln!("Error reading compressed data from file: {}", err);
return (err.to_string(), Option::None);
}
// Decompress the data
let decompressed_data = Self::decompress_bytes(&compressed_data);
if let Err(err) = decompressed_data {
eprintln!("Error decompressing data: {}", err);
return (err.to_string(), Option::None);
}
// Deserialize the MessagePack data
let file_string_data = decompressed_data.unwrap();
// Deserialize into ActuatePreset - return default empty lib if error
let mut unserialized: ActuatePreset = rmp_serde::from_slice(&file_string_data)
.unwrap_or(ActuatePreset {
preset_name: "Error Importing".to_string(),
preset_info: "Corrupted preset or incompatible version".to_string(),
preset_category: PresetType::Select,
tag_acid: false,
tag_analog: false,
tag_bright: false,
tag_chord: false,
tag_crisp: false,
tag_deep: false,
tag_delicate: false,
tag_hard: false,
tag_harsh: false,
tag_lush: false,
tag_mellow: false,
tag_resonant: false,
tag_rich: false,
tag_sharp: false,
tag_silky: false,
tag_smooth: false,
tag_soft: false,
tag_stab: false,
tag_warm: false,
mod1_audio_module_type: AudioModuleType::Osc,
mod1_audio_module_level: 1.0,
mod1_loaded_sample: vec![vec![0.0, 0.0]],
mod1_sample_lib: vec![vec![vec![0.0, 0.0]]],
mod1_loop_wavetable: false,
mod1_single_cycle: false,
mod1_restretch: true,
mod1_prev_restretch: false,
mod1_grain_hold: 200,
mod1_grain_gap: 200,
mod1_start_position: 0.0,
mod1_end_position: 1.0,
mod1_grain_crossfade: 50,
mod1_osc_type: VoiceType::Sine,
mod1_osc_octave: 0,
mod1_osc_semitones: 0,
mod1_osc_detune: 0.0,
mod1_osc_attack: 0.0001,
mod1_osc_decay: 0.0001,
mod1_osc_sustain: 999.9,
mod1_osc_release: 5.0,
mod1_osc_retrigger: RetriggerStyle::Retrigger,
mod1_osc_atk_curve: SmoothStyle::Linear,
mod1_osc_dec_curve: SmoothStyle::Linear,
mod1_osc_rel_curve: SmoothStyle::Linear,
mod1_osc_unison: 1,
mod1_osc_unison_detune: 0.0,
mod1_osc_stereo: 0.0,
mod2_audio_module_type: AudioModuleType::Off,
mod2_audio_module_level: 1.0,
mod2_loaded_sample: vec![vec![0.0, 0.0]],
mod2_sample_lib: vec![vec![vec![0.0, 0.0]]],
mod2_loop_wavetable: false,
mod2_single_cycle: false,
mod2_restretch: true,
mod2_prev_restretch: false,
mod2_grain_hold: 200,
mod2_grain_gap: 200,
mod2_start_position: 0.0,
mod2_end_position: 1.0,
mod2_grain_crossfade: 50,
mod2_osc_type: VoiceType::Sine,
mod2_osc_octave: 0,
mod2_osc_semitones: 0,
mod2_osc_detune: 0.0,
mod2_osc_attack: 0.0001,
mod2_osc_decay: 0.0001,
mod2_osc_sustain: 999.9,
mod2_osc_release: 5.0,
mod2_osc_retrigger: RetriggerStyle::Retrigger,
mod2_osc_atk_curve: SmoothStyle::Linear,
mod2_osc_dec_curve: SmoothStyle::Linear,
mod2_osc_rel_curve: SmoothStyle::Linear,
mod2_osc_unison: 1,
mod2_osc_unison_detune: 0.0,
mod2_osc_stereo: 0.0,
mod3_audio_module_type: AudioModuleType::Off,
mod3_audio_module_level: 1.0,
mod3_loaded_sample: vec![vec![0.0, 0.0]],
mod3_sample_lib: vec![vec![vec![0.0, 0.0]]],
mod3_loop_wavetable: false,
mod3_single_cycle: false,
mod3_restretch: true,
mod3_prev_restretch: false,
mod3_grain_hold: 200,
mod3_grain_gap: 200,
mod3_start_position: 0.0,
mod3_end_position: 1.0,
mod3_grain_crossfade: 50,
mod3_osc_type: VoiceType::Sine,
mod3_osc_octave: 0,
mod3_osc_semitones: 0,
mod3_osc_detune: 0.0,
mod3_osc_attack: 0.0001,
mod3_osc_decay: 0.0001,
mod3_osc_sustain: 999.9,
mod3_osc_release: 5.0,
mod3_osc_retrigger: RetriggerStyle::Retrigger,
mod3_osc_atk_curve: SmoothStyle::Linear,
mod3_osc_dec_curve: SmoothStyle::Linear,
mod3_osc_rel_curve: SmoothStyle::Linear,
mod3_osc_unison: 1,
mod3_osc_unison_detune: 0.0,
mod3_osc_stereo: 0.0,
filter_wet: 1.0,
filter_cutoff: 20000.0,
filter_resonance: 1.0,
filter_res_type: ResonanceType::Default,
filter_lp_amount: 1.0,
filter_hp_amount: 0.0,
filter_bp_amount: 0.0,
filter_env_peak: 0.0,
filter_env_attack: 0.0,
filter_env_decay: 0.0001,
filter_env_sustain: 999.9,
filter_env_release: 5.0,
filter_env_atk_curve: SmoothStyle::Linear,
filter_env_dec_curve: SmoothStyle::Linear,
filter_env_rel_curve: SmoothStyle::Linear,
filter_alg_type: FilterAlgorithms::SVF,
tilt_filter_type: ArduraFilter::ResponseType::Lowpass,
filter_wet_2: 1.0,
filter_cutoff_2: 20000.0,
filter_resonance_2: 1.0,
filter_res_type_2: ResonanceType::Default,
filter_lp_amount_2: 1.0,
filter_hp_amount_2: 0.0,
filter_bp_amount_2: 0.0,
filter_env_peak_2: 0.0,
filter_env_attack_2: 0.0,
filter_env_decay_2: 0.0001,
filter_env_sustain_2: 999.9,
filter_env_release_2: 5.0,
filter_env_atk_curve_2: SmoothStyle::Linear,
filter_env_dec_curve_2: SmoothStyle::Linear,
filter_env_rel_curve_2: SmoothStyle::Linear,
filter_alg_type_2: FilterAlgorithms::SVF,
tilt_filter_type_2: ArduraFilter::ResponseType::Lowpass,
filter_routing: FilterRouting::Parallel,
filter_cutoff_link: false,
// Pitch
pitch_enable: false,
pitch_env_peak: 0.0,
pitch_env_atk_curve: SmoothStyle::Linear,
pitch_env_dec_curve: SmoothStyle::Linear,
pitch_env_rel_curve: SmoothStyle::Linear,
pitch_env_attack: 0.0,
pitch_env_decay: 300.0,
pitch_env_release: 0.0,
pitch_env_sustain: 0.0,
pitch_routing: PitchRouting::Osc1,
pitch_enable_2: false,
pitch_env_peak_2: 0.0,
pitch_env_atk_curve_2: SmoothStyle::Linear,
pitch_env_dec_curve_2: SmoothStyle::Linear,
pitch_env_rel_curve_2: SmoothStyle::Linear,
pitch_env_attack_2: 0.0,
pitch_env_decay_2: 300.0,
pitch_env_release_2: 0.0,
pitch_env_sustain_2: 0.0,
pitch_routing_2: PitchRouting::Osc1,
// LFOs
lfo1_enable: false,
lfo2_enable: false,
lfo3_enable: false,
lfo1_freq: 2.0,
lfo1_retrigger: LFOController::LFORetrigger::None,
lfo1_sync: true,
lfo1_snap: LFOController::LFOSnapValues::Half,
lfo1_waveform: LFOController::Waveform::Sine,
lfo1_phase: 0.0,
lfo2_freq: 2.0,
lfo2_retrigger: LFOController::LFORetrigger::None,
lfo2_sync: true,
lfo2_snap: LFOController::LFOSnapValues::Half,
lfo2_waveform: LFOController::Waveform::Sine,
lfo2_phase: 0.0,
lfo3_freq: 2.0,
lfo3_retrigger: LFOController::LFORetrigger::None,
lfo3_sync: true,
lfo3_snap: LFOController::LFOSnapValues::Half,
lfo3_waveform: LFOController::Waveform::Sine,
lfo3_phase: 0.0,
// Modulations
mod_source_1: ModulationSource::None,
mod_source_2: ModulationSource::None,
mod_source_3: ModulationSource::None,
mod_source_4: ModulationSource::None,
mod_dest_1: ModulationDestination::None,
mod_dest_2: ModulationDestination::None,
mod_dest_3: ModulationDestination::None,
mod_dest_4: ModulationDestination::None,
mod_amount_1: 0.0,
mod_amount_2: 0.0,
mod_amount_3: 0.0,
mod_amount_4: 0.0,
// EQ
pre_use_eq: false,
pre_low_freq: 800.0,
pre_mid_freq: 3000.0,
pre_high_freq: 10000.0,
pre_low_gain: 0.0,
pre_mid_gain: 0.0,
pre_high_gain: 0.0,
// FX
use_fx: true,
use_compressor: false,
comp_amt: 0.5,
comp_atk: 0.5,
comp_rel: 0.5,
comp_drive: 0.5,
use_abass: false,
abass_amount: 0.0011,
use_saturation: false,
sat_amount: 0.0,
sat_type: SaturationType::Tape,
use_delay: false,
delay_amount: 0.0,
delay_time: DelaySnapValues::Quarter,
delay_decay: 0.0,
delay_type: DelayType::Stereo,
use_reverb: false,
reverb_amount: 0.5,
reverb_size: 0.5,
reverb_feedback: 0.5,
use_phaser: false,
phaser_amount: 0.5,
phaser_depth: 0.5,
phaser_rate: 0.5,
phaser_feedback: 0.5,
use_buffermod: false,
buffermod_amount: 0.5,
buffermod_depth: 0.5,
buffermod_rate: 0.5,
buffermod_spread: 0.0,
buffermod_timing: 620.0,
use_flanger: false,
flanger_amount: 0.5,
flanger_depth: 0.5,
flanger_rate: 0.5,
flanger_feedback: 0.5,
use_limiter: false,
limiter_threshold: 0.5,
limiter_knee: 0.5,
});
// Attempt to load the previous version preset type
if unserialized.preset_name.contains("Error") {
// Try loading the previous preset struct version
unserialized = load_unserialized_v114(file_string_data.clone());
}
// Attempt to load the oldest preset type
if unserialized.preset_name.contains("Error") {
// Try loading the previous preset struct version
unserialized = load_unserialized_old(file_string_data.clone());
}
return (return_name, Some(unserialized));
}
return (String::from("Error"), Option::None);
}
// Load presets uses message packing with serde
fn load_preset_bank() -> (String, Vec<ActuatePreset>) {
let loading_bank = FileDialog::new()
.add_filter("actuatebank", &["actuatebank"]) // Use the same filter as in save_preset_bank
.pick_file();
let return_name;
if let Some(loading_bank) = loading_bank {
return_name = loading_bank.to_str().unwrap_or("Invalid Path").to_string();
// Read the compressed data from the file
let mut compressed_data = Vec::new();
if let Err(err) = std::fs::File::open(&return_name)
.and_then(|mut file| file.read_to_end(&mut compressed_data))
{
eprintln!("Error reading compressed data from file: {}", err);
return (err.to_string(), Vec::new());
}
// Decompress the data
let decompressed_data = Self::decompress_bytes(&compressed_data);
if let Err(err) = decompressed_data {
eprintln!("Error decompressing data: {}", err);
return (err.to_string(), Vec::new());
}
// Deserialize the MessagePack data
let file_string_data = decompressed_data.unwrap();
// Deserialize into ActuatePreset - return default empty lib if error
let unserialized: Vec<ActuatePreset> = rmp_serde::from_slice(&file_string_data)
.unwrap_or(vec![
ActuatePreset {
preset_name: "Default".to_string(),
preset_info: "Info".to_string(),
preset_category: PresetType::Select,
tag_acid: false,
tag_analog: false,
tag_bright: false,
tag_chord: false,
tag_crisp: false,
tag_deep: false,
tag_delicate: false,
tag_hard: false,
tag_harsh: false,
tag_lush: false,
tag_mellow: false,
tag_resonant: false,
tag_rich: false,
tag_sharp: false,
tag_silky: false,
tag_smooth: false,
tag_soft: false,
tag_stab: false,
tag_warm: false,
mod1_audio_module_type: AudioModuleType::Osc,
mod1_audio_module_level: 1.0,
mod1_loaded_sample: vec![vec![0.0, 0.0]],
mod1_sample_lib: vec![vec![vec![0.0, 0.0]]],
mod1_loop_wavetable: false,
mod1_single_cycle: false,
mod1_restretch: true,
mod1_prev_restretch: false,
mod1_grain_hold: 200,
mod1_grain_gap: 200,
mod1_start_position: 0.0,
mod1_end_position: 1.0,
mod1_grain_crossfade: 50,
mod1_osc_type: VoiceType::Sine,
mod1_osc_octave: 0,
mod1_osc_semitones: 0,
mod1_osc_detune: 0.0,
mod1_osc_attack: 0.0001,
mod1_osc_decay: 0.0001,
mod1_osc_sustain: 999.9,
mod1_osc_release: 5.0,
mod1_osc_retrigger: RetriggerStyle::Retrigger,
mod1_osc_atk_curve: SmoothStyle::Linear,
mod1_osc_dec_curve: SmoothStyle::Linear,
mod1_osc_rel_curve: SmoothStyle::Linear,
mod1_osc_unison: 1,
mod1_osc_unison_detune: 0.0,
mod1_osc_stereo: 0.0,
mod2_audio_module_type: AudioModuleType::Off,
mod2_audio_module_level: 1.0,
mod2_loaded_sample: vec![vec![0.0, 0.0]],
mod2_sample_lib: vec![vec![vec![0.0, 0.0]]],
mod2_loop_wavetable: false,
mod2_single_cycle: false,
mod2_restretch: true,
mod2_prev_restretch: false,
mod2_grain_hold: 200,
mod2_grain_gap: 200,
mod2_start_position: 0.0,
mod2_end_position: 1.0,
mod2_grain_crossfade: 50,
mod2_osc_type: VoiceType::Sine,
mod2_osc_octave: 0,
mod2_osc_semitones: 0,
mod2_osc_detune: 0.0,
mod2_osc_attack: 0.0001,
mod2_osc_decay: 0.0001,
mod2_osc_sustain: 999.9,
mod2_osc_release: 5.0,
mod2_osc_retrigger: RetriggerStyle::Retrigger,
mod2_osc_atk_curve: SmoothStyle::Linear,
mod2_osc_dec_curve: SmoothStyle::Linear,
mod2_osc_rel_curve: SmoothStyle::Linear,
mod2_osc_unison: 1,
mod2_osc_unison_detune: 0.0,
mod2_osc_stereo: 0.0,
mod3_audio_module_type: AudioModuleType::Off,
mod3_audio_module_level: 1.0,
mod3_loaded_sample: vec![vec![0.0, 0.0]],
mod3_sample_lib: vec![vec![vec![0.0, 0.0]]],
mod3_loop_wavetable: false,
mod3_single_cycle: false,
mod3_restretch: true,
mod3_prev_restretch: false,
mod3_grain_hold: 200,
mod3_grain_gap: 200,
mod3_start_position: 0.0,
mod3_end_position: 1.0,
mod3_grain_crossfade: 50,
mod3_osc_type: VoiceType::Sine,
mod3_osc_octave: 0,
mod3_osc_semitones: 0,
mod3_osc_detune: 0.0,
mod3_osc_attack: 0.0001,
mod3_osc_decay: 0.0001,
mod3_osc_sustain: 999.9,
mod3_osc_release: 5.0,
mod3_osc_retrigger: RetriggerStyle::Retrigger,
mod3_osc_atk_curve: SmoothStyle::Linear,
mod3_osc_dec_curve: SmoothStyle::Linear,
mod3_osc_rel_curve: SmoothStyle::Linear,
mod3_osc_unison: 1,
mod3_osc_unison_detune: 0.0,
mod3_osc_stereo: 0.0,
filter_wet: 1.0,
filter_cutoff: 20000.0,
filter_resonance: 1.0,
filter_res_type: ResonanceType::Default,
filter_lp_amount: 1.0,
filter_hp_amount: 0.0,
filter_bp_amount: 0.0,
filter_env_peak: 0.0,
filter_env_attack: 0.0,
filter_env_decay: 0.0001,
filter_env_sustain: 999.9,
filter_env_release: 5.0,
filter_env_atk_curve: SmoothStyle::Linear,
filter_env_dec_curve: SmoothStyle::Linear,
filter_env_rel_curve: SmoothStyle::Linear,
filter_alg_type: FilterAlgorithms::SVF,
tilt_filter_type: ArduraFilter::ResponseType::Lowpass,
filter_wet_2: 1.0,
filter_cutoff_2: 20000.0,
filter_resonance_2: 1.0,
filter_res_type_2: ResonanceType::Default,
filter_lp_amount_2: 1.0,
filter_hp_amount_2: 0.0,
filter_bp_amount_2: 0.0,
filter_env_peak_2: 0.0,
filter_env_attack_2: 0.0,
filter_env_decay_2: 0.0001,
filter_env_sustain_2: 999.9,
filter_env_release_2: 5.0,
filter_env_atk_curve_2: SmoothStyle::Linear,
filter_env_dec_curve_2: SmoothStyle::Linear,
filter_env_rel_curve_2: SmoothStyle::Linear,
filter_alg_type_2: FilterAlgorithms::SVF,
tilt_filter_type_2: ArduraFilter::ResponseType::Lowpass,
filter_routing: FilterRouting::Parallel,
filter_cutoff_link: false,
pitch_enable: false,
pitch_env_atk_curve: SmoothStyle::Linear,
pitch_env_dec_curve: SmoothStyle::Linear,
pitch_env_rel_curve: SmoothStyle::Linear,
pitch_env_attack: 0.0,
pitch_env_decay: 300.0,
pitch_env_sustain: 0.0,
pitch_env_release: 0.0,
pitch_env_peak: 0.0,
pitch_routing: PitchRouting::Osc1,
pitch_enable_2: false,
pitch_env_peak_2: 0.0,
pitch_env_atk_curve_2: SmoothStyle::Linear,
pitch_env_dec_curve_2: SmoothStyle::Linear,
pitch_env_rel_curve_2: SmoothStyle::Linear,
pitch_env_attack_2: 0.0,
pitch_env_decay_2: 300.0,
pitch_env_release_2: 0.0,
pitch_env_sustain_2: 0.0,
pitch_routing_2: PitchRouting::Osc1,
// LFOs
lfo1_enable: false,
lfo2_enable: false,
lfo3_enable: false,
lfo1_freq: 2.0,
lfo1_retrigger: LFOController::LFORetrigger::None,
lfo1_sync: true,
lfo1_snap: LFOController::LFOSnapValues::Half,
lfo1_waveform: LFOController::Waveform::Sine,
lfo1_phase: 0.0,
lfo2_freq: 2.0,
lfo2_retrigger: LFOController::LFORetrigger::None,
lfo2_sync: true,
lfo2_snap: LFOController::LFOSnapValues::Half,
lfo2_waveform: LFOController::Waveform::Sine,
lfo2_phase: 0.0,
lfo3_freq: 2.0,
lfo3_retrigger: LFOController::LFORetrigger::None,
lfo3_sync: true,
lfo3_snap: LFOController::LFOSnapValues::Half,
lfo3_waveform: LFOController::Waveform::Sine,
lfo3_phase: 0.0,
// Modulations
mod_source_1: ModulationSource::None,
mod_source_2: ModulationSource::None,
mod_source_3: ModulationSource::None,
mod_source_4: ModulationSource::None,
mod_dest_1: ModulationDestination::None,
mod_dest_2: ModulationDestination::None,
mod_dest_3: ModulationDestination::None,
mod_dest_4: ModulationDestination::None,
mod_amount_1: 0.0,
mod_amount_2: 0.0,
mod_amount_3: 0.0,
mod_amount_4: 0.0,
// EQ
pre_use_eq: false,
pre_low_freq: 800.0,
pre_mid_freq: 3000.0,
pre_high_freq: 10000.0,
pre_low_gain: 0.0,
pre_mid_gain: 0.0,
pre_high_gain: 0.0,
// FX
use_fx: true,
use_compressor: false,
comp_amt: 0.5,
comp_atk: 0.5,
comp_rel: 0.5,
comp_drive: 0.5,
use_abass: false,
abass_amount: 0.0011,
use_saturation: false,
sat_amount: 0.0,
sat_type: SaturationType::Tape,
use_delay: false,
delay_amount: 0.0,
delay_time: DelaySnapValues::Quarter,
delay_decay: 0.0,
delay_type: DelayType::Stereo,
use_reverb: false,
reverb_amount: 0.5,
reverb_size: 0.5,
reverb_feedback: 0.5,
use_phaser: false,
phaser_amount: 0.5,
phaser_depth: 0.5,
phaser_rate: 0.5,
phaser_feedback: 0.5,
use_buffermod: false,
buffermod_amount: 0.5,
buffermod_depth: 0.5,
buffermod_rate: 0.5,
buffermod_spread: 0.0,
buffermod_timing: 620.0,
use_flanger: false,
flanger_amount: 0.5,
flanger_depth: 0.5,
flanger_rate: 0.5,
flanger_feedback: 0.5,
use_limiter: false,
limiter_threshold: 0.5,
limiter_knee: 0.5,
};
PRESET_BANK_SIZE
]);
return (return_name, unserialized);
}
return (String::from("Error"), Vec::new());
}
// This gets triggered to force a load/change and to recalculate sample dependent notes
fn reload_entire_preset(
setter: &ParamSetter,
params: Arc<ActuateParams>,
current_preset_index: usize,
arc_preset: Arc<Mutex<Vec<ActuatePreset>>>,
AMod1: Arc<Mutex<AudioModule>>,
AMod2: Arc<Mutex<AudioModule>>,
AMod3: Arc<Mutex<AudioModule>>,
) -> (
ModulationSource,
ModulationSource,
ModulationSource,
ModulationSource,
ModulationDestination,
ModulationDestination,
ModulationDestination,
ModulationDestination,
PresetType,
) {
// Create mutex locks for AudioModule
let mut AMod1 = AMod1.as_ref().lock().unwrap();
let mut AMod2 = AMod2.as_ref().lock().unwrap();
let mut AMod3 = AMod3.as_ref().lock().unwrap();
// Try to load preset into our params if possible
let loaded_preset = &arc_preset.lock().unwrap()[current_preset_index as usize];
setter.set_parameter(
¶ms._audio_module_1_type,
loaded_preset.mod1_audio_module_type,
);
setter.set_parameter(
¶ms.audio_module_1_level,
loaded_preset.mod1_audio_module_level,
);
setter.set_parameter(¶ms.loop_sample_1, loaded_preset.mod1_loop_wavetable);
setter.set_parameter(¶ms.single_cycle_1, loaded_preset.mod1_single_cycle);
setter.set_parameter(¶ms.restretch_1, loaded_preset.mod1_restretch);
setter.set_parameter(¶ms.osc_1_type, loaded_preset.mod1_osc_type);
setter.set_parameter(¶ms.osc_1_octave, loaded_preset.mod1_osc_octave);
setter.set_parameter(¶ms.osc_1_semitones, loaded_preset.mod1_osc_semitones);
setter.set_parameter(¶ms.osc_1_detune, loaded_preset.mod1_osc_detune);
setter.set_parameter(¶ms.osc_1_attack, loaded_preset.mod1_osc_attack);
setter.set_parameter(¶ms.osc_1_decay, loaded_preset.mod1_osc_decay);
setter.set_parameter(¶ms.osc_1_sustain, loaded_preset.mod1_osc_sustain);
setter.set_parameter(¶ms.osc_1_release, loaded_preset.mod1_osc_release);
setter.set_parameter(¶ms.osc_1_retrigger, loaded_preset.mod1_osc_retrigger);
setter.set_parameter(¶ms.osc_1_atk_curve, loaded_preset.mod1_osc_atk_curve);
setter.set_parameter(¶ms.osc_1_dec_curve, loaded_preset.mod1_osc_dec_curve);
setter.set_parameter(¶ms.osc_1_rel_curve, loaded_preset.mod1_osc_rel_curve);
setter.set_parameter(¶ms.osc_1_unison, loaded_preset.mod1_osc_unison);
setter.set_parameter(
¶ms.osc_1_unison_detune,
loaded_preset.mod1_osc_unison_detune,
);
setter.set_parameter(¶ms.osc_1_stereo, loaded_preset.mod1_osc_stereo);
setter.set_parameter(¶ms.grain_gap_1, loaded_preset.mod1_grain_gap);
setter.set_parameter(¶ms.grain_hold_1, loaded_preset.mod1_grain_hold);
setter.set_parameter(
¶ms.grain_crossfade_1,
loaded_preset.mod1_grain_crossfade,
);
setter.set_parameter(¶ms.start_position_1, loaded_preset.mod1_start_position);
setter.set_parameter(¶ms.end_position_1, loaded_preset.mod1_end_position);
// loaded sample, sample_lib, and prev restretch are controlled differently
setter.set_parameter(
¶ms._audio_module_2_type,
loaded_preset.mod2_audio_module_type,
);
setter.set_parameter(
¶ms.audio_module_2_level,
loaded_preset.mod2_audio_module_level,
);
setter.set_parameter(¶ms.loop_sample_2, loaded_preset.mod2_loop_wavetable);
setter.set_parameter(¶ms.single_cycle_2, loaded_preset.mod2_single_cycle);
setter.set_parameter(¶ms.restretch_2, loaded_preset.mod2_restretch);
setter.set_parameter(¶ms.osc_2_type, loaded_preset.mod2_osc_type);
setter.set_parameter(¶ms.osc_2_octave, loaded_preset.mod2_osc_octave);
setter.set_parameter(¶ms.osc_2_semitones, loaded_preset.mod2_osc_semitones);
setter.set_parameter(¶ms.osc_2_detune, loaded_preset.mod2_osc_detune);
setter.set_parameter(¶ms.osc_2_attack, loaded_preset.mod2_osc_attack);
setter.set_parameter(¶ms.osc_2_decay, loaded_preset.mod2_osc_decay);
setter.set_parameter(¶ms.osc_2_sustain, loaded_preset.mod2_osc_sustain);
setter.set_parameter(¶ms.osc_2_release, loaded_preset.mod2_osc_release);
setter.set_parameter(¶ms.osc_2_retrigger, loaded_preset.mod2_osc_retrigger);
setter.set_parameter(¶ms.osc_2_atk_curve, loaded_preset.mod2_osc_atk_curve);
setter.set_parameter(¶ms.osc_2_dec_curve, loaded_preset.mod2_osc_dec_curve);
setter.set_parameter(¶ms.osc_2_rel_curve, loaded_preset.mod2_osc_rel_curve);
setter.set_parameter(¶ms.osc_2_unison, loaded_preset.mod2_osc_unison);
setter.set_parameter(
¶ms.osc_2_unison_detune,
loaded_preset.mod2_osc_unison_detune,
);
setter.set_parameter(¶ms.osc_2_stereo, loaded_preset.mod2_osc_stereo);
setter.set_parameter(¶ms.grain_gap_2, loaded_preset.mod2_grain_gap);
setter.set_parameter(¶ms.grain_hold_2, loaded_preset.mod2_grain_hold);
setter.set_parameter(
¶ms.grain_crossfade_2,
loaded_preset.mod2_grain_crossfade,
);
setter.set_parameter(¶ms.start_position_2, loaded_preset.mod2_start_position);
setter.set_parameter(¶ms.end_position_2, loaded_preset.mod2_end_position);
// loaded sample, sample_lib, and prev restretch are controlled differently
setter.set_parameter(
¶ms._audio_module_3_type,
loaded_preset.mod3_audio_module_type,
);
setter.set_parameter(
¶ms.audio_module_3_level,
loaded_preset.mod3_audio_module_level,
);
setter.set_parameter(¶ms.loop_sample_3, loaded_preset.mod3_loop_wavetable);
setter.set_parameter(¶ms.single_cycle_3, loaded_preset.mod3_single_cycle);
setter.set_parameter(¶ms.restretch_3, loaded_preset.mod3_restretch);
setter.set_parameter(¶ms.osc_3_type, loaded_preset.mod3_osc_type);
setter.set_parameter(¶ms.osc_3_octave, loaded_preset.mod3_osc_octave);
setter.set_parameter(¶ms.osc_3_semitones, loaded_preset.mod3_osc_semitones);
setter.set_parameter(¶ms.osc_3_detune, loaded_preset.mod3_osc_detune);
setter.set_parameter(¶ms.osc_3_attack, loaded_preset.mod3_osc_attack);
setter.set_parameter(¶ms.osc_3_decay, loaded_preset.mod3_osc_decay);
setter.set_parameter(¶ms.osc_3_sustain, loaded_preset.mod3_osc_sustain);
setter.set_parameter(¶ms.osc_3_release, loaded_preset.mod3_osc_release);
setter.set_parameter(¶ms.osc_3_retrigger, loaded_preset.mod3_osc_retrigger);
setter.set_parameter(¶ms.osc_3_atk_curve, loaded_preset.mod3_osc_atk_curve);
setter.set_parameter(¶ms.osc_3_dec_curve, loaded_preset.mod3_osc_dec_curve);
setter.set_parameter(¶ms.osc_3_rel_curve, loaded_preset.mod3_osc_rel_curve);
setter.set_parameter(¶ms.osc_3_unison, loaded_preset.mod3_osc_unison);
setter.set_parameter(
¶ms.osc_3_unison_detune,
loaded_preset.mod3_osc_unison_detune,
);
setter.set_parameter(¶ms.osc_3_stereo, loaded_preset.mod3_osc_stereo);
setter.set_parameter(¶ms.grain_gap_3, loaded_preset.mod3_grain_gap);
setter.set_parameter(¶ms.grain_hold_3, loaded_preset.mod3_grain_hold);
setter.set_parameter(
¶ms.grain_crossfade_3,
loaded_preset.mod3_grain_crossfade,
);
setter.set_parameter(¶ms.start_position_3, loaded_preset.mod3_start_position);
setter.set_parameter(¶ms.end_position_3, loaded_preset.mod3_end_position);
setter.set_parameter(¶ms.lfo1_enable, loaded_preset.lfo1_enable);
setter.set_parameter(¶ms.lfo1_freq, loaded_preset.lfo1_freq);
setter.set_parameter(¶ms.lfo1_phase, loaded_preset.lfo1_phase);
setter.set_parameter(¶ms.lfo1_retrigger, loaded_preset.lfo1_retrigger);
setter.set_parameter(¶ms.lfo1_snap, loaded_preset.lfo1_snap);
setter.set_parameter(¶ms.lfo1_sync, loaded_preset.lfo1_sync);
setter.set_parameter(¶ms.lfo1_waveform, loaded_preset.lfo1_waveform);
setter.set_parameter(¶ms.lfo2_enable, loaded_preset.lfo2_enable);
setter.set_parameter(¶ms.lfo2_freq, loaded_preset.lfo2_freq);
setter.set_parameter(¶ms.lfo2_phase, loaded_preset.lfo2_phase);
setter.set_parameter(¶ms.lfo2_retrigger, loaded_preset.lfo2_retrigger);
setter.set_parameter(¶ms.lfo2_snap, loaded_preset.lfo2_snap);
setter.set_parameter(¶ms.lfo2_sync, loaded_preset.lfo2_sync);
setter.set_parameter(¶ms.lfo2_waveform, loaded_preset.lfo2_waveform);
setter.set_parameter(¶ms.lfo3_enable, loaded_preset.lfo3_enable);
setter.set_parameter(¶ms.lfo3_freq, loaded_preset.lfo3_freq);
setter.set_parameter(¶ms.lfo3_phase, loaded_preset.lfo3_phase);
setter.set_parameter(¶ms.lfo3_retrigger, loaded_preset.lfo3_retrigger);
setter.set_parameter(¶ms.lfo3_snap, loaded_preset.lfo3_snap);
setter.set_parameter(¶ms.lfo3_sync, loaded_preset.lfo3_sync);
setter.set_parameter(¶ms.lfo3_waveform, loaded_preset.lfo3_waveform);
setter.set_parameter(¶ms.mod_amount_knob_1, loaded_preset.mod_amount_1);
setter.set_parameter(¶ms.mod_destination_1, loaded_preset.mod_dest_1.clone());
setter.set_parameter(¶ms.mod_source_1, loaded_preset.mod_source_1.clone());
setter.set_parameter(¶ms.mod_amount_knob_2, loaded_preset.mod_amount_2);
setter.set_parameter(¶ms.mod_destination_2, loaded_preset.mod_dest_2.clone());
setter.set_parameter(¶ms.mod_source_2, loaded_preset.mod_source_2.clone());
setter.set_parameter(¶ms.mod_amount_knob_3, loaded_preset.mod_amount_3);
setter.set_parameter(¶ms.mod_destination_3, loaded_preset.mod_dest_3.clone());
setter.set_parameter(¶ms.mod_source_3, loaded_preset.mod_source_3.clone());
setter.set_parameter(¶ms.mod_amount_knob_4, loaded_preset.mod_amount_4);
setter.set_parameter(¶ms.mod_destination_4, loaded_preset.mod_dest_4.clone());
setter.set_parameter(¶ms.mod_source_4, loaded_preset.mod_source_4.clone());
let mod_source_1_override = loaded_preset.mod_source_1.clone();
let mod_source_2_override = loaded_preset.mod_source_2.clone();
let mod_source_3_override = loaded_preset.mod_source_3.clone();
let mod_source_4_override = loaded_preset.mod_source_4.clone();
let mod_dest_1_override = loaded_preset.mod_dest_1.clone();
let mod_dest_2_override = loaded_preset.mod_dest_2.clone();
let mod_dest_3_override = loaded_preset.mod_dest_3.clone();
let mod_dest_4_override = loaded_preset.mod_dest_4.clone();
setter.set_parameter(¶ms.use_fx, loaded_preset.use_fx);
setter.set_parameter(¶ms.pre_use_eq, loaded_preset.pre_use_eq);
setter.set_parameter(¶ms.pre_low_freq, loaded_preset.pre_low_freq);
setter.set_parameter(¶ms.pre_mid_freq, loaded_preset.pre_mid_freq);
setter.set_parameter(¶ms.pre_high_freq, loaded_preset.pre_high_freq);
setter.set_parameter(¶ms.pre_low_gain, loaded_preset.pre_low_gain);
setter.set_parameter(¶ms.pre_mid_gain, loaded_preset.pre_mid_gain);
setter.set_parameter(¶ms.pre_high_gain, loaded_preset.pre_high_gain);
setter.set_parameter(¶ms.use_compressor, loaded_preset.use_compressor);
setter.set_parameter(¶ms.comp_amt, loaded_preset.comp_amt);
setter.set_parameter(¶ms.comp_atk, loaded_preset.comp_atk);
setter.set_parameter(¶ms.comp_drive, loaded_preset.comp_drive);
setter.set_parameter(¶ms.comp_rel, loaded_preset.comp_rel);
setter.set_parameter(¶ms.use_saturation, loaded_preset.use_saturation);
setter.set_parameter(¶ms.sat_amt, loaded_preset.sat_amount);
setter.set_parameter(¶ms.use_abass, loaded_preset.use_abass);
setter.set_parameter(¶ms.abass_amount, loaded_preset.abass_amount);
setter.set_parameter(¶ms.sat_type, loaded_preset.sat_type.clone());
setter.set_parameter(¶ms.use_delay, loaded_preset.use_delay);
setter.set_parameter(¶ms.delay_amount, loaded_preset.delay_amount);
setter.set_parameter(¶ms.delay_type, loaded_preset.delay_type.clone());
setter.set_parameter(¶ms.delay_decay, loaded_preset.delay_decay);
setter.set_parameter(¶ms.delay_time, loaded_preset.delay_time.clone());
setter.set_parameter(¶ms.use_reverb, loaded_preset.use_reverb);
setter.set_parameter(¶ms.reverb_size, loaded_preset.reverb_size);
setter.set_parameter(¶ms.reverb_amount, loaded_preset.reverb_amount);
setter.set_parameter(¶ms.reverb_feedback, loaded_preset.reverb_feedback);
setter.set_parameter(¶ms.use_phaser, loaded_preset.use_phaser);
setter.set_parameter(¶ms.phaser_amount, loaded_preset.phaser_amount);
setter.set_parameter(¶ms.phaser_depth, loaded_preset.phaser_depth);
setter.set_parameter(¶ms.phaser_feedback, loaded_preset.phaser_feedback);
setter.set_parameter(¶ms.phaser_rate, loaded_preset.phaser_rate);
setter.set_parameter(¶ms.use_buffermod, loaded_preset.use_buffermod);
setter.set_parameter(¶ms.buffermod_amount, loaded_preset.buffermod_amount);
setter.set_parameter(¶ms.buffermod_depth, loaded_preset.buffermod_depth);
setter.set_parameter(¶ms.buffermod_rate, loaded_preset.buffermod_rate);
setter.set_parameter(¶ms.buffermod_spread, loaded_preset.buffermod_spread);
setter.set_parameter(¶ms.buffermod_timing, loaded_preset.buffermod_timing);
setter.set_parameter(¶ms.use_flanger, loaded_preset.use_flanger);
setter.set_parameter(¶ms.flanger_amount, loaded_preset.flanger_amount);
setter.set_parameter(¶ms.flanger_depth, loaded_preset.flanger_depth);
setter.set_parameter(¶ms.flanger_feedback, loaded_preset.flanger_feedback);
setter.set_parameter(¶ms.flanger_rate, loaded_preset.flanger_rate);
setter.set_parameter(¶ms.use_limiter, loaded_preset.use_limiter);
setter.set_parameter(¶ms.limiter_threshold, loaded_preset.limiter_threshold);
setter.set_parameter(¶ms.limiter_knee, loaded_preset.limiter_knee);
setter.set_parameter(¶ms.filter_wet, loaded_preset.filter_wet);
setter.set_parameter(¶ms.filter_cutoff, loaded_preset.filter_cutoff);
setter.set_parameter(¶ms.filter_resonance, loaded_preset.filter_resonance);
setter.set_parameter(
¶ms.filter_res_type,
loaded_preset.filter_res_type.clone(),
);
setter.set_parameter(
¶ms.filter_alg_type,
loaded_preset.filter_alg_type.clone(),
);
setter.set_parameter(
¶ms.filter_alg_type_2,
loaded_preset.filter_alg_type_2.clone(),
);
setter.set_parameter(
¶ms.tilt_filter_type,
loaded_preset.tilt_filter_type.clone(),
);
setter.set_parameter(¶ms.filter_lp_amount, loaded_preset.filter_lp_amount);
setter.set_parameter(¶ms.filter_hp_amount, loaded_preset.filter_hp_amount);
setter.set_parameter(¶ms.filter_bp_amount, loaded_preset.filter_bp_amount);
setter.set_parameter(¶ms.filter_env_peak, loaded_preset.filter_env_peak);
setter.set_parameter(¶ms.filter_env_decay, loaded_preset.filter_env_decay);
setter.set_parameter(
¶ms.filter_env_atk_curve,
loaded_preset.filter_env_atk_curve,
);
setter.set_parameter(
¶ms.filter_env_dec_curve,
loaded_preset.filter_env_dec_curve,
);
setter.set_parameter(
¶ms.filter_env_rel_curve,
loaded_preset.filter_env_rel_curve,
);
setter.set_parameter(¶ms.filter_wet_2, loaded_preset.filter_wet_2);
setter.set_parameter(¶ms.filter_cutoff_2, loaded_preset.filter_cutoff_2);
setter.set_parameter(¶ms.filter_resonance_2, loaded_preset.filter_resonance_2);
setter.set_parameter(
¶ms.filter_res_type_2,
loaded_preset.filter_res_type_2.clone(),
);
setter.set_parameter(
¶ms.tilt_filter_type_2,
loaded_preset.tilt_filter_type_2.clone(),
);
setter.set_parameter(¶ms.filter_lp_amount_2, loaded_preset.filter_lp_amount_2);
setter.set_parameter(¶ms.filter_hp_amount_2, loaded_preset.filter_hp_amount_2);
setter.set_parameter(¶ms.filter_bp_amount_2, loaded_preset.filter_bp_amount_2);
setter.set_parameter(¶ms.filter_env_peak_2, loaded_preset.filter_env_peak_2);
setter.set_parameter(¶ms.filter_env_decay_2, loaded_preset.filter_env_decay_2);
setter.set_parameter(
¶ms.filter_env_atk_curve_2,
loaded_preset.filter_env_atk_curve_2,
);
setter.set_parameter(
¶ms.filter_env_dec_curve_2,
loaded_preset.filter_env_dec_curve_2,
);
setter.set_parameter(
¶ms.filter_env_rel_curve_2,
loaded_preset.filter_env_rel_curve_2,
);
// Somehow I didn't notice these were missing for the longest time
setter.set_parameter(¶ms.filter_env_attack, loaded_preset.filter_env_attack);
setter.set_parameter(¶ms.filter_env_decay, loaded_preset.filter_env_decay);
setter.set_parameter(¶ms.filter_env_sustain, loaded_preset.filter_env_sustain);
setter.set_parameter(¶ms.filter_env_release, loaded_preset.filter_env_release);
setter.set_parameter(
¶ms.filter_env_attack_2,
loaded_preset.filter_env_attack_2,
);
setter.set_parameter(¶ms.filter_env_decay_2, loaded_preset.filter_env_decay_2);
setter.set_parameter(
¶ms.filter_env_sustain_2,
loaded_preset.filter_env_sustain_2,
);
setter.set_parameter(
¶ms.filter_env_release_2,
loaded_preset.filter_env_release_2,
);
setter.set_parameter(¶ms.filter_routing, loaded_preset.filter_routing.clone());
#[allow(unreachable_patterns)]
let preset_category_override = match loaded_preset.preset_category {
PresetType::Bass
| PresetType::FX
| PresetType::Lead
| PresetType::Other
| PresetType::Pad
| PresetType::Percussion
| PresetType::Select
| PresetType::Synth
| PresetType::Atmosphere
| PresetType::Keys
| PresetType::Pluck => {
setter.set_parameter(
¶ms.preset_category,
loaded_preset.preset_category.clone(),
);
loaded_preset.preset_category.clone()
}
// This should be unreachable since unserialize will fail before we get here anyways actually
_ => PresetType::Select,
};
// 1.2.1 Pitch update
setter.set_parameter(¶ms.pitch_enable, loaded_preset.pitch_enable);
setter.set_parameter(¶ms.pitch_env_peak, loaded_preset.pitch_env_peak);
setter.set_parameter(
¶ms.pitch_env_atk_curve,
loaded_preset.pitch_env_atk_curve,
);
setter.set_parameter(
¶ms.pitch_env_dec_curve,
loaded_preset.pitch_env_dec_curve,
);
setter.set_parameter(
¶ms.pitch_env_rel_curve,
loaded_preset.pitch_env_rel_curve,
);
setter.set_parameter(¶ms.pitch_env_attack, loaded_preset.pitch_env_attack);
setter.set_parameter(¶ms.pitch_env_decay, loaded_preset.pitch_env_decay);
setter.set_parameter(¶ms.pitch_env_sustain, loaded_preset.pitch_env_sustain);
setter.set_parameter(¶ms.pitch_env_release, loaded_preset.pitch_env_release);
setter.set_parameter(¶ms.pitch_routing, loaded_preset.pitch_routing.clone());
setter.set_parameter(¶ms.pitch_enable_2, loaded_preset.pitch_enable_2);
setter.set_parameter(¶ms.pitch_env_peak_2, loaded_preset.pitch_env_peak_2);
setter.set_parameter(
¶ms.pitch_env_atk_curve_2,
loaded_preset.pitch_env_atk_curve_2,
);
setter.set_parameter(
¶ms.pitch_env_dec_curve_2,
loaded_preset.pitch_env_dec_curve_2,
);
setter.set_parameter(
¶ms.pitch_env_rel_curve_2,
loaded_preset.pitch_env_rel_curve_2,
);
setter.set_parameter(¶ms.pitch_env_attack_2, loaded_preset.pitch_env_attack_2);
setter.set_parameter(¶ms.pitch_env_decay_2, loaded_preset.pitch_env_decay_2);
setter.set_parameter(
¶ms.pitch_env_sustain_2,
loaded_preset.pitch_env_sustain_2,
);
setter.set_parameter(
¶ms.pitch_env_release_2,
loaded_preset.pitch_env_release_2,
);
setter.set_parameter(
¶ms.pitch_routing_2,
loaded_preset.pitch_routing_2.clone(),
);
// Assign the preset tags
setter.set_parameter(¶ms.tag_acid, loaded_preset.tag_acid);
setter.set_parameter(¶ms.tag_analog, loaded_preset.tag_analog);
setter.set_parameter(¶ms.tag_bright, loaded_preset.tag_bright);
setter.set_parameter(¶ms.tag_chord, loaded_preset.tag_chord);
setter.set_parameter(¶ms.tag_crisp, loaded_preset.tag_crisp);
setter.set_parameter(¶ms.tag_deep, loaded_preset.tag_deep);
setter.set_parameter(¶ms.tag_delicate, loaded_preset.tag_delicate);
setter.set_parameter(¶ms.tag_hard, loaded_preset.tag_hard);
setter.set_parameter(¶ms.tag_harsh, loaded_preset.tag_harsh);
setter.set_parameter(¶ms.tag_lush, loaded_preset.tag_lush);
setter.set_parameter(¶ms.tag_mellow, loaded_preset.tag_mellow);
setter.set_parameter(¶ms.tag_resonant, loaded_preset.tag_resonant);
setter.set_parameter(¶ms.tag_rich, loaded_preset.tag_rich);
setter.set_parameter(¶ms.tag_sharp, loaded_preset.tag_sharp);
setter.set_parameter(¶ms.tag_silky, loaded_preset.tag_silky);
setter.set_parameter(¶ms.tag_smooth, loaded_preset.tag_smooth);
setter.set_parameter(¶ms.tag_soft, loaded_preset.tag_soft);
setter.set_parameter(¶ms.tag_stab, loaded_preset.tag_stab);
setter.set_parameter(¶ms.tag_warm, loaded_preset.tag_warm);
setter.set_parameter(¶ms.filter_cutoff_link, loaded_preset.filter_cutoff_link);
AMod1.loaded_sample = loaded_preset.mod1_loaded_sample.clone();
AMod1.sample_lib = loaded_preset.mod1_sample_lib.clone();
AMod1.restretch = loaded_preset.mod1_restretch;
AMod2.loaded_sample = loaded_preset.mod2_loaded_sample.clone();
AMod2.sample_lib = loaded_preset.mod2_sample_lib.clone();
AMod2.restretch = loaded_preset.mod2_restretch;
AMod3.loaded_sample = loaded_preset.mod3_loaded_sample.clone();
AMod3.sample_lib = loaded_preset.mod3_sample_lib.clone();
AMod3.restretch = loaded_preset.mod3_restretch;
// Note audio module type from the module is used here instead of from the main self type
// This is because preset loading has changed it here first!
AMod1.regenerate_samples();
AMod2.regenerate_samples();
AMod3.regenerate_samples();
(
mod_source_1_override,
mod_source_2_override,
mod_source_3_override,
mod_source_4_override,
mod_dest_1_override,
mod_dest_2_override,
mod_dest_3_override,
mod_dest_4_override,
preset_category_override,
)
}
fn export_preset(&mut self) {
let _updated_preset = self.update_current_preset();
let saving_preset = FileDialog::new()
.add_filter("actuate", &["actuate"]) // Use a binary format for audio data
.set_file_name(&self.preset_name.lock().unwrap().replace(" ", "_"))
.save_file();
if let Some(location) = saving_preset {
// Create our new save file
let file = File::create(location.clone());
if let Ok(_file) = file {
// Serialize our data to a binary format (MessagePack)
let preset_store = Arc::clone(&self.preset_lib);
let mut loaded_preset = preset_store.lock().unwrap()
[self.current_preset.load(Ordering::Relaxed) as usize]
.clone();
// Clear out our generated notes and only keep the samples themselves
loaded_preset.mod1_sample_lib.clear();
loaded_preset.mod2_sample_lib.clear();
loaded_preset.mod3_sample_lib.clear();
// Serialize to MessagePack bytes
let serialized_data = rmp_serde::to_vec::<ActuatePreset>(&loaded_preset);
if let Err(err) = serialized_data {
eprintln!("Error serializing data: {}", err);
return;
}
// Compress the serialized data using different GzEncoder
let compressed_data = Self::compress_bytes(&serialized_data.unwrap());
// Now you can write the compressed data to the file
if let Err(err) = std::fs::write(&location, &compressed_data) {
eprintln!("Error writing compressed data to file: {}", err);
return;
}
} else {
eprintln!("Error creating file at location: {:?}", location);
}
}
*self.save_bank.lock().unwrap() = false;
}
fn save_preset_bank(&mut self) {
let _updated_preset = self.update_current_preset();
let saving_bank = FileDialog::new()
.add_filter("actuatebank", &["actuatebank"]) // Use a binary format for audio data
.set_file_name(&self.preset_lib_name)
.save_file();
if let Some(location) = saving_bank {
// Create our new save file
let file = File::create(location.clone());
if let Ok(_file) = file {
// Serialize our data to a binary format (MessagePack)
let preset_store = Arc::clone(&self.preset_lib);
let mut preset_lock = preset_store.lock().unwrap();
// Clear out our generated notes and only keep the samples themselves
for preset in preset_lock.iter_mut() {
preset.mod1_sample_lib.clear();
preset.mod2_sample_lib.clear();
preset.mod3_sample_lib.clear();
}
// Serialize to MessagePack bytes
let serialized_data =
rmp_serde::to_vec::<&Vec<ActuatePreset>>(&preset_lock.as_ref());
if let Err(err) = serialized_data {
eprintln!("Error serializing data: {}", err);
return;
}
// Compress the serialized data using different GzEncoder
let compressed_data = Self::compress_bytes(&serialized_data.unwrap());
// Now you can write the compressed data to the file
if let Err(err) = std::fs::write(&location, &compressed_data) {
eprintln!("Error writing compressed data to file: {}", err);
return;
}
} else {
eprintln!("Error creating file at location: {:?}", location);
}
}
*self.save_bank.lock().unwrap() = false;
}
// Functions to compress bytes and decompress using gz
fn compress_bytes(data: &[u8]) -> Vec<u8> {
let mut encoder = GzEncoder::new(Vec::new(), Compression::best());
encoder.write_all(data).unwrap();
encoder.finish().unwrap()
}
fn decompress_bytes(compressed_data: &[u8]) -> Result<Vec<u8>, std::io::Error> {
let mut decoder = GzDecoder::new(compressed_data);
let mut decompressed_data = Vec::new();
decoder.read_to_end(&mut decompressed_data)?;
Ok(decompressed_data)
}
// Update our current preset
fn update_current_preset(&mut self) {
let arc_lib = Arc::clone(&self.preset_lib);
let AM1c = self.audio_module_1.clone();
let AM2c = self.audio_module_2.clone();
let AM3c = self.audio_module_3.clone();
let AM1 = AM1c.lock().unwrap();
let AM2 = AM2c.lock().unwrap();
let AM3 = AM3c.lock().unwrap();
arc_lib.lock().unwrap()[self.current_preset.load(Ordering::Acquire) as usize] =
ActuatePreset {
preset_name: self.preset_name.lock().unwrap().clone(),
preset_info: self.preset_info.lock().unwrap().clone(),
preset_category: self.params.preset_category.value(),
tag_acid: self.params.tag_acid.value(),
tag_analog: self.params.tag_analog.value(),
tag_bright: self.params.tag_bright.value(),
tag_chord: self.params.tag_chord.value(),
tag_crisp: self.params.tag_crisp.value(),
tag_deep: self.params.tag_deep.value(),
tag_delicate: self.params.tag_delicate.value(),
tag_hard: self.params.tag_hard.value(),
tag_harsh: self.params.tag_harsh.value(),
tag_lush: self.params.tag_lush.value(),
tag_mellow: self.params.tag_mellow.value(),
tag_resonant: self.params.tag_resonant.value(),
tag_rich: self.params.tag_rich.value(),
tag_sharp: self.params.tag_sharp.value(),
tag_silky: self.params.tag_silky.value(),
tag_smooth: self.params.tag_smooth.value(),
tag_soft: self.params.tag_soft.value(),
tag_stab: self.params.tag_stab.value(),
tag_warm: self.params.tag_warm.value(),
// Modules 1
///////////////////////////////////////////////////////////
mod1_audio_module_type: self.params._audio_module_1_type.value(),
mod1_audio_module_level: self.params.audio_module_1_level.value(),
// Granulizer/Sampler
mod1_loaded_sample: AM1.loaded_sample.clone(),
mod1_sample_lib: AM1.sample_lib.clone(),
mod1_loop_wavetable: AM1.loop_wavetable,
mod1_single_cycle: AM1.single_cycle,
mod1_restretch: AM1.restretch,
mod1_prev_restretch: AM1.prev_restretch,
mod1_start_position: AM1.start_position,
mod1_end_position: AM1._end_position,
mod1_grain_crossfade: AM1.grain_crossfade,
mod1_grain_gap: AM1.grain_gap,
mod1_grain_hold: AM1.grain_hold,
// Osc module knob storage
mod1_osc_type: AM1.osc_type,
mod1_osc_octave: AM1.osc_octave,
mod1_osc_semitones: AM1.osc_semitones,
mod1_osc_detune: AM1.osc_detune,
mod1_osc_attack: AM1.osc_attack,
mod1_osc_decay: AM1.osc_decay,
mod1_osc_sustain: AM1.osc_sustain,
mod1_osc_release: AM1.osc_release,
mod1_osc_retrigger: AM1.osc_retrigger,
mod1_osc_atk_curve: AM1.osc_atk_curve,
mod1_osc_dec_curve: AM1.osc_dec_curve,
mod1_osc_rel_curve: AM1.osc_rel_curve,
mod1_osc_unison: AM1.osc_unison,
mod1_osc_unison_detune: AM1.osc_unison_detune,
mod1_osc_stereo: AM1.osc_stereo,
// Modules 2
///////////////////////////////////////////////////////////
mod2_audio_module_type: self.params._audio_module_2_type.value(),
mod2_audio_module_level: self.params.audio_module_2_level.value(),
// Granulizer/Sampler
mod2_loaded_sample: AM2.loaded_sample.clone(),
mod2_sample_lib: AM2.sample_lib.clone(),
mod2_loop_wavetable: AM2.loop_wavetable,
mod2_single_cycle: AM2.single_cycle,
mod2_restretch: AM2.restretch,
mod2_prev_restretch: AM2.prev_restretch,
mod2_start_position: AM2.start_position,
mod2_end_position: AM2._end_position,
mod2_grain_crossfade: AM2.grain_crossfade,
mod2_grain_gap: AM2.grain_gap,
mod2_grain_hold: AM2.grain_hold,
// Osc module knob storage
mod2_osc_type: AM2.osc_type,
mod2_osc_octave: AM2.osc_octave,
mod2_osc_semitones: AM2.osc_semitones,
mod2_osc_detune: AM2.osc_detune,
mod2_osc_attack: AM2.osc_attack,
mod2_osc_decay: AM2.osc_decay,
mod2_osc_sustain: AM2.osc_sustain,
mod2_osc_release: AM2.osc_release,
mod2_osc_retrigger: AM2.osc_retrigger,
mod2_osc_atk_curve: AM2.osc_atk_curve,
mod2_osc_dec_curve: AM2.osc_dec_curve,
mod2_osc_rel_curve: AM2.osc_rel_curve,
mod2_osc_unison: AM2.osc_unison,
mod2_osc_unison_detune: AM2.osc_unison_detune,
mod2_osc_stereo: AM2.osc_stereo,
// Modules 3
///////////////////////////////////////////////////////////
mod3_audio_module_type: self.params._audio_module_3_type.value(),
mod3_audio_module_level: self.params.audio_module_3_level.value(),
// Granulizer/Sampler
mod3_loaded_sample: AM3.loaded_sample.clone(),
mod3_sample_lib: AM3.sample_lib.clone(),
mod3_loop_wavetable: AM3.loop_wavetable,
mod3_single_cycle: AM3.single_cycle,
mod3_restretch: AM3.restretch,
mod3_prev_restretch: AM3.prev_restretch,
mod3_start_position: AM3.start_position,
mod3_end_position: AM3._end_position,
mod3_grain_crossfade: AM3.grain_crossfade,
mod3_grain_gap: AM3.grain_gap,
mod3_grain_hold: AM3.grain_hold,
// Osc module knob storage
mod3_osc_type: AM3.osc_type,
mod3_osc_octave: AM3.osc_octave,
mod3_osc_semitones: AM3.osc_semitones,
mod3_osc_detune: AM3.osc_detune,
mod3_osc_attack: AM3.osc_attack,
mod3_osc_decay: AM3.osc_decay,
mod3_osc_sustain: AM3.osc_sustain,
mod3_osc_release: AM3.osc_release,
mod3_osc_retrigger: AM3.osc_retrigger,
mod3_osc_atk_curve: AM3.osc_atk_curve,
mod3_osc_dec_curve: AM3.osc_dec_curve,
mod3_osc_rel_curve: AM3.osc_rel_curve,
mod3_osc_unison: AM3.osc_unison,
mod3_osc_unison_detune: AM3.osc_unison_detune,
mod3_osc_stereo: AM3.osc_stereo,
// Filter storage - gotten from params
filter_wet: self.params.filter_wet.value(),
filter_cutoff: self.params.filter_cutoff.value(),
filter_resonance: self.params.filter_resonance.value(),
filter_res_type: self.params.filter_res_type.value(),
filter_lp_amount: self.params.filter_lp_amount.value(),
filter_hp_amount: self.params.filter_hp_amount.value(),
filter_bp_amount: self.params.filter_bp_amount.value(),
filter_env_peak: self.params.filter_env_peak.value(),
filter_env_attack: self.params.filter_env_attack.value(),
filter_env_decay: self.params.filter_env_decay.value(),
filter_env_sustain: self.params.filter_env_sustain.value(),
filter_env_release: self.params.filter_env_release.value(),
filter_env_atk_curve: self.params.filter_env_atk_curve.value(),
filter_env_dec_curve: self.params.filter_env_dec_curve.value(),
filter_env_rel_curve: self.params.filter_env_rel_curve.value(),
filter_alg_type: self.params.filter_alg_type.value(),
tilt_filter_type: self.params.tilt_filter_type.value(),
filter_wet_2: self.params.filter_wet_2.value(),
filter_cutoff_2: self.params.filter_cutoff_2.value(),
filter_resonance_2: self.params.filter_resonance_2.value(),
filter_res_type_2: self.params.filter_res_type_2.value(),
filter_lp_amount_2: self.params.filter_lp_amount_2.value(),
filter_hp_amount_2: self.params.filter_hp_amount_2.value(),
filter_bp_amount_2: self.params.filter_bp_amount_2.value(),
filter_env_peak_2: self.params.filter_env_peak_2.value(),
filter_env_attack_2: self.params.filter_env_attack_2.value(),
filter_env_decay_2: self.params.filter_env_decay_2.value(),
filter_env_sustain_2: self.params.filter_env_sustain_2.value(),
filter_env_release_2: self.params.filter_env_release_2.value(),
filter_env_atk_curve_2: self.params.filter_env_atk_curve_2.value(),
filter_env_dec_curve_2: self.params.filter_env_dec_curve_2.value(),
filter_env_rel_curve_2: self.params.filter_env_rel_curve_2.value(),
filter_alg_type_2: self.params.filter_alg_type_2.value(),
tilt_filter_type_2: self.params.tilt_filter_type_2.value(),
filter_routing: self.params.filter_routing.value(),
filter_cutoff_link: self.params.filter_cutoff_link.value(),
// Pitch
pitch_enable: self.params.pitch_enable.value(),
pitch_env_atk_curve: self.params.pitch_env_atk_curve.value(),
pitch_env_dec_curve: self.params.pitch_env_dec_curve.value(),
pitch_env_rel_curve: self.params.pitch_env_rel_curve.value(),
pitch_env_attack: self.params.pitch_env_attack.value(),
pitch_env_decay: self.params.pitch_env_decay.value(),
pitch_env_sustain: self.params.pitch_env_sustain.value(),
pitch_env_release: self.params.pitch_env_release.value(),
pitch_env_peak: self.params.pitch_env_peak.value(),
pitch_routing: self.params.pitch_routing.value(),
pitch_enable_2: self.params.pitch_enable_2.value(),
pitch_env_atk_curve_2: self.params.pitch_env_atk_curve_2.value(),
pitch_env_dec_curve_2: self.params.pitch_env_dec_curve_2.value(),
pitch_env_rel_curve_2: self.params.pitch_env_rel_curve_2.value(),
pitch_env_attack_2: self.params.pitch_env_attack_2.value(),
pitch_env_decay_2: self.params.pitch_env_decay_2.value(),
pitch_env_sustain_2: self.params.pitch_env_sustain_2.value(),
pitch_env_release_2: self.params.pitch_env_release_2.value(),
pitch_env_peak_2: self.params.pitch_env_peak_2.value(),
pitch_routing_2: self.params.pitch_routing_2.value(),
// LFOs
lfo1_enable: self.params.lfo1_enable.value(),
lfo2_enable: self.params.lfo2_enable.value(),
lfo3_enable: self.params.lfo3_enable.value(),
lfo1_freq: self.params.lfo1_freq.value(),
lfo1_retrigger: self.params.lfo1_retrigger.value(),
lfo1_sync: self.params.lfo1_sync.value(),
lfo1_snap: self.params.lfo1_snap.value(),
lfo1_waveform: self.params.lfo1_waveform.value(),
lfo1_phase: self.params.lfo1_phase.value(),
lfo2_freq: self.params.lfo2_freq.value(),
lfo2_retrigger: self.params.lfo2_retrigger.value(),
lfo2_sync: self.params.lfo2_sync.value(),
lfo2_snap: self.params.lfo2_snap.value(),
lfo2_waveform: self.params.lfo2_waveform.value(),
lfo2_phase: self.params.lfo2_phase.value(),
lfo3_freq: self.params.lfo3_freq.value(),
lfo3_retrigger: self.params.lfo3_retrigger.value(),
lfo3_sync: self.params.lfo3_sync.value(),
lfo3_snap: self.params.lfo3_snap.value(),
lfo3_waveform: self.params.lfo3_waveform.value(),
lfo3_phase: self.params.lfo3_phase.value(),
mod_source_1: self.params.mod_source_1.value(),
mod_source_2: self.params.mod_source_2.value(),
mod_source_3: self.params.mod_source_3.value(),
mod_source_4: self.params.mod_source_4.value(),
mod_dest_1: self.params.mod_destination_1.value(),
mod_dest_2: self.params.mod_destination_2.value(),
mod_dest_3: self.params.mod_destination_3.value(),
mod_dest_4: self.params.mod_destination_4.value(),
mod_amount_1: self.params.mod_amount_knob_1.value(),
mod_amount_2: self.params.mod_amount_knob_2.value(),
mod_amount_3: self.params.mod_amount_knob_3.value(),
mod_amount_4: self.params.mod_amount_knob_4.value(),
pre_use_eq: self.params.pre_use_eq.value(),
pre_low_freq: self.params.pre_low_freq.value(),
pre_mid_freq: self.params.pre_mid_freq.value(),
pre_high_freq: self.params.pre_high_freq.value(),
pre_low_gain: self.params.pre_low_gain.value(),
pre_mid_gain: self.params.pre_mid_gain.value(),
pre_high_gain: self.params.pre_high_gain.value(),
use_fx: self.params.use_fx.value(),
use_compressor: self.params.use_compressor.value(),
comp_amt: self.params.comp_amt.value(),
comp_atk: self.params.comp_atk.value(),
comp_rel: self.params.comp_rel.value(),
comp_drive: self.params.comp_drive.value(),
use_abass: self.params.use_abass.value(),
abass_amount: self.params.abass_amount.value(),
use_saturation: self.params.use_saturation.value(),
sat_amount: self.params.sat_amt.value(),
sat_type: self.params.sat_type.value(),
use_delay: self.params.use_delay.value(),
delay_amount: self.params.delay_amount.value(),
delay_time: self.params.delay_time.value(),
delay_decay: self.params.delay_decay.value(),
delay_type: self.params.delay_type.value(),
use_reverb: self.params.use_reverb.value(),
reverb_amount: self.params.reverb_amount.value(),
reverb_size: self.params.reverb_size.value(),
reverb_feedback: self.params.reverb_feedback.value(),
use_phaser: self.params.use_phaser.value(),
phaser_amount: self.params.phaser_amount.value(),
phaser_depth: self.params.phaser_depth.value(),
phaser_rate: self.params.phaser_rate.value(),
phaser_feedback: self.params.phaser_feedback.value(),
use_buffermod: self.params.use_buffermod.value(),
buffermod_amount: self.params.buffermod_amount.value(),
buffermod_depth: self.params.buffermod_depth.value(),
buffermod_rate: self.params.buffermod_rate.value(),
buffermod_spread: self.params.buffermod_spread.value(),
buffermod_timing: self.params.buffermod_timing.value(),
use_flanger: self.params.use_flanger.value(),
flanger_amount: self.params.flanger_amount.value(),
flanger_depth: self.params.flanger_depth.value(),
flanger_rate: self.params.flanger_rate.value(),
flanger_feedback: self.params.flanger_feedback.value(),
use_limiter: self.params.use_limiter.value(),
limiter_threshold: self.params.limiter_threshold.value(),
limiter_knee: self.params.limiter_knee.value(),
};
}
fn filter_process_1(
&mut self,
note_off_filter_controller1: bool,
note_off_filter_controller2: bool,
note_off_filter_controller3: bool,
reset_filter_controller1: bool,
reset_filter_controller2: bool,
reset_filter_controller3: bool,
left_input_filter1: f32,
right_input_filter1: f32,
left_output: &mut f32,
right_output: &mut f32,
filter_cutoff_mod: f32,
filter_resonance_mod: f32,
) {
// Filter 1 Processing
///////////////////////////////////////////////////////////////
if self.params.filter_wet.value() > 0.0 && !self.file_dialog.load(Ordering::Relaxed) {
// Filter state movement code
//////////////////////////////////////////
// If a note is ending and we should enter releasing
if note_off_filter_controller1
|| note_off_filter_controller2
|| note_off_filter_controller3
{
self.filter_state_1 = OscState::Releasing;
self.filter_rel_smoother_1 = match self.params.filter_env_rel_curve.value() {
SmoothStyle::Linear => Smoother::new(SmoothingStyle::Linear(
self.params.filter_env_release.value(),
)),
SmoothStyle::Logarithmic => Smoother::new(SmoothingStyle::Logarithmic(
self.params.filter_env_release.value(),
)),
SmoothStyle::Exponential => Smoother::new(SmoothingStyle::Exponential(
self.params.filter_env_release.value(),
)),
SmoothStyle::LogSteep => Smoother::new(SmoothingStyle::LogSteep(
self.params.filter_env_release.value(),
)),
};
// Reset our filter release to be at sustain level to start
self.filter_rel_smoother_1.reset(
self.params.filter_cutoff.value()
* (self.params.filter_env_sustain.value() / 999.9),
);
// Move release to the cutoff to end
self.filter_rel_smoother_1
.set_target(self.sample_rate, self.params.filter_cutoff.value());
}
// Try to trigger our filter mods on note on! This is sequential/single because we just need a trigger at a point in time
if reset_filter_controller1 || reset_filter_controller2 || reset_filter_controller3 {
// Set our filter in attack state
self.filter_state_1 = OscState::Attacking;
// Consume our params for smoothing
self.filter_atk_smoother_1 = match self.params.filter_env_atk_curve.value() {
SmoothStyle::Linear => Smoother::new(SmoothingStyle::Linear(
self.params.filter_env_attack.value(),
)),
SmoothStyle::Logarithmic => Smoother::new(SmoothingStyle::Logarithmic(
self.params.filter_env_attack.value(),
)),
SmoothStyle::Exponential => Smoother::new(SmoothingStyle::Exponential(
self.params.filter_env_attack.value(),
)),
SmoothStyle::LogSteep => Smoother::new(SmoothingStyle::LogSteep(
self.params.filter_env_attack.value(),
)),
};
// Reset our attack to start from the filter cutoff
self.filter_atk_smoother_1
.reset(self.params.filter_cutoff.value());
// Since we're in attack state at the start of our note we need to setup the attack going to the env peak
self.filter_atk_smoother_1.set_target(
self.sample_rate,
(self.params.filter_cutoff.value()
+ (
// This scales the peak env to be much gentler for the TILT filter
match self.params.filter_alg_type.value() {
FilterAlgorithms::SVF => self.params.filter_env_peak.value(),
FilterAlgorithms::TILT => adv_scale_value(
self.params.filter_env_peak.value(),
-19980.0,
19980.0,
-5000.0,
5000.0,
),
FilterAlgorithms::VCF => self.params.filter_env_peak.value(),
}
))
.clamp(20.0, 20000.0),
);
}
// If our attack has finished
if self.filter_atk_smoother_1.steps_left() == 0
&& self.filter_state_1 == OscState::Attacking
{
self.filter_state_1 = OscState::Decaying;
self.filter_dec_smoother_1 = match self.params.filter_env_dec_curve.value() {
SmoothStyle::Linear => {
Smoother::new(SmoothingStyle::Linear(self.params.filter_env_decay.value()))
}
SmoothStyle::Logarithmic => Smoother::new(SmoothingStyle::Logarithmic(
self.params.filter_env_decay.value(),
)),
SmoothStyle::Exponential => Smoother::new(SmoothingStyle::Exponential(
self.params.filter_env_decay.value(),
)),
SmoothStyle::LogSteep => Smoother::new(SmoothingStyle::LogSteep(
self.params.filter_env_decay.value(),
)),
};
// This makes our filter decay start at env peak point
self.filter_dec_smoother_1.reset(
(self.params.filter_cutoff.value()
+ (
// This scales the peak env to be much gentler for the TILT filter
match self.params.filter_alg_type.value() {
FilterAlgorithms::SVF => self.params.filter_env_peak.value(),
FilterAlgorithms::TILT => adv_scale_value(
self.params.filter_env_peak.value(),
-19980.0,
19980.0,
-5000.0,
5000.0,
),
FilterAlgorithms::VCF => self.params.filter_env_peak.value(),
}
))
.clamp(20.0, 20000.0),
);
// Set up the smoother for our filter movement to go from our decay point to our sustain point
self.filter_dec_smoother_1.set_target(
self.sample_rate,
self.params.filter_cutoff.value()
* (self.params.filter_env_sustain.value() / 999.9),
);
}
// If our decay has finished move to sustain state
if self.filter_dec_smoother_1.steps_left() == 0
&& self.filter_state_1 == OscState::Decaying
{
self.filter_state_1 = OscState::Sustaining;
}
// use proper variable now that there are four filters and multiple states
let next_filter_step = match self.filter_state_1 {
OscState::Attacking => {
(self.filter_atk_smoother_1.next() + filter_cutoff_mod).clamp(20.0, 20000.0)
}
OscState::Decaying | OscState::Sustaining => {
(self.filter_dec_smoother_1.next() + filter_cutoff_mod).clamp(20.0, 20000.0)
}
OscState::Releasing => {
(self.filter_rel_smoother_1.next() + filter_cutoff_mod).clamp(20.0, 20000.0)
}
// I don't expect this to be used
_ => (self.params.filter_cutoff.value() + filter_cutoff_mod).clamp(20.0, 20000.0),
};
match self.params.filter_alg_type.value() {
FilterAlgorithms::SVF => {
// Filtering before output
self.filter_l_1.update(
next_filter_step,
self.params.filter_resonance.value() - filter_resonance_mod,
self.sample_rate,
self.params.filter_res_type.value(),
);
self.filter_r_1.update(
next_filter_step,
self.params.filter_resonance.value() - filter_resonance_mod,
self.sample_rate,
self.params.filter_res_type.value(),
);
let low_l: f32;
let band_l: f32;
let high_l: f32;
let low_r: f32;
let band_r: f32;
let high_r: f32;
(low_l, band_l, high_l) = self.filter_l_1.process(left_input_filter1);
(low_r, band_r, high_r) = self.filter_r_1.process(right_input_filter1);
*left_output += (low_l * self.params.filter_lp_amount.value()
+ band_l * self.params.filter_bp_amount.value()
+ high_l * self.params.filter_hp_amount.value())
* self.params.filter_wet.value()
+ left_input_filter1 * (1.0 - self.params.filter_wet.value());
*right_output += (low_r * self.params.filter_lp_amount.value()
+ band_r * self.params.filter_bp_amount.value()
+ high_r * self.params.filter_hp_amount.value())
* self.params.filter_wet.value()
+ right_input_filter1 * (1.0 - self.params.filter_wet.value());
}
FilterAlgorithms::TILT => {
self.tilt_filter_l_1.update(
self.sample_rate,
next_filter_step,
self.params.filter_resonance.value() - filter_resonance_mod,
self.params.tilt_filter_type.value(),
);
self.tilt_filter_r_1.update(
self.sample_rate,
next_filter_step,
self.params.filter_resonance.value() - filter_resonance_mod,
self.params.tilt_filter_type.value(),
);
let tilt_out_l = self.tilt_filter_l_1.process(left_input_filter1);
let tilt_out_r = self.tilt_filter_r_1.process(right_input_filter1);
*left_output += tilt_out_l * self.params.filter_wet.value()
+ left_input_filter1 * (1.0 - self.params.filter_wet.value());
*right_output += tilt_out_r * self.params.filter_wet.value()
+ right_input_filter1 * (1.0 - self.params.filter_wet.value());
}
FilterAlgorithms::VCF => {
self.vcf_filter_l_1.update(
next_filter_step,
self.params.filter_resonance.value() - filter_resonance_mod,
self.params.vcf_filter_type.value(),
self.sample_rate,
);
self.vcf_filter_r_1.update(
next_filter_step,
self.params.filter_resonance.value() - filter_resonance_mod,
self.params.vcf_filter_type.value(),
self.sample_rate,
);
let vcf_out_l = self.vcf_filter_l_1.process(left_input_filter1);
let vcf_out_r = self.vcf_filter_r_1.process(right_input_filter1);
*left_output += vcf_out_l * self.params.filter_wet.value()
+ left_input_filter1 * (1.0 - self.params.filter_wet.value());
*right_output += vcf_out_r * self.params.filter_wet.value()
+ right_input_filter1 * (1.0 - self.params.filter_wet.value());
}
}
}
}
fn filter_process_2(
&mut self,
note_off_filter_controller1: bool,
note_off_filter_controller2: bool,
note_off_filter_controller3: bool,
reset_filter_controller1: bool,
reset_filter_controller2: bool,
reset_filter_controller3: bool,
left_input_filter2: f32,
right_input_filter2: f32,
left_output: &mut f32,
right_output: &mut f32,
filter_cutoff_mod: f32,
filter_resonance_mod: f32,
) {
// Filter 2 Processing
///////////////////////////////////////////////////////////////
if self.params.filter_wet_2.value() > 0.0 && !self.file_dialog.load(Ordering::Relaxed) {
// Filter state movement code
//////////////////////////////////////////
// If a note is ending and we should enter releasing
if note_off_filter_controller1
|| note_off_filter_controller2
|| note_off_filter_controller3
{
self.filter_state_2 = OscState::Releasing;
self.filter_rel_smoother_2 = match self.params.filter_env_rel_curve_2.value() {
SmoothStyle::Linear => Smoother::new(SmoothingStyle::Linear(
self.params.filter_env_release_2.value(),
)),
SmoothStyle::Logarithmic => Smoother::new(SmoothingStyle::Logarithmic(
self.params.filter_env_release_2.value(),
)),
SmoothStyle::Exponential => Smoother::new(SmoothingStyle::Exponential(
self.params.filter_env_release_2.value(),
)),
SmoothStyle::LogSteep => Smoother::new(SmoothingStyle::LogSteep(
self.params.filter_env_release_2.value(),
)),
};
// Reset our filter release to be at sustain level to start
self.filter_rel_smoother_2.reset(
self.params.filter_cutoff_2.value()
* (self.params.filter_env_sustain_2.value() / 999.9),
);
// Move release to the cutoff to end
self.filter_rel_smoother_2
.set_target(self.sample_rate, self.params.filter_cutoff_2.value());
}
// Try to trigger our filter mods on note on! This is sequential/single because we just need a trigger at a point in time
if reset_filter_controller1 || reset_filter_controller2 || reset_filter_controller3 {
// Set our filter in attack state
self.filter_state_2 = OscState::Attacking;
// Consume our params for smoothing
self.filter_atk_smoother_2 = match self.params.filter_env_atk_curve_2.value() {
SmoothStyle::Linear => Smoother::new(SmoothingStyle::Linear(
self.params.filter_env_attack_2.value(),
)),
SmoothStyle::Logarithmic => Smoother::new(SmoothingStyle::Logarithmic(
self.params.filter_env_attack_2.value(),
)),
SmoothStyle::Exponential => Smoother::new(SmoothingStyle::Exponential(
self.params.filter_env_attack_2.value(),
)),
SmoothStyle::LogSteep => Smoother::new(SmoothingStyle::LogSteep(
self.params.filter_env_attack_2.value(),
)),
};
// Reset our attack to start from the filter cutoff
self.filter_atk_smoother_2
.reset(self.params.filter_cutoff_2.value());
// Since we're in attack state at the start of our note we need to setup the attack going to the env peak
self.filter_atk_smoother_2.set_target(
self.sample_rate,
(self.params.filter_cutoff_2.value()
+ (
// This scales the peak env to be much gentler for the TILT filter
match self.params.filter_alg_type_2.value() {
FilterAlgorithms::SVF => self.params.filter_env_peak_2.value(),
FilterAlgorithms::TILT => adv_scale_value(
self.params.filter_env_peak_2.value(),
-19980.0,
19980.0,
-5000.0,
5000.0,
),
FilterAlgorithms::VCF => self.params.filter_env_peak_2.value(),
}
))
.clamp(20.0, 20000.0),
);
}
// If our attack has finished
if self.filter_atk_smoother_2.steps_left() == 0
&& self.filter_state_2 == OscState::Attacking
{
self.filter_state_2 = OscState::Decaying;
self.filter_dec_smoother_2 = match self.params.filter_env_dec_curve_2.value() {
SmoothStyle::Linear => Smoother::new(SmoothingStyle::Linear(
self.params.filter_env_decay_2.value(),
)),
SmoothStyle::Logarithmic => Smoother::new(SmoothingStyle::Logarithmic(
self.params.filter_env_decay_2.value(),
)),
SmoothStyle::Exponential => Smoother::new(SmoothingStyle::Exponential(
self.params.filter_env_decay_2.value(),
)),
SmoothStyle::LogSteep => Smoother::new(SmoothingStyle::LogSteep(
self.params.filter_env_decay_2.value(),
)),
};
// This makes our filter decay start at env peak point
self.filter_dec_smoother_2.reset(
(self.params.filter_cutoff_2.value()
+ (
// This scales the peak env to be much gentler for the TILT filter
match self.params.filter_alg_type_2.value() {
FilterAlgorithms::SVF => self.params.filter_env_peak_2.value(),
FilterAlgorithms::TILT => adv_scale_value(
self.params.filter_env_peak_2.value(),
-19980.0,
19980.0,
-5000.0,
5000.0,
),
FilterAlgorithms::VCF => self.params.filter_env_peak_2.value(),
}
))
.clamp(20.0, 20000.0),
);
// Set up the smoother for our filter movement to go from our decay point to our sustain point
self.filter_dec_smoother_2.set_target(
self.sample_rate,
self.params.filter_cutoff_2.value()
* (self.params.filter_env_sustain_2.value() / 999.9),
);
}
// If our decay has finished move to sustain state
if self.filter_dec_smoother_2.steps_left() == 0
&& self.filter_state_2 == OscState::Decaying
{
self.filter_state_2 = OscState::Sustaining;
}
// use proper variable now that there are four filters and multiple states
let next_filter_step = match self.filter_state_2 {
OscState::Attacking => {
(self.filter_atk_smoother_2.next() + filter_cutoff_mod).clamp(20.0, 20000.0)
}
OscState::Decaying | OscState::Sustaining => {
(self.filter_dec_smoother_2.next() + filter_cutoff_mod).clamp(20.0, 20000.0)
}
OscState::Releasing => {
(self.filter_rel_smoother_2.next() + filter_cutoff_mod).clamp(20.0, 20000.0)
}
// I don't expect this to be used
_ => self.params.filter_cutoff_2.value() + filter_cutoff_mod,
};
match self.params.filter_alg_type.value() {
FilterAlgorithms::SVF => {
// Filtering before output
self.filter_l_2.update(
next_filter_step,
self.params.filter_resonance_2.value(),
self.sample_rate,
self.params.filter_res_type_2.value(),
);
self.filter_r_2.update(
next_filter_step,
self.params.filter_resonance_2.value() + filter_resonance_mod,
self.sample_rate,
self.params.filter_res_type_2.value(),
);
let low_l: f32;
let band_l: f32;
let high_l: f32;
let low_r: f32;
let band_r: f32;
let high_r: f32;
(low_l, band_l, high_l) = self.filter_l_2.process(left_input_filter2);
(low_r, band_r, high_r) = self.filter_r_2.process(right_input_filter2);
*left_output += (low_l * self.params.filter_lp_amount_2.value()
+ band_l * self.params.filter_bp_amount_2.value()
+ high_l * self.params.filter_hp_amount_2.value())
* self.params.filter_wet_2.value()
+ *left_output * (1.0 - self.params.filter_wet_2.value());
*right_output += (low_r * self.params.filter_lp_amount_2.value()
+ band_r * self.params.filter_bp_amount_2.value()
+ high_r * self.params.filter_hp_amount_2.value())
* self.params.filter_wet_2.value()
+ *right_output * (1.0 - self.params.filter_wet_2.value());
}
FilterAlgorithms::TILT => {
self.tilt_filter_l_2.update(
self.sample_rate,
next_filter_step,
self.params.filter_resonance_2.value(),
self.params.tilt_filter_type_2.value(),
);
self.tilt_filter_r_2.update(
self.sample_rate,
next_filter_step,
self.params.filter_resonance_2.value(),
self.params.tilt_filter_type_2.value(),
);
let tilt_out_l = self.tilt_filter_l_2.process(left_input_filter2);
let tilt_out_r = self.tilt_filter_r_2.process(right_input_filter2);
*left_output += tilt_out_l * self.params.filter_wet_2.value()
+ left_input_filter2 * (1.0 - self.params.filter_wet_2.value());
*right_output += tilt_out_r * self.params.filter_wet_2.value()
+ right_input_filter2 * (1.0 - self.params.filter_wet_2.value());
}
FilterAlgorithms::VCF => {
self.vcf_filter_l_2.update(
next_filter_step,
self.params.filter_resonance_2.value(),
self.params.vcf_filter_type_2.value(),
self.sample_rate,
);
self.vcf_filter_r_2.update(
next_filter_step,
self.params.filter_resonance_2.value(),
self.params.vcf_filter_type_2.value(),
self.sample_rate,
);
let vcf_out_l = self.vcf_filter_l_2.process(left_input_filter2);
let vcf_out_r = self.vcf_filter_r_2.process(right_input_filter2);
*left_output += vcf_out_l * self.params.filter_wet_2.value()
+ left_input_filter2 * (1.0 - self.params.filter_wet_2.value());
*right_output += vcf_out_r * self.params.filter_wet_2.value()
+ right_input_filter2 * (1.0 - self.params.filter_wet_2.value());
}
}
}
}
}
impl ClapPlugin for Actuate {
const CLAP_ID: &'static str = "com.ardura.actuate";
const CLAP_DESCRIPTION: Option<&'static str> = Some("Sampler + Synth");
const CLAP_MANUAL_URL: Option<&'static str> = Some(Self::URL);
const CLAP_SUPPORT_URL: Option<&'static str> = None;
const CLAP_FEATURES: &'static [ClapFeature] = &[
ClapFeature::AudioEffect,
ClapFeature::Stereo,
ClapFeature::Mono,
ClapFeature::Utility,
];
}
impl Vst3Plugin for Actuate {
const VST3_CLASS_ID: [u8; 16] = *b"ActuateArduraAAA";
const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] =
&[Vst3SubCategory::Instrument, Vst3SubCategory::Sampler];
}
nih_export_clap!(Actuate);
nih_export_vst3!(Actuate);
// I use this when I want to remove label and unit from a param in gui
pub fn format_nothing() -> Arc<dyn Fn(f32) -> String + Send + Sync> {
Arc::new(move |_| String::new())
}
fn adv_scale_value(input: f32, in_min: f32, in_max: f32, out_min: f32, out_max: f32) -> f32 {
// Ensure that the input value is within the specified input range
let input = input.max(in_min).min(in_max);
// Calculate the scaled value using linear mapping
let scaled_value = (input - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
scaled_value
}