2025-08-05 12:03:26 +02:00

196 lines
6.0 KiB
Rust

mod allocator;
mod args;
mod audio_chunk;
mod audio_data;
mod beep;
mod chunk_factory;
mod column;
mod connection_manager;
mod controllers;
mod looper_error;
mod metronome;
mod midi;
mod notification_handler;
mod osc_server;
mod persistence_manager;
mod post_record_handler;
mod process_handler;
mod state;
mod tap_tempo;
mod track;
mod track_matrix;
use std::sync::Arc;
use allocator::Allocator;
use args::Args;
use audio_chunk::AudioChunk;
use audio_data::AudioData;
use beep::generate_beep;
use chunk_factory::ChunkFactory;
use column::Column;
use connection_manager::ConnectionManager;
use controllers::ColumnControllers;
use controllers::MatrixControllers;
use controllers::TrackControllers;
use looper_error::LooperError;
use looper_error::Result;
use metronome::BufferTiming;
use metronome::Metronome;
use notification_handler::JackNotification;
use notification_handler::NotificationHandler;
use osc_server::Osc;
use osc_server::OscController;
use persistence_manager::PersistenceManager;
use persistence_manager::PersistenceManagerController;
use post_record_handler::PostRecordController;
use post_record_handler::PostRecordHandler;
use post_record_handler::PostRecordResponse;
use process_handler::ProcessHandler;
use state::State;
use tap_tempo::TapTempo;
use track::Track;
use track::TrackState;
use track_matrix::TrackMatrix;
const COLS: usize = 5;
const ROWS: usize = 5;
pub struct JackPorts {
pub audio_in: jack::Port<jack::AudioIn>,
pub audio_out: jack::Port<jack::AudioOut>,
pub click_track_out: jack::Port<jack::AudioOut>,
pub midi_in: jack::Port<jack::MidiIn>,
pub midi_out: jack::Port<jack::MidiOut>,
}
#[tokio::main]
async fn main() {
let args = Args::new().expect("Could not parse arguments");
simple_logger::SimpleLogger::new()
.init()
.expect("Could not initialize logger");
let (jack_client, ports) = setup_jack();
let mut allocator = Allocator::spawn(jack_client.sample_rate(), 3);
let beep_samples = generate_beep(jack_client.sample_rate() as u32, &mut allocator)
.expect("Could not generate beep samples");
let notification_handler = NotificationHandler::new();
let mut notification_channel = notification_handler.subscribe();
let (mut persistence_manager, persistence_controller) =
PersistenceManager::new(&args, notification_handler.subscribe());
let state = persistence_manager.state();
// Calculate tempo from state and jack client
let tempo_bpm =
(jack_client.sample_rate() as f32 * 60.0) / state.borrow().metronome.frames_per_beat as f32;
let (mut osc, osc_controller) = Osc::<COLS, ROWS>::new(&args.socket, tempo_bpm)
.await
.expect("Could not create OSC server");
// Create post-record handler and get controller for ProcessHandler
let (mut post_record_handler, post_record_controller) =
PostRecordHandler::new(&args).expect("Could not create post-record handler");
let process_handler = ProcessHandler::<_, COLS, ROWS>::new(
&jack_client,
ports,
allocator,
beep_samples,
&state.borrow(),
post_record_controller,
osc_controller,
persistence_controller,
)
.expect("Could not create process handler");
let mut connection_manager = ConnectionManager::new(
persistence_manager.state(),
notification_handler.subscribe(),
jack_client.name().to_string(),
)
.expect("Could not create connection manager");
let _active_client = jack_client
.activate_async(notification_handler, process_handler)
.expect("Could not activate Jack");
loop {
tokio::select! {
result = osc.run() => {
if let Err(e) = result {
log::error!("OSC task failed: {}", e);
}
break;
}
notification = notification_channel.recv() => {
if let Ok(JackNotification::Shutdown {reason, status}) = notification {
log::error!("Jack shutdown: {reason} {status:?}");
break;
}
}
result = persistence_manager.run() => {
if let Err(e) = result {
log::error!("StateManager task failed: {}", e);
}
break;
}
result = connection_manager.run() => {
if let Err(e) = result {
log::error!("ConnectionManager task failed: {}", e);
}
break;
}
result = post_record_handler.run() => {
if let Err(e) = result {
log::error!("PostRecordHandler task failed: {}", e);
}
break;
}
_ = tokio::signal::ctrl_c() => {
log::info!("Stopping");
break;
}
}
}
}
fn setup_jack() -> (jack::Client, JackPorts) {
let (jack_client, jack_status) =
jack::Client::new("looper", jack::ClientOptions::NO_START_SERVER)
.expect("Could not create Jack client");
if !jack_status.is_empty() {
panic!("Could not start jack client: {jack_status:?}");
}
let audio_in = jack_client
.register_port("audio_in", jack::AudioIn::default())
.expect("Could not create audio_in port");
let audio_out = jack_client
.register_port("audio_out", jack::AudioOut::default())
.expect("Could not create audio_out port");
let click_track_out = jack_client
.register_port("click_track", jack::AudioOut::default())
.expect("Could not create click_track_out port");
let midi_in = jack_client
.register_port("midi_in", jack::MidiIn::default())
.expect("Could not create midi_in port");
let midi_out = jack_client
.register_port("midi_out", jack::MidiOut::default())
.expect("Could not create midi_out port");
let ports = JackPorts {
audio_in,
audio_out,
click_track_out,
midi_in,
midi_out,
};
(jack_client, ports)
}