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
29#[derive(PartialEq, Clone, Copy, Hash, Eq, Deserialize, Debug)]
35pub enum MusicChannelTag {
36 TitleMusic,
37 Exploration,
38 Combat,
39}
40
41pub struct MusicChannel {
44 tag: MusicChannelTag,
45 track: TrackHandle,
46 source: Option<AnySoundHandle>,
47 length: f32,
48 loop_data: (bool, LoopPoint, LoopPoint), }
50
51#[derive(Clone, Copy, Debug, PartialEq)]
52pub enum LoopPoint {
53 Start,
54 End,
55 Point(f64),
56}
57
58impl MusicChannel {
59 pub fn new(route_to: &mut TrackHandle) -> Result<Self, kira::ResourceLimitReached> {
60 let track = route_to.add_sub_track(TrackBuilder::new().volume(audio::to_decibels(0.0)))?;
61 Ok(Self {
62 tag: MusicChannelTag::TitleMusic,
63 track,
64 source: None,
65 length: 0.0,
66 loop_data: (false, LoopPoint::Start, LoopPoint::End),
67 })
68 }
69
70 pub fn set_tag(&mut self, tag: MusicChannelTag) { self.tag = tag; }
71
72 pub fn set_source(&mut self, source_handle: Option<AnySoundHandle>) {
73 self.source = source_handle;
74 }
75
76 pub fn set_length(&mut self, length: f32) { self.length = length; }
77
78 pub fn get_loop_data(&self) -> (bool, LoopPoint, LoopPoint) { self.loop_data }
80
81 pub fn set_loop_data(&mut self, loops: bool, start: LoopPoint, end: LoopPoint) {
83 if let Some(source) = self.source.as_mut() {
84 self.loop_data = (loops, start, end);
85 if loops {
86 match (start, end) {
87 (LoopPoint::Start, LoopPoint::End) => {
88 source.set_loop_region(0.0..);
89 },
90 (LoopPoint::Start, LoopPoint::Point(end)) => {
91 source.set_loop_region(..end);
92 },
93 (LoopPoint::Point(start), LoopPoint::End) => {
94 source.set_loop_region(start..);
95 },
96 (LoopPoint::Point(start), LoopPoint::Point(end)) => {
97 source.set_loop_region(start..end);
98 },
99 _ => {
100 warn!("Invalid loop points given")
101 },
102 }
103 } else {
104 source.set_loop_region(None);
105 }
106 }
107 }
108
109 pub fn play(
110 &mut self,
111 mut source: AnySoundData,
112 now: ClockTime,
113 fade_in: Option<f32>,
114 delay: Option<f32>,
115 ) {
116 if let Some(fade_in) = fade_in {
117 let fade_in_tween = Tween {
118 duration: Duration::from_secs_f32(fade_in),
119 ..Default::default()
120 };
121 source = source.fade_in_tween(fade_in_tween);
122 }
123
124 if let Some(delay) = delay {
125 source = source.start_time(now + delay as f64);
126 }
127
128 match self.track.play(source) {
129 Ok(handle) => self.source = Some(handle),
130 Err(e) => {
131 warn!(?e, "Cannot play music")
132 },
133 }
134 }
135
136 pub fn stop(&mut self, duration: Option<f32>, delay: Option<f32>) {
139 if let Some(source) = self.source.as_mut() {
140 let tween = Tween {
141 duration: Duration::from_secs_f32(duration.unwrap_or(0.1)),
142 start_time: StartTime::Delayed(Duration::from_secs_f32(delay.unwrap_or(0.0))),
143 ..Default::default()
144 };
145 source.stop(tween)
146 };
147 }
148
149 pub fn set_volume(&mut self, volume: f32) {
151 self.track
152 .set_volume(audio::to_decibels(volume), Tween::default());
153 }
154
155 pub fn fade_to(&mut self, volume: f32, duration: f32, delay: Option<f32>) {
158 let mut start_time = StartTime::Immediate;
159 if let Some(delay) = delay {
160 start_time = StartTime::Delayed(Duration::from_secs_f32(delay))
161 }
162 let tween = Tween {
163 start_time,
164 duration: Duration::from_secs_f32(duration),
165 easing: Easing::Linear,
166 };
167 self.track.set_volume(audio::to_decibels(volume), tween);
168 }
169
170 pub fn fade_out(&mut self, duration: f32, delay: Option<f32>) {
173 self.stop(Some(duration), delay);
174 }
175
176 pub fn is_done(&self) -> bool {
179 self.source
180 .as_ref()
181 .is_none_or(|source| source.state() == PlaybackState::Stopped)
182 }
183
184 pub fn get_tag(&self) -> MusicChannelTag { self.tag }
185
186 pub fn get_track(&mut self) -> &mut TrackHandle { &mut self.track }
188
189 pub fn get_source(&mut self) -> Option<&mut AnySoundHandle> { self.source.as_mut() }
190
191 pub fn get_length(&self) -> f32 { self.length }
192}
193
194#[derive(Debug, PartialEq, Eq, Clone, Copy, Deserialize, EnumIter)]
197pub enum AmbienceChannelTag {
198 Wind,
199 Rain,
200 ThunderRumbling,
201 Leaves,
202 Cave,
203 Thunder,
204}
205
206#[derive(Debug)]
209pub struct AmbienceChannel {
210 tag: AmbienceChannelTag,
211 target_volume: f32,
212 track: TrackHandle,
213 source: Option<AnySoundHandle>,
214 pub looping: bool,
215}
216
217impl AmbienceChannel {
218 pub fn new(
219 tag: AmbienceChannelTag,
220 init_volume: f32,
221 route_to: &mut TrackHandle,
222 looping: bool,
223 ) -> Result<Self, kira::ResourceLimitReached> {
224 let ambience_track_builder = TrackBuilder::new();
225 let track =
226 route_to.add_sub_track(ambience_track_builder.volume(audio::to_decibels(0.0)))?;
227
228 Ok(Self {
229 tag,
230 target_volume: init_volume,
231 track,
232 source: None,
233 looping,
234 })
235 }
236
237 pub fn set_source(&mut self, source_handle: Option<AnySoundHandle>) {
238 self.source = source_handle;
239 }
240
241 pub fn play(&mut self, mut source: AnySoundData, fade_in: Option<f32>, delay: Option<f32>) {
242 let mut tween = Tween::default();
243 if let Some(fade_in) = fade_in {
244 tween.duration = Duration::from_secs_f32(fade_in);
245 }
246 if let Some(delay) = delay {
247 tween.start_time = StartTime::Delayed(Duration::from_secs_f32(delay));
248 }
249 source = source.fade_in_tween(tween);
250 match self.track.play(source) {
251 Ok(handle) => self.source = Some(handle),
252 Err(e) => {
253 warn!(?e, "Cannot play ambience")
254 },
255 }
256 }
257
258 pub fn stop(&mut self, duration: Option<f32>, delay: Option<f32>) {
261 if let Some(source) = self.source.as_mut() {
262 let tween = Tween {
263 duration: Duration::from_secs_f32(duration.unwrap_or(0.1)),
264 start_time: StartTime::Delayed(Duration::from_secs_f32(delay.unwrap_or(0.0))),
265 ..Default::default()
266 };
267 source.stop(tween)
268 }
269 }
270
271 pub fn fade_to(&mut self, volume: f32, duration: f32) {
273 self.track.set_volume(audio::to_decibels(volume), Tween {
274 start_time: StartTime::Immediate,
275 duration: Duration::from_secs_f32(duration),
276 easing: Easing::Linear,
277 });
278 self.target_volume = volume;
279 }
280
281 pub fn set_looping(&mut self, loops: bool) {
283 if let Some(source) = self.source.as_mut() {
284 if loops {
285 source.set_loop_region(0.0..);
286 } else {
287 source.set_loop_region(None);
288 }
289 }
290 }
291
292 pub fn get_source(&mut self) -> Option<&mut AnySoundHandle> { self.source.as_mut() }
293
294 pub fn get_track(&self) -> &TrackHandle { &self.track }
297
298 pub fn get_track_mut(&mut self) -> &mut TrackHandle { &mut self.track }
300
301 pub fn get_target_volume(&self) -> f32 { self.target_volume }
304
305 pub fn get_tag(&self) -> AmbienceChannelTag { self.tag }
306
307 pub fn set_tag(&mut self, tag: AmbienceChannelTag) { self.tag = tag }
308
309 pub fn is_active(&self) -> bool { self.get_target_volume() == 0.0 }
310
311 pub fn is_stopped(&self) -> bool {
312 if let Some(source) = self.source.as_ref() {
313 source.state() == PlaybackState::Stopped
314 } else {
315 false
316 }
317 }
318}
319
320#[derive(Debug)]
326pub struct SfxChannel {
327 track: SpatialTrackHandle,
328 source: Option<AnySoundHandle>,
329 pub pos: Vec3<f32>,
330}
331
332impl SfxChannel {
333 pub fn new(
334 route_to: &mut TrackHandle,
335 listener: ListenerId,
336 ) -> Result<Self, kira::ResourceLimitReached> {
337 let sfx_track_builder = SpatialTrackBuilder::new()
338 .distances((1.0, 200.0))
339 .attenuation_function(Some(Easing::OutPowf(0.45)));
340 let track = route_to.add_spatial_sub_track(listener, Vec3::zero(), sfx_track_builder)?;
341 Ok(Self {
342 track,
343 source: None,
344 pos: Vec3::zero(),
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) {
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 }
360
361 pub fn stop(&mut self) {
362 if let Some(source) = self.source.as_mut() {
363 source.stop(Tween::default())
364 }
365 }
366
367 pub fn set_volume(&mut self, volume: f32) {
368 if let Some(source) = self.source.as_mut() {
369 source.set_volume(audio::to_decibels(volume), Tween::default())
370 }
371 }
372
373 pub fn is_done(&self) -> bool {
374 self.source
375 .as_ref()
376 .is_none_or(|source| source.state() == PlaybackState::Stopped)
377 }
378
379 pub fn update(&mut self, pos: Vec3<f32>) {
380 let tween = Tween {
381 duration: Duration::from_secs_f32(0.0),
382 ..Default::default()
383 };
384 self.track.set_position(pos, tween);
385 self.pos = pos;
386 }
387}
388
389pub struct UiChannel {
393 track: TrackHandle,
394 source: Option<AnySoundHandle>,
395}
396
397impl UiChannel {
398 pub fn new(route_to: &mut TrackHandle) -> Result<Self, kira::ResourceLimitReached> {
399 let track = route_to.add_sub_track(TrackBuilder::default())?;
400 Ok(Self {
401 track,
402 source: None,
403 })
404 }
405
406 pub fn set_source(&mut self, source_handle: Option<AnySoundHandle>) {
407 self.source = source_handle;
408 }
409
410 pub fn play(&mut self, source: AnySoundData) {
411 match self.track.play(source) {
412 Ok(handle) => self.source = Some(handle),
413 Err(e) => {
414 warn!(?e, "Cannot play ui sfx")
415 },
416 }
417 }
418
419 pub fn stop(&mut self) {
420 if let Some(source) = self.source.as_mut() {
421 source.stop(Tween::default())
422 }
423 }
424
425 pub fn set_volume(&mut self, volume: f32) {
426 self.track
427 .set_volume(audio::to_decibels(volume), Tween::default())
428 }
429
430 pub fn is_done(&self) -> bool {
431 self.source
432 .as_ref()
433 .is_none_or(|source| source.state() == PlaybackState::Stopped)
434 }
435}