bevy_kira_components/sources/audio_file/
mod.rs1use bevy::prelude::*;
11use bevy::utils::error;
12use kira::sound::static_sound::{StaticSoundData, StaticSoundHandle, StaticSoundSettings};
13use std::io::Cursor;
14
15use std::path::PathBuf;
16use std::sync::Arc;
17
18use crate::backend::AudioBackend;
19use kira::manager::error::PlaySoundError;
20use kira::manager::AudioManager;
21use kira::sound::streaming::{StreamingSoundData, StreamingSoundHandle, StreamingSoundSettings};
22use kira::sound::{FromFileError, PlaybackRate, PlaybackState, Region};
23
24use crate::sources::audio_file::loader::AudioFileLoader;
25use crate::AudioPlaybackSet;
26use kira::tween::{Tween, Value};
27use kira::{CommandError, OutputDestination};
28use serde::{Deserialize, Serialize};
29use thiserror::Error;
30
31use super::{AudioBundle, AudioHandle, AudioSource, AudioSourcePlugin};
32
33pub mod loader;
34
35#[doc(hidden)]
36#[allow(missing_docs)]
37pub mod prelude {
38 pub use super::loader::*;
39 pub use super::{
40 AudioFile, AudioFileBundle, AudioFileError, AudioFileHandle, AudioFileSettings,
41 };
42}
43
44pub type AudioFileBundle = AudioBundle<AudioFile>;
46
47pub struct AudioFilePlugin;
49
50impl Plugin for AudioFilePlugin {
51 fn build(&self, app: &mut App) {
52 app.init_asset_loader::<AudioFileLoader>()
53 .add_plugins(AudioSourcePlugin::<AudioFile>::default())
54 .add_systems(PostUpdate, audio_finished.in_set(AudioPlaybackSet::Cleanup));
55 }
56}
57
58fn audio_finished(
59 mut commands: Commands,
60 q_sources: Query<(Entity, &AudioHandle<AudioFileHandle>)>,
61) {
62 for (entity, AudioHandle(handle)) in &q_sources {
63 if matches!(handle.playback_state(), PlaybackState::Stopped) {
64 commands
65 .entity(entity)
66 .remove::<AudioHandle<AudioFileHandle>>();
67 }
68 }
69}
70
71#[derive(Asset, Clone, TypePath)]
76pub enum AudioFile {
77 Static(Arc<[u8]>, StaticSoundSettings),
79 Streaming {
81 path: PathBuf,
83 settings: StreamingSoundSettings,
85 },
86}
87
88#[derive(Debug, Error)]
90pub enum AudioFileError {
91 #[error(transparent)]
93 FromFileError(#[from] FromFileError),
94}
95
96#[derive(Debug, Component, Deserialize, Serialize)]
98pub struct AudioFileSettings {
99 pub start_paused: bool,
102 pub volume: f64,
104 pub panning: f64,
106 pub loop_region: Option<Region>,
108 pub play_region: Region,
110 pub reverse: bool,
112 }
114
115impl Default for AudioFileSettings {
116 fn default() -> Self {
117 Self {
118 start_paused: false,
119 volume: 1.0,
120 panning: 0.5,
121 loop_region: None,
122 play_region: Region::from(..),
123 reverse: false,
124 }
125 }
126}
127
128fn play_sound_error_transmute<Out>(err: PlaySoundError<()>) -> PlaySoundError<Out> {
129 match err {
130 PlaySoundError::CommandError(cmd) => PlaySoundError::CommandError(cmd),
131 PlaySoundError::SoundLimitReached => PlaySoundError::SoundLimitReached,
132 _ => unreachable!(),
133 }
134}
135
136fn play_sound_error_cast<In, Out: From<In>>(err: PlaySoundError<In>) -> PlaySoundError<Out> {
137 match err {
138 PlaySoundError::CommandError(cmd) => PlaySoundError::CommandError(cmd),
139 PlaySoundError::SoundLimitReached => PlaySoundError::SoundLimitReached,
140 PlaySoundError::IntoSoundError(input) => PlaySoundError::IntoSoundError(input.into()),
141 _ => unreachable!(),
142 }
143}
144
145impl AudioSource for AudioFile {
146 type Error = PlaySoundError<AudioFileError>;
147 type Handle = AudioFileHandle;
148 type Settings = AudioFileSettings;
149
150 fn create_handle(
151 &self,
152 manager: &mut AudioManager<AudioBackend>,
153 settings: &Self::Settings,
154 output_destination: OutputDestination,
155 ) -> Result<Self::Handle, Self::Error> {
156 let start_paused = settings.start_paused;
157 match self {
158 Self::Static(data, kira_settings) => {
159 let settings = (*kira_settings)
160 .output_destination(output_destination)
161 .volume(settings.volume)
162 .panning(settings.panning)
163 .loop_region(settings.loop_region)
164 .reverse(settings.reverse)
165 .playback_region(settings.play_region);
166 let static_data = StaticSoundData::from_cursor(Cursor::new(data.clone()), settings)
167 .map_err(|err| {
168 PlaySoundError::IntoSoundError(AudioFileError::FromFileError(err))
169 })?;
170 manager
171 .play(static_data)
172 .map_err(play_sound_error_transmute)
173 .map(|mut handle| {
174 if start_paused {
175 error(handle.pause(Tween::default()));
176 }
177 handle
178 })
179 .map(RawAudioHandleImpl::Static)
180 .map(AudioFileHandle)
181 }
182 Self::Streaming {
183 path,
184 settings: kira_settings,
185 } => {
186 let settings = (*kira_settings)
187 .output_destination(output_destination)
188 .volume(settings.volume)
189 .panning(settings.panning)
190 .loop_region(settings.loop_region)
191 .playback_region(settings.play_region);
192 let streaming_sound_data =
193 StreamingSoundData::from_file(path, settings).map_err(|err| {
194 PlaySoundError::IntoSoundError(AudioFileError::FromFileError(err))
195 })?;
196 manager
197 .play(streaming_sound_data)
198 .map_err(play_sound_error_cast)
199 .map(|mut handle| {
200 if start_paused {
201 error(handle.pause(Tween::default()));
202 }
203 handle
204 })
205 .map(RawAudioHandleImpl::Streaming)
206 .map(AudioFileHandle)
207 }
208 }
209 }
210}
211
212enum RawAudioHandleImpl {
214 Static(StaticSoundHandle),
215 Streaming(StreamingSoundHandle<FromFileError>),
216}
217
218pub struct AudioFileHandle(RawAudioHandleImpl);
221
222macro_rules! defer_call {
223 (fn $name:ident(&self $(, $argname:ident: $argtype:ty)*) -> $ret:ty) => {
224 defer_call!(fn $name :: $name(&self $(, $argname: $argtype)*) -> $ret);
225 };
226 (fn $name:ident :: $fnname:ident(&self $(, $argname:ident: $argtype:ty)*) -> $ret:ty) => {
228 pub fn $fnname(&self, $($argname: $argtype),*) -> $ret {
230 match self {
231 Self(RawAudioHandleImpl::Static(handle)) => handle.$name($($argname),*),
232 Self(RawAudioHandleImpl::Streaming(handle)) => handle.$name($($argname),*),
233 }
234 }
235 };
236 (fn $name:ident(&mut self $(, $argname:ident: $argtype:ty)*) -> $ret:ty) => {
237 pub fn $name(&mut self, $($argname: $argtype),*) -> $ret {
239 match self {
240 Self(RawAudioHandleImpl::Static(handle)) => handle.$name($($argname),*),
241 Self(RawAudioHandleImpl::Streaming(handle)) => handle.$name($($argname),*),
242 }
243 }
244 };
245}
246
247impl AudioFileHandle {
248 defer_call!(fn state :: playback_state(&self) -> PlaybackState);
249 defer_call!(fn position(&self) -> f64);
250 defer_call!(fn set_playback_rate(&mut self, rate: impl Into<Value<PlaybackRate>>, tween: Tween) -> Result<(), CommandError>);
251 defer_call!(fn set_panning(&mut self, panning: impl Into<Value<f64>>, tween: Tween) ->Result<(), CommandError>);
252 defer_call!(fn set_playback_region(&mut self, region: impl Into<Region>) -> Result<(), CommandError>);
253 defer_call!(fn set_loop_region(&mut self, region: impl Into<Region>) -> Result<(), CommandError>);
254 defer_call!(fn pause(&mut self, tween: Tween) -> Result<(), CommandError>);
255 defer_call!(fn resume(&mut self, tween: Tween) -> Result<(), CommandError>);
256 defer_call!(fn stop(&mut self, tween: Tween) -> Result<(), CommandError>);
257 defer_call!(fn seek_to(&mut self, position: f64) -> Result<(), CommandError>);
258 defer_call!(fn seek_by(&mut self, amount: f64) -> Result<(), CommandError>);
259}