use std::fs;
use std::io::Cursor;
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, Weak};
use std::time::Duration;
use rodio::source::Empty;
use rodio::{Decoder, Device, Sample, Source};
use crate::error::{Result, TetraError};
use crate::Context;
pub(crate) struct AudioContext {
device: Option<Device>,
master_volume: Arc<Mutex<f32>>,
}
impl AudioContext {
pub(crate) fn new() -> AudioContext {
let device = rodio::default_output_device();
if let Some(active_device) = &device {
rodio::play_raw(&active_device, Empty::new());
}
AudioContext {
device,
master_volume: Arc::new(Mutex::new(1.0)),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Sound {
data: Arc<[u8]>,
}
impl Sound {
pub fn new<P>(path: P) -> Result<Sound>
where
P: AsRef<Path>,
{
Ok(Sound {
data: fs::read(path)?.into(),
})
}
pub fn play(&self, ctx: &Context) -> Result<SoundInstance> {
self.start_source(ctx, true, false, 1.0, 1.0)
}
pub fn repeat(&self, ctx: &Context) -> Result<SoundInstance> {
self.start_source(ctx, true, true, 1.0, 1.0)
}
pub fn spawn(&self, ctx: &Context) -> Result<SoundInstance> {
self.start_source(ctx, false, false, 1.0, 1.0)
}
pub fn play_with(&self, ctx: &Context, volume: f32, speed: f32) -> Result<SoundInstance> {
self.start_source(ctx, true, false, volume, speed)
}
pub fn repeat_with(&self, ctx: &Context, volume: f32, speed: f32) -> Result<SoundInstance> {
self.start_source(ctx, true, true, volume, speed)
}
pub fn spawn_with(&self, ctx: &Context, volume: f32, speed: f32) -> Result<SoundInstance> {
self.start_source(ctx, false, false, volume, speed)
}
fn start_source(
&self,
ctx: &Context,
playing: bool,
repeating: bool,
volume: f32,
speed: f32,
) -> Result<SoundInstance> {
let controls = Arc::new(RemoteControls {
playing: AtomicBool::new(playing),
repeating: AtomicBool::new(repeating),
rewind: AtomicBool::new(false),
volume: Mutex::new(volume),
speed: Mutex::new(speed),
});
let master_volume = {
*ctx.audio.master_volume.lock().unwrap()
};
let source = TetraSource {
data: Arc::clone(&self.data),
cursor: Decoder::new(Cursor::new(Arc::clone(&self.data)))?,
remote_master_volume: Arc::clone(&ctx.audio.master_volume),
remote_controls: Arc::downgrade(&Arc::clone(&controls)),
time_till_update: 220,
detached: false,
playing,
repeating,
rewind: false,
master_volume,
volume,
speed,
};
rodio::play_raw(ctx.audio.device.as_ref().ok_or(TetraError::NoAudioDevice)?, source.convert_samples());
Ok(SoundInstance { controls })
}
}
#[derive(Debug)]
struct RemoteControls {
playing: AtomicBool,
repeating: AtomicBool,
rewind: AtomicBool,
volume: Mutex<f32>,
speed: Mutex<f32>,
}
#[derive(Debug, Clone)]
pub struct SoundInstance {
controls: Arc<RemoteControls>,
}
impl SoundInstance {
pub fn play(&self) {
self.controls.playing.store(true, Ordering::SeqCst);
}
pub fn stop(&self) {
self.controls.playing.store(false, Ordering::SeqCst);
self.controls.rewind.store(true, Ordering::SeqCst);
}
pub fn pause(&self) {
self.controls.playing.store(false, Ordering::SeqCst);
}
pub fn set_volume(&self, volume: f32) {
*self.controls.volume.lock().unwrap() = volume;
}
pub fn set_speed(&self, speed: f32) {
*self.controls.speed.lock().unwrap() = speed;
}
pub fn set_repeating(&self, repeating: bool) {
self.controls.repeating.store(repeating, Ordering::SeqCst);
}
pub fn toggle_repeating(&self) {
if self.controls.repeating.load(Ordering::SeqCst) {
self.controls.repeating.store(false, Ordering::SeqCst);
} else {
self.controls.repeating.store(true, Ordering::SeqCst);
}
}
}
struct TetraSource {
data: Arc<[u8]>,
cursor: Decoder<Cursor<Arc<[u8]>>>,
remote_master_volume: Arc<Mutex<f32>>,
remote_controls: Weak<RemoteControls>,
time_till_update: u32,
detached: bool,
playing: bool,
repeating: bool,
rewind: bool,
master_volume: f32,
volume: f32,
speed: f32,
}
impl Iterator for TetraSource {
type Item = i16;
#[inline]
fn next(&mut self) -> Option<i16> {
self.time_till_update -= 1;
if self.time_till_update == 0 {
self.master_volume = *self.remote_master_volume.lock().unwrap();
if let Some(controls) = self.remote_controls.upgrade() {
self.playing = controls.playing.load(Ordering::SeqCst);
if self.playing {
self.repeating = controls.repeating.load(Ordering::SeqCst);
self.rewind = controls.rewind.load(Ordering::SeqCst);
self.volume = *controls.volume.lock().unwrap();
self.speed = *controls.speed.lock().unwrap();
}
} else {
self.detached = true;
}
self.time_till_update = 220;
}
if !self.playing {
return if self.detached { None } else { Some(0) };
}
if self.rewind {
self.cursor = Decoder::new(Cursor::new(Arc::clone(&self.data))).unwrap();
self.rewind = false;
if let Some(controls) = self.remote_controls.upgrade() {
controls.rewind.store(false, Ordering::SeqCst);
}
}
self.cursor
.next()
.or_else(|| {
if self.repeating {
self.cursor = Decoder::new(Cursor::new(Arc::clone(&self.data))).unwrap();
self.cursor.next()
} else {
None
}
})
.map(|v| v.amplify(self.volume).amplify(self.master_volume))
.or_else(|| {
if self.detached {
None
} else {
if !self.rewind {
self.playing = false;
self.rewind = true;
if let Some(controls) = self.remote_controls.upgrade() {
controls.playing.store(false, Ordering::SeqCst);
controls.rewind.store(true, Ordering::SeqCst);
}
}
Some(0)
}
})
}
}
impl Source for TetraSource {
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.cursor.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.cursor.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
(self.cursor.sample_rate() as f32 * self.speed) as u32
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.cursor.total_duration()
}
}
pub fn set_master_volume(ctx: &mut Context, volume: f32) {
*ctx.audio.master_volume.lock().unwrap() = volume;
}
pub fn get_master_volume(ctx: &mut Context) -> f32 {
*ctx.audio.master_volume.lock().unwrap()
}