211 lines
6.4 KiB
Rust
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(())
|
|
}
|
|
}
|