use std::ops::{AddAssign, IndexMut};
use crate::audio_processing::playback::{PlaybackState, PlaybackStatus};
use crate::audio_processing::sample::Interpolation;
use crate::audio_processing::sample::SamplePlayer;
use crate::audio_processing::Frame;
use crate::manager::{OutputConfig, ToWorkerMsg};
use crate::project::song::Song;
use crate::sample::Sample;
use dasp::sample::ToSample;
use simple_left_right::Reader;
pub(crate) struct LiveAudio {
song: Reader<Song>,
playback_state: Option<PlaybackState>,
live_note: Option<SamplePlayer>,
manager: rtrb::Consumer<ToWorkerMsg>,
state_sender: triple_buffer::Input<Option<PlaybackStatus>>,
config: OutputConfig,
buffer: Box<[Frame]>,
}
const INTERPOLATION: u8 = Interpolation::Linear as u8;
impl LiveAudio {
pub fn new(
song: Reader<Song>,
manager: rtrb::Consumer<ToWorkerMsg>,
state_sender: triple_buffer::Input<Option<PlaybackStatus>>,
config: OutputConfig,
) -> Self {
Self {
song,
playback_state: None,
live_note: None,
manager,
state_sender,
config,
buffer: vec![Frame::default(); usize::try_from(config.buffer_size).unwrap() * 2].into(),
}
}
#[rtsan_standalone::nonblocking]
fn send_state(&mut self) {
self.state_sender
.write(self.playback_state.as_ref().map(|s| s.get_status()));
}
#[rtsan_standalone::nonblocking]
fn fill_internal_buffer(&mut self, len: usize) -> bool {
let buffer = &mut self.buffer[..len];
let song = self.song.lock();
while let Ok(event) = self.manager.pop() {
match event {
ToWorkerMsg::StopPlayback => self.playback_state = None,
ToWorkerMsg::Playback(settings) => {
self.playback_state =
PlaybackState::new(&song, self.config.sample_rate, settings);
}
ToWorkerMsg::PlayEvent(note) => {
if let Some(sample) = &song.samples[usize::from(note.sample_instr)] {
let sample_player = SamplePlayer::new(
Sample::clone(&sample.1),
sample.0,
self.config.sample_rate,
note.note,
);
self.live_note = Some(sample_player);
}
}
ToWorkerMsg::StopLiveNote => self.live_note = None,
}
}
if self.live_note.is_none() && self.playback_state.is_none() {
return false;
}
buffer.fill(Frame::default());
if let Some(live_note) = &mut self.live_note {
let note_iter = live_note.iter::<{ INTERPOLATION }>();
buffer
.iter_mut()
.zip(note_iter)
.for_each(|(buf, note)| buf.add_assign(note));
if live_note.check_position().is_break() {
self.live_note = None;
}
}
if let Some(playback) = &mut self.playback_state {
let playback_iter = playback.iter::<{ INTERPOLATION }>(&song);
buffer
.iter_mut()
.zip(playback_iter)
.for_each(|(buf, frame)| buf.add_assign(frame));
if playback.is_done() {
self.playback_state = None;
}
}
true
}
#[rtsan_standalone::nonblocking]
#[inline]
fn fill_from_internal<Sample: dasp::sample::Sample + dasp::sample::FromSample<f32>>(
&mut self,
data: &mut [Sample],
) {
if self.config.channel_count.get() == 1 {
data.iter_mut()
.zip(self.buffer.iter())
.for_each(|(out, buf)| *out = buf.sum_to_mono().to_sample_());
} else {
data.chunks_exact_mut(usize::from(self.config.channel_count.get()))
.map(|frame| frame.split_first_chunk_mut::<2>().unwrap().0)
.zip(self.buffer.iter())
.for_each(|(out, buf)| *out = buf.to_sample());
}
}
pub fn get_typed_callback<Sample: dasp::sample::Sample + dasp::sample::FromSample<f32>>(
mut self,
) -> impl FnMut(&mut [Sample]) {
move |data| {
let channel_count = usize::from(self.config.channel_count.get());
assert!(data.len().is_multiple_of(channel_count));
let out_frames = data.len() / channel_count;
assert!(self.buffer.len() > out_frames);
if self.fill_internal_buffer(out_frames) {
self.fill_from_internal(data);
}
self.send_state();
}
}
}
#[allow(dead_code)]
fn sine(output: &mut [[f32; 2]], sample_rate: f32) {
let mut sample_clock = 0f32;
for frame in output {
sample_clock = (sample_clock + 1.) % sample_rate;
let value = (sample_clock * 440. * 2. * std::f32::consts::PI / sample_rate).sin();
*frame.index_mut(0) = value;
*frame.index_mut(1) = value;
}
}