bevy_kira_components/sources/
mod.rs1pub mod audio_file;
3
4use crate::backend::AudioBackend;
5use crate::spatial::SpatialEmitterHandle;
6
7use crate::{AudioPlaybackSet, AudioSourceSetup, AudioWorld, InternalAudioMarker};
8use bevy::prelude::*;
9use kira::manager::AudioManager;
10
11use std::fmt;
12use std::marker::PhantomData;
13
14#[doc(hidden)]
15pub mod prelude {
16 pub use super::audio_file::prelude::*;
17 pub use super::{AudioBundle, AudioHandle, AudioSource, AudioSourcePlugin, OutputDestination};
18}
19
20pub trait AudioSource: Asset {
32 type Error: fmt::Display;
34 type Handle: 'static + Send + Sync;
38 type Settings: Send + Sync + Default + Component;
40
41 fn create_handle(
43 &self,
44 manager: &mut AudioManager<AudioBackend>,
45 settings: &Self::Settings,
46 output_destination: kira::OutputDestination,
47 ) -> Result<Self::Handle, Self::Error>;
48}
49
50#[derive(Debug, Deref, DerefMut, Component)]
53pub struct AudioHandle<T>(pub T);
54
55#[derive(Debug)]
58pub struct AudioSourcePlugin<T>(PhantomData<T>);
59
60impl<T> Default for AudioSourcePlugin<T> {
61 fn default() -> Self {
62 Self(PhantomData)
63 }
64}
65
66impl<T: AudioSource> Plugin for AudioSourcePlugin<T> {
67 fn build(&self, app: &mut App) {
68 app.init_asset::<T>().add_systems(
69 PostUpdate,
70 Self::audio_added
71 .in_set(AudioPlaybackSet::Update)
72 .in_set(AudioSourceSetup),
73 );
74 }
75}
76
77#[derive(Debug, Default, Component)]
80pub enum OutputDestination {
81 #[default]
83 MainOutput,
84}
85
86#[derive(Bundle)]
88pub struct AudioBundle<T: AudioSource> {
89 pub source: Handle<T>,
91 pub settings: T::Settings,
93 pub output: OutputDestination,
95 pub marker: InternalAudioMarker,
99}
100
101impl<T: AudioSource> Default for AudioBundle<T> {
102 fn default() -> Self {
103 Self {
104 source: Handle::default(),
105 settings: T::Settings::default(),
106 output: OutputDestination::MainOutput,
107 marker: InternalAudioMarker,
108 }
109 }
110}
111
112impl<T: AudioSource> AudioSourcePlugin<T> {
113 #[allow(clippy::type_complexity)]
114 fn audio_added(
115 mut commands: Commands,
116 mut audio_world: ResMut<AudioWorld>,
117 asset_server: Res<AssetServer>,
118 assets: Res<Assets<T>>,
119 q_added: Query<
120 (
121 Entity,
122 &Handle<T>,
123 &T::Settings,
124 Option<&SpatialEmitterHandle>,
125 &OutputDestination,
126 ),
127 Without<AudioHandle<T::Handle>>,
128 >,
129 ) {
130 let main_track_handle = audio_world.audio_manager.main_track();
131 for (entity, source, settings, spatial_emitter, output_destination) in &q_added {
132 let output_destination = if let Some(emitter) = spatial_emitter {
133 kira::OutputDestination::Emitter(emitter.0.id())
134 } else {
135 let output_handle = match output_destination {
136 OutputDestination::MainOutput => &main_track_handle,
137 };
138 kira::OutputDestination::Track(output_handle.id())
139 };
140 let result = match assets.get(source) {
141 Some(asset)
142 if asset_server.is_loaded_with_dependencies(source)
143 || !asset_server.is_managed(source) =>
144 {
145 asset.create_handle(
146 &mut audio_world.audio_manager,
147 settings,
148 output_destination,
149 )
150 }
151 _ => {
152 debug!("Asset not ready");
153 continue;
154 } };
156 let handle = match result {
157 Ok(handle) => handle,
158 Err(err) => {
159 error!("Cannot create handle: {err}");
160 return;
161 }
162 };
163 debug!("Added sound for {} in {entity:?}", T::type_path());
164 commands.entity(entity).insert(AudioHandle(handle));
165 }
166 }
167}