diff --git a/Cargo.toml b/Cargo.toml index b9f919b..e356770 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,3 +33,10 @@ minimad = "0.13" crossbeam-channel = "0.5" confy = "2" rustfft = "6" + +[profile.release] +opt-level = 3 +lto = "fat" +codegen-units = 1 +panic = "abort" +strip = true diff --git a/src/app.rs b/src/app.rs index d6724f4..c6c3554 100644 --- a/src/app.rs +++ b/src/app.rs @@ -5,7 +5,6 @@ use std::path::PathBuf; use std::sync::{Arc, Mutex}; use crossbeam_channel::Sender; -use ratatui::style::Color; use crate::commands::AppCommand; use crate::engine::{ @@ -16,8 +15,8 @@ use crate::page::Page; use crate::services::pattern_editor; use crate::settings::Settings; use crate::state::{ - AudioSettings, EditorContext, Focus, LiveKeyState, Metrics, Modal, PanelState, PatternField, - PatternsNav, PlaybackState, ProjectState, UiState, + AudioSettings, EditorContext, FlashKind, Focus, LiveKeyState, Metrics, Modal, PanelState, + PatternField, PatternsNav, PlaybackState, ProjectState, UiState, }; use crate::views::{dict_view, help_view}; @@ -320,7 +319,7 @@ impl App { Some(cmds.join("\n")) }; } - self.ui.flash("Script compiled", 150, Color::White); + self.ui.flash("Script compiled", 150, FlashKind::Info); } Err(e) => { if let Some(step) = self @@ -331,7 +330,7 @@ impl App { { step.command = None; } - self.ui.flash(&format!("Script error: {e}"), 300, Color::Red); + self.ui.flash(&format!("Script error: {e}"), 300, FlashKind::Error); } } } @@ -540,7 +539,7 @@ impl App { { self.load_step_to_editor(); } - self.ui.flash("Step deleted", 150, Color::Green); + self.ui.flash("Step deleted", 150, FlashKind::Success); } pub fn reset_pattern(&mut self, bank: usize, pattern: usize) { @@ -549,7 +548,7 @@ impl App { if self.editor_ctx.bank == bank && self.editor_ctx.pattern == pattern { self.load_step_to_editor(); } - self.ui.flash("Pattern reset", 150, Color::Green); + self.ui.flash("Pattern reset", 150, FlashKind::Success); } pub fn reset_bank(&mut self, bank: usize) { @@ -560,13 +559,13 @@ impl App { if self.editor_ctx.bank == bank { self.load_step_to_editor(); } - self.ui.flash("Bank reset", 150, Color::Green); + self.ui.flash("Bank reset", 150, FlashKind::Success); } pub fn copy_pattern(&mut self, bank: usize, pattern: usize) { let pat = self.project_state.project.banks[bank].patterns[pattern].clone(); self.copied_pattern = Some(pat); - self.ui.flash("Pattern copied", 150, Color::Green); + self.ui.flash("Pattern copied", 150, FlashKind::Success); } pub fn paste_pattern(&mut self, bank: usize, pattern: usize) { @@ -582,14 +581,14 @@ impl App { if self.editor_ctx.bank == bank && self.editor_ctx.pattern == pattern { self.load_step_to_editor(); } - self.ui.flash("Pattern pasted", 150, Color::Green); + self.ui.flash("Pattern pasted", 150, FlashKind::Success); } } pub fn copy_bank(&mut self, bank: usize) { let b = self.project_state.project.banks[bank].clone(); self.copied_bank = Some(b); - self.ui.flash("Bank copied", 150, Color::Green); + self.ui.flash("Bank copied", 150, FlashKind::Success); } pub fn paste_bank(&mut self, bank: usize) { @@ -607,7 +606,7 @@ impl App { if self.editor_ctx.bank == bank { self.load_step_to_editor(); } - self.ui.flash("Bank pasted", 150, Color::Green); + self.ui.flash("Bank pasted", 150, FlashKind::Success); } } @@ -676,7 +675,7 @@ impl App { self.project_state.mark_dirty(bank, pattern); self.load_step_to_editor(); self.ui - .flash(&format!("Linked to step {:02}", copied.step + 1), 150, Color::Green); + .flash(&format!("Linked to step {:02}", copied.step + 1), 150, FlashKind::Success); } pub fn harden_step(&mut self) { @@ -709,7 +708,7 @@ impl App { } self.project_state.mark_dirty(bank, pattern); self.load_step_to_editor(); - self.ui.flash("Step hardened", 150, Color::Green); + self.ui.flash("Step hardened", 150, FlashKind::Success); } pub fn open_pattern_modal(&mut self, field: PatternField) { @@ -842,8 +841,8 @@ impl App { AppCommand::Flash { message, duration_ms, - color, - } => self.ui.flash(&message, duration_ms, color), + kind, + } => self.ui.flash(&message, duration_ms, kind), AppCommand::OpenModal(modal) => { if matches!(modal, Modal::Editor) { // If current step is a shallow copy, navigate to source step diff --git a/src/commands.rs b/src/commands.rs index 49e8269..a9ff0ef 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,10 +1,8 @@ use std::path::PathBuf; -use ratatui::style::Color; - use crate::engine::PatternChange; use crate::model::PatternSpeed; -use crate::state::{Modal, PatternField}; +use crate::state::{FlashKind, Modal, PatternField}; #[allow(dead_code)] pub enum AppCommand { @@ -102,7 +100,7 @@ pub enum AppCommand { Flash { message: String, duration_ms: u64, - color: Color, + kind: FlashKind, }, OpenModal(Modal), CloseModal, diff --git a/src/engine/audio.rs b/src/engine/audio.rs index 32257a2..c15e886 100644 --- a/src/engine/audio.rs +++ b/src/engine/audio.rs @@ -193,10 +193,9 @@ pub fn build_stream( let sr = sample_rate; let channels = config.channels as usize; - let max_voices = config.max_voices; let metrics_clone = Arc::clone(&metrics); - let mut engine = Engine::new_with_metrics(sample_rate, channels, max_voices, Arc::clone(&metrics)); + let mut engine = Engine::new_with_metrics(sample_rate, channels, Arc::clone(&metrics)); engine.sample_index = initial_samples; let mut analyzer = SpectrumAnalyzer::new(sample_rate); @@ -225,7 +224,7 @@ pub fn build_stream( AudioCommand::ResetEngine => { let old_samples = std::mem::take(&mut engine.sample_index); engine = - Engine::new_with_metrics(sr, channels, max_voices, Arc::clone(&metrics_clone)); + Engine::new_with_metrics(sr, channels, Arc::clone(&metrics_clone)); engine.sample_index = old_samples; } } diff --git a/src/state/mod.rs b/src/state/mod.rs index f4adcd0..e690e98 100644 --- a/src/state/mod.rs +++ b/src/state/mod.rs @@ -19,4 +19,4 @@ pub use patterns_nav::{PatternsColumn, PatternsNav}; pub use playback::PlaybackState; pub use project::ProjectState; pub use sample_browser::SampleBrowserState; -pub use ui::{DictFocus, UiState}; +pub use ui::{DictFocus, FlashKind, UiState}; diff --git a/src/state/ui.rs b/src/state/ui.rs index af65cac..77bc0ee 100644 --- a/src/state/ui.rs +++ b/src/state/ui.rs @@ -1,9 +1,15 @@ use std::time::{Duration, Instant}; -use ratatui::style::Color; - use crate::state::Modal; +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub enum FlashKind { + #[default] + Success, + Error, + Info, +} + pub struct Sparkle { pub x: u16, pub y: u16, @@ -22,7 +28,7 @@ pub struct UiState { pub sparkles: Vec, pub status_message: Option, pub flash_until: Option, - pub flash_color: Color, + pub flash_kind: FlashKind, pub modal: Modal, pub help_topic: usize, pub help_scroll: usize, @@ -41,7 +47,7 @@ impl Default for UiState { sparkles: Vec::new(), status_message: None, flash_until: None, - flash_color: Color::Green, + flash_kind: FlashKind::Success, modal: Modal::None, help_topic: 0, help_scroll: 0, @@ -57,14 +63,14 @@ impl Default for UiState { } impl UiState { - pub fn flash(&mut self, msg: &str, duration_ms: u64, color: Color) { + pub fn flash(&mut self, msg: &str, duration_ms: u64, kind: FlashKind) { self.status_message = Some(msg.to_string()); self.flash_until = Some(Instant::now() + Duration::from_millis(duration_ms)); - self.flash_color = color; + self.flash_kind = kind; } - pub fn flash_color(&self) -> Option { - if self.is_flashing() { Some(self.flash_color) } else { None } + pub fn flash_kind(&self) -> Option { + if self.is_flashing() { Some(self.flash_kind) } else { None } } pub fn set_status(&mut self, msg: String) { diff --git a/src/views/render.rs b/src/views/render.rs index e74bd1f..b45f9a7 100644 --- a/src/views/render.rs +++ b/src/views/render.rs @@ -10,7 +10,7 @@ use crate::app::App; use crate::engine::{LinkState, SequencerSnapshot}; use crate::model::SourceSpan; use crate::page::Page; -use crate::state::{Modal, PanelFocus, PatternField, SidePanel}; +use crate::state::{FlashKind, Modal, PanelFocus, PatternField, SidePanel}; use crate::views::highlight::{self, highlight_line, highlight_line_with_runtime}; use crate::widgets::{ConfirmModal, ModalFrame, NavMinimap, NavTile, SampleBrowser, TextInputModal}; @@ -524,9 +524,11 @@ fn render_modal(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, term let height = (term.height * 60 / 100).max(10); let step_num = app.editor_ctx.step + 1; - let flash_color = app.ui.flash_color(); - let border_color = match flash_color { - Some(c) => c, + let flash_kind = app.ui.flash_kind(); + let border_color = match flash_kind { + Some(FlashKind::Error) => Color::Red, + Some(FlashKind::Info) => Color::White, + Some(FlashKind::Success) => Color::Green, None => Color::Rgb(100, 160, 180), }; @@ -566,11 +568,11 @@ fn render_modal(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, term let editor_area = Rect::new(inner.x, inner.y, inner.width, inner.height.saturating_sub(1)); let hint_area = Rect::new(inner.x, inner.y + editor_area.height, inner.width, 1); - if let Some(c) = flash_color { - let bg = match c { - Color::Red => Color::Rgb(60, 10, 10), - Color::White => Color::Rgb(30, 30, 40), - _ => Color::Rgb(10, 30, 10), + if let Some(kind) = flash_kind { + let bg = match kind { + FlashKind::Error => Color::Rgb(60, 10, 10), + FlashKind::Info => Color::Rgb(30, 30, 40), + FlashKind::Success => Color::Rgb(10, 30, 10), }; let flash_block = Block::default().style(Style::default().bg(bg)); frame.render_widget(flash_block, editor_area);