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::SceneGraph;
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)]
77#[reflect(derived_type = "Node")]
78pub struct Sound {
79 base: Base,
80
81 #[reflect(setter = "set_buffer")]
82 buffer: InheritableVariable<Option<SoundBufferResource>>,
83
84 #[reflect(setter = "set_play_once")]
85 play_once: InheritableVariable<bool>,
86
87 #[reflect(min_value = 0.0, step = 0.05)]
88 #[reflect(setter = "set_gain")]
89 gain: InheritableVariable<f32>,
90
91 #[reflect(min_value = -1.0, max_value = 1.0, step = 0.05)]
92 #[reflect(setter = "set_panning")]
93 panning: InheritableVariable<f32>,
94
95 #[reflect(setter = "set_status")]
96 pub(crate) status: InheritableVariable<Status>,
97
98 #[reflect(setter = "set_looping")]
99 looping: InheritableVariable<bool>,
100
101 #[reflect(min_value = 0.0, step = 0.05)]
102 #[reflect(setter = "set_pitch")]
103 pitch: InheritableVariable<f64>,
104
105 #[reflect(min_value = 0.0, step = 0.05)]
106 #[reflect(setter = "set_radius")]
107 radius: InheritableVariable<f32>,
108
109 #[reflect(min_value = 0.0, step = 0.05)]
110 #[reflect(setter = "set_max_distance")]
111 max_distance: InheritableVariable<f32>,
112
113 #[reflect(min_value = 0.0, step = 0.05)]
114 #[reflect(setter = "set_rolloff_factor")]
115 rolloff_factor: InheritableVariable<f32>,
116
117 #[visit(optional)]
118 #[reflect(setter = "set_playback_time", min_value = 0.0)]
119 playback_time: InheritableVariable<f32>,
120
121 #[reflect(setter = "set_spatial_blend")]
122 spatial_blend: InheritableVariable<f32>,
123
124 #[visit(optional)]
126 audio_bus: InheritableVariable<String>,
127
128 #[reflect(hidden)]
129 #[visit(skip)]
130 pub(crate) native: Cell<Handle<SoundSource>>,
131}
132
133impl Deref for Sound {
134 type Target = Base;
135
136 fn deref(&self) -> &Self::Target {
137 &self.base
138 }
139}
140
141impl DerefMut for Sound {
142 fn deref_mut(&mut self) -> &mut Self::Target {
143 &mut self.base
144 }
145}
146
147impl Default for Sound {
148 fn default() -> Self {
149 Self {
150 base: Default::default(),
151 buffer: InheritableVariable::new_modified(None),
152 play_once: InheritableVariable::new_modified(false),
153 gain: InheritableVariable::new_modified(1.0),
154 panning: InheritableVariable::new_modified(0.0),
155 status: InheritableVariable::new_modified(Status::Stopped),
156 looping: InheritableVariable::new_modified(false),
157 pitch: InheritableVariable::new_modified(1.0),
158 radius: InheritableVariable::new_modified(10.0),
159 max_distance: InheritableVariable::new_modified(f32::MAX),
160 rolloff_factor: InheritableVariable::new_modified(1.0),
161 playback_time: Default::default(),
162 spatial_blend: InheritableVariable::new_modified(1.0),
163 audio_bus: InheritableVariable::new_modified(AudioBusGraph::PRIMARY_BUS.to_string()),
164 native: Default::default(),
165 }
166 }
167}
168
169impl Clone for Sound {
170 fn clone(&self) -> Self {
171 Self {
172 base: self.base.clone(),
173 buffer: self.buffer.clone(),
174 play_once: self.play_once.clone(),
175 gain: self.gain.clone(),
176 panning: self.panning.clone(),
177 status: self.status.clone(),
178 looping: self.looping.clone(),
179 pitch: self.pitch.clone(),
180 radius: self.radius.clone(),
181 max_distance: self.max_distance.clone(),
182 rolloff_factor: self.rolloff_factor.clone(),
183 playback_time: self.playback_time.clone(),
184 spatial_blend: self.spatial_blend.clone(),
185 audio_bus: self.audio_bus.clone(),
186 native: Default::default(),
188 }
189 }
190}
191
192impl TypeUuidProvider for Sound {
193 fn type_uuid() -> Uuid {
194 uuid!("28621735-8cd1-4fad-8faf-ecd24bf8aa99")
195 }
196}
197
198impl Sound {
199 pub fn set_buffer(
202 &mut self,
203 buffer: Option<SoundBufferResource>,
204 ) -> Option<SoundBufferResource> {
205 self.buffer.set_value_and_mark_modified(buffer)
206 }
207
208 pub fn buffer(&self) -> Option<SoundBufferResource> {
210 (*self.buffer).clone()
211 }
212
213 pub fn set_play_once(&mut self, play_once: bool) -> bool {
221 self.play_once.set_value_and_mark_modified(play_once)
222 }
223
224 pub fn is_play_once(&self) -> bool {
226 *self.play_once
227 }
228
229 pub fn set_spatial_blend(&mut self, k: f32) -> f32 {
233 self.spatial_blend
234 .set_value_and_mark_modified(k.clamp(0.0, 1.0))
235 }
236
237 pub fn spatial_blend(&self) -> f32 {
239 *self.spatial_blend
240 }
241
242 pub fn set_gain(&mut self, gain: f32) -> f32 {
250 self.gain.set_value_and_mark_modified(gain)
251 }
252
253 pub fn gain(&self) -> f32 {
255 *self.gain
256 }
257
258 pub fn set_panning(&mut self, panning: f32) -> f32 {
261 self.panning
262 .set_value_and_mark_modified(panning.clamp(-1.0, 1.0))
263 }
264
265 pub fn panning(&self) -> f32 {
267 *self.panning
268 }
269
270 pub fn set_status(&mut self, status: Status) -> Status {
272 let prev = self.status();
273 match status {
274 Status::Stopped => self.stop(),
275 Status::Playing => self.play(),
276 Status::Paused => self.pause(),
277 }
278 prev
279 }
280
281 pub fn status(&self) -> Status {
283 *self.status
284 }
285
286 pub fn play(&mut self) {
288 self.status.set_value_and_mark_modified(Status::Playing);
289 }
290
291 pub fn try_play(&mut self) -> bool {
294 if *self.status == Status::Playing {
295 false
296 } else {
297 self.play();
298 true
299 }
300 }
301
302 pub fn pause(&mut self) {
304 self.status.set_value_and_mark_modified(Status::Paused);
305 }
306
307 pub fn set_looping(&mut self, looping: bool) -> bool {
310 self.looping.set_value_and_mark_modified(looping)
311 }
312
313 pub fn is_looping(&self) -> bool {
315 *self.looping
316 }
317
318 pub fn set_pitch(&mut self, pitch: f64) -> f64 {
320 self.pitch.set_value_and_mark_modified(pitch.abs())
321 }
322
323 pub fn pitch(&self) -> f64 {
325 *self.pitch
326 }
327
328 pub fn stop(&mut self) {
330 self.status.set_value_and_mark_modified(Status::Stopped);
331 }
332
333 pub fn playback_time(&self) -> f32 {
335 *self.playback_time
336 }
337
338 pub fn set_playback_time(&mut self, time: f32) -> f32 {
340 self.playback_time.set_value_and_mark_modified(time)
341 }
342
343 pub fn set_radius(&mut self, radius: f32) -> f32 {
345 self.radius.set_value_and_mark_modified(radius)
346 }
347
348 pub fn radius(&self) -> f32 {
350 *self.radius
351 }
352
353 pub fn set_rolloff_factor(&mut self, rolloff_factor: f32) -> f32 {
357 self.rolloff_factor
358 .set_value_and_mark_modified(rolloff_factor)
359 }
360
361 pub fn rolloff_factor(&self) -> f32 {
363 *self.rolloff_factor
364 }
365
366 pub fn set_max_distance(&mut self, max_distance: f32) -> f32 {
371 self.max_distance.set_value_and_mark_modified(max_distance)
372 }
373
374 pub fn max_distance(&self) -> f32 {
376 *self.max_distance
377 }
378
379 pub fn set_audio_bus(&mut self, name: String) {
381 self.audio_bus.set_value_and_mark_modified(name);
382 }
383
384 pub fn audio_bus(&self) -> &str {
386 &self.audio_bus
387 }
388}
389
390impl ConstructorProvider<Node, Graph> for Sound {
391 fn constructor() -> NodeConstructor {
392 NodeConstructor::new::<Self>()
393 .with_variant("Sound Source", |_| {
394 SoundBuilder::new(BaseBuilder::new().with_name("Sound Source"))
395 .build_node()
396 .into()
397 })
398 .with_group("Sound")
399 }
400}
401
402impl NodeTrait for Sound {
403 fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
404 AxisAlignedBoundingBox::unit()
405 }
406
407 fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
408 self.local_bounding_box()
409 .transform(&self.global_transform())
410 }
411
412 fn id(&self) -> Uuid {
413 Self::type_uuid()
414 }
415
416 fn on_removed_from_graph(&mut self, graph: &mut Graph) {
417 graph
418 .sound_context
419 .remove_sound(self.native.get(), &self.name);
420 self.native.set(Default::default());
421 }
422
423 fn sync_native(&self, _self_handle: Handle<Node>, context: &mut SyncContext) {
424 context.sound_context.sync_to_sound(
425 _self_handle,
426 self,
427 context.switches.and_then(|s| s.node_overrides.as_ref()),
428 )
429 }
430
431 fn on_global_transform_changed(
432 &self,
433 new_global_transform: &Matrix4<f32>,
434 context: &mut SyncContext,
435 ) {
436 if !m4x4_approx_eq(new_global_transform, &self.global_transform()) {
437 context.sound_context.set_sound_position(self);
438 }
439 }
440
441 fn is_alive(&self) -> bool {
442 if self.is_play_once() {
443 self.status() != Status::Stopped
444 } else {
445 true
446 }
447 }
448
449 fn update(&mut self, context: &mut UpdateContext) {
450 context.sound_context.sync_with_sound(self);
451 }
452
453 fn validate(&self, _scene: &Scene) -> Result<(), String> {
454 match self.buffer.as_ref() {
455 Some(buffer) => {
456 let header = buffer.header();
457 match header.state {
458 ResourceState::Pending { .. } | ResourceState::Ok { .. } => Ok(()),
459 ResourceState::Unloaded => {
460 Err("Sound buffer is unloaded because it was never requested.".to_string())
461 }
462 ResourceState::LoadError { ref error, .. } => match &error.0 {
463 None => {
464 Err("Sound buffer is failed to load, the reason is unknown!"
465 .to_string())
466 }
467 Some(err) => Err(format!("Sound buffer is failed to load. Reason: {err}")),
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<Sound> {
610 graph.add_node(self.build_node()).to_variant()
611 }
612}