use std::{
collections::HashMap,
sync::{Arc, atomic::Ordering},
};
use crate::{
Decibels, PlaySoundError, ResourceLimitReached, StartTime, Tween, Value,
backend::{RendererShared, resources::ResourceController},
command::{CommandWriter, ValueChangeCommand},
listener::ListenerId,
sound::{Sound, SoundData},
track::TrackPlaybackState,
};
use super::{
CommandWriters, NonexistentRoute, SendTrackId, SpatialTrackBuilder, SpatialTrackHandle, Track,
TrackBuilder, TrackShared,
};
#[derive(Debug)]
pub struct TrackHandle {
pub(crate) renderer_shared: Arc<RendererShared>,
pub(crate) shared: Arc<TrackShared>,
pub(crate) command_writers: CommandWriters,
pub(crate) sound_controller: ResourceController<Box<dyn Sound>>,
pub(crate) sub_track_controller: ResourceController<Track>,
pub(crate) send_volume_command_writers:
HashMap<SendTrackId, CommandWriter<ValueChangeCommand<Decibels>>>,
pub(crate) internal_buffer_size: usize,
}
impl TrackHandle {
#[must_use]
pub fn state(&self) -> TrackPlaybackState {
self.shared.state()
}
pub fn play<D: SoundData>(
&mut self,
sound_data: D,
) -> Result<D::Handle, PlaySoundError<D::Error>> {
let (sound, handle) = sound_data
.into_sound()
.map_err(PlaySoundError::IntoSoundError)?;
self.sound_controller
.insert(sound)
.map_err(|_| PlaySoundError::SoundLimitReached)?;
Ok(handle)
}
pub fn add_sub_track(
&mut self,
builder: TrackBuilder,
) -> Result<TrackHandle, ResourceLimitReached> {
let (mut track, handle) =
builder.build(self.renderer_shared.clone(), self.internal_buffer_size);
track.init_effects(self.renderer_shared.sample_rate.load(Ordering::SeqCst));
self.sub_track_controller.insert(track)?;
Ok(handle)
}
pub fn add_spatial_sub_track(
&mut self,
listener: impl Into<ListenerId>,
position: impl Into<Value<mint::Vector3<f32>>>,
builder: SpatialTrackBuilder,
) -> Result<SpatialTrackHandle, ResourceLimitReached> {
let (mut track, handle) = builder.build(
self.renderer_shared.clone(),
self.internal_buffer_size,
listener.into(),
position.into().to_(),
);
track.init_effects(self.renderer_shared.sample_rate.load(Ordering::SeqCst));
self.sub_track_controller.insert(track)?;
Ok(handle)
}
pub fn set_volume(&mut self, volume: impl Into<Value<Decibels>>, tween: Tween) {
self.command_writers.set_volume.write(ValueChangeCommand {
target: volume.into(),
tween,
})
}
pub fn set_send(
&mut self,
to: impl Into<SendTrackId>,
volume: impl Into<Value<Decibels>>,
tween: Tween,
) -> Result<(), NonexistentRoute> {
let to = to.into();
self.send_volume_command_writers
.get_mut(&to)
.ok_or(NonexistentRoute)?
.write(ValueChangeCommand {
target: volume.into(),
tween,
});
Ok(())
}
pub fn pause(&mut self, tween: Tween) {
self.command_writers.pause.write(tween)
}
pub fn resume(&mut self, tween: Tween) {
self.resume_at(StartTime::Immediate, tween)
}
pub fn resume_at(&mut self, start_time: StartTime, tween: Tween) {
self.command_writers.resume.write((start_time, tween))
}
#[must_use]
pub fn sound_capacity(&self) -> usize {
self.sound_controller.capacity()
}
#[must_use]
pub fn num_sounds(&self) -> usize {
self.sound_controller.len()
}
#[must_use]
pub fn sub_track_capacity(&self) -> usize {
self.sub_track_controller.capacity()
}
#[must_use]
pub fn num_sub_tracks(&self) -> usize {
self.sub_track_controller.len()
}
}
impl Drop for TrackHandle {
fn drop(&mut self) {
self.shared.mark_for_removal();
}
}