fcb_looper/audio_engine/src/process_handler.rs
2025-08-05 12:03:26 +02:00

211 lines
6.4 KiB
Rust

use crate::*;
pub struct ProcessHandler<F: ChunkFactory, const COLS: usize, const ROWS: usize> {
post_record_controller: PostRecordController,
osc: OscController,
persistence: PersistenceManagerController,
ports: JackPorts,
metronome: Metronome,
track_matrix: TrackMatrix<F, COLS, ROWS>,
selected_row: usize,
selected_column: usize,
last_volume_setting: f32,
sample_rate: usize,
tap_tempo: TapTempo,
}
impl<F: ChunkFactory, const COLS: usize, const ROWS: usize> ProcessHandler<F, COLS, ROWS> {
pub fn new(
client: &jack::Client,
ports: JackPorts,
chunk_factory: F,
beep_samples: Arc<AudioChunk>,
state: &State,
post_record_controller: PostRecordController,
osc: OscController,
persistence: PersistenceManagerController,
) -> Result<Self> {
let track_matrix = TrackMatrix::new(client, chunk_factory, state)?;
Ok(Self {
post_record_controller,
osc,
persistence,
ports,
metronome: Metronome::new(beep_samples, state),
track_matrix,
selected_row: 0,
selected_column: 0,
last_volume_setting: 1.0,
sample_rate: client.sample_rate(),
tap_tempo: TapTempo::new(client.sample_rate()),
})
}
pub fn ports(&self) -> &JackPorts {
&self.ports
}
pub fn handle_pedal_a(&mut self, value: u8) -> Result<()> {
self.last_volume_setting = (value as f32) / 127.0;
let controllers = TrackControllers::new(
&self.post_record_controller,
&self.osc,
&self.persistence,
self.sample_rate,
self.selected_column,
self.selected_row,
);
self.track_matrix
.handle_volume_update(self.last_volume_setting, &controllers)
}
pub fn handle_pedal_b(&mut self, value: u8) -> Result<()> {
self.metronome
.set_click_volume(value as f32 / 127.0, &mut self.persistence)
}
pub fn handle_button_1(&mut self) -> Result<()> {
let controllers = TrackControllers::new(
&self.post_record_controller,
&self.osc,
&self.persistence,
self.sample_rate,
self.selected_column,
self.selected_row,
);
self.track_matrix
.handle_record_button(self.last_volume_setting, &controllers)
}
pub fn handle_button_2(&mut self) -> Result<()> {
let controllers = TrackControllers::new(
&self.post_record_controller,
&self.osc,
&self.persistence,
self.sample_rate,
self.selected_column,
self.selected_row,
);
self.track_matrix.handle_play_button(&controllers)
}
pub fn handle_button_3(&mut self) -> Result<()> {
Ok(())
}
pub fn handle_button_4(&mut self) -> Result<()> {
let controllers = TrackControllers::new(
&self.post_record_controller,
&self.osc,
&self.persistence,
self.sample_rate,
self.selected_column,
self.selected_row,
);
self.track_matrix.handle_clear_button(&controllers)
}
pub fn handle_button_5(&mut self, ps: &jack::ProcessScope) -> Result<()> {
if !self.track_matrix.is_all_tracks_cleared() {
// Ignore tap if not all tracks are clear
return Ok(());
}
let current_frame_time = ps.last_frame_time();
let tempo_updated = self.tap_tempo.handle_tap(current_frame_time);
if let Some(frames_per_beat) = tempo_updated {
// If tempo was updated, we need to update all columns with new frames_per_beat
self.metronome.set_frames_per_beat(frames_per_beat);
self.persistence
.update_metronome_frames_per_beat(frames_per_beat)?;
self.track_matrix.set_frames_per_beat(frames_per_beat);
}
Ok(())
}
pub fn handle_button_6(&mut self) -> Result<()> {
self.selected_column = 0;
self.osc.selected_column_changed(self.selected_column)?;
Ok(())
}
pub fn handle_button_7(&mut self) -> Result<()> {
self.selected_column = 1;
self.osc.selected_column_changed(self.selected_column)?;
Ok(())
}
pub fn handle_button_8(&mut self) -> Result<()> {
self.selected_column = 2;
self.osc.selected_column_changed(self.selected_column)?;
Ok(())
}
pub fn handle_button_9(&mut self) -> Result<()> {
self.selected_column = 3;
self.osc.selected_column_changed(self.selected_column)?;
Ok(())
}
pub fn handle_button_10(&mut self) -> Result<()> {
self.selected_column = 4;
self.osc.selected_column_changed(self.selected_column)?;
Ok(())
}
pub fn handle_button_up(&mut self) -> Result<()> {
if self.selected_row == 0 {
self.selected_row = ROWS - 1;
} else {
self.selected_row -= 1;
}
self.osc.selected_row_changed(self.selected_row)?;
Ok(())
}
pub fn handle_button_down(&mut self) -> Result<()> {
self.selected_row = (self.selected_row + 1) % ROWS;
self.osc.selected_row_changed(self.selected_row)?;
Ok(())
}
}
impl<F: ChunkFactory, const COLS: usize, const ROWS: usize> jack::ProcessHandler
for ProcessHandler<F, COLS, ROWS>
{
fn process(&mut self, _client: &jack::Client, ps: &jack::ProcessScope) -> jack::Control {
if let Err(e) = self.process_with_error_handling(ps) {
log::error!("Error processing audio: {}", e);
jack::Control::Quit
} else {
jack::Control::Continue
}
}
}
impl<F: ChunkFactory, const COLS: usize, const ROWS: usize> ProcessHandler<F, COLS, ROWS> {
fn process_with_error_handling(&mut self, ps: &jack::ProcessScope) -> Result<()> {
// Process metronome and get beat timing information
let timing = self.metronome.process(ps, &mut self.ports, &self.osc)?;
// Process MIDI
midi::process_events(self, ps)?;
// Process audio
let controllers = MatrixControllers::new(
&self.post_record_controller,
&self.osc,
&self.persistence,
self.sample_rate,
);
self.track_matrix
.process(ps, &mut self.ports, &timing, &controllers)?;
Ok(())
}
}