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 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 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, pub audio_out: jack::Port, pub click_track_out: jack::Port, pub midi_in: jack::Port, pub midi_out: jack::Port, } #[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::::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) }