use crate::{
ffi::data_callback,
enums::{PlayerState, CMD, ResamplingQuality, EngineSignal},
singletons::{get_played, set_total, set_decoder_eof}
};
use ffmpeg_next::{self, packet::Mut};
use miniaudio_aurex::{self as miniaudio, ma_device_config_init};
use soxr::{Soxr, format::{self}, params::{Interpolation, QualitySpec, RuntimeSpec}};
use std::{
ffi::c_void,
ptr,
sync::{
Arc, Mutex,
},
thread::{self}
};
use crossbeam_channel::{unbounded, Receiver, Sender};
#[allow(unused_imports)]
use ffmpeg_next::{self as av, ffi::AVAudioFifo, frame::Audio as AudioFrame, media, sys};
pub struct AudioFifo(pub *mut AVAudioFifo);
unsafe impl Send for AudioFifo {}
pub struct AudioEngine {
device: miniaudio::ma_device,
buffer: Arc<Mutex<AudioFifo>>,
channels: i32,
sample_rate: Arc<Mutex<i32>>,
state: Arc<Mutex<PlayerState>>,
device_config: miniaudio::ma_device_config,
initialised: bool,
tx: Option<Sender<CMD>>,
duration: Arc<Mutex<f64>>, total_samples: Arc<Mutex<Option<u64>>>, resampling_quality: ResamplingQuality,
signal_receiver: Receiver<EngineSignal>,
user_data: Arc<Mutex<(Arc<Mutex<AudioFifo>>, Sender<EngineSignal>)>>,
callback: fn(event: EngineSignal)
}
impl AudioEngine {
pub fn new(
resampling_quality: Option<ResamplingQuality>,
callback: fn(event: EngineSignal)
) -> Result<Arc<Mutex<Self>>, i32> {
let m_resampling_quality = resampling_quality.unwrap_or(ResamplingQuality::High);
let mut device: miniaudio::ma_device = unsafe { std::mem::zeroed() };
let buffer_ptr =
unsafe { sys::av_audio_fifo_alloc(sys::AVSampleFormat::AV_SAMPLE_FMT_S32, 2, 128_000) };
let buffer = Arc::new(Mutex::new(AudioFifo(buffer_ptr)));
let (signal_tx, signal_rx) = unbounded::<EngineSignal>();
let m_user_data = (buffer.clone(), signal_tx);
let user_data = Arc::new(Mutex::new(m_user_data));
let mut device_config =
unsafe { ma_device_config_init(miniaudio::ma_device_type_ma_device_type_playback) };
device_config.playback.format = miniaudio::ma_format_ma_format_s32;
device_config.playback.channels = 0;
device_config.sampleRate = 0;
device_config.dataCallback = Some(data_callback);
device_config.pUserData = Arc::as_ptr(&user_data) as *mut c_void;
if cfg!(target_os = "windows") {
println!("Detected Windows");
device_config.wasapi.noAutoConvertSRC = miniaudio::MA_TRUE as u8;
device_config.wasapi.noDefaultQualitySRC = miniaudio::MA_TRUE as u8;
}
let device_result =
unsafe { miniaudio::ma_device_init(ptr::null_mut(), &device_config, &mut device) };
if device_result != miniaudio::ma_result_MA_SUCCESS {
println!("Failed to init device.");
}
println!(
"Detected system configuration: {} channels at {} hz",
device.playback.channels, device.sampleRate
);
let engine = AudioEngine {
device: device,
buffer: buffer,
channels: device.playback.channels as i32,
sample_rate: Arc::new(Mutex::new(device.sampleRate as i32)),
state: Arc::new(Mutex::new(PlayerState::EMPTY)),
device_config: device_config,
initialised: false,
tx: None,
duration: Arc::new(Mutex::new(-1.0)),
total_samples: Arc::new(Mutex::new(None)),
resampling_quality: m_resampling_quality,
signal_receiver: signal_rx,
user_data: user_data,
callback: callback
};
Ok(Arc::new(Mutex::new(engine)))
}
pub fn get_duration(&self) -> f64 {
*self.duration.lock().unwrap()
}
pub fn load(audio_engine: Arc<Mutex<Self>>, file: &str) -> Result<(), i32> {
let mut engine = audio_engine.lock().unwrap();
engine.clear()?;
if !engine.initialised {
let (tx, rx) = unbounded::<CMD>();
engine.tx = Some(tx);
_ = engine.reinit_device(); _ = engine.spawn_decoder_thread(rx.clone());
_ = AudioEngine::spawn_listening_thread(audio_engine.clone(), engine.signal_receiver.clone());
engine.initialised = true;
}
println!("Loading {}", &file);
let resampling_quality = engine.resampling_quality;
_ = engine.tx.as_mut().unwrap().send(CMD::Start(file.to_string(), resampling_quality));
Ok(())
}
pub fn get_progress(&self) -> Result<f64, i32> {
let sample_rate = *self.sample_rate.lock().unwrap() as f64;
if sample_rate <= 0.0 {
return Err(-1);
}
let played_samples = get_played() as f64;
Ok(played_samples / sample_rate)
}
pub fn clear(&mut self) -> Result<(), i32> {
if *self.state.lock().unwrap() == PlayerState::PLAYING {
self.pause()?;
}
unsafe {
sys::av_audio_fifo_reset(self.buffer.lock().unwrap().0);
}
*self.state.lock().unwrap() = PlayerState::EMPTY;
println!("Cleared audio buffer");
Ok(())
}
pub fn play(&mut self) -> Result<(), i32> {
if *self.state.lock().unwrap() != PlayerState::PLAYING {
if unsafe { miniaudio::ma_device_start(&mut self.device) }
!= miniaudio::ma_result_MA_SUCCESS
{
println!("Failed to start device");
} else {
*self.state.lock().unwrap() = PlayerState::PLAYING;
}
}
Ok(())
}
pub fn pause(&mut self) -> Result<(), i32> {
if *self.state.lock().unwrap() != PlayerState::PAUSED {
if unsafe { miniaudio::ma_device_stop(&mut self.device) }
!= miniaudio::ma_result_MA_SUCCESS
{
println!("Failed to stop device");
} else {
*self.state.lock().unwrap() = PlayerState::PAUSED;
}
}
Ok(())
}
fn reinit_device(&mut self) -> Result<(), i32> {
unsafe {
self.device = std::mem::zeroed();
self.device_config.pUserData = Arc::as_ptr(&self.user_data) as *mut c_void;
miniaudio::ma_device_init(ptr::null_mut(), &self.device_config, &mut self.device);
}
Ok(())
}
fn spawn_listening_thread (engine: Arc<Mutex<Self>>, receiver: Receiver<EngineSignal>) -> Result<(), i32> {
thread::spawn(move || {
for signal in receiver {
match signal {
EngineSignal::MediaEnd => {
set_decoder_eof(false);
let mut m_engine = engine.lock().unwrap();
_ = m_engine.pause();
_ = m_engine.clear();
println!("Player empty and ready. Executing callback");
(m_engine.callback)(EngineSignal::MediaEnd);
}
}
}
});
Ok(())
}
fn spawn_decoder_thread(&mut self, rx: Receiver<CMD>) -> Result<(), i32> {
let sample_rate_handle = self.sample_rate.clone();
let buffer_handle = self.buffer.clone();
let duration_handle = self.duration.clone();
let total_samples_handle = self.total_samples.clone();
thread::spawn(move || {
for cmd in rx {
match cmd {
CMD::Start(url, resampling_quality) => {
let mut format_ctx = av::format::input(&url).expect("Failed to open file.");
let sample_rate = *sample_rate_handle.lock().unwrap() as f64;
let mut duration = duration_handle.lock().unwrap();
let mut total_samples = total_samples_handle.lock().unwrap();
*duration = format_ctx.duration() as f64 / f64::from(av::ffi::AV_TIME_BASE);
*total_samples = Some((*duration * sample_rate) as u64);
set_total(total_samples.unwrap());
let audio_stream_index = format_ctx
.streams()
.best(media::Type::Audio)
.expect("No audio stream found.")
.index();
let codec_params = format_ctx
.streams()
.nth(audio_stream_index)
.expect("Stream Disappeared")
.parameters();
let codec_ctx = av::codec::context::Context::from_parameters(codec_params)
.expect("Failed to allocate codec context");
let mut decoder = codec_ctx
.decoder()
.audio()
.expect("Failed to open decoder.");
let mut resampler = av::software::resampling::Context::get(
decoder.format(),
decoder.channel_layout(),
decoder.rate(),
av::format::Sample::I32(av::format::sample::Type::Packed),
decoder.channel_layout(),
decoder.rate(),
)
.expect("Failed to init resampler");
let runtime = RuntimeSpec::new(0)
.with_interpolation(Interpolation::High);
let mut soxr_resampler = Soxr::<format::Interleaved<i32, 2>>::new_with_params(
decoder.rate() as f64,
sample_rate,
resampling_quality.get_quality_spec().expect("Failed to get quality spec for soxr."),
runtime
).expect("Failed to setup soxr");
for (stream, packet) in format_ctx.packets() {
if stream.index() != audio_stream_index {
continue;
}
decoder
.send_packet(&packet)
.expect("Failed to send packet to decoder.");
let mut frame = AudioFrame::empty();
while decoder.receive_frame(&mut frame).is_ok() {
let mut resampled_frame = AudioFrame::empty();
_ = resampler.run(&frame, &mut resampled_frame);
let input_samples: &[[i32; 2]] =
bytemuck::cast_slice(resampled_frame.data(0));
let mut output_buf = vec![
[0i32; 2];
(input_samples.len() as usize
* *sample_rate_handle.lock().unwrap()
as usize)
/ decoder.rate() as usize
];
let res = soxr_resampler
.process(input_samples, &mut output_buf)
.unwrap();
let mut soxr_frame = AudioFrame::new(
av::format::Sample::I32(av::format::sample::Type::Packed),
res.output_frames,
av::ChannelLayout::STEREO,
);
soxr_frame.set_rate(*sample_rate_handle.lock().unwrap() as u32);
let data_plane = soxr_frame.data_mut(0);
let dst_slice: &mut [[i32; 2]] =
bytemuck::cast_slice_mut(data_plane);
dst_slice[..res.output_frames]
.copy_from_slice(&output_buf[..res.output_frames]);
unsafe {
let data_ptr0 =
soxr_frame.data_mut(0).as_mut_ptr() as *mut c_void;
let mut data_ptrs: [*mut c_void; 1] = [data_ptr0];
let written = sys::av_audio_fifo_write(
buffer_handle.lock().unwrap().0,
data_ptrs.as_mut_ptr(),
soxr_frame.samples() as i32,
);
if written < 0 {
}
}
}
}
set_decoder_eof(true);
}
}
}
});
Ok(())
}
}
impl Drop for AudioEngine {
fn drop(&mut self) {
let _ = self.pause();
unsafe {
miniaudio::ma_device_uninit(&mut self.device);
if !self.buffer.lock().unwrap().0.is_null() {
sys::av_audio_fifo_free(self.buffer.lock().unwrap().0);
}
}
}
}
unsafe impl Send for AudioEngine {}
unsafe impl Sync for AudioEngine {}