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::{Buffered, 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 from_file_data(data: &[u8]) -> Sound {
Sound { data: data.into() }
}
#[deprecated(
since = "0.2.13",
note = "Renamed to `from_file_data` to disambiguate from other sound data formats (e.g. PCM)."
)]
#[allow(missing_docs)]
#[inline]
pub fn from_data(data: &[u8]) -> Sound {
Sound::from_file_data(data)
}
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 data = Decoder::new(Cursor::new(Arc::clone(&self.data)))?.buffered();
let source = TetraSource {
repeat_source: data.clone(),
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);
}
}
}
type TetraSourceData = Buffered<Decoder<Cursor<Arc<[u8]>>>>;
struct TetraSource {
data: TetraSourceData,
repeat_source: TetraSourceData,
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.data = self.repeat_source.clone();
self.rewind = false;
if let Some(controls) = self.remote_controls.upgrade() {
controls.rewind.store(false, Ordering::SeqCst);
}
}
self.data
.next()
.or_else(|| {
if self.repeating {
self.data = self.repeat_source.clone();
self.data.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)
}
})
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(0, None)
}
}
impl Source for TetraSource {
#[inline]
fn current_frame_len(&self) -> Option<usize> {
match self.data.current_frame_len() {
Some(0) => self.repeat_source.current_frame_len(),
a => a,
}
}
#[inline]
fn channels(&self) -> u16 {
match self.data.current_frame_len() {
Some(0) => self.repeat_source.channels(),
_ => self.data.channels(),
}
}
#[inline]
fn sample_rate(&self) -> u32 {
match self.data.current_frame_len() {
Some(0) => (self.repeat_source.sample_rate() as f32 * self.speed) as u32,
_ => (self.data.sample_rate() as f32 * self.speed) as u32,
}
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
None
}
}
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()
}