rg3d_sound/source/
mod.rs

1//! Sound source module.
2//!
3//! # Overview
4//!
5//! Sound source is a "work horse" of the engine. It allows you to play sounds, apply effects (such as positioning, HRTF,
6//! etc.), control volume, pitch, panning and other. Exact behaviour defined by a variant of sound buffer (generic or
7//! spatial). See docs at those modules for more info.
8
9use crate::source::{generic::GenericSource, spatial::SpatialSource};
10use rg3d_core::inspect::{Inspect, PropertyInfo};
11use rg3d_core::visitor::{Visit, VisitError, VisitResult, Visitor};
12use std::ops::{Deref, DerefMut};
13
14pub mod generic;
15pub mod spatial;
16
17/// Status (state) of sound source.
18#[derive(Eq, PartialEq, Copy, Clone, Debug, Inspect)]
19#[repr(u32)]
20pub enum Status {
21    /// Sound is stopped - it won't produces any sample and won't load mixer. This is default
22    /// state of all sound sources.
23    Stopped = 0,
24
25    /// Sound is playing.
26    Playing = 1,
27
28    /// Sound is paused, it can stay in this state any amount if time. Playback can be continued by
29    /// setting `Playing` status.
30    Paused = 2,
31}
32
33/// See module docs.
34#[derive(Debug, Clone)]
35pub enum SoundSource {
36    /// See `generic` module docs.
37    Generic(GenericSource),
38
39    /// See `spatial` module docs.
40    Spatial(SpatialSource),
41}
42
43impl Inspect for SoundSource {
44    fn properties(&self) -> Vec<PropertyInfo<'_>> {
45        match self {
46            SoundSource::Generic(v) => v.properties(),
47            SoundSource::Spatial(v) => v.properties(),
48        }
49    }
50}
51
52impl SoundSource {
53    /// Tries to "cast" sound source to spatial source. It will panic if this is not spatial source.
54    /// This is useful method for situations where you definitely know that source is spatial. So there
55    /// is no need to use pattern matching to take reference as a spatial source.
56    pub fn spatial(&self) -> &SpatialSource {
57        match self {
58            SoundSource::Generic(_) => panic!("Cast as spatial sound failed!"),
59            SoundSource::Spatial(ref spatial) => spatial,
60        }
61    }
62
63    /// Tries to "cast" sound source to spatial source. It will panic if this is not spatial source.
64    /// This is useful method for situations where you definitely know that source is spatial. So there
65    /// is no need to use pattern matching to take reference as a spatial source.
66    pub fn spatial_mut(&mut self) -> &mut SpatialSource {
67        match self {
68            SoundSource::Generic(_) => panic!("Cast as spatial sound failed!"),
69            SoundSource::Spatial(ref mut spatial) => spatial,
70        }
71    }
72}
73
74impl Deref for SoundSource {
75    type Target = GenericSource;
76
77    /// Returns shared reference to generic source of each sound source variant. It is possible because
78    /// `Spatial` sources are composed using generic source.
79    fn deref(&self) -> &Self::Target {
80        match self {
81            SoundSource::Generic(v) => v,
82            SoundSource::Spatial(v) => v,
83        }
84    }
85}
86
87impl DerefMut for SoundSource {
88    /// Returns mutable reference to generic source of each sound source variant. It is possible because
89    /// `Spatial` sources are composed using generic source.
90    fn deref_mut(&mut self) -> &mut Self::Target {
91        match self {
92            SoundSource::Generic(v) => v,
93            SoundSource::Spatial(v) => v,
94        }
95    }
96}
97
98impl Visit for Status {
99    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
100        let mut kind = *self as u8;
101
102        kind.visit(name, visitor)?;
103
104        if visitor.is_reading() {
105            *self = match kind {
106                0 => Status::Stopped,
107                1 => Status::Playing,
108                2 => Status::Paused,
109                _ => return Err(VisitError::User("invalid status".to_string())),
110            }
111        }
112
113        Ok(())
114    }
115}
116
117impl Visit for SoundSource {
118    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
119        visitor.enter_region(name)?;
120
121        let mut kind: u8 = match self {
122            SoundSource::Generic(_) => 0,
123            SoundSource::Spatial(_) => 1,
124        };
125
126        kind.visit("Id", visitor)?;
127
128        if visitor.is_reading() {
129            *self = match kind {
130                0 => SoundSource::Generic(GenericSource::default()),
131                1 => SoundSource::Spatial(SpatialSource::default()),
132                _ => return Err(VisitError::User("invalid source kind".to_string())),
133            }
134        }
135
136        match self {
137            SoundSource::Generic(generic) => generic.visit("Content", visitor)?,
138            SoundSource::Spatial(spatial) => spatial.visit("Content", visitor)?,
139        }
140
141        visitor.leave_region()
142    }
143}
144
145impl Default for SoundSource {
146    fn default() -> Self {
147        SoundSource::Generic(Default::default())
148    }
149}