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