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 Some(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 Some(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()
182 || !node_overrides.map_or(true, |f| f.contains(&sound_handle))
183 {
184 self.remove_sound(sound.native.get(), &sound.name);
185 sound.native.set(Default::default());
186 return;
187 }
188
189 if sound.native.get().is_some() {
190 let mut state = self.native.state();
191 let source = state.source_mut(sound.native.get());
192 sound.buffer.try_sync_model(|v| {
193 Log::verify(source.set_buffer(v));
194 });
195 sound.max_distance.try_sync_model(|v| {
196 source.set_max_distance(v);
197 });
198 sound.rolloff_factor.try_sync_model(|v| {
199 source.set_rolloff_factor(v);
200 });
201 sound.radius.try_sync_model(|v| {
202 source.set_radius(v);
203 });
204 sound.playback_time.try_sync_model(|v| {
205 source.set_playback_time(Duration::from_secs_f32(v));
206 });
207 sound.pitch.try_sync_model(|v| {
208 source.set_pitch(v);
209 });
210 sound.looping.try_sync_model(|v| {
211 source.set_looping(v);
212 });
213 sound.panning.try_sync_model(|v| {
214 source.set_panning(v);
215 });
216 sound.gain.try_sync_model(|v| {
217 source.set_gain(v);
218 });
219 sound
220 .spatial_blend
221 .try_sync_model(|v| source.set_spatial_blend(v));
222 sound.status.try_sync_model(|v| match v {
223 Status::Stopped => {
224 Log::verify(source.stop());
225 }
226 Status::Playing => {
227 source.play();
228 }
229 Status::Paused => {
230 source.pause();
231 }
232 });
233 sound.audio_bus.try_sync_model(|audio_bus| {
234 source.set_bus(audio_bus);
235 });
236 } else {
237 match SoundSourceBuilder::new()
238 .with_gain(sound.gain())
239 .with_opt_buffer(sound.buffer())
240 .with_looping(sound.is_looping())
241 .with_panning(sound.panning())
242 .with_pitch(sound.pitch())
243 .with_status(sound.status())
244 .with_playback_time(Duration::from_secs_f32(sound.playback_time()))
245 .with_position(sound.global_position())
246 .with_radius(sound.radius())
247 .with_max_distance(sound.max_distance())
248 .with_bus(sound.audio_bus())
249 .with_rolloff_factor(sound.rolloff_factor())
250 .build()
251 {
252 Ok(source) => {
253 sound.native.set(self.native.state().add_source(source));
254
255 Log::writeln(
256 MessageKind::Information,
257 format!("Native sound source was created for node: {}", sound.name()),
258 );
259 }
260 Err(err) => {
261 Log::writeln(
262 MessageKind::Error,
263 format!(
264 "Unable to create native sound source for node: {}. Reason: {:?}",
265 sound.name(),
266 err
267 ),
268 );
269 }
270 }
271 }
272 }
273}