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 = 100.0;
35pub const SFX_DIST_LIMIT_SQR: f32 = 10000.0;
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 pub pos: Vec3<f32>,
327}
328
329impl SfxChannel {
330 pub fn new(
331 route_to: &mut TrackHandle,
332 listener: ListenerId,
333 ) -> Result<Self, kira::ResourceLimitReached> {
334 let sfx_track_builder = SpatialTrackBuilder::new()
335 .distances((1.0, SFX_DIST_LIMIT))
336 .attenuation_function(Some(Easing::OutPowf(0.66)));
337 let track = route_to.add_spatial_sub_track(listener, Vec3::zero(), sfx_track_builder)?;
338 Ok(Self {
339 track,
340 source: None,
341 pos: Vec3::zero(),
342 })
343 }
344
345 pub fn set_source(&mut self, source_handle: Option<AnySoundHandle>) {
346 self.source = source_handle;
347 }
348
349 pub fn play(&mut self, source: AnySoundData) {
350 match self.track.play(source) {
351 Ok(handle) => self.source = Some(handle),
352 Err(e) => {
353 warn!(?e, "Cannot play sfx")
354 },
355 }
356 }
357
358 pub fn stop(&mut self) {
359 if let Some(source) = self.source.as_mut() {
360 source.stop(Tween::default())
361 }
362 }
363
364 pub fn set_volume(&mut self, volume: f32) {
367 let tween = Tween {
368 duration: Duration::from_secs_f32(0.0),
369 ..Default::default()
370 };
371 self.track.set_volume(audio::to_decibels(volume), tween)
372 }
373
374 pub fn is_done(&self) -> bool {
375 self.source
376 .as_ref()
377 .is_none_or(|source| source.state() == PlaybackState::Stopped)
378 }
379
380 pub fn update(&mut self, emitter_pos: Vec3<f32>, player_pos: Vec3<f32>) {
382 let tween = Tween {
383 duration: Duration::from_secs_f32(0.0),
384 ..Default::default()
385 };
386 self.track.set_position(emitter_pos, tween);
387 self.pos = emitter_pos;
388
389 let player_distance_to_source_sqr = player_pos
390 .distance_squared(self.pos)
391 .min(SFX_DIST_LIMIT_SQR);
392 let ratio = (-(player_distance_to_source_sqr - SFX_DIST_LIMIT_SQR) / SFX_DIST_LIMIT_SQR)
395 .powf(5.0)
396 .clamp(0.0, 1.0);
397 self.set_volume(ratio);
398 }
399}
400
401#[derive(Eq, PartialEq, Copy, Clone, Debug)]
402pub enum UiChannelTag {
403 LevelUp,
404}
405
406pub struct UiChannel {
410 track: TrackHandle,
411 source: Option<AnySoundHandle>,
412 pub tag: Option<UiChannelTag>,
413}
414
415impl UiChannel {
416 pub fn new(route_to: &mut TrackHandle) -> Result<Self, kira::ResourceLimitReached> {
417 let track = route_to.add_sub_track(TrackBuilder::default())?;
418 Ok(Self {
419 track,
420 source: None,
421 tag: None,
422 })
423 }
424
425 pub fn set_source(&mut self, source_handle: Option<AnySoundHandle>) {
426 self.source = source_handle;
427 }
428
429 pub fn play(&mut self, source: AnySoundData, tag: Option<UiChannelTag>) {
430 match self.track.play(source) {
431 Ok(handle) => {
432 self.source = Some(handle);
433 self.tag = tag;
434 },
435 Err(e) => {
436 warn!(?e, "Cannot play ui sfx")
437 },
438 }
439 }
440
441 pub fn stop(&mut self) {
442 if let Some(source) = self.source.as_mut() {
443 source.stop(Tween::default())
444 }
445 }
446
447 pub fn set_volume(&mut self, volume: f32) {
448 self.track
449 .set_volume(audio::to_decibels(volume), Tween::default())
450 }
451
452 pub fn is_done(&self) -> bool {
453 self.source
454 .as_ref()
455 .is_none_or(|source| source.state() == PlaybackState::Stopped)
456 }
457}
458
459#[cfg(test)]
460mod tests {
461 use crate::audio::channel::{SFX_DIST_LIMIT, SFX_DIST_LIMIT_SQR};
462
463 #[test]
464 fn test_sfx_dist_limit_eq_sfx_dist_limit_sqr() {
466 assert!(SFX_DIST_LIMIT.powf(2.0) == SFX_DIST_LIMIT_SQR)
467 }
468}