concinnity_asset/
audio_emitter.rs1use crate::{AssetId, AudioBus, AudioClipHandle, de_opt_asset_ref, de_opt_audio_clip_handle};
4
5#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
24#[serde(default)]
25pub struct AudioEmitter {
26 #[serde(deserialize_with = "de_opt_audio_clip_handle")]
28 pub clip: Option<AudioClipHandle>,
29 pub position: [f32; 3],
31 pub volume: f32,
33 pub looping: bool,
35 #[serde(deserialize_with = "de_opt_asset_ref")]
37 pub prop: Option<AssetId>,
38 pub min_distance: f32,
40 pub max_distance: f32,
43 pub rolloff: Rolloff,
45 pub bus: Option<AudioBus>,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
51#[serde(rename_all = "lowercase")]
52pub enum Rolloff {
53 #[default]
55 Logarithmic,
56 Linear,
58 None,
60}
61
62impl Default for AudioEmitter {
63 fn default() -> Self {
64 Self {
65 clip: None,
66 position: [0.0; 3],
67 volume: 1.0,
68 looping: true,
69 prop: None,
70 min_distance: 1.0,
71 max_distance: 50.0,
72 rolloff: Rolloff::Logarithmic,
73 bus: None,
74 }
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn a_blank_emitter_loops_at_the_origin() {
84 let e = AudioEmitter::default();
86 assert!(e.looping);
87 assert_eq!(e.volume, 1.0);
88 assert_eq!(e.position, [0.0, 0.0, 0.0]);
89 assert!(e.clip.is_none());
90 assert!(e.prop.is_none());
91 assert_eq!(e.min_distance, 1.0);
92 assert_eq!(e.max_distance, 50.0);
93 assert_eq!(e.rolloff, Rolloff::Logarithmic);
94 assert!(e.bus.is_none());
95 }
96
97 #[test]
98 fn an_emitter_attached_to_a_prop_parses_and_round_trips_through_postcard() {
99 crate::test_support::install_resolvers();
100 let e: AudioEmitter = serde_json::from_str(
101 r#"{"clip":"hum","prop":"lamp","position":[1,2,3],"volume":0.5,"looping":false}"#,
102 )
103 .unwrap();
104 assert_eq!(e.clip, Some(AudioClipHandle(3)));
105 assert_eq!(e.prop, Some(AssetId(4)));
106 assert_eq!(e.position, [1.0, 2.0, 3.0]);
107 assert!(!e.looping);
108
109 let bytes = postcard::to_allocvec(&e).unwrap();
110 let back: AudioEmitter = postcard::from_bytes(&bytes).unwrap();
111 assert_eq!(back.clip, Some(AudioClipHandle(3)));
112 assert_eq!(back.prop, Some(AssetId(4)));
113 assert_eq!(back.volume, 0.5);
114 }
115
116 #[test]
117 fn authored_rolloff_and_bus_parse_and_round_trip() {
118 crate::test_support::install_resolvers();
119 let e: AudioEmitter = serde_json::from_str(
120 r#"{"clip":"hum","min_distance":2.5,"max_distance":80.0,"rolloff":"linear","bus":"voice"}"#,
121 )
122 .unwrap();
123 assert_eq!(e.min_distance, 2.5);
124 assert_eq!(e.max_distance, 80.0);
125 assert_eq!(e.rolloff, Rolloff::Linear);
126 assert_eq!(e.bus, Some(crate::AudioBus::Voice));
127
128 let bytes = postcard::to_allocvec(&e).unwrap();
129 let back: AudioEmitter = postcard::from_bytes(&bytes).unwrap();
130 assert_eq!(back.max_distance, 80.0);
131 assert_eq!(back.rolloff, Rolloff::Linear);
132 assert_eq!(back.bus, Some(crate::AudioBus::Voice));
133
134 let none: AudioEmitter = serde_json::from_str(r#"{"rolloff":"none"}"#).unwrap();
135 assert_eq!(none.rolloff, Rolloff::None);
136 }
137}