1use crate::{
24 core::{
25 algebra::Matrix4,
26 math::{aabb::AxisAlignedBoundingBox, m4x4_approx_eq},
27 pool::Handle,
28 reflect::prelude::*,
29 type_traits::prelude::*,
30 uuid::{uuid, Uuid},
31 variable::InheritableVariable,
32 visitor::prelude::*,
33 },
34 define_with,
35 scene::{
36 base::{Base, BaseBuilder},
37 graph::Graph,
38 node::{Node, NodeTrait, SyncContext, UpdateContext},
39 },
40};
41
42pub use fyrox_sound::{
44 buffer::{
45 generic::Samples,
46 loader::{SoundBufferImportOptions, SoundBufferLoader},
47 DataSource, SoundBuffer, SoundBufferResource, SoundBufferResourceLoadError,
48 },
49 bus::*,
50 context::{DistanceModel, SAMPLE_RATE},
51 dsp::{filters::*, DelayLine},
52 effects::*,
53 engine::SoundEngine,
54 error::SoundError,
55 hrtf::HrirSphere,
56 renderer::{hrtf::*, Renderer},
57 source::Status,
58};
59
60use crate::scene::node::constructor::NodeConstructor;
61use crate::scene::Scene;
62use fyrox_graph::constructor::ConstructorProvider;
63use fyrox_graph::BaseSceneGraph;
64use fyrox_resource::state::ResourceState;
65use fyrox_sound::source::SoundSource;
66use std::{
67 cell::Cell,
68 ops::{Deref, DerefMut},
69 time::Duration,
70};
71
72pub mod context;
73pub mod listener;
74
75#[derive(Visit, Reflect, Debug, ComponentProvider)]
77pub struct Sound {
78 base: Base,
79
80 #[reflect(setter = "set_buffer")]
81 buffer: InheritableVariable<Option<SoundBufferResource>>,
82
83 #[reflect(setter = "set_play_once")]
84 play_once: InheritableVariable<bool>,
85
86 #[reflect(min_value = 0.0, step = 0.05)]
87 #[reflect(setter = "set_gain")]
88 gain: InheritableVariable<f32>,
89
90 #[reflect(min_value = -1.0, max_value = 1.0, step = 0.05)]
91 #[reflect(setter = "set_panning")]
92 panning: InheritableVariable<f32>,
93
94 #[reflect(setter = "set_status")]
95 pub(crate) status: InheritableVariable<Status>,
96
97 #[reflect(setter = "set_looping")]
98 looping: InheritableVariable<bool>,
99
100 #[reflect(min_value = 0.0, step = 0.05)]
101 #[reflect(setter = "set_pitch")]
102 pitch: InheritableVariable<f64>,
103
104 #[reflect(min_value = 0.0, step = 0.05)]
105 #[reflect(setter = "set_radius")]
106 radius: InheritableVariable<f32>,
107
108 #[reflect(min_value = 0.0, step = 0.05)]
109 #[reflect(setter = "set_max_distance")]
110 max_distance: InheritableVariable<f32>,
111
112 #[reflect(min_value = 0.0, step = 0.05)]
113 #[reflect(setter = "set_rolloff_factor")]
114 rolloff_factor: InheritableVariable<f32>,
115
116 #[visit(optional)]
117 #[reflect(setter = "set_playback_time", min_value = 0.0)]
118 playback_time: InheritableVariable<f32>,
119
120 #[reflect(setter = "set_spatial_blend")]
121 spatial_blend: InheritableVariable<f32>,
122
123 #[visit(optional)]
124 #[reflect(
125 description = "A name of a sound effect to which the sound will attach to when instantiated."
126 )]
127 audio_bus: InheritableVariable<String>,
128
129 #[reflect(hidden)]
130 #[visit(skip)]
131 pub(crate) native: Cell<Handle<SoundSource>>,
132}
133
134impl Deref for Sound {
135 type Target = Base;
136
137 fn deref(&self) -> &Self::Target {
138 &self.base
139 }
140}
141
142impl DerefMut for Sound {
143 fn deref_mut(&mut self) -> &mut Self::Target {
144 &mut self.base
145 }
146}
147
148impl Default for Sound {
149 fn default() -> Self {
150 Self {
151 base: Default::default(),
152 buffer: InheritableVariable::new_modified(None),
153 play_once: InheritableVariable::new_modified(false),
154 gain: InheritableVariable::new_modified(1.0),
155 panning: InheritableVariable::new_modified(0.0),
156 status: InheritableVariable::new_modified(Status::Stopped),
157 looping: InheritableVariable::new_modified(false),
158 pitch: InheritableVariable::new_modified(1.0),
159 radius: InheritableVariable::new_modified(10.0),
160 max_distance: InheritableVariable::new_modified(f32::MAX),
161 rolloff_factor: InheritableVariable::new_modified(1.0),
162 playback_time: Default::default(),
163 spatial_blend: InheritableVariable::new_modified(1.0),
164 audio_bus: InheritableVariable::new_modified(AudioBusGraph::PRIMARY_BUS.to_string()),
165 native: Default::default(),
166 }
167 }
168}
169
170impl Clone for Sound {
171 fn clone(&self) -> Self {
172 Self {
173 base: self.base.clone(),
174 buffer: self.buffer.clone(),
175 play_once: self.play_once.clone(),
176 gain: self.gain.clone(),
177 panning: self.panning.clone(),
178 status: self.status.clone(),
179 looping: self.looping.clone(),
180 pitch: self.pitch.clone(),
181 radius: self.radius.clone(),
182 max_distance: self.max_distance.clone(),
183 rolloff_factor: self.rolloff_factor.clone(),
184 playback_time: self.playback_time.clone(),
185 spatial_blend: self.spatial_blend.clone(),
186 audio_bus: self.audio_bus.clone(),
187 native: Default::default(),
189 }
190 }
191}
192
193impl TypeUuidProvider for Sound {
194 fn type_uuid() -> Uuid {
195 uuid!("28621735-8cd1-4fad-8faf-ecd24bf8aa99")
196 }
197}
198
199impl Sound {
200 pub fn set_buffer(
203 &mut self,
204 buffer: Option<SoundBufferResource>,
205 ) -> Option<SoundBufferResource> {
206 self.buffer.set_value_and_mark_modified(buffer)
207 }
208
209 pub fn buffer(&self) -> Option<SoundBufferResource> {
211 (*self.buffer).clone()
212 }
213
214 pub fn set_play_once(&mut self, play_once: bool) -> bool {
222 self.play_once.set_value_and_mark_modified(play_once)
223 }
224
225 pub fn is_play_once(&self) -> bool {
227 *self.play_once
228 }
229
230 pub fn set_spatial_blend(&mut self, k: f32) -> f32 {
234 self.spatial_blend
235 .set_value_and_mark_modified(k.clamp(0.0, 1.0))
236 }
237
238 pub fn spatial_blend(&self) -> f32 {
240 *self.spatial_blend
241 }
242
243 pub fn set_gain(&mut self, gain: f32) -> f32 {
251 self.gain.set_value_and_mark_modified(gain)
252 }
253
254 pub fn gain(&self) -> f32 {
256 *self.gain
257 }
258
259 pub fn set_panning(&mut self, panning: f32) -> f32 {
262 self.panning
263 .set_value_and_mark_modified(panning.clamp(-1.0, 1.0))
264 }
265
266 pub fn panning(&self) -> f32 {
268 *self.panning
269 }
270
271 pub fn set_status(&mut self, status: Status) -> Status {
273 let prev = self.status();
274 match status {
275 Status::Stopped => self.stop(),
276 Status::Playing => self.play(),
277 Status::Paused => self.pause(),
278 }
279 prev
280 }
281
282 pub fn status(&self) -> Status {
284 *self.status
285 }
286
287 pub fn play(&mut self) {
289 self.status.set_value_and_mark_modified(Status::Playing);
290 }
291
292 pub fn try_play(&mut self) -> bool {
295 if *self.status == Status::Playing {
296 false
297 } else {
298 self.play();
299 true
300 }
301 }
302
303 pub fn pause(&mut self) {
305 self.status.set_value_and_mark_modified(Status::Paused);
306 }
307
308 pub fn set_looping(&mut self, looping: bool) -> bool {
311 self.looping.set_value_and_mark_modified(looping)
312 }
313
314 pub fn is_looping(&self) -> bool {
316 *self.looping
317 }
318
319 pub fn set_pitch(&mut self, pitch: f64) -> f64 {
321 self.pitch.set_value_and_mark_modified(pitch.abs())
322 }
323
324 pub fn pitch(&self) -> f64 {
326 *self.pitch
327 }
328
329 pub fn stop(&mut self) {
331 self.status.set_value_and_mark_modified(Status::Stopped);
332 }
333
334 pub fn playback_time(&self) -> f32 {
336 *self.playback_time
337 }
338
339 pub fn set_playback_time(&mut self, time: f32) -> f32 {
341 self.playback_time.set_value_and_mark_modified(time)
342 }
343
344 pub fn set_radius(&mut self, radius: f32) -> f32 {
346 self.radius.set_value_and_mark_modified(radius)
347 }
348
349 pub fn radius(&self) -> f32 {
351 *self.radius
352 }
353
354 pub fn set_rolloff_factor(&mut self, rolloff_factor: f32) -> f32 {
358 self.rolloff_factor
359 .set_value_and_mark_modified(rolloff_factor)
360 }
361
362 pub fn rolloff_factor(&self) -> f32 {
364 *self.rolloff_factor
365 }
366
367 pub fn set_max_distance(&mut self, max_distance: f32) -> f32 {
372 self.max_distance.set_value_and_mark_modified(max_distance)
373 }
374
375 pub fn max_distance(&self) -> f32 {
377 *self.max_distance
378 }
379
380 pub fn set_audio_bus(&mut self, name: String) {
382 self.audio_bus.set_value_and_mark_modified(name);
383 }
384
385 pub fn audio_bus(&self) -> &str {
387 &self.audio_bus
388 }
389}
390
391impl ConstructorProvider<Node, Graph> for Sound {
392 fn constructor() -> NodeConstructor {
393 NodeConstructor::new::<Self>()
394 .with_variant("Sound Source", |_| {
395 SoundBuilder::new(BaseBuilder::new().with_name("Sound Source"))
396 .build_node()
397 .into()
398 })
399 .with_group("Sound")
400 }
401}
402
403impl NodeTrait for Sound {
404 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
405 AxisAlignedBoundingBox::unit()
406 }
407
408 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
409 self.local_bounding_box()
410 .transform(&self.global_transform())
411 }
412
413 fn id(&self) -> Uuid {
414 Self::type_uuid()
415 }
416
417 fn on_removed_from_graph(&mut self, graph: &mut Graph) {
418 graph
419 .sound_context
420 .remove_sound(self.native.get(), &self.name);
421 self.native.set(Default::default());
422 }
423
424 fn sync_native(&self, _self_handle: Handle<Node>, context: &mut SyncContext) {
425 context.sound_context.sync_to_sound(
426 _self_handle,
427 self,
428 context.switches.and_then(|s| s.node_overrides.as_ref()),
429 )
430 }
431
432 fn on_global_transform_changed(
433 &self,
434 new_global_transform: &Matrix4<f32>,
435 context: &mut SyncContext,
436 ) {
437 if !m4x4_approx_eq(new_global_transform, &self.global_transform()) {
438 context.sound_context.set_sound_position(self);
439 }
440 }
441
442 fn is_alive(&self) -> bool {
443 if self.is_play_once() {
444 self.status() != Status::Stopped
445 } else {
446 true
447 }
448 }
449
450 fn update(&mut self, context: &mut UpdateContext) {
451 context.sound_context.sync_with_sound(self);
452 }
453
454 fn validate(&self, _scene: &Scene) -> Result<(), String> {
455 match self.buffer.as_ref() {
456 Some(buffer) => {
457 let header = buffer.header();
458 match header.state {
459 ResourceState::Pending { .. } | ResourceState::Ok(_) => Ok(()),
460 ResourceState::LoadError { ref error, .. } => {
461 match &error.0 {
462 None => Err("Sound buffer is failed to load, the reason is unknown!"
463 .to_string()),
464 Some(err) => {
465 Err(format!("Sound buffer is failed to load. Reason: {err:?}"))
466 }
467 }
468 }
469 }
470 }
471 None => Err("Sound buffer is not set, the sound won't play!".to_string()),
472 }
473 }
474}
475
476pub struct SoundBuilder {
478 base_builder: BaseBuilder,
479 buffer: Option<SoundBufferResource>,
480 play_once: bool,
481 gain: f32,
482 panning: f32,
483 status: Status,
484 looping: bool,
485 pitch: f64,
486 radius: f32,
487 max_distance: f32,
488 rolloff_factor: f32,
489 playback_time: Duration,
490 spatial_blend: f32,
491 audio_bus: String,
492}
493
494impl SoundBuilder {
495 pub fn new(base_builder: BaseBuilder) -> Self {
497 Self {
498 base_builder,
499 buffer: None,
500 play_once: false,
501 gain: 1.0,
502 panning: 0.0,
503 status: Status::Stopped,
504 looping: false,
505 pitch: 1.0,
506 radius: 10.0,
507 max_distance: f32::MAX,
508 rolloff_factor: 1.0,
509 spatial_blend: 1.0,
510 playback_time: Default::default(),
511 audio_bus: AudioBusGraph::PRIMARY_BUS.to_string(),
512 }
513 }
514
515 define_with!(
516 fn with_buffer(buffer: Option<SoundBufferResource>)
518 );
519
520 define_with!(
521 fn with_play_once(play_once: bool)
523 );
524
525 define_with!(
526 fn with_gain(gain: f32)
528 );
529
530 define_with!(
531 fn with_panning(panning: f32)
533 );
534
535 define_with!(
536 fn with_status(status: Status)
538 );
539
540 define_with!(
541 fn with_looping(looping: bool)
543 );
544
545 define_with!(
546 fn with_pitch(pitch: f64)
548 );
549
550 define_with!(
551 fn with_radius(radius: f32)
553 );
554
555 define_with!(
556 fn with_max_distance(max_distance: f32)
558 );
559
560 define_with!(
561 fn with_rolloff_factor(rolloff_factor: f32)
563 );
564
565 define_with!(
566 fn with_spatial_blend_factor(spatial_blend: f32)
568 );
569
570 define_with!(
571 fn with_playback_time(playback_time: Duration)
573 );
574
575 define_with!(
576 fn with_audio_bus(audio_bus: String)
578 );
579
580 #[must_use]
582 pub fn build_sound(self) -> Sound {
583 Sound {
584 base: self.base_builder.build_base(),
585 buffer: self.buffer.into(),
586 play_once: self.play_once.into(),
587 gain: self.gain.into(),
588 panning: self.panning.into(),
589 status: self.status.into(),
590 looping: self.looping.into(),
591 pitch: self.pitch.into(),
592 radius: self.radius.into(),
593 max_distance: self.max_distance.into(),
594 rolloff_factor: self.rolloff_factor.into(),
595 playback_time: self.playback_time.as_secs_f32().into(),
596 spatial_blend: self.spatial_blend.into(),
597 audio_bus: self.audio_bus.into(),
598 native: Default::default(),
599 }
600 }
601
602 #[must_use]
604 pub fn build_node(self) -> Node {
605 Node::new(self.build_sound())
606 }
607
608 pub fn build(self, graph: &mut Graph) -> Handle<Node> {
610 graph.add_node(self.build_node())
611 }
612}