130 lines
4.6 KiB
Rust
130 lines
4.6 KiB
Rust
use crate::*;
|
|
use crate::metronome::Metronome;
|
|
|
|
pub struct ProcessHandler<F: ChunkFactory> {
|
|
track: Track,
|
|
playback_position: usize,
|
|
pub ports: JackPorts,
|
|
chunk_factory: F,
|
|
metronome: Metronome,
|
|
}
|
|
|
|
impl<F: ChunkFactory> ProcessHandler<F> {
|
|
pub fn new(ports: JackPorts, mut chunk_factory: F, beep_samples: Arc<AudioChunk>) -> Result<Self> {
|
|
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<F: ChunkFactory> jack::ProcessHandler for ProcessHandler<F> {
|
|
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
|
|
}
|
|
}
|