Ungoing refactoring

This commit is contained in:
2026-02-04 18:47:40 +01:00
parent 2097997372
commit 6cf9d2eec1
19 changed files with 749 additions and 451 deletions

View File

@@ -1,4 +1,5 @@
mod app;
mod init;
mod commands;
mod engine;
mod input;
@@ -14,7 +15,7 @@ mod widgets;
use std::io;
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU32, AtomicU64, Ordering};
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::{Duration, Instant};
@@ -24,18 +25,12 @@ use crossterm::terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
};
use crossterm::ExecutableCommand;
use doux::EngineMetrics;
use ratatui::prelude::CrosstermBackend;
use ratatui::Terminal;
use app::App;
use engine::{
build_stream, spawn_sequencer, AudioStreamConfig, LinkState, ScopeBuffer, SequencerConfig,
SpectrumBuffer,
};
use engine::{build_stream, AudioStreamConfig};
use init::InitArgs;
use input::{handle_key, InputContext, InputResult};
use settings::Settings;
use state::audio::RefreshRate;
#[derive(Parser)]
#[command(name = "cagire", version, about = "Forth-based live coding sequencer")]
@@ -62,149 +57,33 @@ struct Args {
}
fn main() -> io::Result<()> {
// Lock memory BEFORE any threads are spawned to prevent page faults in RT context
#[cfg(unix)]
engine::realtime::lock_memory();
let args = Args::parse();
let settings = Settings::load();
let link = Arc::new(LinkState::new(settings.link.tempo, settings.link.quantum));
if settings.link.enabled {
link.enable();
}
let b = init::init(InitArgs {
samples: args.samples,
output: args.output,
input: args.input,
channels: args.channels,
buffer: args.buffer,
});
let playing = Arc::new(AtomicBool::new(true));
let nudge_us = Arc::new(AtomicI64::new(0));
let mut app = App::new();
app.playback
.queued_changes
.push(crate::state::StagedChange {
change: engine::PatternChange::Start {
bank: 0,
pattern: 0,
},
quantization: crate::model::LaunchQuantization::Immediate,
sync_mode: crate::model::SyncMode::Reset,
});
app.audio.config.output_device = args.output.or(settings.audio.output_device);
app.audio.config.input_device = args.input.or(settings.audio.input_device);
app.audio.config.channels = args.channels.unwrap_or(settings.audio.channels);
app.audio.config.buffer_size = args.buffer.unwrap_or(settings.audio.buffer_size);
app.audio.config.max_voices = settings.audio.max_voices;
app.audio.config.lookahead_ms = settings.audio.lookahead_ms;
app.audio.config.sample_paths = args.samples;
app.audio.config.refresh_rate = RefreshRate::from_fps(settings.display.fps);
app.ui.runtime_highlight = settings.display.runtime_highlight;
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.color_scheme = settings.display.color_scheme;
app.ui.hue_rotation = settings.display.hue_rotation;
app.audio.config.layout = settings.display.layout;
let base_theme = settings.display.color_scheme.to_theme();
let rotated =
cagire_ratatui::theme::transform::rotate_theme(base_theme, settings.display.hue_rotation);
theme::set(rotated);
// Load MIDI settings
let outputs = midi::list_midi_outputs();
let inputs = midi::list_midi_inputs();
for (slot, name) in settings.midi.output_devices.iter().enumerate() {
if !name.is_empty() {
if let Some(idx) = outputs.iter().position(|d| &d.name == name) {
let _ = app.midi.connect_output(slot, idx);
}
}
}
for (slot, name) in settings.midi.input_devices.iter().enumerate() {
if !name.is_empty() {
if let Some(idx) = inputs.iter().position(|d| &d.name == name) {
let _ = app.midi.connect_input(slot, idx);
}
}
}
let metrics = Arc::new(EngineMetrics::default());
let scope_buffer = Arc::new(ScopeBuffer::new());
let spectrum_buffer = Arc::new(SpectrumBuffer::new());
let audio_sample_pos = Arc::new(AtomicU64::new(0));
let sample_rate_shared = Arc::new(AtomicU32::new(44100));
let lookahead_ms = Arc::new(AtomicU32::new(settings.audio.lookahead_ms));
let mut initial_samples = Vec::new();
for path in &app.audio.config.sample_paths {
let index = doux::sampling::scan_samples_dir(path);
app.audio.config.sample_count += index.len();
initial_samples.extend(index);
}
#[cfg(feature = "desktop")]
let mouse_x = Arc::new(AtomicU32::new(0.5_f32.to_bits()));
#[cfg(feature = "desktop")]
let mouse_y = Arc::new(AtomicU32::new(0.5_f32.to_bits()));
#[cfg(feature = "desktop")]
let mouse_down = Arc::new(AtomicU32::new(0.0_f32.to_bits()));
let seq_config = SequencerConfig {
audio_sample_pos: Arc::clone(&audio_sample_pos),
sample_rate: Arc::clone(&sample_rate_shared),
lookahead_ms: Arc::clone(&lookahead_ms),
cc_access: Some(Arc::new(app.midi.cc_memory.clone()) as Arc<dyn crate::model::CcAccess>),
#[cfg(feature = "desktop")]
mouse_x: Arc::clone(&mouse_x),
#[cfg(feature = "desktop")]
mouse_y: Arc::clone(&mouse_y),
#[cfg(feature = "desktop")]
mouse_down: Arc::clone(&mouse_down),
};
let (sequencer, initial_audio_rx, mut midi_rx) = spawn_sequencer(
Arc::clone(&link),
Arc::clone(&playing),
Arc::clone(&app.variables),
Arc::clone(&app.dict),
Arc::clone(&app.rng),
settings.link.quantum,
Arc::clone(&app.live_keys),
Arc::clone(&nudge_us),
seq_config,
);
let stream_config = AudioStreamConfig {
output_device: app.audio.config.output_device.clone(),
channels: app.audio.config.channels,
buffer_size: app.audio.config.buffer_size,
max_voices: app.audio.config.max_voices,
};
let (mut _stream, mut _analysis_handle) = match build_stream(
&stream_config,
initial_audio_rx,
Arc::clone(&scope_buffer),
Arc::clone(&spectrum_buffer),
Arc::clone(&metrics),
initial_samples,
Arc::clone(&audio_sample_pos),
) {
Ok((s, info, analysis)) => {
app.audio.config.sample_rate = info.sample_rate;
app.audio.config.host_name = info.host_name;
app.audio.config.channels = info.channels;
sample_rate_shared.store(info.sample_rate as u32, Ordering::Relaxed);
(Some(s), Some(analysis))
}
Err(e) => {
app.ui.set_status(format!("Audio failed: {e}"));
app.audio.error = Some(e);
(None, None)
}
};
app.mark_all_patterns_dirty();
let mut app = b.app;
let link = b.link;
let sequencer = b.sequencer;
let playing = b.playing;
let nudge_us = b.nudge_us;
let lookahead_ms = b.lookahead_ms;
let metrics = b.metrics;
let scope_buffer = b.scope_buffer;
let spectrum_buffer = b.spectrum_buffer;
let audio_sample_pos = b.audio_sample_pos;
let sample_rate_shared = b.sample_rate_shared;
let mut _stream = b.stream;
let mut _analysis_handle = b.analysis_handle;
let mut midi_rx = b.midi_rx;
enable_raw_mode()?;
io::stdout().execute(EnableBracketedPaste)?;
@@ -268,7 +147,6 @@ fn main() -> io::Result<()> {
app.playback.playing = playing.load(Ordering::Relaxed);
// Process pending MIDI commands
while let Ok(midi_cmd) = midi_rx.try_recv() {
match midi_cmd {
engine::MidiCommand::NoteOn {
@@ -335,7 +213,6 @@ fn main() -> io::Result<()> {
let seq_snapshot = sequencer.snapshot();
app.metrics.event_count = seq_snapshot.event_count;
app.metrics.dropped_events = seq_snapshot.dropped_events;
app.flush_queued_changes(&sequencer.cmd_tx);
app.flush_dirty_patterns(&sequencer.cmd_tx);