196 lines
6.0 KiB
Rust
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)
|
|
}
|