1use kira::{
13 Easing, StartTime, Tween,
14 clock::ClockTime,
15 sound::PlaybackState,
16 track::{SpatialTrackHandle, TrackBuilder, TrackHandle},
17};
18use serde::Deserialize;
19use std::time::Duration;
20use strum::EnumIter;
21use tracing::warn;
22use vek::*;
23
24use crate::audio;
25
26use super::soundcache::{AnySoundData, AnySoundHandle};
27
28pub const SFX_DIST_LIMIT: f32 = 200.0;
34pub const SFX_DIST_LIMIT_SQR: f32 = SFX_DIST_LIMIT * SFX_DIST_LIMIT;
35
36pub fn calculate_player_attenuation(player_pos: Vec3<f32>, emitter_pos: Vec3<f32>) -> f32 {
37 1.0 - (player_pos.distance(emitter_pos) * (1.0 / SFX_DIST_LIMIT))
38 .clamp(0.0, 1.0)
39 .sqrt()
40}
41
42#[derive(PartialEq, Clone, Copy, Hash, Eq, Deserialize, Debug)]
48pub enum MusicChannelTag {
49 TitleMusic,
50 Exploration,
51 Combat,
52}
53
54pub struct MusicChannel {
57 tag: MusicChannelTag,
58 track: TrackHandle,
59 source: Option<AnySoundHandle>,
60 length: f32,
61 loop_data: (bool, LoopPoint, LoopPoint), }
63
64#[derive(Clone, Copy, Debug, PartialEq)]
65pub enum LoopPoint {
66 Start,
67 End,
68 Point(f64),
69}
70
71impl MusicChannel {
72 pub fn new(route_to: &mut TrackHandle) -> Result<Self, kira::ResourceLimitReached> {
73 let track = route_to.add_sub_track(TrackBuilder::new().volume(audio::to_decibels(0.0)))?;
74 Ok(Self {
75 tag: MusicChannelTag::TitleMusic,
76 track,
77 source: None,
78 length: 0.0,
79 loop_data: (false, LoopPoint::Start, LoopPoint::End),
80 })
81 }
82
83 pub fn set_tag(&mut self, tag: MusicChannelTag) { self.tag = tag; }
84
85 pub fn set_source(&mut self, source_handle: Option<AnySoundHandle>) {
86 self.source = source_handle;
87 }
88
89 pub fn set_length(&mut self, length: f32) { self.length = length; }
90
91 pub fn get_loop_data(&self) -> (bool, LoopPoint, LoopPoint) { self.loop_data }
93
94 pub fn set_loop_data(&mut self, loops: bool, start: LoopPoint, end: LoopPoint) {
96 if let Some(source) = self.source.as_mut() {
97 self.loop_data = (loops, start, end);
98 if loops {
99 match (start, end) {
100 (LoopPoint::Start, LoopPoint::End) => {
101 source.set_loop_region(0.0..);
102 },
103 (LoopPoint::Start, LoopPoint::Point(end)) => {
104 source.set_loop_region(..end);
105 },
106 (LoopPoint::Point(start), LoopPoint::End) => {
107 source.set_loop_region(start..);
108 },
109 (LoopPoint::Point(start), LoopPoint::Point(end)) => {
110 source.set_loop_region(start..end);
111 },
112 _ => {
113 warn!("Invalid loop points given")
114 },
115 }
116 } else {
117 source.set_loop_region(None);
118 }
119 }
120 }
121
122 pub fn play(
123 &mut self,
124 mut source: AnySoundData,
125 now: ClockTime,
126 fade_in: Option<f32>,
127 delay: Option<f32>,
128 ) {
129 if let Some(fade_in) = fade_in {
130 let fade_in_tween = Tween {
131 duration: Duration::from_secs_f32(fade_in),
132 ..Default::default()
133 };
134 source = source.fade_in_tween(fade_in_tween);
135 }
136
137 if let Some(delay) = delay {
138 source = source.start_time(now + delay as f64);
139 }
140
141 match self.track.play(source) {
142 Ok(handle) => self.source = Some(handle),
143 Err(e) => {
144 warn!(?e, "Cannot play music")
145 },
146 }
147 }
148
149 pub fn stop(&mut self, duration: Option<f32>, delay: Option<f32>) {
152 if let Some(source) = self.source.as_mut() {
153 let tween = Tween {
154 duration: Duration::from_secs_f32(duration.unwrap_or(0.1)),
155 start_time: StartTime::Delayed(Duration::from_secs_f32(delay.unwrap_or(0.0))),
156 ..Default::default()
157 };
158 source.stop(tween)
159 };
160 }
161
162 pub fn set_volume(&mut self, volume: f32) {
164 self.track
165 .set_volume(audio::to_decibels(volume), Tween::default());
166 }
167
168 pub fn fade_to(&mut self, volume: f32, duration: f32, delay: Option<f32>) {
171 let mut start_time = StartTime::Immediate;
172 if let Some(delay) = delay {
173 start_time = StartTime::Delayed(Duration::from_secs_f32(delay))
174 }
175 let tween = Tween {
176 start_time,
177 duration: Duration::from_secs_f32(duration),
178 easing: Easing::Linear,
179 };
180 self.track.set_volume(audio::to_decibels(volume), tween);
181 }
182
183 pub fn fade_out(&mut self, duration: f32, delay: Option<f32>) {
186 self.stop(Some(duration), delay);
187 }
188
189 pub fn is_done(&self) -> bool {
192 self.source
193 .as_ref()
194 .is_none_or(|source| source.state() == PlaybackState::Stopped)
195 }
196
197 pub fn get_tag(&self) -> MusicChannelTag { self.tag }
198
199 pub fn get_track(&mut self) -> &mut TrackHandle { &mut self.track }
201
202 pub fn get_source(&mut self) -> Option<&mut AnySoundHandle> { self.source.as_mut() }
203
204 pub fn get_length(&self) -> f32 { self.length }
205}
206
207#[derive(Debug, PartialEq, Eq, Clone, Copy, Deserialize, EnumIter)]
210pub enum AmbienceChannelTag {
211 Wind,
212 Rain,
213 ThunderRumbling,
214 Leaves,
215 Cave,
216 Thunder,
217}
218
219#[derive(Debug)]
222pub struct AmbienceChannel {
223 tag: AmbienceChannelTag,
224 target_volume: f32,
225 track: TrackHandle,
226 source: Option<AnySoundHandle>,
227 pub looping: bool,
228}
229
230impl AmbienceChannel {
231 pub fn new(
232 tag: AmbienceChannelTag,
233 init_volume: f32,
234 route_to: &mut TrackHandle,
235 looping: bool,
236 ) -> Result<Self, kira::ResourceLimitReached> {
237 let ambience_track_builder = TrackBuilder::new();
238 let track =
239 route_to.add_sub_track(ambience_track_builder.volume(audio::to_decibels(0.0)))?;
240
241 Ok(Self {
242 tag,
243 target_volume: init_volume,
244 track,
245 source: None,
246 looping,
247 })
248 }
249
250 pub fn set_source(&mut self, source_handle: Option<AnySoundHandle>) {
251 self.source = source_handle;
252 }
253
254 pub fn play(&mut self, mut source: AnySoundData, fade_in: Option<f32>, delay: Option<f32>) {
255 let mut tween = Tween::default();
256 if let Some(fade_in) = fade_in {
257 tween.duration = Duration::from_secs_f32(fade_in);
258 }
259 if let Some(delay) = delay {
260 tween.start_time = StartTime::Delayed(Duration::from_secs_f32(delay));
261 }
262 source = source.fade_in_tween(tween);
263 match self.track.play(source) {
264 Ok(handle) => self.source = Some(handle),
265 Err(e) => {
266 warn!(?e, "Cannot play ambience")
267 },
268 }
269 }
270
271 pub fn stop(&mut self, duration: Option<f32>, delay: Option<f32>) {
274 if let Some(source) = self.source.as_mut() {
275 let tween = Tween {
276 duration: Duration::from_secs_f32(duration.unwrap_or(0.1)),
277 start_time: StartTime::Delayed(Duration::from_secs_f32(delay.unwrap_or(0.0))),
278 ..Default::default()
279 };
280 source.stop(tween)
281 }
282 }
283
284 pub fn fade_to(&mut self, volume: f32, duration: f32) {
286 self.track.set_volume(audio::to_decibels(volume), Tween {
287 start_time: StartTime::Immediate,
288 duration: Duration::from_secs_f32(duration),
289 easing: Easing::Linear,
290 });
291 self.target_volume = volume;
292 }
293
294 pub fn get_source(&mut self) -> Option<&mut AnySoundHandle> { self.source.as_mut() }
295
296 pub fn get_track(&self) -> &TrackHandle { &self.track }
299
300 pub fn get_track_mut(&mut self) -> &mut TrackHandle { &mut self.track }
302
303 pub fn get_target_volume(&self) -> f32 { self.target_volume }
306
307 pub fn get_tag(&self) -> AmbienceChannelTag { self.tag }
308
309 pub fn set_tag(&mut self, tag: AmbienceChannelTag) { self.tag = tag }
310
311 pub fn is_active(&self) -> bool { self.get_target_volume() == 0.0 }
312
313 pub fn is_stopped(&self) -> bool {
314 if let Some(source) = self.source.as_ref() {
315 source.state() == PlaybackState::Stopped
316 } else {
317 false
318 }
319 }
320}
321
322#[derive(Debug)]
328pub struct SfxChannel {
329 track: Option<SpatialTrackHandle>,
330 source: Option<AnySoundHandle>,
331 source_initial_volume: f32,
332 pos: Vec3<f32>,
333 pub play_counter: usize,
335}
336
337impl SfxChannel {
338 pub fn new() -> Self {
339 Self {
340 track: None,
341 source: None,
342 source_initial_volume: 0.0,
343 pos: Vec3::zero(),
344 play_counter: 0,
345 }
346 }
347
348 pub fn drop_track(&mut self) { self.track = None; }
349
350 pub fn set_source(&mut self, source_handle: Option<AnySoundHandle>) {
351 self.source = source_handle;
352 }
353
354 pub fn set_source_volume(&mut self, volume: f32) {
358 let tween = Tween {
359 duration: Duration::from_secs_f32(0.0),
360 ..Default::default()
361 };
362 if let Some(source) = self.source.as_mut() {
363 source.set_volume(audio::to_decibels(volume), tween);
364 }
365 }
366
367 pub fn play(
368 &mut self,
369 source: AnySoundData,
370 volume: f32,
371 mut track: SpatialTrackHandle,
372 ) -> usize {
373 match track.play(source) {
374 Ok(handle) => {
375 self.source = Some(handle);
376 self.source_initial_volume = volume
377 },
378 Err(e) => {
379 warn!(?e, "Cannot play sfx")
380 },
381 }
382 self.track = Some(track);
383 self.play_counter += 1;
384 self.play_counter
385 }
386
387 pub fn stop(&mut self) {
388 if let Some(source) = self.source.as_mut() {
389 source.stop(Tween::default())
390 }
391 }
392
393 pub fn set_volume(&mut self, volume: f32, duration: Option<f32>) {
395 if let Some(track) = self.track.as_mut() {
396 let tween = Tween {
397 duration: Duration::from_secs_f32(duration.unwrap_or(0.0)),
398 ..Default::default()
399 };
400 track.set_volume(audio::to_decibels(volume), tween)
401 }
402 }
403
404 pub fn set_pos(&mut self, pos: Vec3<f32>) { self.pos = pos; }
405
406 pub fn is_done(&self) -> bool {
407 self.source
408 .as_ref()
409 .is_none_or(|source| source.state() == PlaybackState::Stopped)
410 }
411
412 pub fn update(&mut self, player_pos: Vec3<f32>) {
414 if let Some(track) = self.track.as_mut() {
415 let tween = Tween {
416 duration: Duration::from_secs_f32(0.0),
417 ..Default::default()
418 };
419 track.set_position(self.pos, tween);
420 }
421
422 let ratio = calculate_player_attenuation(player_pos, self.pos);
425 self.set_source_volume(self.source_initial_volume * 5.0 * ratio);
426 }
427}
428
429impl Default for SfxChannel {
430 fn default() -> Self { Self::new() }
431}
432
433#[derive(Eq, PartialEq, Copy, Clone, Debug)]
434pub enum UiChannelTag {
435 LevelUp,
436}
437
438pub struct UiChannel {
442 track: TrackHandle,
443 source: Option<AnySoundHandle>,
444 pub tag: Option<UiChannelTag>,
445}
446
447impl UiChannel {
448 pub fn new(route_to: &mut TrackHandle) -> Result<Self, kira::ResourceLimitReached> {
449 let track = route_to.add_sub_track(TrackBuilder::default())?;
450 Ok(Self {
451 track,
452 source: None,
453 tag: None,
454 })
455 }
456
457 pub fn set_source(&mut self, source_handle: Option<AnySoundHandle>) {
458 self.source = source_handle;
459 }
460
461 pub fn play(&mut self, source: AnySoundData, tag: Option<UiChannelTag>) {
462 match self.track.play(source) {
463 Ok(handle) => {
464 self.source = Some(handle);
465 self.tag = tag;
466 },
467 Err(e) => {
468 warn!(?e, "Cannot play ui sfx")
469 },
470 }
471 }
472
473 pub fn stop(&mut self) {
474 if let Some(source) = self.source.as_mut() {
475 source.stop(Tween::default())
476 }
477 }
478
479 pub fn set_volume(&mut self, volume: f32) {
480 self.track
481 .set_volume(audio::to_decibels(volume), Tween::default())
482 }
483
484 pub fn is_done(&self) -> bool {
485 self.source
486 .as_ref()
487 .is_none_or(|source| source.state() == PlaybackState::Stopped)
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use crate::audio::channel::{SFX_DIST_LIMIT, SFX_DIST_LIMIT_SQR};
494
495 #[test]
496 fn test_sfx_dist_limit_eq_sfx_dist_limit_sqr() {
498 assert!(SFX_DIST_LIMIT.powf(2.0) == SFX_DIST_LIMIT_SQR)
499 }
500}