Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions src/bin/gui.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
use anyhow::{Context, Result};
use log::info;
use rustortion::gui::amp::start;
use rustortion::io::manager::ProcessorManager;

pub fn main() -> iced::Result {
start()
pub fn main() -> Result<()> {
dotenv::dotenv().ok();
env_logger::init();

info!("Rustortion GUI v{}", env!("CARGO_PKG_VERSION"));
info!(
r#"
__________ __ __ .__
\______ \__ __ _______/ |_ ____________/ |_|__| ____ ____
| _/ | \/ ___/\ __\/ _ \_ __ \ __\ |/ _ \ / \
| | \ | /\___ \ | | ( <_> ) | \/| | | ( <_> ) | \
|____|_ /____//____ > |__| \____/|__| |__| |__|\____/|___| /
\/ \/ \/
"#
);

// Check required environment variables (same as CLI)
let required_vars = ["RUST_LOG", "PIPEWIRE_LATENCY", "JACK_PROMISCUOUS_SERVER"];
for &key in &required_vars {
match std::env::var(key) {
Ok(val) => info!("{} = {}", key, val),
Err(_) => anyhow::bail!("environment variable '{}' must be set.", key),
}
}

// Create ProcessorManager with proper error handling
let processor_manager = ProcessorManager::new().context("failed to create ProcessorManager")?;

info!("ProcessorManager created successfully, starting GUI...");

// Start the GUI with the processor manager
start(processor_manager).map_err(|e| anyhow::anyhow!("GUI error: {}", e))?;

Ok(())
}
83 changes: 49 additions & 34 deletions src/gui/amp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use iced::Length::Fill;
use iced::widget::{button, column, container, pick_list, row, scrollable};
use iced::{Alignment, Element, Length, Task, Theme};

use crate::io::manager::ProcessorManager;
use crate::sim::chain::AmplifierChain;
use crate::sim::stages::{
clipper::ClipperType,
Expand All @@ -14,11 +15,20 @@ use crate::sim::stages::{

use crate::gui::widgets::{compressor, filter, poweramp, preamp, tonestack};

pub fn start() -> iced::Result {
pub fn start(processor_manager: ProcessorManager) -> iced::Result {
iced::application("Rustortion", AmplifierGui::update, AmplifierGui::view)
.window_size((800.0, 600.0))
.theme(AmplifierGui::theme)
.run()
.run_with(move || {
(
AmplifierGui {
processor_manager,
stages: Vec::new(),
selected_stage_type: StageType::default(),
},
Task::none(),
)
})
}

// Stage type enum
Expand Down Expand Up @@ -149,8 +159,9 @@ impl Default for PowerAmpConfig {
}

// Main GUI state
#[derive(Debug, Default)]
#[derive(Debug)]
struct AmplifierGui {
processor_manager: ProcessorManager,
stages: Vec<StageConfig>,
selected_stage_type: StageType,
}
Expand All @@ -161,7 +172,6 @@ pub enum Message {
AddStage,
RemoveStage(usize),
StageTypeSelected(StageType),
Start,

// Filter messages
FilterTypeChanged(usize, FilterType),
Expand Down Expand Up @@ -196,14 +206,9 @@ pub enum Message {
// Application implementation
impl AmplifierGui {
fn update(&mut self, msg: Message) -> Task<Message> {
let mut should_update_chain = false;

match msg {
Message::Start => {
let sample_rate = 44100.0;
let _chain = self.to_amp_chain(sample_rate);
println!("Starting with {} stages!", self.stages.len());
// Here you would connect to ProcessorManager
Task::none()
}
Message::AddStage => {
let new_stage = match self.selected_stage_type {
StageType::Filter => StageConfig::Filter(FilterConfig::default()),
Expand All @@ -213,143 +218,154 @@ impl AmplifierGui {
StageType::PowerAmp => StageConfig::PowerAmp(PowerAmpConfig::default()),
};
self.stages.push(new_stage);
Task::none()
should_update_chain = true;
Copy link

Copilot AI Jun 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider consolidating the repeated should_update_chain = true; assignments in each message arm—perhaps by returning an indication from each variant or using a helper method—to reduce duplication and improve readability.

Copilot uses AI. Check for mistakes.
}
Message::RemoveStage(idx) => {
if idx < self.stages.len() {
self.stages.remove(idx);
}
Task::none()
should_update_chain = true;
}
Message::StageTypeSelected(stage_type) => {
self.selected_stage_type = stage_type;
Task::none()
}

// Filter updates
Message::FilterTypeChanged(idx, filter_type) => {
if let Some(StageConfig::Filter(cfg)) = self.stages.get_mut(idx) {
cfg.filter_type = filter_type;
}
Task::none()
should_update_chain = true;
}
Message::FilterCutoffChanged(idx, v) => {
if let Some(StageConfig::Filter(cfg)) = self.stages.get_mut(idx) {
cfg.cutoff_hz = v;
}
Task::none()
should_update_chain = true;
}
Message::FilterResonanceChanged(idx, v) => {
if let Some(StageConfig::Filter(cfg)) = self.stages.get_mut(idx) {
cfg.resonance = v;
}
Task::none()
should_update_chain = true;
}

// Preamp updates
Message::PreampGainChanged(idx, v) => {
if let Some(StageConfig::Preamp(cfg)) = self.stages.get_mut(idx) {
cfg.gain = v;
}
Task::none()
should_update_chain = true;
}
Message::PreampBiasChanged(idx, v) => {
if let Some(StageConfig::Preamp(cfg)) = self.stages.get_mut(idx) {
cfg.bias = v;
}
Task::none()
should_update_chain = true;
}
Message::PreampClipperChanged(idx, clipper) => {
if let Some(StageConfig::Preamp(cfg)) = self.stages.get_mut(idx) {
cfg.clipper_type = clipper;
}
Task::none()
should_update_chain = true;
}

// Compressor updates
Message::CompressorThresholdChanged(idx, v) => {
if let Some(StageConfig::Compressor(cfg)) = self.stages.get_mut(idx) {
cfg.threshold_db = v;
}
Task::none()
should_update_chain = true;
}
Message::CompressorRatioChanged(idx, v) => {
if let Some(StageConfig::Compressor(cfg)) = self.stages.get_mut(idx) {
cfg.ratio = v;
}
Task::none()
should_update_chain = true;
}
Message::CompressorAttackChanged(idx, v) => {
if let Some(StageConfig::Compressor(cfg)) = self.stages.get_mut(idx) {
cfg.attack_ms = v;
}
Task::none()
should_update_chain = true;
}
Message::CompressorReleaseChanged(idx, v) => {
if let Some(StageConfig::Compressor(cfg)) = self.stages.get_mut(idx) {
cfg.release_ms = v;
}
Task::none()
should_update_chain = true;
}
Message::CompressorMakeupChanged(idx, v) => {
if let Some(StageConfig::Compressor(cfg)) = self.stages.get_mut(idx) {
cfg.makeup_db = v;
}
Task::none()
should_update_chain = true;
}

// ToneStack updates
Message::ToneStackModelChanged(idx, model) => {
if let Some(StageConfig::ToneStack(cfg)) = self.stages.get_mut(idx) {
cfg.model = model;
}
Task::none()
should_update_chain = true;
}
Message::ToneStackBassChanged(idx, v) => {
if let Some(StageConfig::ToneStack(cfg)) = self.stages.get_mut(idx) {
cfg.bass = v;
}
Task::none()
should_update_chain = true;
}
Message::ToneStackMidChanged(idx, v) => {
if let Some(StageConfig::ToneStack(cfg)) = self.stages.get_mut(idx) {
cfg.mid = v;
}
Task::none()
should_update_chain = true;
}
Message::ToneStackTrebleChanged(idx, v) => {
if let Some(StageConfig::ToneStack(cfg)) = self.stages.get_mut(idx) {
cfg.treble = v;
}
Task::none()
should_update_chain = true;
}
Message::ToneStackPresenceChanged(idx, v) => {
if let Some(StageConfig::ToneStack(cfg)) = self.stages.get_mut(idx) {
cfg.presence = v;
}
Task::none()
should_update_chain = true;
}

// PowerAmp updates
Message::PowerAmpTypeChanged(idx, amp_type) => {
if let Some(StageConfig::PowerAmp(cfg)) = self.stages.get_mut(idx) {
cfg.amp_type = amp_type;
}
Task::none()
should_update_chain = true;
}
Message::PowerAmpDriveChanged(idx, v) => {
if let Some(StageConfig::PowerAmp(cfg)) = self.stages.get_mut(idx) {
cfg.drive = v;
}
Task::none()
should_update_chain = true;
}
Message::PowerAmpSagChanged(idx, v) => {
if let Some(StageConfig::PowerAmp(cfg)) = self.stages.get_mut(idx) {
cfg.sag = v;
}
Task::none()
should_update_chain = true;
}
}

if should_update_chain {
self.update_processor_chain();
}

Task::none()
}

fn update_processor_chain(&self) {
let sample_rate = self.processor_manager.sample_rate();
let chain = self.to_amp_chain(sample_rate);
self.processor_manager.set_amp_chain(chain);
}

fn view(&self) -> Element<Message> {
Expand Down Expand Up @@ -384,7 +400,6 @@ impl AmplifierGui {
Message::StageTypeSelected
),
button("Add Stage").on_press(Message::AddStage),
button("Start").on_press(Message::Start),
]
.spacing(10)
.align_y(Alignment::Center);
Expand Down
9 changes: 9 additions & 0 deletions src/io/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@ impl ProcessorManager {
}
}

impl std::fmt::Debug for ProcessorManager {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ProcessorManager")
.field("sample_rate", &self.sample_rate)
.field("recorder", &self.recorder.is_some())
.finish()
}
}

impl Drop for ProcessorManager {
fn drop(&mut self) {
self.disable_recording();
Expand Down