use super::*;
use crate::{console::*, *};
use std::sync::{Arc, Mutex};
use std::{thread, time::{Instant,Duration}};
use alsa::{
pcm::{Access, Format},
Output, ValueOr,
};
#[derive(Clone, Default)]
pub struct FlufflAudioContext {
}
#[derive(Copy, Clone, PartialEq, Eq)]
enum DeviceState {
Playing,
Paused,
}
pub struct FlufflAudioDeviceContext<Callback, State>
where
Callback: FnMut(&mut State, &mut [f32]) + Copy + Send + 'static,
State: Send + 'static,
{
fluffl_audio_device: Arc<Mutex<FlufflAudioDevice<Callback, State>>>,
pcm: Arc<Mutex<alsa::pcm::PCM>>,
state: Arc<Mutex<DeviceState>>,
}
impl<Callback, State> Clone for FlufflAudioDeviceContext<Callback, State>
where
Callback: FnMut(&mut State, &mut [f32]) + Copy + Send,
State: Send,
{
fn clone(&self) -> Self {
Self {
fluffl_audio_device: self.fluffl_audio_device.clone(),
pcm: self.pcm.clone(),
state: self.state.clone(),
}
}
}
impl<Callback, State> FlufflAudioDeviceContext<Callback, State>
where
Callback: FnMut(&mut State, &mut [f32]) + Copy + Send,
State: Send,
{
pub fn new(core: AudioDeviceCore<Callback, State>, _actx: FlufflAudioContext) -> Self {
let audio_device = Arc::new(Mutex::new(FlufflAudioDevice { core }));
let pcm = alsa::pcm::PCM::new("default", alsa::Direction::Playback, true)
.expect("alsa: default device failed");
Self {
fluffl_audio_device: audio_device,
pcm: Arc::new(Mutex::new(pcm)),
state: Arc::new(Mutex::new(DeviceState::Paused)),
}
}
pub fn modify_state<ModifyCallback>(&self, mut cb: ModifyCallback)
where
ModifyCallback: FnMut(Option<&mut State>) -> Option<()>,
{
let mut lck = self.fluffl_audio_device.lock().unwrap();
let device = &mut *lck;
let s = device.core.state.as_mut();
let _ = cb(s);
}
pub fn resume(&self) {
let ctx = self.clone();
let audio_device = self.fluffl_audio_device.clone();
if *ctx.state.lock().unwrap() == DeviceState::Playing {
println!("already playing!");
return;
}
*ctx.state.lock().unwrap() = DeviceState::Playing;
thread::spawn(move || {
let ConcreteSpecs {
sample_rate,
channels,
buffer_size,
} = audio_device
.lock()
.unwrap()
.core
.desired_specs
.make_concrete();
let pcm_ptr = { &*ctx.pcm.lock().unwrap() } as *const _;
let pcm = unsafe { &*pcm_ptr };
let hwp = alsa::pcm::HwParams::any(pcm).expect("hw params failed");
hwp.set_channels(channels as u32)
.expect("set_channels(..) failed");
hwp.set_rate(sample_rate as u32, ValueOr::Nearest)
.expect("set_format(..) failed");
hwp.set_format(Format::float())
.expect("set_format(..) failed");
hwp.set_access(Access::RWInterleaved)
.expect("set_access(..) failed");
pcm.hw_params(&hwp).unwrap();
let hwp = pcm.hw_params_current().unwrap();
let swp = pcm.sw_params_current().unwrap();
swp.set_start_threshold(hwp.get_buffer_size().unwrap())
.unwrap();
pcm.sw_params(&swp).unwrap();
console_log!(
"PCM status: {:?}, {:?}",
pcm.state(),
pcm.hw_params_current().unwrap()
);
let mut outp = Output::buffer_open().unwrap();
pcm.dump(&mut outp).unwrap();
console_log!("== PCM dump ==\n{}", outp);
let mut buffer = vec![0.0f32; channels * buffer_size];
let audio_device = audio_device;
let mut frames_written = 0u128;
const NANOS_IN_ONE_SEC: u128 = 1_000_000_000;
let frequency = sample_rate as u128;
let buffer_size = buffer_size as u128;
let calc_elasped_time_nanos = |frames, frequency| {
(frames * NANOS_IN_ONE_SEC) / frequency
};
let io = pcm.io_f32().unwrap();
let real_time = Instant::now();
if pcm.state() != alsa::pcm::State::Running {
pcm.start().unwrap();
}
const BUFFER_DELTA_IN_NANOS: u128 = NANOS_IN_ONE_SEC / 10;
let mut is_sleep_requested = None;
loop {
if let Some(excess_time) = is_sleep_requested.take(){
thread::sleep(Duration::from_nanos((excess_time as u64*5)/16));
}
if let Ok(DeviceState::Paused) = ctx.state.try_lock().map(|a| *a) {
break;
}
if let Ok(mut dev) = audio_device.try_lock() {
let mut callback = dev.core.callback();
let state = dev.state().expect("state not initalized");
let written_time_nanos = calc_elasped_time_nanos(frames_written, frequency);
let real_time_nanos = real_time.elapsed().as_nanos();
if written_time_nanos > (BUFFER_DELTA_IN_NANOS + real_time_nanos) {
let excess_time = written_time_nanos - real_time_nanos;
is_sleep_requested = Some(excess_time);
continue;
}
callback(state, &mut buffer[..]);
let mut frames_pending = buffer_size as isize;
while let Ok(frames_written) = io.writei(&buffer[..]) {
frames_pending -= frames_written as isize;
if frames_pending <= 0 {
break;
}
}
frames_written += buffer_size;
}
}
pcm.drain().unwrap();
});
}
pub fn pause(&self) {
*self.state.lock().unwrap() = DeviceState::Paused;
}
}
pub struct FlufflAudioDevice<F, S>
where
F: FnMut(&mut S, &mut [f32]) + std::marker::Copy + Send + 'static,
S: Send,
{
core: AudioDeviceCore<F, S>,
}
impl<F, S> FlufflAudioDevice<F, S>
where
F: FnMut(&mut S, &mut [f32]) + std::marker::Copy + Send + 'static,
S: Send + 'static,
{
pub fn callback(&self) -> F {
self.core.callback()
}
pub fn state(&mut self) -> Option<&mut S> {
self.core.state.as_mut()
}
}