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 = 256.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 .powf(1.0 / 2.0)
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)]
209pub enum AmbienceChannelTag {
210 Wind,
211 Rain,
212 ThunderRumbling,
213 Leaves,
214 Cave,
215 Thunder,
216 RiverLoud,
217 RiverQuiet,
218}
219
220#[derive(Debug)]
223pub struct AmbienceChannel {
224 tag: AmbienceChannelTag,
225 target_volume: f32,
226 track: TrackHandle,
227 source: Option<AnySoundHandle>,
228 pub looping: bool,
229}
230
231impl AmbienceChannel {
232 pub fn new(
233 tag: AmbienceChannelTag,
234 init_volume: f32,
235 route_to: &mut TrackHandle,
236 looping: bool,
237 ) -> Result<Self, kira::ResourceLimitReached> {
238 let ambience_track_builder = TrackBuilder::new();
239 let track =
240 route_to.add_sub_track(ambience_track_builder.volume(audio::to_decibels(0.0)))?;
241
242 Ok(Self {
243 tag,
244 target_volume: init_volume,
245 track,
246 source: None,
247 looping,
248 })
249 }
250
251 pub fn set_source(&mut self, source_handle: Option<AnySoundHandle>) {
252 self.source = source_handle;
253 }
254
255 pub fn play(&mut self, mut source: AnySoundData, fade_in: Option<f32>, delay: Option<f32>) {
256 let mut tween = Tween::default();
257 if let Some(fade_in) = fade_in {
258 tween.duration = Duration::from_secs_f32(fade_in);
259 }
260 if let Some(delay) = delay {
261 tween.start_time = StartTime::Delayed(Duration::from_secs_f32(delay));
262 }
263 source = source.fade_in_tween(tween);
264 match self.track.play(source) {
265 Ok(handle) => self.source = Some(handle),
266 Err(e) => {
267 warn!(?e, "Cannot play ambience")
268 },
269 }
270 }
271
272 pub fn stop(&mut self, duration: Option<f32>, delay: Option<f32>) {
275 if let Some(source) = self.source.as_mut() {
276 let tween = Tween {
277 duration: Duration::from_secs_f32(duration.unwrap_or(0.1)),
278 start_time: StartTime::Delayed(Duration::from_secs_f32(delay.unwrap_or(0.0))),
279 ..Default::default()
280 };
281 source.stop(tween)
282 }
283 }
284
285 pub fn fade_to(&mut self, volume: f32, duration: f32) {
287 self.track.set_volume(audio::to_decibels(volume), Tween {
288 start_time: StartTime::Immediate,
289 duration: Duration::from_secs_f32(duration),
290 easing: Easing::Linear,
291 });
292 self.target_volume = volume;
293 }
294
295 pub fn get_source(&mut self) -> Option<&mut AnySoundHandle> { self.source.as_mut() }
296
297 pub fn get_track(&self) -> &TrackHandle { &self.track }
300
301 pub fn get_track_mut(&mut self) -> &mut TrackHandle { &mut self.track }
303
304 pub fn get_target_volume(&self) -> f32 { self.target_volume }
307
308 pub fn get_tag(&self) -> AmbienceChannelTag { self.tag }
309
310 pub fn set_tag(&mut self, tag: AmbienceChannelTag) { self.tag = tag }
311
312 pub fn is_active(&self) -> bool { self.get_target_volume() == 0.0 }
313
314 pub fn is_stopped(&self) -> bool {
315 if let Some(source) = self.source.as_ref() {
316 source.state() == PlaybackState::Stopped
317 } else {
318 false
319 }
320 }
321}
322
323#[derive(Debug)]
329pub struct SfxChannel {
330 track: Option<SpatialTrackHandle>,
331 source: Option<AnySoundHandle>,
332 source_initial_volume: f32,
333 pos: Vec3<f32>,
334 pub play_counter: usize,
336}
337
338impl SfxChannel {
339 pub fn new() -> Self {
340 Self {
341 track: None,
342 source: None,
343 source_initial_volume: 0.0,
344 pos: Vec3::zero(),
345 play_counter: 0,
346 }
347 }
348
349 pub fn drop_track(&mut self) { self.track = None; }
350
351 pub fn set_source(&mut self, source_handle: Option<AnySoundHandle>) {
352 self.source = source_handle;
353 }
354
355 pub fn set_source_volume(&mut self, volume: f32) {
359 let tween = Tween {
360 duration: Duration::from_secs_f32(0.0),
361 ..Default::default()
362 };
363 if let Some(source) = self.source.as_mut() {
364 source.set_volume(audio::to_decibels(volume), tween);
365 }
366 }
367
368 pub fn play(
369 &mut self,
370 source: AnySoundData,
371 volume: f32,
372 mut track: SpatialTrackHandle,
373 ) -> usize {
374 match track.play(source) {
375 Ok(handle) => {
376 self.source = Some(handle);
377 self.source_initial_volume = volume
378 },
379 Err(e) => {
380 warn!(?e, "Cannot play sfx")
381 },
382 }
383 self.track = Some(track);
384 self.play_counter += 1;
385 self.play_counter
386 }
387
388 pub fn stop(&mut self) {
389 if let Some(source) = self.source.as_mut() {
390 source.stop(Tween::default())
391 }
392 }
393
394 pub fn set_volume(&mut self, volume: f32, duration: Option<f32>) {
396 if let Some(track) = self.track.as_mut() {
397 let tween = Tween {
398 duration: Duration::from_secs_f32(duration.unwrap_or(0.0)),
399 ..Default::default()
400 };
401 track.set_volume(audio::to_decibels(volume), tween)
402 }
403 }
404
405 pub fn set_pos(&mut self, pos: Vec3<f32>) { self.pos = pos; }
406
407 pub fn is_done(&self) -> bool {
408 self.source
409 .as_ref()
410 .is_none_or(|source| source.state() == PlaybackState::Stopped)
411 }
412
413 pub fn update(&mut self, player_pos: Vec3<f32>) {
415 if let Some(track) = self.track.as_mut() {
416 let tween = Tween {
417 duration: Duration::from_secs_f32(0.0),
418 ..Default::default()
419 };
420 track.set_position(self.pos, tween);
421 }
422
423 let ratio = calculate_player_attenuation(player_pos, self.pos);
426 self.set_source_volume(self.source_initial_volume * 5.0 * ratio);
427 }
428}
429
430impl Default for SfxChannel {
431 fn default() -> Self { Self::new() }
432}
433
434#[derive(Eq, PartialEq, Copy, Clone, Debug)]
435pub enum UiChannelTag {
436 LevelUp,
437}
438
439pub struct UiChannel {
443 track: TrackHandle,
444 source: Option<AnySoundHandle>,
445 pub tag: Option<UiChannelTag>,
446}
447
448impl UiChannel {
449 pub fn new(route_to: &mut TrackHandle) -> Result<Self, kira::ResourceLimitReached> {
450 let track = route_to.add_sub_track(TrackBuilder::default())?;
451 Ok(Self {
452 track,
453 source: None,
454 tag: None,
455 })
456 }
457
458 pub fn set_source(&mut self, source_handle: Option<AnySoundHandle>) {
459 self.source = source_handle;
460 }
461
462 pub fn play(&mut self, source: AnySoundData, tag: Option<UiChannelTag>) {
463 match self.track.play(source) {
464 Ok(handle) => {
465 self.source = Some(handle);
466 self.tag = tag;
467 },
468 Err(e) => {
469 warn!(?e, "Cannot play ui sfx")
470 },
471 }
472 }
473
474 pub fn stop(&mut self) {
475 if let Some(source) = self.source.as_mut() {
476 source.stop(Tween::default())
477 }
478 }
479
480 pub fn set_volume(&mut self, volume: f32) {
481 self.track
482 .set_volume(audio::to_decibels(volume), Tween::default())
483 }
484
485 pub fn is_done(&self) -> bool {
486 self.source
487 .as_ref()
488 .is_none_or(|source| source.state() == PlaybackState::Stopped)
489 }
490}
491
492#[cfg(test)]
493mod tests {
494 use crate::audio::channel::{SFX_DIST_LIMIT, SFX_DIST_LIMIT_SQR};
495
496 #[test]
497 fn test_sfx_dist_limit_eq_sfx_dist_limit_sqr() {
499 assert!(SFX_DIST_LIMIT.powf(2.0) == SFX_DIST_LIMIT_SQR)
500 }
501}