use crate::plugins::assets::GodotResource;
use crate::plugins::audio::{
AudioCommand, AudioPlayerType, AudioSettings, AudioTween, PlayCommand, SoundId,
};
use bevy::asset::Handle;
use bevy::prelude::*;
use parking_lot::RwLock;
use std::collections::VecDeque;
use std::marker::PhantomData;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ChannelId(pub &'static str);
#[derive(Debug, Clone)]
pub(crate) struct ChannelState {
#[allow(dead_code)]
pub volume: f32,
#[allow(dead_code)]
pub pitch: f32,
#[allow(dead_code)]
pub paused: bool,
#[allow(dead_code)]
pub panning: f32, }
impl Default for ChannelState {
fn default() -> Self {
Self {
volume: 1.0,
pitch: 1.0,
paused: false,
panning: 0.0,
}
}
}
pub trait AudioChannelMarker: Resource {
const CHANNEL_NAME: &'static str;
}
#[derive(Resource)]
pub struct AudioChannel<T: AudioChannelMarker> {
pub(crate) channel_id: ChannelId,
pub(crate) commands: RwLock<VecDeque<AudioCommand>>,
_marker: PhantomData<T>,
}
impl<T: AudioChannelMarker> AudioChannel<T> {
pub fn new(channel_id: ChannelId) -> Self {
Self {
channel_id,
commands: RwLock::new(VecDeque::new()),
_marker: PhantomData,
}
}
pub fn id(&self) -> &ChannelId {
&self.channel_id
}
fn queue_command(&self, command: AudioCommand) {
self.commands.write().push_back(command);
}
pub fn play(&self, handle: Handle<GodotResource>) -> PlayAudioCommand<T> {
PlayAudioCommand::new(
self.channel_id,
handle,
AudioPlayerType::NonPositional,
self,
)
}
pub fn play_2d(&self, handle: Handle<GodotResource>, position: Vec2) -> PlayAudioCommand<T> {
PlayAudioCommand::new(
self.channel_id,
handle,
AudioPlayerType::Spatial2D { position },
self,
)
}
pub fn play_3d(&self, handle: Handle<GodotResource>, position: Vec3) -> PlayAudioCommand<T> {
PlayAudioCommand::new(
self.channel_id,
handle,
AudioPlayerType::Spatial3D { position },
self,
)
}
pub fn stop(&self) {
self.queue_command(AudioCommand::Stop(self.channel_id, None));
}
pub fn stop_with_fade(&self, fade_out: AudioTween) {
self.queue_command(AudioCommand::Stop(self.channel_id, Some(fade_out)));
}
pub fn pause(&self) {
self.queue_command(AudioCommand::Pause(self.channel_id, None));
}
pub fn resume(&self) {
self.queue_command(AudioCommand::Resume(self.channel_id, None));
}
pub fn set_volume(&self, volume: f32) {
self.queue_command(AudioCommand::SetVolume(
self.channel_id,
volume.clamp(0.0, 1.0),
None,
));
}
pub fn set_volume_with_fade(&self, volume: f32, tween: AudioTween) {
self.queue_command(AudioCommand::SetVolume(
self.channel_id,
volume.clamp(0.0, 1.0),
Some(tween),
));
}
pub fn set_pitch(&self, pitch: f32) {
self.queue_command(AudioCommand::SetPitch(
self.channel_id,
pitch.clamp(0.1, 4.0),
None,
));
}
pub fn set_panning(&self, panning: f32) {
self.queue_command(AudioCommand::SetPanning(
self.channel_id,
panning.clamp(-1.0, 1.0),
None,
));
}
}
pub struct PlayAudioCommand<'a, T: AudioChannelMarker> {
channel_id: ChannelId,
handle: Handle<GodotResource>,
player_type: AudioPlayerType,
settings: AudioSettings,
sound_id: SoundId,
channel: &'a AudioChannel<T>,
}
impl<'a, T: AudioChannelMarker> PlayAudioCommand<'a, T> {
pub(crate) fn new(
channel_id: ChannelId,
handle: Handle<GodotResource>,
player_type: AudioPlayerType,
channel: &'a AudioChannel<T>,
) -> Self {
let sound_id = SoundId::next();
Self {
channel_id,
handle,
player_type,
settings: AudioSettings::default(),
sound_id,
channel,
}
}
pub fn volume(mut self, volume: f32) -> Self {
self.settings.volume = volume.clamp(0.0, 1.0);
self
}
pub fn pitch(mut self, pitch: f32) -> Self {
self.settings.pitch = pitch.clamp(0.1, 4.0);
self
}
pub fn looped(mut self) -> Self {
self.settings.looping = true;
self
}
pub fn fade_in(mut self, duration: std::time::Duration) -> Self {
self.settings.fade_in = Some(AudioTween::linear(duration));
self
}
pub fn fade_in_with_easing(mut self, tween: AudioTween) -> Self {
self.settings.fade_in = Some(tween);
self
}
pub fn start_from(mut self, position: f32) -> Self {
self.settings.start_position = position.max(0.0);
self
}
pub fn panning(mut self, panning: f32) -> Self {
self.settings.panning = Some(panning.clamp(-1.0, 1.0));
self
}
}
impl<T: AudioChannelMarker> Drop for PlayAudioCommand<'_, T> {
fn drop(&mut self) {
let command = AudioCommand::Play(PlayCommand {
channel_id: self.channel_id,
handle: self.handle.clone(),
player_type: self.player_type.clone(),
settings: self.settings.clone(),
sound_id: self.sound_id,
});
self.channel.queue_command(command);
}
}
#[derive(Resource)]
pub struct MainAudioTrack;
impl AudioChannelMarker for MainAudioTrack {
const CHANNEL_NAME: &'static str = "main";
}