use crate::*; use crate::metronome::Metronome; pub struct ProcessHandler { track: Track, playback_position: usize, pub ports: JackPorts, chunk_factory: F, metronome: Metronome, } impl ProcessHandler { pub fn new(ports: JackPorts, mut chunk_factory: F, beep_samples: Arc) -> Result { Ok(Self { track: Track::new(&mut chunk_factory)?, playback_position: 0, ports, chunk_factory, metronome: Metronome::new(beep_samples), }) } /// Handle record/play toggle button (Button 1) pub fn record_toggle(&mut self) -> Result<()> { match self.track.state() { TrackState::Idle => { // Clear previous recording and start new recording self.track.clear(&mut self.chunk_factory)?; self.playback_position = 0; self.track.set_state(TrackState::Recording); } TrackState::Recording => { self.track.set_state(TrackState::Playing); self.playback_position = 0; } TrackState::Playing => { self.track.set_state(TrackState::Idle); } } Ok(()) } /// Handle play/mute toggle button (Button 2) pub fn play_toggle(&mut self) -> Result<()> { // Trigger beep for testing self.metronome.trigger_beep(); match self.track.state() { TrackState::Idle | TrackState::Recording => { if self.track.len() > 0 { self.track.set_state(TrackState::Playing); self.playback_position = 0; } } TrackState::Playing => { self.track.set_state(TrackState::Idle); } } Ok(()) } } impl jack::ProcessHandler for ProcessHandler { fn process(&mut self, client: &jack::Client, ps: &jack::ProcessScope) -> jack::Control { // Process MIDI first if midi::process_events(self, ps).is_err() { return jack::Control::Quit; } let input_buffer = self.ports.audio_in.as_slice(ps); let output_buffer = self.ports.audio_out.as_mut_slice(ps); let click_track_buffer = self.ports.click_track_out.as_mut_slice(ps); let jack_buffer_size = client.buffer_size() as usize; // Process metronome/click track if self.metronome.process_audio(click_track_buffer).is_err() { return jack::Control::Quit; } let mut index = 0; while index < jack_buffer_size { match self.track.state() { TrackState::Recording => { // Recording - continue until manually stopped let sample_count_to_append = jack_buffer_size - index; let samples_to_append = &input_buffer[index..index + sample_count_to_append]; if self .track .append_samples(samples_to_append, &mut self.chunk_factory) .is_err() { return jack::Control::Quit; }; output_buffer[index..(index + sample_count_to_append)].fill(0.0); index += sample_count_to_append; } TrackState::Playing => { // Playback let sample_count_to_play = jack_buffer_size - index; let sample_count_to_play = sample_count_to_play.min(self.track.len() - self.playback_position); if self .track .copy_samples( &mut output_buffer[index..(index + sample_count_to_play)], self.playback_position, ) .is_err() { return jack::Control::Quit; } index += sample_count_to_play; self.playback_position += sample_count_to_play; if self.playback_position >= self.track.len() { self.playback_position = 0; } } TrackState::Idle => { // Idle - output silence output_buffer[index..jack_buffer_size].fill(0.0); break; } } } jack::Control::Continue } }