diff --git a/CHANGELOG.md b/CHANGELOG.md index 035b705..19ccfe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Added - Mute/solo for patterns: stage with `m`/`x`, commit with `c`. Solo mutes all other patterns. Clear with `M`/`X`. - Lookahead scheduling: scripts are pre-evaluated ahead of time and audio commands are scheduled at precise beat positions, improving timing accuracy under CPU load. +- Realtime thread scheduling (`SCHED_FIFO`) for sequencer thread on Unix systems, improving timing reliability. ## [0.0.4] - 2026-02-02 diff --git a/crates/ratatui/src/theme/catppuccin_latte.rs b/crates/ratatui/src/theme/catppuccin_latte.rs index 70dd492..71e9577 100644 --- a/crates/ratatui/src/theme/catppuccin_latte.rs +++ b/crates/ratatui/src/theme/catppuccin_latte.rs @@ -110,7 +110,6 @@ pub fn theme() -> ThemeColors { success_fg: green, info_bg: surface0, info_fg: text, - event_rgb: (225, 215, 240), }, list: ListColors { playing_bg: Color::Rgb(210, 235, 220), diff --git a/crates/ratatui/src/theme/catppuccin_mocha.rs b/crates/ratatui/src/theme/catppuccin_mocha.rs index 67f8ded..2723200 100644 --- a/crates/ratatui/src/theme/catppuccin_mocha.rs +++ b/crates/ratatui/src/theme/catppuccin_mocha.rs @@ -110,7 +110,6 @@ pub fn theme() -> ThemeColors { success_fg: green, info_bg: surface0, info_fg: text, - event_rgb: (55, 45, 70), }, list: ListColors { playing_bg: Color::Rgb(35, 55, 45), diff --git a/crates/ratatui/src/theme/dracula.rs b/crates/ratatui/src/theme/dracula.rs index a029a9b..ac61abe 100644 --- a/crates/ratatui/src/theme/dracula.rs +++ b/crates/ratatui/src/theme/dracula.rs @@ -104,7 +104,6 @@ pub fn theme() -> ThemeColors { success_fg: green, info_bg: current_line, info_fg: foreground, - event_rgb: (70, 55, 85), }, list: ListColors { playing_bg: Color::Rgb(40, 65, 50), diff --git a/crates/ratatui/src/theme/fairyfloss.rs b/crates/ratatui/src/theme/fairyfloss.rs index 8f5b04d..f1e53ae 100644 --- a/crates/ratatui/src/theme/fairyfloss.rs +++ b/crates/ratatui/src/theme/fairyfloss.rs @@ -105,7 +105,6 @@ pub fn theme() -> ThemeColors { success_fg: mint, info_bg: bg_light, info_fg: fg, - event_rgb: (100, 85, 110), }, list: ListColors { playing_bg: Color::Rgb(65, 95, 85), diff --git a/crates/ratatui/src/theme/gruvbox_dark.rs b/crates/ratatui/src/theme/gruvbox_dark.rs index ea42740..4bbb6ff 100644 --- a/crates/ratatui/src/theme/gruvbox_dark.rs +++ b/crates/ratatui/src/theme/gruvbox_dark.rs @@ -106,7 +106,6 @@ pub fn theme() -> ThemeColors { success_fg: green, info_bg: bg1, info_fg: fg, - event_rgb: (70, 55, 45), }, list: ListColors { playing_bg: Color::Rgb(50, 65, 45), diff --git a/crates/ratatui/src/theme/hot_dog_stand.rs b/crates/ratatui/src/theme/hot_dog_stand.rs index 1cbdfa3..6a7fd6f 100644 --- a/crates/ratatui/src/theme/hot_dog_stand.rs +++ b/crates/ratatui/src/theme/hot_dog_stand.rs @@ -101,7 +101,6 @@ pub fn theme() -> ThemeColors { success_fg: black, info_bg: dark_red, info_fg: yellow, - event_rgb: (170, 100, 0), }, list: ListColors { playing_bg: Color::Rgb(180, 180, 0), diff --git a/crates/ratatui/src/theme/kanagawa.rs b/crates/ratatui/src/theme/kanagawa.rs index 18077a7..954c0d9 100644 --- a/crates/ratatui/src/theme/kanagawa.rs +++ b/crates/ratatui/src/theme/kanagawa.rs @@ -106,7 +106,6 @@ pub fn theme() -> ThemeColors { success_fg: autumn_green, info_bg: bg_light, info_fg: fg, - event_rgb: (50, 50, 60), }, list: ListColors { playing_bg: Color::Rgb(40, 55, 45), diff --git a/crates/ratatui/src/theme/mod.rs b/crates/ratatui/src/theme/mod.rs index 52c77c8..2721ce2 100644 --- a/crates/ratatui/src/theme/mod.rs +++ b/crates/ratatui/src/theme/mod.rs @@ -172,7 +172,6 @@ pub struct FlashColors { pub success_fg: Color, pub info_bg: Color, pub info_fg: Color, - pub event_rgb: (u8, u8, u8), } #[derive(Clone)] diff --git a/crates/ratatui/src/theme/monochrome_black.rs b/crates/ratatui/src/theme/monochrome_black.rs index d6ffd09..3849937 100644 --- a/crates/ratatui/src/theme/monochrome_black.rs +++ b/crates/ratatui/src/theme/monochrome_black.rs @@ -103,7 +103,6 @@ pub fn theme() -> ThemeColors { success_fg: bright, info_bg: surface, info_fg: fg, - event_rgb: (40, 40, 40), }, list: ListColors { playing_bg: Color::Rgb(50, 50, 50), diff --git a/crates/ratatui/src/theme/monochrome_white.rs b/crates/ratatui/src/theme/monochrome_white.rs index 384aecd..9604338 100644 --- a/crates/ratatui/src/theme/monochrome_white.rs +++ b/crates/ratatui/src/theme/monochrome_white.rs @@ -103,7 +103,6 @@ pub fn theme() -> ThemeColors { success_fg: dark, info_bg: surface, info_fg: fg, - event_rgb: (220, 220, 220), }, list: ListColors { playing_bg: Color::Rgb(200, 200, 200), diff --git a/crates/ratatui/src/theme/monokai.rs b/crates/ratatui/src/theme/monokai.rs index 788db3a..e761704 100644 --- a/crates/ratatui/src/theme/monokai.rs +++ b/crates/ratatui/src/theme/monokai.rs @@ -104,7 +104,6 @@ pub fn theme() -> ThemeColors { success_fg: green, info_bg: bg_light, info_fg: fg, - event_rgb: (70, 55, 70), }, list: ListColors { playing_bg: Color::Rgb(50, 70, 45), diff --git a/crates/ratatui/src/theme/nord.rs b/crates/ratatui/src/theme/nord.rs index feb713e..79fddbc 100644 --- a/crates/ratatui/src/theme/nord.rs +++ b/crates/ratatui/src/theme/nord.rs @@ -104,7 +104,6 @@ pub fn theme() -> ThemeColors { success_fg: aurora_green, info_bg: polar_night1, info_fg: snow_storm2, - event_rgb: (60, 55, 75), }, list: ListColors { playing_bg: Color::Rgb(50, 65, 55), diff --git a/crates/ratatui/src/theme/pitch_black.rs b/crates/ratatui/src/theme/pitch_black.rs index 7c1f951..2063592 100644 --- a/crates/ratatui/src/theme/pitch_black.rs +++ b/crates/ratatui/src/theme/pitch_black.rs @@ -105,7 +105,6 @@ pub fn theme() -> ThemeColors { success_fg: green, info_bg: surface, info_fg: fg, - event_rgb: (40, 30, 50), }, list: ListColors { playing_bg: Color::Rgb(15, 45, 25), diff --git a/crates/ratatui/src/theme/rose_pine.rs b/crates/ratatui/src/theme/rose_pine.rs index 30eea13..97218ff 100644 --- a/crates/ratatui/src/theme/rose_pine.rs +++ b/crates/ratatui/src/theme/rose_pine.rs @@ -105,7 +105,6 @@ pub fn theme() -> ThemeColors { success_fg: foam, info_bg: bg_light, info_fg: fg, - event_rgb: (50, 45, 60), }, list: ListColors { playing_bg: Color::Rgb(35, 55, 55), diff --git a/crates/ratatui/src/theme/tokyo_night.rs b/crates/ratatui/src/theme/tokyo_night.rs index e493c93..45c5ab0 100644 --- a/crates/ratatui/src/theme/tokyo_night.rs +++ b/crates/ratatui/src/theme/tokyo_night.rs @@ -105,7 +105,6 @@ pub fn theme() -> ThemeColors { success_fg: green, info_bg: bg_light, info_fg: fg, - event_rgb: (55, 50, 70), }, list: ListColors { playing_bg: Color::Rgb(45, 60, 45), diff --git a/crates/ratatui/src/theme/transform.rs b/crates/ratatui/src/theme/transform.rs index 1d92b95..c8bf34a 100644 --- a/crates/ratatui/src/theme/transform.rs +++ b/crates/ratatui/src/theme/transform.rs @@ -170,7 +170,6 @@ pub fn rotate_theme(theme: ThemeColors, degrees: f32) -> ThemeColors { success_fg: rotate_color(theme.flash.success_fg, degrees), info_bg: rotate_color(theme.flash.info_bg, degrees), info_fg: rotate_color(theme.flash.info_fg, degrees), - event_rgb: rotate_tuple(theme.flash.event_rgb, degrees), }, list: ListColors { playing_bg: rotate_color(theme.list.playing_bg, degrees), diff --git a/src/app.rs b/src/app.rs index ea506a6..0fd8110 100644 --- a/src/app.rs +++ b/src/app.rs @@ -110,7 +110,6 @@ impl App { show_scope: self.audio.config.show_scope, show_spectrum: self.audio.config.show_spectrum, show_completion: self.ui.show_completion, - flash_brightness: self.ui.flash_brightness, color_scheme: self.ui.color_scheme, layout: self.audio.config.layout, hue_rotation: self.ui.hue_rotation, @@ -1384,10 +1383,6 @@ impl App { .editor .set_completion_enabled(self.ui.show_completion); } - AppCommand::AdjustFlashBrightness(delta) => { - self.ui.flash_brightness = (self.ui.flash_brightness + delta).clamp(0.0, 1.0); - } - // Live keys AppCommand::ToggleLiveKeysFill => { self.live_keys.flip_fill(); diff --git a/src/bin/desktop.rs b/src/bin/desktop.rs index 02c2b7b..5978097 100644 --- a/src/bin/desktop.rs +++ b/src/bin/desktop.rs @@ -187,7 +187,6 @@ impl CagireDesktop { app.audio.config.show_scope = settings.display.show_scope; app.audio.config.show_spectrum = settings.display.show_spectrum; app.ui.show_completion = settings.display.show_completion; - app.ui.flash_brightness = settings.display.flash_brightness; let metrics = Arc::new(EngineMetrics::default()); let scope_buffer = Arc::new(ScopeBuffer::new()); @@ -419,17 +418,6 @@ impl eframe::App for CagireDesktop { self.app.metrics.event_count = seq_snapshot.event_count; self.app.metrics.dropped_events = seq_snapshot.dropped_events; - self.app.ui.event_flash = (self.app.ui.event_flash - 0.1).max(0.0); - let new_events = self - .app - .metrics - .event_count - .saturating_sub(self.app.ui.last_event_count); - if new_events > 0 { - self.app.ui.event_flash = (new_events as f32 * 0.4).min(1.0); - } - self.app.ui.last_event_count = self.app.metrics.event_count; - self.app.flush_queued_changes(&sequencer.cmd_tx); self.app.flush_dirty_patterns(&sequencer.cmd_tx); diff --git a/src/commands.rs b/src/commands.rs index 81aba47..d8bdb26 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -169,7 +169,6 @@ pub enum AppCommand { SetHueRotation(f32), ToggleRuntimeHighlight, ToggleCompletion, - AdjustFlashBrightness(f32), // Live keys ToggleLiveKeysFill, diff --git a/src/engine/sequencer.rs b/src/engine/sequencer.rs index ba415e3..a88baf6 100644 --- a/src/engine/sequencer.rs +++ b/src/engine/sequencer.rs @@ -7,7 +7,9 @@ use std::sync::atomic::{AtomicI64, AtomicU64}; use std::sync::Arc; use std::thread::{self, JoinHandle}; use std::time::Duration; -use thread_priority::{set_current_thread_priority, ThreadPriority}; +use thread_priority::ThreadPriority; +#[cfg(not(unix))] +use thread_priority::set_current_thread_priority; use super::LinkState; use crate::model::{ @@ -1286,7 +1288,25 @@ fn sequencer_loop( ) { use std::sync::atomic::Ordering; - let _ = set_current_thread_priority(ThreadPriority::Max); + #[cfg(unix)] + { + use thread_priority::unix::{ + set_thread_priority_and_policy, thread_native_id, RealtimeThreadSchedulePolicy, + ThreadSchedulePolicy, + }; + + let policy = ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Fifo); + if let Err(e) = + set_thread_priority_and_policy(thread_native_id(), ThreadPriority::Max, policy) + { + eprintln!("Warning: Could not set SCHED_FIFO: {e:?}"); + } + } + + #[cfg(not(unix))] + { + let _ = set_current_thread_priority(ThreadPriority::Max); + } let mut seq_state = SequencerState::new(variables, dict, rng, cc_access); diff --git a/src/input.rs b/src/input.rs index dcbfcf9..e4e00ec 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1443,10 +1443,6 @@ fn handle_options_page(ctx: &mut InputContext, key: KeyEvent) -> InputResult { OptionsFocus::ShowCompletion => { ctx.dispatch(AppCommand::ToggleCompletion); } - OptionsFocus::FlashBrightness => { - let delta = if key.code == KeyCode::Left { -0.1 } else { 0.1 }; - ctx.dispatch(AppCommand::AdjustFlashBrightness(delta)); - } OptionsFocus::LinkEnabled => ctx.link.set_enabled(!ctx.link.is_enabled()), OptionsFocus::StartStopSync => ctx .link diff --git a/src/main.rs b/src/main.rs index ee041f6..d0310d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -98,7 +98,6 @@ fn main() -> io::Result<()> { app.audio.config.show_scope = settings.display.show_scope; app.audio.config.show_spectrum = settings.display.show_spectrum; app.ui.show_completion = settings.display.show_completion; - app.ui.flash_brightness = settings.display.flash_brightness; app.ui.color_scheme = settings.display.color_scheme; app.ui.hue_rotation = settings.display.hue_rotation; app.audio.config.layout = settings.display.layout; @@ -327,16 +326,6 @@ fn main() -> io::Result<()> { app.metrics.event_count = seq_snapshot.event_count; app.metrics.dropped_events = seq_snapshot.dropped_events; - app.ui.event_flash = (app.ui.event_flash - 0.1).max(0.0); - let new_events = app - .metrics - .event_count - .saturating_sub(app.ui.last_event_count); - if new_events > 0 { - app.ui.event_flash = (new_events as f32 * 0.4).min(1.0); - } - app.ui.last_event_count = app.metrics.event_count; - app.flush_queued_changes(&sequencer.cmd_tx); app.flush_dirty_patterns(&sequencer.cmd_tx); diff --git a/src/settings.rs b/src/settings.rs index 22f9845..5cc53d9 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -44,8 +44,6 @@ pub struct DisplaySettings { pub show_spectrum: bool, #[serde(default = "default_true")] pub show_completion: bool, - #[serde(default = "default_flash_brightness")] - pub flash_brightness: f32, #[serde(default = "default_font")] pub font: String, #[serde(default)] @@ -60,8 +58,6 @@ fn default_font() -> String { "8x13".to_string() } -fn default_flash_brightness() -> f32 { 1.0 } - #[derive(Debug, Serialize, Deserialize)] pub struct LinkSettings { pub enabled: bool, @@ -92,7 +88,6 @@ impl Default for DisplaySettings { show_scope: true, show_spectrum: true, show_completion: true, - flash_brightness: 1.0, font: default_font(), color_scheme: ColorScheme::default(), layout: MainLayout::default(), diff --git a/src/state/options.rs b/src/state/options.rs index b6e3652..79452c1 100644 --- a/src/state/options.rs +++ b/src/state/options.rs @@ -10,7 +10,6 @@ pub enum OptionsFocus { ShowScope, ShowSpectrum, ShowCompletion, - FlashBrightness, LinkEnabled, StartStopSync, Quantum, @@ -33,7 +32,6 @@ impl CyclicEnum for OptionsFocus { Self::ShowScope, Self::ShowSpectrum, Self::ShowCompletion, - Self::FlashBrightness, Self::LinkEnabled, Self::StartStopSync, Self::Quantum, diff --git a/src/state/ui.rs b/src/state/ui.rs index eaa98f6..63556dc 100644 --- a/src/state/ui.rs +++ b/src/state/ui.rs @@ -46,9 +46,6 @@ pub struct UiState { pub runtime_highlight: bool, pub show_completion: bool, pub minimap_until: Option, - pub last_event_count: usize, - pub event_flash: f32, - pub flash_brightness: f32, pub color_scheme: ColorScheme, pub hue_rotation: f32, } @@ -75,9 +72,6 @@ impl Default for UiState { runtime_highlight: false, show_completion: true, minimap_until: None, - last_event_count: 0, - event_flash: 0.0, - flash_brightness: 1.0, color_scheme: ColorScheme::default(), hue_rotation: 0.0, } diff --git a/src/views/options_view.rs b/src/views/options_view.rs index 0d6b2f5..522a32c 100644 --- a/src/views/options_view.rs +++ b/src/views/options_view.rs @@ -63,7 +63,6 @@ pub fn render(frame: &mut Frame, app: &App, link: &LinkState, area: Rect) { Span::styled(peer_text, Style::new().fg(theme.ui.text_muted)), ]); - let flash_str = format!("{:.0}%", app.ui.flash_brightness * 100.0); let quantum_str = format!("{:.0}", link.quantum()); let tempo_str = format!("{:.1} BPM", link.tempo()); let beat_str = format!("{:.2}", link.beat()); @@ -161,7 +160,6 @@ pub fn render(frame: &mut Frame, app: &App, link: &LinkState, area: Rect) { focus == OptionsFocus::ShowCompletion, &theme, ), - render_option_line("Flash brightness", &flash_str, focus == OptionsFocus::FlashBrightness, &theme), Line::from(""), link_header, render_divider(content_width, &theme), @@ -215,18 +213,17 @@ pub fn render(frame: &mut Frame, app: &App, link: &LinkState, area: Rect) { OptionsFocus::ShowScope => 6, OptionsFocus::ShowSpectrum => 7, OptionsFocus::ShowCompletion => 8, - OptionsFocus::FlashBrightness => 9, - OptionsFocus::LinkEnabled => 13, - OptionsFocus::StartStopSync => 14, - OptionsFocus::Quantum => 15, - OptionsFocus::MidiOutput0 => 26, - OptionsFocus::MidiOutput1 => 27, - OptionsFocus::MidiOutput2 => 28, - OptionsFocus::MidiOutput3 => 29, - OptionsFocus::MidiInput0 => 33, - OptionsFocus::MidiInput1 => 34, - OptionsFocus::MidiInput2 => 35, - OptionsFocus::MidiInput3 => 36, + OptionsFocus::LinkEnabled => 12, + OptionsFocus::StartStopSync => 13, + OptionsFocus::Quantum => 14, + OptionsFocus::MidiOutput0 => 25, + OptionsFocus::MidiOutput1 => 26, + OptionsFocus::MidiOutput2 => 27, + OptionsFocus::MidiOutput3 => 28, + OptionsFocus::MidiInput0 => 32, + OptionsFocus::MidiInput1 => 33, + OptionsFocus::MidiInput2 => 34, + OptionsFocus::MidiInput3 => 35, }; let scroll_offset = if total_lines <= max_visible { diff --git a/src/views/render.rs b/src/views/render.rs index 7106093..6471923 100644 --- a/src/views/render.rs +++ b/src/views/render.rs @@ -7,7 +7,7 @@ use std::time::Instant; use rand::rngs::StdRng; use rand::SeedableRng; use ratatui::layout::{Alignment, Constraint, Layout, Rect}; -use ratatui::style::{Color, Modifier, Style}; +use ratatui::style::{Modifier, Style}; use ratatui::text::{Line, Span}; use ratatui::widgets::{Block, Borders, Cell, Clear, Paragraph, Row, Table}; use ratatui::Frame; @@ -150,17 +150,7 @@ pub fn render(frame: &mut Frame, app: &App, link: &LinkState, snapshot: &Sequenc let term = frame.area(); let theme = theme::get(); - let bg_color = if app.ui.event_flash > 0.0 { - let t = (app.ui.event_flash * app.ui.flash_brightness).min(1.0); - let (base_r, base_g, base_b) = theme.ui.bg_rgb; - let (tgt_r, tgt_g, tgt_b) = theme.flash.event_rgb; - let r = base_r + ((tgt_r as f32 - base_r as f32) * t) as u8; - let g = base_g + ((tgt_g as f32 - base_g as f32) * t) as u8; - let b = base_b + ((tgt_b as f32 - base_b as f32) * t) as u8; - Color::Rgb(r, g, b) - } else { - theme.ui.bg - }; + let bg_color = theme.ui.bg; let blank = " ".repeat(term.width as usize); let lines: Vec = (0..term.height).map(|_| Line::raw(&blank)).collect();