1pub mod ambience;
4pub mod channel;
5pub mod music;
6pub mod sfx;
7pub mod soundcache;
8
9use anim::vek::Quaternion;
10use channel::{
11 AmbienceChannel, AmbienceChannelTag, LoopPoint, MusicChannel, MusicChannelTag, SfxChannel,
12 UiChannel,
13};
14use cpal::{
15 Device, StreamConfig, SupportedStreamConfigRange,
16 traits::{DeviceTrait, HostTrait},
17};
18use kira::{
19 AudioManager, AudioManagerSettings, Decibels, Tween, Value,
20 backend::{
21 self,
22 cpal::{CpalBackend, CpalBackendSettings},
23 },
24 clock::{ClockHandle, ClockSpeed, ClockTime},
25 effect::filter::{FilterBuilder, FilterHandle},
26 listener::ListenerHandle,
27 track::{SpatialTrackBuilder, TrackBuilder, TrackHandle},
28};
29use music::MusicTransitionManifest;
30use sfx::{SfxEvent, SfxTag, SfxTriggerItem};
31use soundcache::load_ogg;
32use std::{cmp::Ordering, collections::VecDeque, time::Duration};
33use strum::Display;
34use tracing::{debug, error, info, warn};
35
36use common::{
37 assets::{AssetExt, AssetHandle, Ron},
38 comp::Ori,
39};
40use vek::*;
41
42use crate::{
43 audio::channel::{SFX_DIST_LIMIT, calculate_player_attenuation},
44 hud::Subtitle,
45};
46
47pub fn to_decibels(amplitude: f32) -> Decibels {
48 if amplitude <= 0.001 {
49 Decibels::SILENCE
50 } else if amplitude == 1.0 {
51 Decibels::IDENTITY
52 } else {
53 Decibels(amplitude.log10() * 20.0)
54 }
55}
56
57struct Tracks {
58 music: TrackHandle,
59 ui: TrackHandle,
60 sfx: TrackHandle,
61 instrument: TrackHandle,
62 ambience: TrackHandle,
63}
64
65#[derive(Clone, Copy, Debug, Display)]
66pub enum SfxChannelSettings {
67 Low,
68 Medium,
69 High,
70}
71
72impl SfxChannelSettings {
73 pub fn from_str_slice(str: &str) -> Self {
74 match str {
75 "Low" => SfxChannelSettings::Low,
76 "Medium" => SfxChannelSettings::Medium,
77 "High" => SfxChannelSettings::High,
78 _ => SfxChannelSettings::High,
79 }
80 }
81
82 pub fn to_usize(&self) -> usize {
83 match self {
84 SfxChannelSettings::Low => 16,
85 SfxChannelSettings::Medium => 32,
86 SfxChannelSettings::High => 64,
87 }
88 }
89}
90
91struct Effects {
92 sfx: FilterHandle,
93 ambience: FilterHandle,
94}
95
96#[derive(Copy, Clone)]
97pub struct SfxHandle {
98 channel_idx: usize,
99 play_id: usize,
100}
101
102#[derive(Default)]
103struct Channels {
104 music: Vec<MusicChannel>,
105 ambience: Vec<AmbienceChannel>,
106 sfx: Vec<SfxChannel>,
107 ui: Vec<UiChannel>,
108}
109
110impl Channels {
111 fn get_music_channel(&mut self, channel_tag: MusicChannelTag) -> Option<&mut MusicChannel> {
114 self.music.iter_mut().find(|c| c.get_tag() == channel_tag)
115 }
116
117 fn get_valid_sfx_channel(
119 &mut self,
120 tag: SfxTag,
121 distance_sq: f32,
122 ) -> Option<(usize, &mut SfxChannel)> {
123 let index = if let Some(channel) = self.sfx.iter().position(|c| c.is_done()) {
125 Some(channel)
126 } else {
127 self.sfx
131 .iter()
132 .enumerate()
133 .filter(|a| {
134 a.1.is_lower_or_equal_priority_than(tag) && a.1.distance_sq() > distance_sq
135 })
136 .max_by(|a, b| {
137 let tag_cmp = a.1.tag_priority().cmp(&b.1.tag_priority());
138 if matches!(tag_cmp, Ordering::Equal) {
139 a.1.distance_sq().total_cmp(&(b.1.distance_sq()))
140 } else {
141 tag_cmp
142 }
143 })
144 .map(|channel| channel.0)
145 };
146 if let Some(index) = index {
147 Some((index, &mut self.sfx[index]))
148 } else {
149 None
152 }
153 }
154
155 fn get_sfx_channel(&mut self, sfx: &SfxHandle) -> Option<&mut SfxChannel> {
156 self.sfx
157 .get_mut(sfx.channel_idx)
158 .filter(|c| c.play_counter == sfx.play_id)
159 }
160
161 fn get_ui_channel(&mut self) -> Option<&mut UiChannel> {
163 self.ui.iter_mut().find(|c| c.is_done())
164 }
165
166 fn get_ambience_channel(
169 &mut self,
170 channel_tag: AmbienceChannelTag,
171 ) -> Option<&mut AmbienceChannel> {
172 self.ambience
173 .iter_mut()
174 .find(|channel| channel.get_tag() == channel_tag)
175 }
176
177 fn count_active(&self) -> ActiveChannels {
178 ActiveChannels {
179 music: self.music.iter().filter(|c| !c.is_done()).count(),
180 ambience: self.ambience.iter().filter(|c| c.is_active()).count(),
181 sfx: self.sfx.iter().filter(|c| !c.is_done()).count(),
182 ui: self.ui.iter().filter(|c| !c.is_done()).count(),
183 }
184 }
185}
186
187#[derive(Default)]
188pub struct ActiveChannels {
189 pub music: usize,
190 pub ambience: usize,
191 pub sfx: usize,
192 pub ui: usize,
193}
194
195#[derive(Default)]
196struct Volumes {
197 sfx: f32,
198 instrument: f32,
199 ambience: f32,
200 music: f32,
201 master: f32,
202}
203
204struct ListenerInstance {
205 handle: ListenerHandle,
206 pos: Vec3<f32>,
207 ori: Vec3<f32>,
208}
209
210struct AudioFrontendInner {
211 manager: AudioManager,
212 tracks: Tracks,
213 effects: Effects,
214 channels: Channels,
215 listener: ListenerInstance,
216 player_pos: Vec3<f32>,
219 clock: ClockHandle,
220}
221
222enum AudioCreationError {
223 Manager(<CpalBackend as backend::Backend>::Error),
224 Clock(kira::ResourceLimitReached),
225 Track(kira::ResourceLimitReached),
226 Listener(kira::ResourceLimitReached),
227}
228
229impl AudioFrontendInner {
230 fn new(
231 num_sfx_channels: usize,
232 num_ui_channels: usize,
233 buffer_size: usize,
234 device: Option<Device>,
235 config: Option<StreamConfig>,
236 ) -> Result<Self, AudioCreationError> {
237 let backend_settings = CpalBackendSettings { device, config };
238 let manager_settings = AudioManagerSettings {
239 internal_buffer_size: buffer_size,
240 backend_settings,
241 ..Default::default()
242 };
243 let mut manager = AudioManager::<CpalBackend>::new(manager_settings)
244 .map_err(AudioCreationError::Manager)?;
245
246 let mut clock = manager
247 .add_clock(ClockSpeed::TicksPerSecond(1.0))
248 .map_err(AudioCreationError::Clock)?;
249 clock.start();
250
251 let mut sfx_track_builder = TrackBuilder::new();
252 let mut ambience_track_builder = TrackBuilder::new();
253
254 let effects = Effects {
255 sfx: sfx_track_builder.add_effect(FilterBuilder::new().cutoff(Value::Fixed(20000.0))),
256 ambience: ambience_track_builder
257 .add_effect(FilterBuilder::new().cutoff(Value::Fixed(20000.0))),
258 };
259
260 let listener_handle = manager
261 .add_listener(Vec3::zero(), Quaternion::identity())
262 .map_err(AudioCreationError::Listener)?;
263
264 let listener = ListenerInstance {
265 handle: listener_handle,
266 pos: Vec3::zero(),
267 ori: Vec3::unit_x(),
268 };
269
270 let mut sfx_track = manager
271 .add_sub_track(sfx_track_builder)
272 .map_err(AudioCreationError::Track)?;
273 let instrument_track = sfx_track
274 .add_sub_track(TrackBuilder::new())
275 .map_err(AudioCreationError::Track)?;
276
277 let mut tracks = Tracks {
278 music: manager
279 .add_sub_track(TrackBuilder::new())
280 .map_err(AudioCreationError::Track)?,
281 ui: manager
282 .add_sub_track(TrackBuilder::new())
283 .map_err(AudioCreationError::Track)?,
284 sfx: sfx_track,
285 instrument: instrument_track,
286 ambience: manager
287 .add_sub_track(ambience_track_builder)
288 .map_err(AudioCreationError::Track)?,
289 };
290
291 let mut channels = Channels::default();
292
293 for _ in 0..num_sfx_channels {
294 channels.sfx.push(SfxChannel::new());
295 }
296
297 for _ in 0..num_ui_channels {
298 if let Ok(channel) = UiChannel::new(&mut tracks.ui) {
299 channels.ui.push(channel);
300 } else {
301 warn!("Cannot create ui channel")
302 }
303 }
304
305 Ok(Self {
306 manager,
307 tracks,
308 effects,
309 channels,
310 listener,
311 player_pos: Vec3::zero(),
312 clock,
313 })
314 }
315
316 fn manager(&mut self) -> &mut AudioManager { &mut self.manager }
317
318 fn clock(&self) -> &ClockHandle { &self.clock }
319
320 fn create_music_channel(&mut self, channel_tag: MusicChannelTag) {
321 let channel = MusicChannel::new(&mut self.tracks.music);
322 match channel {
323 Ok(mut next_music_channel) => {
324 next_music_channel.set_volume(1.0);
325 next_music_channel.set_tag(channel_tag);
326 self.channels.music.push(next_music_channel);
327 },
328 Err(e) => error!(
329 ?e,
330 "Failed to crate new music channel, music may fail playing"
331 ),
332 }
333 }
334
335 fn new_ambience_channel(&mut self, channel_tag: AmbienceChannelTag) {
337 let channel = AmbienceChannel::new(channel_tag, 0.0, &mut self.tracks.ambience, true);
338 match channel {
339 Ok(ambience_channel) => self.channels.ambience.push(ambience_channel),
340 Err(e) => error!(
341 ?e,
342 "Failed to crate new ambience channel, sounds may fail playing"
343 ),
344 }
345 }
346}
347
348pub struct AudioFrontend {
357 inner: Option<AudioFrontendInner>,
358
359 pub subtitles_enabled: bool,
360 pub subtitles: VecDeque<Subtitle>,
361
362 volumes: Volumes,
363 music_spacing: f32,
364 pub combat_music_enabled: bool,
365
366 mtm: AssetHandle<Ron<MusicTransitionManifest>>,
367}
368
369impl AudioFrontend {
370 pub fn new(
371 num_sfx_channels: usize,
372 num_ui_channels: usize,
373 subtitles: bool,
374 combat_music_enabled: bool,
375 buffer_size: usize,
376 set_samplerate: Option<u32>,
377 ) -> Self {
378 let mut device = cpal::default_host().default_output_device();
381 let mut supported_config = None;
382 let mut samplerate = 44100;
383 if let Some(device) = device.as_mut()
384 && let Ok(default_output_config) = device.default_output_config()
385 {
386 info!(
387 "Current default samplerate: {:?}",
388 default_output_config.sample_rate()
389 );
390 samplerate = default_output_config.sample_rate();
391 if samplerate > 48000 && set_samplerate.is_none() {
392 warn!(
393 "Current default samplerate is higher than 48000; attempting to lower \
394 samplerate"
395 );
396 let supported_configs = device.supported_output_configs();
397 if let Ok(supported_configs) = supported_configs {
398 let best_config = supported_configs
399 .max_by(SupportedStreamConfigRange::cmp_default_heuristics);
400 if let Some(best_config) = best_config {
401 warn!("Attempting to change samplerate to 48khz");
402 supported_config = best_config.try_with_sample_rate(48000);
403 if supported_config.is_none() {
404 warn!("Attempting to change samplerate to 44.1khz");
405 supported_config = best_config.try_with_sample_rate(44100);
406 }
407 if supported_config.is_none() {
408 warn!("Could not change samplerate, using default")
409 }
410 }
411 }
412 } else if let Some(set_samplerate) = set_samplerate {
413 let supported_configs = device.supported_output_configs();
414 if let Ok(supported_configs) = supported_configs {
415 let best_config = supported_configs
416 .max_by(SupportedStreamConfigRange::cmp_default_heuristics);
417 if let Some(best_config) = best_config {
418 warn!("Attempting to force samplerate to {:?}", set_samplerate);
419 supported_config = best_config.try_with_sample_rate(set_samplerate);
420 if supported_config.is_none() {
421 error!(
422 "Could not set samplerate to {:?}, falling back to default.",
423 set_samplerate
424 );
425 }
426 }
427 }
428 }
429 }
430 let mut config = None;
431 if let Some(supported_config) = supported_config {
432 info!("Samplerate is {:?}", supported_config.config().sample_rate);
433 config = Some(supported_config.config())
434 } else {
435 info!("Samplerate is {:?}", samplerate)
436 }
437 let inner = AudioFrontendInner::new(
438 num_sfx_channels,
439 num_ui_channels,
440 buffer_size,
441 device,
442 config,
443 )
444 .inspect_err(|err| match err {
445 AudioCreationError::Manager(e) => {
446 #[cfg(unix)]
447 error!(
448 ?e,
449 "failed to construct audio frontend manager. Is `pulseaudio-alsa` installed?"
450 );
451 #[cfg(not(unix))]
452 error!(?e, "failed to construct audio frontend manager.");
453 },
454 AudioCreationError::Clock(e) => {
455 error!(?e, "Failed to construct audio frontend clock.")
456 },
457 AudioCreationError::Track(e) => {
458 error!(?e, "Failed to construct audio frontend track.")
459 },
460 AudioCreationError::Listener(e) => {
461 error!(?e, "Failed to construct audio frontend listener.")
462 },
463 })
464 .ok();
465
466 if let Some(inner) = inner {
467 Self {
468 inner: Some(inner),
469 volumes: Volumes::default(),
470 music_spacing: 1.0,
471 mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"),
472 subtitles: VecDeque::new(),
473 subtitles_enabled: subtitles,
474 combat_music_enabled,
475 }
476 } else {
477 Self {
478 inner: None,
479 volumes: Volumes::default(),
480 music_spacing: 1.0,
481 mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"),
482 subtitles: VecDeque::new(),
483 subtitles_enabled: subtitles,
484 combat_music_enabled,
485 }
486 }
487 }
488
489 fn channels_mut(&mut self) -> Option<&mut Channels> { Some(&mut self.inner.as_mut()?.channels) }
490
491 pub fn no_audio() -> Self {
493 Self {
494 inner: None,
495 music_spacing: 1.0,
496 volumes: Volumes::default(),
497 mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"),
498 subtitles: VecDeque::new(),
499 subtitles_enabled: false,
500 combat_music_enabled: false,
501 }
502 }
503
504 pub fn maintain(&mut self) {
507 if let Some(inner) = &mut self.inner {
508 inner.channels.music.retain(|c| !c.is_done());
509 inner.channels.ambience.retain(|c| !c.is_stopped());
510 inner.channels.sfx.iter_mut().for_each(|c| {
511 if c.is_done() {
512 c.drop_track();
513 }
514 });
515 inner.channels.ui.iter_mut().for_each(|c| {
516 if c.is_done() {
517 c.tag = None
518 }
519 });
520 }
521 }
522
523 pub fn get_clock(&self) -> Option<&ClockHandle> { self.inner.as_ref().map(|i| i.clock()) }
524
525 pub fn get_clock_time(&self) -> Option<ClockTime> { self.get_clock().map(|clock| clock.time()) }
526
527 pub fn get_num_active_channels(&self) -> ActiveChannels {
529 self.inner
530 .as_ref()
531 .map(|i| i.channels.count_active())
532 .unwrap_or_default()
533 }
534
535 pub fn get_cpu_usage(&mut self) -> f32 {
536 if let Some(inner) = self.inner.as_mut() {
537 inner.manager.backend_mut().pop_cpu_usage().unwrap_or(0.0)
538 } else {
539 0.0
540 }
541 }
542
543 fn play_music(&mut self, sound: &str, channel_tag: MusicChannelTag, length: f32) {
546 if self.music_enabled()
547 && let Some(inner) = &mut self.inner
548 {
549 let mtm = self.mtm.read();
550
551 if let Some(current_channel) = inner.channels.music.iter_mut().find(|c| !c.is_done()) {
552 let (fade_out, _fade_in) = mtm
553 .0
554 .fade_timings
555 .get(&(current_channel.get_tag(), channel_tag))
556 .unwrap_or(&(1.0, 1.0));
557 current_channel.fade_out(*fade_out, None);
558 }
559
560 let now = inner.clock().time();
561
562 let channel = match inner.channels.get_music_channel(channel_tag) {
563 Some(c) => c,
564 None => {
565 inner.create_music_channel(channel_tag);
566 inner
567 .channels
568 .music
569 .last_mut()
570 .expect("We just created this")
571 },
572 };
573
574 let (fade_out, fade_in) = mtm
575 .0
576 .fade_timings
577 .get(&(channel.get_tag(), channel_tag))
578 .unwrap_or(&(1.0, 0.1));
579 let source = load_ogg(sound, true);
580 channel.stop(Some(*fade_out), None);
581 channel.set_length(length);
582 channel.set_tag(channel_tag);
583 channel.set_loop_data(false, LoopPoint::Start, LoopPoint::End);
584 channel.play(source, now, Some(*fade_in), Some(*fade_out));
585 }
586 }
587
588 pub fn set_loop(&mut self, channel_tag: MusicChannelTag, sound_loops: bool) {
590 if let Some(inner) = self.inner.as_mut() {
591 let channel = inner.channels.get_music_channel(channel_tag);
592 if let Some(channel) = channel {
593 let loop_data = channel.get_loop_data();
594 channel.set_loop_data(sound_loops, loop_data.1, loop_data.2);
595 }
596 }
597 }
598
599 pub fn set_loop_points(&mut self, channel_tag: MusicChannelTag, start: f32, end: f32) {
601 if let Some(inner) = self.inner.as_mut() {
602 let channel = inner.channels.get_music_channel(channel_tag);
603 if let Some(channel) = channel {
604 channel.set_loop_data(
605 true,
606 LoopPoint::Point(start as f64),
607 LoopPoint::Point(end as f64),
608 );
609 }
610 }
611 }
612
613 pub fn get_sfx_file<'a>(
618 trigger_item: Option<(&'a SfxEvent, &'a SfxTriggerItem)>,
619 ) -> Option<(&'a str, f32, Option<&'a str>)> {
620 trigger_item.map(|(event, item)| {
621 let file = match item.files.len() {
622 0 => {
623 debug!("Sfx event {:?} is missing audio file.", event);
624 "voxygen.audio.sfx.placeholder"
625 },
626 1 => item
627 .files
628 .last()
629 .expect("Failed to determine sound file for this trigger item."),
630 _ => {
631 let rand_step = (rand::random::<u64>() as usize) % item.files.len();
633 &item.files[rand_step]
634 },
635 };
636
637 (file, item.threshold, item.subtitle.as_deref())
641 })
642 }
643
644 pub fn set_sfx_master_filter(&mut self, frequency: u32) {
646 if let Some(inner) = self.inner.as_mut() {
647 inner
648 .effects
649 .sfx
650 .set_cutoff(Value::Fixed(frequency as f64), Tween::default());
651 }
652 }
653
654 pub fn emit_sfx(
657 &mut self,
658 trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
659 emitter_pos: Vec3<f32>,
660 volume: Option<f32>,
661 tag: SfxTag,
662 ) -> Option<SfxHandle> {
663 if let Some((sfx_file, dur, subtitle)) = Self::get_sfx_file(trigger_item) {
664 self.emit_subtitle(subtitle, Some(emitter_pos), dur);
665 if self.sfx_enabled()
667 && let distance_sq = Vec3::distance_squared(self.get_listener_pos(), emitter_pos)
668 && let Some(inner) = self.inner.as_mut()
669 && let Some((channel_idx, channel)) =
670 inner.channels.get_valid_sfx_channel(tag, distance_sq)
671 {
672 let listener_id = inner.listener.handle.id();
673 let sound = load_ogg(sfx_file, false);
674 channel.set_pos(emitter_pos);
675
676 let ratio = calculate_player_attenuation(inner.player_pos, emitter_pos);
678
679 let source_volume = volume.unwrap_or(1.0);
680 let source = sound.volume(to_decibels(source_volume * 5.0 * ratio));
681
682 let is_instrument = matches!(trigger_item, Some((SfxEvent::Music(_, _), _)));
683
684 let sfx_track_builder = SpatialTrackBuilder::new()
687 .distances((1.0, SFX_DIST_LIMIT))
688 .attenuation_function(Some(kira::Easing::OutPowf(0.66)));
689 let track = if is_instrument {
690 inner.tracks.instrument.add_spatial_sub_track(
691 listener_id,
692 emitter_pos,
693 sfx_track_builder,
694 )
695 } else {
696 inner.tracks.sfx.add_spatial_sub_track(
697 listener_id,
698 emitter_pos,
699 sfx_track_builder,
700 )
701 };
702 if let Ok(track) = track {
703 Some(SfxHandle {
704 channel_idx,
705 play_id: channel.play(source, source_volume, track, tag, distance_sq),
706 })
707 } else {
708 debug!("Could not add SpacialTrack to play sfx");
709 None
710 }
711 } else {
712 None
713 }
714 } else {
715 warn!(
716 "Missing sfx trigger config for sfx event: {:?}; {:?}",
717 trigger_item,
718 backtrace::Backtrace::new(),
719 );
720 None
721 }
722 }
723
724 pub fn emit_ui_sfx(
727 &mut self,
728 trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
729 volume: Option<f32>,
730 tag: Option<channel::UiChannelTag>,
731 ) {
732 if let Some((sfx_file, dur, subtitle)) = Self::get_sfx_file(trigger_item) {
733 self.emit_subtitle(subtitle, None, dur);
734
735 if self.sfx_enabled()
737 && let Some(inner) = self.inner.as_mut()
738 && !inner
739 .channels
740 .ui
741 .iter()
742 .any(|c| tag.is_some() && c.tag == tag)
743 && let Some(channel) = inner.channels.get_ui_channel()
744 {
745 let sound = load_ogg(sfx_file, false).volume(to_decibels(volume.unwrap_or(1.0)));
746 channel.play(sound, tag);
747 }
748 } else {
749 warn!("Missing sfx trigger config for ui sfx event.",);
750 }
751 }
752
753 pub fn emit_subtitle(
755 &mut self,
756 subtitle: Option<&str>,
757 position: Option<Vec3<f32>>,
758 duration: f32,
759 ) {
760 if self.subtitles_enabled
761 && let Some(subtitle) = subtitle
762 {
763 self.subtitles.push_back(Subtitle {
764 localization: subtitle.to_string(),
765 position,
766 show_for: duration as f64,
767 });
768 if self.subtitles.len() > 10 {
769 self.subtitles.pop_front();
770 }
771 }
772 }
773
774 pub fn set_ambience_master_filter(&mut self, frequency: u32, tween: Tween) {
776 if let Some(inner) = self.inner.as_mut() {
777 inner
778 .effects
779 .ambience
780 .set_cutoff(Value::Fixed(frequency as f64), tween);
781 }
782 }
783
784 pub fn play_ambience_looping(
786 &mut self,
787 channel_tag: AmbienceChannelTag,
788 sound: &str,
789 start: usize,
790 end: usize,
791 ) {
792 if self.ambience_enabled()
793 && let Some(inner) = self.inner.as_mut()
794 && let Some(channel) = inner.channels.get_ambience_channel(channel_tag)
795 {
796 let source = load_ogg(sound, true).loop_region(
797 kira::sound::PlaybackPosition::Samples(start)
798 ..kira::sound::PlaybackPosition::Samples(end),
799 );
800 channel.play(source, Some(1.0), None);
801 }
802 }
803
804 pub fn play_ambience_oneshot(
808 &mut self,
809 channel_tag: AmbienceChannelTag,
810 trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
811 volume: Option<f32>,
812 delay: Option<f32>,
813 ) {
814 if self.ambience_enabled()
815 && trigger_item.is_some()
816 && let Some(inner) = self.inner.as_mut()
817 && let Some(channel) = inner.channels.get_ambience_channel(channel_tag)
818 {
819 let sound = AudioFrontend::get_sfx_file(trigger_item)
820 .unwrap_or(("", 0.0, Some("")))
821 .0;
822 let source = load_ogg(sound, false)
823 .loop_region(None)
824 .volume(to_decibels(volume.unwrap_or(1.0)));
825 channel.fade_to(1.0, 0.0);
826 channel.play(source, None, delay);
827 }
828 }
829
830 pub fn set_listener_pos(&mut self, pos: Vec3<f32>, ori: Vec3<f32>) {
831 if let Some(inner) = self.inner.as_mut() {
832 let tween = Tween {
833 duration: Duration::from_secs_f32(0.01),
834 ..Default::default()
835 };
836
837 inner.listener.pos = pos;
838 inner.listener.ori = ori;
839
840 inner.listener.handle.set_position(pos, tween);
841
842 let ori_quat = Ori::from(ori).to_quat();
843 inner
844 .listener
845 .handle
846 .set_orientation(ori_quat.normalized(), tween);
847 }
848 }
849
850 pub fn get_listener(&mut self) -> Option<&mut ListenerHandle> {
851 self.inner.as_mut().map(|i| &mut i.listener.handle)
852 }
853
854 pub fn get_listener_pos(&self) -> Vec3<f32> {
855 self.inner
856 .as_ref()
857 .map(|i| i.listener.pos)
858 .unwrap_or_default()
859 }
860
861 pub fn get_listener_ori(&self) -> Vec3<f32> {
862 self.inner
863 .as_ref()
864 .map(|i| i.listener.ori)
865 .unwrap_or_else(Vec3::unit_x)
866 }
867
868 pub fn play_title_music(&mut self) {
871 if self.music_enabled() {
872 self.play_music(
873 "voxygen.audio.soundtrack.veloren_title_tune",
874 MusicChannelTag::TitleMusic,
875 43.0,
876 );
877 self.set_loop(MusicChannelTag::TitleMusic, true);
878 }
879 }
880
881 pub fn get_master_volume(&self) -> f32 { self.volumes.master }
883
884 pub fn get_music_volume(&self) -> f32 { self.volumes.music }
886
887 pub fn get_ambience_volume(&self) -> f32 { self.volumes.ambience }
889
890 pub fn get_sfx_volume(&self) -> f32 { self.volumes.sfx }
892
893 pub fn music_enabled(&self) -> bool { self.get_music_volume() > 0.0 }
895
896 pub fn ambience_enabled(&self) -> bool { self.get_ambience_volume() > 0.0 }
898
899 pub fn sfx_enabled(&self) -> bool { self.get_sfx_volume() > 0.0 }
901
902 pub fn set_music_volume(&mut self, music_volume: f32) {
903 self.volumes.music = music_volume;
904
905 if let Some(inner) = self.inner.as_mut() {
906 inner
907 .tracks
908 .music
909 .set_volume(to_decibels(music_volume), Tween::default())
910 }
911 }
912
913 pub fn set_ambience_volume(&mut self, ambience_volume: f32) {
914 self.volumes.ambience = ambience_volume;
915
916 if let Some(inner) = self.inner.as_mut() {
917 inner
918 .tracks
919 .ambience
920 .set_volume(to_decibels(ambience_volume), Tween::default())
921 }
922 }
923
924 pub fn set_sfx_volume(&mut self, sfx_volume: f32) {
927 self.volumes.sfx = sfx_volume;
928
929 if let Some(inner) = self.inner.as_mut() {
930 inner
931 .tracks
932 .sfx
933 .set_volume(to_decibels(sfx_volume), Tween::default())
934 }
935 }
936
937 pub fn set_instrument_volume(&mut self, instrument_volume: f32) {
938 self.volumes.instrument = instrument_volume;
939
940 if let Some(inner) = self.inner.as_mut() {
941 inner
942 .tracks
943 .instrument
944 .set_volume(to_decibels(instrument_volume), Tween::default())
945 }
946 }
947
948 pub fn set_music_spacing(&mut self, multiplier: f32) { self.music_spacing = multiplier }
949
950 pub fn set_subtitles(&mut self, enabled: bool) { self.subtitles_enabled = enabled }
951
952 pub fn set_master_volume(&mut self, master_volume: f32) {
954 self.volumes.master = master_volume;
955
956 if let Some(inner) = self.inner.as_mut() {
957 inner
958 .manager()
959 .main_track()
960 .set_volume(to_decibels(master_volume), Tween::default());
961 }
962 }
963
964 pub fn stop_all_ambience(&mut self) {
965 if let Some(inner) = self.inner.as_mut() {
966 for channel in &mut inner.channels.ambience {
967 channel.stop(None, None);
968 }
969 }
970 }
971
972 pub fn stop_all_music(&mut self) {
973 if let Some(inner) = self.inner.as_mut() {
974 for channel in &mut inner.channels.music {
975 channel.stop(None, None);
976 }
977 }
978 }
979
980 pub fn stop_all_sfx(&mut self) {
981 if let Some(inner) = self.inner.as_mut() {
982 for channel in &mut inner.channels.sfx {
983 channel.stop();
984 }
985 for channel in &mut inner.channels.ui {
986 channel.stop();
987 }
988 }
989 }
990
991 pub fn set_num_sfx_channels(&mut self, channels: usize) {
992 if let Some(inner) = self.inner.as_mut() {
993 inner.channels.sfx = Vec::new();
994 for _ in 0..channels {
995 inner.channels.sfx.push(SfxChannel::new());
996 }
997 }
998 }
999
1000 pub fn get_num_music_channels(&self) -> usize {
1001 self.inner
1002 .as_ref()
1003 .map(|i| i.channels.music.len())
1004 .unwrap_or(0)
1005 }
1006
1007 pub fn get_num_ambience_channels(&self) -> usize {
1008 self.inner
1009 .as_ref()
1010 .map(|i| i.channels.ambience.len())
1011 .unwrap_or(0)
1012 }
1013}