1pub mod ambience;
4pub mod channel;
5pub mod fader;
6pub mod music;
7pub mod sfx;
8pub mod soundcache;
9
10use anim::vek::Quaternion;
11use channel::{
12 AmbienceChannel, AmbienceChannelTag, LoopPoint, MusicChannel, MusicChannelTag, SfxChannel,
13 UiChannel,
14};
15use kira::{
16 AudioManager, AudioManagerSettings, Decibels, Tween, Value,
17 backend::{self, DefaultBackend},
18 clock::{ClockHandle, ClockSpeed, ClockTime},
19 effect::filter::{FilterBuilder, FilterHandle},
20 listener::ListenerHandle,
21 track::{TrackBuilder, TrackHandle},
22};
23use music::MusicTransitionManifest;
24use sfx::{SfxEvent, SfxTriggerItem};
25use soundcache::load_ogg;
26use std::{collections::VecDeque, time::Duration};
27use tracing::{debug, error, warn};
28
29use common::{
30 assets::{AssetExt, AssetHandle},
31 comp::Ori,
32};
33use vek::*;
34
35use crate::hud::Subtitle;
36
37pub fn to_decibels(amplitude: f32) -> Decibels {
58 if amplitude <= 0.001 {
59 Decibels::SILENCE
60 } else if amplitude == 1.0 {
61 Decibels::IDENTITY
62 } else {
63 Decibels(amplitude.log10() * 20.0)
64 }
65}
66
67struct Tracks {
68 music: TrackHandle,
69 ui: TrackHandle,
70 sfx: TrackHandle,
71 ambience: TrackHandle,
72}
73
74struct Effects {
75 sfx: FilterHandle,
76 ambience: FilterHandle,
77}
78
79#[derive(Default)]
80struct Channels {
81 music: Vec<MusicChannel>,
82 ambience: Vec<AmbienceChannel>,
83 sfx: Vec<SfxChannel>,
84 ui: Vec<UiChannel>,
85}
86
87impl Channels {
88 fn get_music_channel(&mut self, channel_tag: MusicChannelTag) -> Option<&mut MusicChannel> {
91 self.music.iter_mut().find(|c| c.get_tag() == channel_tag)
92 }
93
94 fn get_sfx_channel(&mut self) -> Option<&mut SfxChannel> {
96 self.sfx.iter_mut().find(|c| c.is_done())
97 }
98
99 fn get_ui_channel(&mut self) -> Option<&mut UiChannel> {
101 self.ui.iter_mut().find(|c| c.is_done())
102 }
103
104 fn get_ambience_channel(
107 &mut self,
108 channel_tag: AmbienceChannelTag,
109 ) -> Option<&mut AmbienceChannel> {
110 self.ambience
111 .iter_mut()
112 .find(|channel| channel.get_tag() == channel_tag)
113 }
114
115 fn count_active(&self) -> ActiveChannels {
116 ActiveChannels {
117 music: self.music.iter().filter(|c| !c.is_done()).count(),
118 ambience: self.ambience.iter().filter(|c| c.is_active()).count(),
119 sfx: self.sfx.iter().filter(|c| !c.is_done()).count(),
120 ui: self.ui.iter().filter(|c| !c.is_done()).count(),
121 }
122 }
123}
124
125#[derive(Default)]
126pub struct ActiveChannels {
127 pub music: usize,
128 pub ambience: usize,
129 pub sfx: usize,
130 pub ui: usize,
131}
132
133#[derive(Default)]
134struct Volumes {
135 sfx: f32,
136 ambience: f32,
137 music: f32,
138 master: f32,
139}
140
141struct ListenerInstance {
142 handle: ListenerHandle,
143 pos: Vec3<f32>,
144 ori: Vec3<f32>,
145}
146
147struct AudioFrontendInner {
148 manager: AudioManager,
149 tracks: Tracks,
150 effects: Effects,
151 channels: Channels,
152 listener: ListenerInstance,
153 clock: ClockHandle,
154}
155
156enum AudioCreationError {
157 Manager(<DefaultBackend as backend::Backend>::Error),
158 Clock(kira::ResourceLimitReached),
159 Track(kira::ResourceLimitReached),
160 Listener(kira::ResourceLimitReached),
161}
162
163impl AudioFrontendInner {
164 fn new(
165 num_sfx_channels: usize,
166 num_ui_channels: usize,
167 buffer_size: usize,
168 ) -> Result<Self, AudioCreationError> {
169 let manager_settings = AudioManagerSettings {
170 internal_buffer_size: buffer_size,
171 ..Default::default()
172 };
173 let mut manager = AudioManager::<DefaultBackend>::new(manager_settings)
174 .map_err(AudioCreationError::Manager)?;
175
176 let mut clock = manager
177 .add_clock(ClockSpeed::TicksPerSecond(1.0))
178 .map_err(AudioCreationError::Clock)?;
179 clock.start();
180
181 let mut sfx_track_builder = TrackBuilder::new();
182 let mut ambience_track_builder = TrackBuilder::new();
183
184 let effects = Effects {
185 sfx: sfx_track_builder.add_effect(FilterBuilder::new().cutoff(Value::Fixed(20000.0))),
186 ambience: ambience_track_builder
187 .add_effect(FilterBuilder::new().cutoff(Value::Fixed(20000.0))),
188 };
189
190 let listener_handle = manager
191 .add_listener(Vec3::zero(), Quaternion::identity())
192 .map_err(AudioCreationError::Listener)?;
193
194 let listener = ListenerInstance {
195 handle: listener_handle,
196 pos: Vec3::zero(),
197 ori: Vec3::unit_x(),
198 };
199
200 let mut tracks = Tracks {
201 music: manager
202 .add_sub_track(TrackBuilder::new())
203 .map_err(AudioCreationError::Track)?,
204 ui: manager
205 .add_sub_track(TrackBuilder::new())
206 .map_err(AudioCreationError::Track)?,
207 sfx: manager
208 .add_sub_track(sfx_track_builder)
209 .map_err(AudioCreationError::Track)?,
210 ambience: manager
211 .add_sub_track(ambience_track_builder)
212 .map_err(AudioCreationError::Track)?,
213 };
214
215 let mut channels = Channels::default();
216
217 for _ in 0..num_sfx_channels {
218 if let Ok(channel) = SfxChannel::new(&mut tracks.sfx, listener.handle.id()) {
219 channels.sfx.push(channel);
220 } else {
221 warn!("Cannot create sfx channel")
222 }
223 }
224
225 for _ in 0..num_ui_channels {
226 if let Ok(channel) = UiChannel::new(&mut tracks.ui) {
227 channels.ui.push(channel);
228 } else {
229 warn!("Cannot create ui channel")
230 }
231 }
232
233 Ok(Self {
234 manager,
235 tracks,
236 effects,
237 channels,
238 listener,
239 clock,
240 })
241 }
242
243 fn manager(&mut self) -> &mut AudioManager { &mut self.manager }
244
245 fn clock(&self) -> &ClockHandle { &self.clock }
246
247 fn create_music_channel(&mut self, channel_tag: MusicChannelTag) {
248 let channel = MusicChannel::new(&mut self.tracks.music);
249 match channel {
250 Ok(mut next_music_channel) => {
251 next_music_channel.set_volume(1.0);
252 next_music_channel.set_tag(channel_tag);
253 self.channels.music.push(next_music_channel);
254 },
255 Err(e) => error!(
256 ?e,
257 "Failed to crate new music channel, music may fail playing"
258 ),
259 }
260 }
261
262 fn new_ambience_channel(&mut self, channel_tag: AmbienceChannelTag) {
264 let channel = AmbienceChannel::new(channel_tag, 0.0, &mut self.tracks.ambience, true);
265 match channel {
266 Ok(ambience_channel) => self.channels.ambience.push(ambience_channel),
267 Err(e) => error!(
268 ?e,
269 "Failed to crate new ambience channel, sounds may fail playing"
270 ),
271 }
272 }
273}
274
275pub struct AudioFrontend {
284 inner: Option<AudioFrontendInner>,
285
286 pub subtitles_enabled: bool,
287 pub subtitles: VecDeque<Subtitle>,
288
289 volumes: Volumes,
290 music_spacing: f32,
291 pub combat_music_enabled: bool,
292
293 mtm: AssetHandle<MusicTransitionManifest>,
294}
295
296impl AudioFrontend {
297 pub fn new(
298 num_sfx_channels: usize,
299 num_ui_channels: usize,
300 subtitles: bool,
301 combat_music_enabled: bool,
302 buffer_size: usize,
303 ) -> Self {
304 let inner = AudioFrontendInner::new(num_sfx_channels, num_ui_channels, buffer_size)
305 .inspect_err(|err| match err {
306 AudioCreationError::Manager(e) => {
307 #[cfg(unix)]
308 error!(
309 ?e,
310 "failed to construct audio frontend manager. Is `pulseaudio-alsa` \
311 installed?"
312 );
313 #[cfg(not(unix))]
314 error!(?e, "failed to construct audio frontend manager.");
315 },
316 AudioCreationError::Clock(e) => {
317 error!(?e, "Failed to construct audio frontend clock.")
318 },
319 AudioCreationError::Track(e) => {
320 error!(?e, "Failed to construct audio frontend track.")
321 },
322 AudioCreationError::Listener(e) => {
323 error!(?e, "Failed to construct audio frontend listener.")
324 },
325 })
326 .ok();
327
328 Self {
329 inner,
330 volumes: Volumes::default(),
331 music_spacing: 1.0,
332 mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"),
333 subtitles: VecDeque::new(),
334 subtitles_enabled: subtitles,
335 combat_music_enabled,
336 }
337 }
338
339 pub fn no_audio() -> Self {
341 Self {
342 inner: None,
343 music_spacing: 1.0,
344 volumes: Volumes::default(),
345 mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"),
346 subtitles: VecDeque::new(),
347 subtitles_enabled: false,
348 combat_music_enabled: false,
349 }
350 }
351
352 pub fn maintain(&mut self) {
354 if let Some(inner) = &mut self.inner {
355 inner.channels.music.retain(|c| !c.is_done());
356 inner.channels.ambience.retain(|c| !c.is_stopped());
357 }
358 }
359
360 pub fn get_clock(&self) -> Option<&ClockHandle> { self.inner.as_ref().map(|i| i.clock()) }
361
362 pub fn get_clock_time(&self) -> Option<ClockTime> { self.get_clock().map(|clock| clock.time()) }
363
364 pub fn get_num_active_channels(&self) -> ActiveChannels {
366 self.inner
367 .as_ref()
368 .map(|i| i.channels.count_active())
369 .unwrap_or_default()
370 }
371
372 fn play_music(&mut self, sound: &str, channel_tag: MusicChannelTag, length: f32) {
375 if self.music_enabled()
376 && let Some(inner) = &mut self.inner
377 {
378 let mtm = self.mtm.read();
379
380 if let Some(current_channel) = inner.channels.music.iter_mut().find(|c| !c.is_done()) {
381 let (fade_out, _fade_in) = mtm
382 .fade_timings
383 .get(&(current_channel.get_tag(), channel_tag))
384 .unwrap_or(&(1.0, 1.0));
385 current_channel.fade_out(*fade_out, None);
386 }
387
388 let now = inner.clock().time();
389
390 let channel = match inner.channels.get_music_channel(channel_tag) {
391 Some(c) => c,
392 None => {
393 inner.create_music_channel(channel_tag);
394 inner
395 .channels
396 .music
397 .last_mut()
398 .expect("We just created this")
399 },
400 };
401
402 let (fade_out, fade_in) = mtm
403 .fade_timings
404 .get(&(channel.get_tag(), channel_tag))
405 .unwrap_or(&(1.0, 0.1));
406 let source = load_ogg(sound, true);
407 channel.stop(Some(*fade_out), None);
408 channel.set_length(length);
409 channel.set_tag(channel_tag);
410 channel.set_loop_data(false, LoopPoint::Start, LoopPoint::End);
411 channel.play(source, now, Some(*fade_in), Some(*fade_out));
412 }
413 }
414
415 pub fn set_loop(&mut self, channel_tag: MusicChannelTag, sound_loops: bool) {
417 if let Some(inner) = self.inner.as_mut() {
418 let channel = inner.channels.get_music_channel(channel_tag);
419 if let Some(channel) = channel {
420 let loop_data = channel.get_loop_data();
421 channel.set_loop_data(sound_loops, loop_data.1, loop_data.2);
422 }
423 }
424 }
425
426 pub fn set_loop_points(&mut self, channel_tag: MusicChannelTag, start: f32, end: f32) {
428 if let Some(inner) = self.inner.as_mut() {
429 let channel = inner.channels.get_music_channel(channel_tag);
430 if let Some(channel) = channel {
431 channel.set_loop_data(
432 true,
433 LoopPoint::Point(start as f64),
434 LoopPoint::Point(end as f64),
435 );
436 }
437 }
438 }
439
440 pub fn set_ambience_master_filter(&mut self, frequency: u32) {
442 if let Some(inner) = self.inner.as_mut() {
443 inner
444 .effects
445 .ambience
446 .set_cutoff(Value::Fixed(frequency as f64), Tween::default());
447 }
448 }
449
450 pub fn get_sfx_file<'a>(
455 trigger_item: Option<(&'a SfxEvent, &'a SfxTriggerItem)>,
456 ) -> Option<(&'a str, f32, Option<&'a str>)> {
457 trigger_item.map(|(event, item)| {
458 let file = match item.files.len() {
459 0 => {
460 debug!("Sfx event {:?} is missing audio file.", event);
461 "voxygen.audio.sfx.placeholder"
462 },
463 1 => item
464 .files
465 .last()
466 .expect("Failed to determine sound file for this trigger item."),
467 _ => {
468 let rand_step = rand::random::<usize>() % item.files.len();
470 &item.files[rand_step]
471 },
472 };
473
474 (file, item.threshold, item.subtitle.as_deref())
478 })
479 }
480
481 pub fn set_sfx_master_filter(&mut self, frequency: u32) {
483 if let Some(inner) = self.inner.as_mut() {
484 inner
485 .effects
486 .sfx
487 .set_cutoff(Value::Fixed(frequency as f64), Tween::default());
488 }
489 }
490
491 pub fn emit_sfx(
494 &mut self,
495 trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
496 position: Vec3<f32>,
497 volume: Option<f32>,
498 ) {
499 if let Some((sfx_file, dur, subtitle)) = Self::get_sfx_file(trigger_item) {
500 self.emit_subtitle(subtitle, Some(position), dur);
501 if self.sfx_enabled()
503 && let Some(inner) = self.inner.as_mut()
504 && let Some(channel) = inner.channels.get_sfx_channel()
505 {
506 let sound = load_ogg(sfx_file, false);
507 channel.update(position);
508
509 let source = sound.volume(to_decibels(volume.unwrap_or(1.0) * 5.0));
510 channel.play(source);
511 }
512 } else {
513 warn!(
514 "Missing sfx trigger config for sfx event at position: {:?}",
515 position
516 );
517 }
518 }
519
520 pub fn emit_ui_sfx(
523 &mut self,
524 trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
525 volume: Option<f32>,
526 ) {
527 if let Some((sfx_file, dur, subtitle)) = Self::get_sfx_file(trigger_item) {
528 self.emit_subtitle(subtitle, None, dur);
529
530 if self.sfx_enabled()
532 && let Some(inner) = self.inner.as_mut()
533 && let Some(channel) = inner.channels.get_ui_channel()
534 {
535 let sound = load_ogg(sfx_file, false).volume(to_decibels(volume.unwrap_or(1.0)));
536 channel.play(sound);
537 }
538 } else {
539 warn!("Missing sfx trigger config for ui sfx event.",);
540 }
541 }
542
543 pub fn emit_subtitle(
545 &mut self,
546 subtitle: Option<&str>,
547 position: Option<Vec3<f32>>,
548 duration: f32,
549 ) {
550 if self.subtitles_enabled {
551 if let Some(subtitle) = subtitle {
552 self.subtitles.push_back(Subtitle {
553 localization: subtitle.to_string(),
554 position,
555 show_for: duration as f64,
556 });
557 if self.subtitles.len() > 10 {
558 self.subtitles.pop_front();
559 }
560 }
561 }
562 }
563
564 pub fn play_ambience_looping(&mut self, channel_tag: AmbienceChannelTag, sound: &str) {
566 if self.ambience_enabled()
567 && let Some(inner) = self.inner.as_mut()
568 && let Some(channel) = inner.channels.get_ambience_channel(channel_tag)
569 {
570 let source = load_ogg(sound, true).loop_region(0.0..);
571 channel.set_looping(true);
572 channel.play(source, Some(1.0), None);
573 }
574 }
575
576 pub fn play_ambience_oneshot(
580 &mut self,
581 channel_tag: AmbienceChannelTag,
582 trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
583 volume: Option<f32>,
584 delay: Option<f32>,
585 ) {
586 if self.ambience_enabled()
587 && trigger_item.is_some()
588 && let Some(inner) = self.inner.as_mut()
589 && let Some(channel) = inner.channels.get_ambience_channel(channel_tag)
590 {
591 let sound = AudioFrontend::get_sfx_file(trigger_item)
592 .unwrap_or(("", 0.0, Some("")))
593 .0;
594 let source = load_ogg(sound, false)
595 .loop_region(None)
596 .volume(to_decibels(volume.unwrap_or(1.0)));
597 channel.set_looping(false);
598 channel.fade_to(1.0, 0.0);
599 channel.play(source, None, delay);
600 }
601 }
602
603 pub fn set_listener_pos(&mut self, pos: Vec3<f32>, ori: Vec3<f32>) {
604 if let Some(inner) = self.inner.as_mut() {
605 let tween = Tween {
606 duration: Duration::from_secs_f32(0.01),
607 ..Default::default()
608 };
609
610 inner.listener.pos = pos;
611 inner.listener.ori = ori;
612
613 inner.listener.handle.set_position(pos, tween);
614
615 let ori_quat = Ori::from(ori).to_quat();
616 inner
617 .listener
618 .handle
619 .set_orientation(ori_quat.normalized(), tween);
620 }
621 }
622
623 pub fn get_listener(&mut self) -> Option<&mut ListenerHandle> {
624 self.inner.as_mut().map(|i| &mut i.listener.handle)
625 }
626
627 pub fn get_listener_pos(&self) -> Vec3<f32> {
628 self.inner
629 .as_ref()
630 .map(|i| i.listener.pos)
631 .unwrap_or_default()
632 }
633
634 pub fn get_listener_ori(&self) -> Vec3<f32> {
635 self.inner
636 .as_ref()
637 .map(|i| i.listener.ori)
638 .unwrap_or_else(Vec3::unit_x)
639 }
640
641 pub fn play_title_music(&mut self) {
644 if self.music_enabled() {
645 self.play_music(
646 "voxygen.audio.soundtrack.veloren_title_tune",
647 MusicChannelTag::TitleMusic,
648 43.0,
649 );
650 self.set_loop(MusicChannelTag::TitleMusic, true);
651 }
652 }
653
654 pub fn get_music_volume(&self) -> f32 { self.volumes.music }
656
657 pub fn get_ambience_volume(&self) -> f32 { self.volumes.ambience }
659
660 pub fn get_sfx_volume(&self) -> f32 { self.volumes.sfx }
662
663 pub fn music_enabled(&self) -> bool { self.get_music_volume() > 0.0 }
665
666 pub fn ambience_enabled(&self) -> bool { self.get_ambience_volume() > 0.0 }
668
669 pub fn sfx_enabled(&self) -> bool { self.get_sfx_volume() > 0.0 }
671
672 pub fn set_music_volume(&mut self, music_volume: f32) {
673 self.volumes.music = music_volume;
674
675 if let Some(inner) = self.inner.as_mut() {
676 inner
677 .tracks
678 .music
679 .set_volume(to_decibels(music_volume), Tween::default())
680 }
681 }
682
683 pub fn set_ambience_volume(&mut self, ambience_volume: f32) {
684 self.volumes.ambience = ambience_volume;
685
686 if let Some(inner) = self.inner.as_mut() {
687 inner
688 .tracks
689 .ambience
690 .set_volume(to_decibels(ambience_volume), Tween::default())
691 }
692 }
693
694 pub fn set_sfx_volume(&mut self, sfx_volume: f32) {
697 self.volumes.sfx = sfx_volume;
698
699 if let Some(inner) = self.inner.as_mut() {
700 inner
701 .tracks
702 .sfx
703 .set_volume(to_decibels(sfx_volume), Tween::default())
704 }
705 }
706
707 pub fn set_music_spacing(&mut self, multiplier: f32) { self.music_spacing = multiplier }
708
709 pub fn set_subtitles(&mut self, enabled: bool) { self.subtitles_enabled = enabled }
710
711 pub fn set_master_volume(&mut self, master_volume: f32) {
713 self.volumes.master = master_volume;
714
715 if let Some(inner) = self.inner.as_mut() {
716 inner
717 .manager()
718 .main_track()
719 .set_volume(to_decibels(master_volume), Tween::default());
720 }
721 }
722
723 pub fn stop_all_ambience(&mut self) {
724 if let Some(inner) = self.inner.as_mut() {
725 for channel in &mut inner.channels.ambience {
726 channel.stop(None, None);
727 }
728 }
729 }
730
731 pub fn stop_all_music(&mut self) {
732 if let Some(inner) = self.inner.as_mut() {
733 for channel in &mut inner.channels.music {
734 channel.stop(None, None);
735 }
736 }
737 }
738
739 pub fn stop_all_sfx(&mut self) {
740 if let Some(inner) = self.inner.as_mut() {
741 for channel in &mut inner.channels.sfx {
742 channel.stop();
743 }
744 for channel in &mut inner.channels.ui {
745 channel.stop();
746 }
747 }
748 }
749
750 pub fn get_num_music_channels(&self) -> usize {
751 self.inner
752 .as_ref()
753 .map(|i| i.channels.music.len())
754 .unwrap_or(0)
755 }
756
757 pub fn get_num_ambience_channels(&self) -> usize {
758 self.inner
759 .as_ref()
760 .map(|i| i.channels.ambience.len())
761 .unwrap_or(0)
762 }
763}