fyrox_impl/scene/sound/
context.rs1use crate::{
24 core::{
25 log::{Log, MessageKind},
26 pool::Handle,
27 visitor::prelude::*,
28 },
29 scene::{node::Node, sound::Sound},
30};
31use fxhash::FxHashSet;
32use fyrox_sound::{
33 bus::AudioBusGraph,
34 context::DistanceModel,
35 renderer::Renderer,
36 source::{SoundSource, SoundSourceBuilder, Status},
37};
38use std::{sync::MutexGuard, time::Duration};
39
40#[derive(Debug, Visit)]
42pub struct SoundContext {
43 #[visit(optional)]
44 pub(crate) native: fyrox_sound::context::SoundContext,
45}
46
47pub struct SoundContextGuard<'a> {
49 guard: MutexGuard<'a, fyrox_sound::context::State>,
50}
51
52impl SoundContextGuard<'_> {
53 pub fn bus_graph_ref(&self) -> &AudioBusGraph {
55 self.guard.bus_graph_ref()
56 }
57
58 pub fn bus_graph_mut(&mut self) -> &mut AudioBusGraph {
60 self.guard.bus_graph_mut()
61 }
62
63 pub fn pause(&mut self, pause: bool) {
65 self.guard.pause(pause);
66 }
67
68 pub fn is_paused(&self) -> bool {
70 self.guard.is_paused()
71 }
72
73 pub fn set_distance_model(&mut self, distance_model: DistanceModel) {
75 self.guard.set_distance_model(distance_model);
76 }
77
78 pub fn distance_model(&self) -> DistanceModel {
80 self.guard.distance_model()
81 }
82
83 pub fn normalize_frequency(&self, f: f32) -> f32 {
86 self.guard.normalize_frequency(f)
87 }
88
89 pub fn full_render_duration(&self) -> Duration {
91 self.guard.full_render_duration()
92 }
93
94 pub fn renderer(&self) -> Renderer {
96 self.guard.renderer().clone()
97 }
98
99 pub fn renderer_ref(&self) -> &Renderer {
101 self.guard.renderer()
102 }
103
104 pub fn renderer_ref_mut(&mut self) -> &mut Renderer {
106 self.guard.renderer_mut()
107 }
108
109 pub fn set_renderer(&mut self, renderer: Renderer) -> Renderer {
111 self.guard.set_renderer(renderer)
112 }
113
114 pub fn destroy_sound_sources(&mut self) {
116 self.guard.sources_mut().clear();
117 }
118}
119
120impl Default for SoundContext {
121 fn default() -> Self {
122 let native = fyrox_sound::context::SoundContext::new();
123 let mut state = native.state();
124 state.serialization_options.skip_sources = true;
126 drop(state);
127 Self { native }
128 }
129}
130
131impl SoundContext {
132 pub(crate) fn new() -> Self {
133 Default::default()
134 }
135
136 pub fn deep_clone(&self) -> Self {
138 Self {
139 native: self.native.deep_clone(),
140 }
141 }
142
143 pub fn state(&self) -> SoundContextGuard {
145 SoundContextGuard {
146 guard: self.native.state(),
147 }
148 }
149
150 pub(crate) fn remove_sound(&mut self, sound: Handle<SoundSource>, name: &str) {
151 let mut state = self.native.state();
152 if state.is_valid_handle(sound) {
153 state.remove_source(sound);
154
155 Log::info(format!("Native sound source was removed for node: {name}"));
156 }
157 }
158
159 pub(crate) fn set_sound_position(&mut self, sound: &Sound) {
160 if let Ok(source) = self.native.state().try_get_source_mut(sound.native.get()) {
161 source.set_position(sound.global_position());
162 }
163 }
164
165 pub(crate) fn sync_with_sound(&self, sound: &mut Sound) {
166 if let Ok(source) = self.native.state().try_get_source_mut(sound.native.get()) {
167 sound.status.set_value_silent(source.status());
169 sound
170 .playback_time
171 .set_value_silent(source.playback_time().as_secs_f32());
172 }
173 }
174
175 pub(crate) fn sync_to_sound(
176 &mut self,
177 sound_handle: Handle<Node>,
178 sound: &Sound,
179 node_overrides: Option<&FxHashSet<Handle<Node>>>,
180 ) {
181 if !sound.is_globally_enabled() || !node_overrides.is_none_or(|f| f.contains(&sound_handle))
182 {
183 self.remove_sound(sound.native.get(), &sound.name);
184 sound.native.set(Default::default());
185 return;
186 }
187
188 if sound.native.get().is_some() {
189 let mut state = self.native.state();
190 let source = state.source_mut(sound.native.get());
191 sound.buffer.try_sync_model(|v| {
192 Log::verify(source.set_buffer(v));
193 });
194 sound.max_distance.try_sync_model(|v| {
195 source.set_max_distance(v);
196 });
197 sound.rolloff_factor.try_sync_model(|v| {
198 source.set_rolloff_factor(v);
199 });
200 sound.radius.try_sync_model(|v| {
201 source.set_radius(v);
202 });
203 sound.playback_time.try_sync_model(|v| {
204 source.set_playback_time(Duration::from_secs_f32(v));
205 });
206 sound.pitch.try_sync_model(|v| {
207 source.set_pitch(v);
208 });
209 sound.looping.try_sync_model(|v| {
210 source.set_looping(v);
211 });
212 sound.panning.try_sync_model(|v| {
213 source.set_panning(v);
214 });
215 sound.gain.try_sync_model(|v| {
216 source.set_gain(v);
217 });
218 sound
219 .spatial_blend
220 .try_sync_model(|v| source.set_spatial_blend(v));
221 sound.status.try_sync_model(|v| match v {
222 Status::Stopped => {
223 Log::verify(source.stop());
224 }
225 Status::Playing => {
226 source.play();
227 }
228 Status::Paused => {
229 source.pause();
230 }
231 });
232 sound.audio_bus.try_sync_model(|audio_bus| {
233 source.set_bus(audio_bus);
234 });
235 } else {
236 match SoundSourceBuilder::new()
237 .with_gain(sound.gain())
238 .with_opt_buffer(sound.buffer())
239 .with_looping(sound.is_looping())
240 .with_panning(sound.panning())
241 .with_pitch(sound.pitch())
242 .with_status(sound.status())
243 .with_playback_time(Duration::from_secs_f32(sound.playback_time()))
244 .with_position(sound.global_position())
245 .with_radius(sound.radius())
246 .with_max_distance(sound.max_distance())
247 .with_bus(sound.audio_bus())
248 .with_rolloff_factor(sound.rolloff_factor())
249 .build()
250 {
251 Ok(source) => {
252 sound.native.set(self.native.state().add_source(source));
253
254 Log::writeln(
255 MessageKind::Information,
256 format!("Native sound source was created for node: {}", sound.name()),
257 );
258 }
259 Err(err) => {
260 Log::writeln(
261 MessageKind::Error,
262 format!(
263 "Unable to create native sound source for node: {}. Reason: {:?}",
264 sound.name(),
265 err
266 ),
267 );
268 }
269 }
270 }
271 }
272}