veloren_voxygen/audio/
mod.rs

1//! Handles audio device detection and playback of sound effects and music
2
3pub 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 cpal::{
16    Device, SampleRate, StreamConfig, SupportedStreamConfigRange,
17    traits::{DeviceTrait, HostTrait},
18};
19use kira::{
20    AudioManager, AudioManagerSettings, Decibels, Tween, Value,
21    backend::{
22        self,
23        cpal::{CpalBackend, CpalBackendSettings},
24    },
25    clock::{ClockHandle, ClockSpeed, ClockTime},
26    effect::filter::{FilterBuilder, FilterHandle},
27    listener::ListenerHandle,
28    track::{TrackBuilder, TrackHandle},
29};
30use music::MusicTransitionManifest;
31use sfx::{SfxEvent, SfxTriggerItem};
32use soundcache::load_ogg;
33use std::{collections::VecDeque, time::Duration};
34use strum::Display;
35use tracing::{debug, error, info, warn};
36
37use common::{
38    assets::{AssetExt, AssetHandle},
39    comp::Ori,
40};
41use vek::*;
42
43use crate::hud::Subtitle;
44
45// #[derive(Clone)]
46// pub struct Listener {
47//     pub pos: Vec3<f32>,
48//     pub ori: Vec3<f32>,
49
50//     ear_left_rpos: Vec3<f32>,
51//     ear_right_rpos: Vec3<f32>,
52// }
53
54// impl Default for Listener {
55//     fn default() -> Self {
56//         Self {
57//             pos: Default::default(),
58//             ori: Default::default(),
59//             ear_left_rpos: Vec3::unit_x(),
60//             ear_right_rpos: -Vec3::unit_x(),
61//         }
62//     }
63// }
64
65pub fn to_decibels(amplitude: f32) -> Decibels {
66    if amplitude <= 0.001 {
67        Decibels::SILENCE
68    } else if amplitude == 1.0 {
69        Decibels::IDENTITY
70    } else {
71        Decibels(amplitude.log10() * 20.0)
72    }
73}
74
75struct Tracks {
76    music: TrackHandle,
77    ui: TrackHandle,
78    sfx: TrackHandle,
79    ambience: TrackHandle,
80}
81
82#[derive(Clone, Copy, Debug, Display)]
83pub enum SfxChannelSettings {
84    Low,
85    Medium,
86    High,
87}
88
89impl SfxChannelSettings {
90    pub fn from_str_slice(str: &str) -> Self {
91        match str {
92            "Low" => SfxChannelSettings::Low,
93            "Medium" => SfxChannelSettings::Medium,
94            "High" => SfxChannelSettings::High,
95            _ => SfxChannelSettings::Medium,
96        }
97    }
98
99    pub fn to_usize(&self) -> usize {
100        match self {
101            SfxChannelSettings::Low => 16,
102            SfxChannelSettings::Medium => 32,
103            SfxChannelSettings::High => 64,
104        }
105    }
106}
107
108struct Effects {
109    sfx: FilterHandle,
110    ambience: FilterHandle,
111}
112
113#[derive(Default)]
114struct Channels {
115    music: Vec<MusicChannel>,
116    ambience: Vec<AmbienceChannel>,
117    sfx: Vec<SfxChannel>,
118    ui: Vec<UiChannel>,
119}
120
121impl Channels {
122    /// Gets the music channel matching the given tag, of which there should be
123    /// only one, if any.
124    fn get_music_channel(&mut self, channel_tag: MusicChannelTag) -> Option<&mut MusicChannel> {
125        self.music.iter_mut().find(|c| c.get_tag() == channel_tag)
126    }
127
128    /// Retrive an empty sfx channel from the list
129    fn get_sfx_channel(&mut self) -> Option<&mut SfxChannel> {
130        self.sfx.iter_mut().find(|c| c.is_done())
131    }
132
133    /// Retrive an empty UI channel from the list
134    fn get_ui_channel(&mut self) -> Option<&mut UiChannel> {
135        self.ui.iter_mut().find(|c| c.is_done())
136    }
137
138    /// Retrieves the channel currently having the given tag
139    /// If no channel with the given tag is found, returns None
140    fn get_ambience_channel(
141        &mut self,
142        channel_tag: AmbienceChannelTag,
143    ) -> Option<&mut AmbienceChannel> {
144        self.ambience
145            .iter_mut()
146            .find(|channel| channel.get_tag() == channel_tag)
147    }
148
149    fn count_active(&self) -> ActiveChannels {
150        ActiveChannels {
151            music: self.music.iter().filter(|c| !c.is_done()).count(),
152            ambience: self.ambience.iter().filter(|c| c.is_active()).count(),
153            sfx: self.sfx.iter().filter(|c| !c.is_done()).count(),
154            ui: self.ui.iter().filter(|c| !c.is_done()).count(),
155        }
156    }
157}
158
159#[derive(Default)]
160pub struct ActiveChannels {
161    pub music: usize,
162    pub ambience: usize,
163    pub sfx: usize,
164    pub ui: usize,
165}
166
167#[derive(Default)]
168struct Volumes {
169    sfx: f32,
170    ambience: f32,
171    music: f32,
172    master: f32,
173}
174
175struct ListenerInstance {
176    handle: ListenerHandle,
177    pos: Vec3<f32>,
178    ori: Vec3<f32>,
179}
180
181struct AudioFrontendInner {
182    manager: AudioManager,
183    tracks: Tracks,
184    effects: Effects,
185    channels: Channels,
186    listener: ListenerInstance,
187    clock: ClockHandle,
188}
189
190enum AudioCreationError {
191    Manager(<CpalBackend as backend::Backend>::Error),
192    Clock(kira::ResourceLimitReached),
193    Track(kira::ResourceLimitReached),
194    Listener(kira::ResourceLimitReached),
195}
196
197impl AudioFrontendInner {
198    fn new(
199        num_sfx_channels: usize,
200        num_ui_channels: usize,
201        buffer_size: usize,
202        device: Option<Device>,
203        config: Option<StreamConfig>,
204    ) -> Result<Self, AudioCreationError> {
205        let backend_settings = CpalBackendSettings { device, config };
206        let manager_settings = AudioManagerSettings {
207            internal_buffer_size: buffer_size,
208            backend_settings,
209            ..Default::default()
210        };
211        let mut manager = AudioManager::<CpalBackend>::new(manager_settings)
212            .map_err(AudioCreationError::Manager)?;
213
214        let mut clock = manager
215            .add_clock(ClockSpeed::TicksPerSecond(1.0))
216            .map_err(AudioCreationError::Clock)?;
217        clock.start();
218
219        let mut sfx_track_builder = TrackBuilder::new();
220        let mut ambience_track_builder = TrackBuilder::new();
221
222        let effects = Effects {
223            sfx: sfx_track_builder.add_effect(FilterBuilder::new().cutoff(Value::Fixed(20000.0))),
224            ambience: ambience_track_builder
225                .add_effect(FilterBuilder::new().cutoff(Value::Fixed(20000.0))),
226        };
227
228        let listener_handle = manager
229            .add_listener(Vec3::zero(), Quaternion::identity())
230            .map_err(AudioCreationError::Listener)?;
231
232        let listener = ListenerInstance {
233            handle: listener_handle,
234            pos: Vec3::zero(),
235            ori: Vec3::unit_x(),
236        };
237
238        let mut tracks = Tracks {
239            music: manager
240                .add_sub_track(TrackBuilder::new())
241                .map_err(AudioCreationError::Track)?,
242            ui: manager
243                .add_sub_track(TrackBuilder::new())
244                .map_err(AudioCreationError::Track)?,
245            sfx: manager
246                .add_sub_track(sfx_track_builder)
247                .map_err(AudioCreationError::Track)?,
248            ambience: manager
249                .add_sub_track(ambience_track_builder)
250                .map_err(AudioCreationError::Track)?,
251        };
252
253        let mut channels = Channels::default();
254
255        for _ in 0..num_sfx_channels {
256            if let Ok(channel) = SfxChannel::new(&mut tracks.sfx, listener.handle.id()) {
257                channels.sfx.push(channel);
258            } else {
259                warn!("Cannot create sfx channel")
260            }
261        }
262
263        for _ in 0..num_ui_channels {
264            if let Ok(channel) = UiChannel::new(&mut tracks.ui) {
265                channels.ui.push(channel);
266            } else {
267                warn!("Cannot create ui channel")
268            }
269        }
270
271        Ok(Self {
272            manager,
273            tracks,
274            effects,
275            channels,
276            listener,
277            clock,
278        })
279    }
280
281    fn manager(&mut self) -> &mut AudioManager { &mut self.manager }
282
283    fn clock(&self) -> &ClockHandle { &self.clock }
284
285    fn create_music_channel(&mut self, channel_tag: MusicChannelTag) {
286        let channel = MusicChannel::new(&mut self.tracks.music);
287        match channel {
288            Ok(mut next_music_channel) => {
289                next_music_channel.set_volume(1.0);
290                next_music_channel.set_tag(channel_tag);
291                self.channels.music.push(next_music_channel);
292            },
293            Err(e) => error!(
294                ?e,
295                "Failed to crate new music channel, music may fail playing"
296            ),
297        }
298    }
299
300    /// Adds a new ambience channel of the given tag at zero volume
301    fn new_ambience_channel(&mut self, channel_tag: AmbienceChannelTag) {
302        let channel = AmbienceChannel::new(channel_tag, 0.0, &mut self.tracks.ambience, true);
303        match channel {
304            Ok(ambience_channel) => self.channels.ambience.push(ambience_channel),
305            Err(e) => error!(
306                ?e,
307                "Failed to crate new ambience channel, sounds may fail playing"
308            ),
309        }
310    }
311}
312
313/// Holds information about the system audio devices and internal channels used
314/// for sfx and music playback. An instance of `AudioFrontend` is used by
315/// Voxygen's [`GlobalState`](../struct.GlobalState.html#structfield.audio) to
316/// provide access to devices and playback control in-game
317///
318/// TODO: Use a listener struct (like the one commented out above) instead of
319/// keeping all listener data in the AudioFrontend struct. Will be helpful when
320/// we do more with spatial audio.
321pub struct AudioFrontend {
322    inner: Option<AudioFrontendInner>,
323
324    pub subtitles_enabled: bool,
325    pub subtitles: VecDeque<Subtitle>,
326
327    volumes: Volumes,
328    music_spacing: f32,
329    pub combat_music_enabled: bool,
330
331    mtm: AssetHandle<MusicTransitionManifest>,
332}
333
334impl AudioFrontend {
335    pub fn new(
336        num_sfx_channels: usize,
337        num_ui_channels: usize,
338        subtitles: bool,
339        combat_music_enabled: bool,
340        buffer_size: usize,
341    ) -> Self {
342        let mut device = cpal::default_host().default_output_device();
343        let mut supported_config = None;
344        let mut samplerate = 44100;
345        if let Some(device) = device.as_mut() {
346            if let Ok(default_output_config) = device.default_output_config() {
347                info!(
348                    "Current default samplerate: {:?}",
349                    default_output_config.sample_rate().0
350                );
351                samplerate = default_output_config.sample_rate().0;
352                if samplerate > 48000 {
353                    warn!(
354                        "Current default samplerate is higher than 48000; attempting to lower \
355                         samplerate"
356                    );
357                    let supported_configs = device.supported_output_configs();
358                    if let Ok(supported_configs) = supported_configs {
359                        let best_config = supported_configs.max_by(|x, y| {
360                            SupportedStreamConfigRange::cmp_default_heuristics(x, y)
361                        });
362                        if let Some(best_config) = best_config {
363                            warn!("Attempting to change samplerate to 48khz");
364                            supported_config = best_config.try_with_sample_rate(SampleRate(48000));
365                            if supported_config.is_none() {
366                                warn!("Attempting to change samplerate to 44.1khz");
367                                supported_config =
368                                    best_config.try_with_sample_rate(SampleRate(44100));
369                            }
370                            if supported_config.is_none() {
371                                warn!("Could not change samplerate, using default")
372                            }
373                        }
374                    }
375                }
376            }
377        }
378        let mut config = None;
379        if let Some(supported_config) = supported_config {
380            info!(
381                "Samplerate is {:?}",
382                supported_config.config().sample_rate.0
383            );
384            config = Some(supported_config.config())
385        } else {
386            info!("Samplerate is {:?}", samplerate)
387        }
388        let inner = AudioFrontendInner::new(
389            num_sfx_channels,
390            num_ui_channels,
391            buffer_size,
392            device,
393            config,
394        )
395        .inspect_err(|err| match err {
396            AudioCreationError::Manager(e) => {
397                #[cfg(unix)]
398                error!(
399                    ?e,
400                    "failed to construct audio frontend manager. Is `pulseaudio-alsa` installed?"
401                );
402                #[cfg(not(unix))]
403                error!(?e, "failed to construct audio frontend manager.");
404            },
405            AudioCreationError::Clock(e) => {
406                error!(?e, "Failed to construct audio frontend clock.")
407            },
408            AudioCreationError::Track(e) => {
409                error!(?e, "Failed to construct audio frontend track.")
410            },
411            AudioCreationError::Listener(e) => {
412                error!(?e, "Failed to construct audio frontend listener.")
413            },
414        })
415        .ok();
416
417        if let Some(inner) = inner {
418            Self {
419                inner: Some(inner),
420                volumes: Volumes::default(),
421                music_spacing: 1.0,
422                mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"),
423                subtitles: VecDeque::new(),
424                subtitles_enabled: subtitles,
425                combat_music_enabled,
426            }
427        } else {
428            Self {
429                inner: None,
430                volumes: Volumes::default(),
431                music_spacing: 1.0,
432                mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"),
433                subtitles: VecDeque::new(),
434                subtitles_enabled: subtitles,
435                combat_music_enabled,
436            }
437        }
438    }
439
440    /// Construct in `no-audio` mode for debugging
441    pub fn no_audio() -> Self {
442        Self {
443            inner: None,
444            music_spacing: 1.0,
445            volumes: Volumes::default(),
446            mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"),
447            subtitles: VecDeque::new(),
448            subtitles_enabled: false,
449            combat_music_enabled: false,
450        }
451    }
452
453    /// Drop any unused music channels
454    pub fn maintain(&mut self) {
455        if let Some(inner) = &mut self.inner {
456            inner.channels.music.retain(|c| !c.is_done());
457            inner.channels.ambience.retain(|c| !c.is_stopped());
458        }
459    }
460
461    pub fn get_clock(&self) -> Option<&ClockHandle> { self.inner.as_ref().map(|i| i.clock()) }
462
463    pub fn get_clock_time(&self) -> Option<ClockTime> { self.get_clock().map(|clock| clock.time()) }
464
465    /// Returns [music channels, ambience channels, sfx channels, ui channels]
466    pub fn get_num_active_channels(&self) -> ActiveChannels {
467        self.inner
468            .as_ref()
469            .map(|i| i.channels.count_active())
470            .unwrap_or_default()
471    }
472
473    pub fn get_cpu_usage(&mut self) -> f32 {
474        if let Some(inner) = self.inner.as_mut() {
475            inner.manager.backend_mut().pop_cpu_usage().unwrap_or(0.0)
476        } else {
477            0.0
478        }
479    }
480
481    /// Play a music file with the given tag. Pass in the length of the track in
482    /// seconds.
483    fn play_music(&mut self, sound: &str, channel_tag: MusicChannelTag, length: f32) {
484        if self.music_enabled()
485            && let Some(inner) = &mut self.inner
486        {
487            let mtm = self.mtm.read();
488
489            if let Some(current_channel) = inner.channels.music.iter_mut().find(|c| !c.is_done()) {
490                let (fade_out, _fade_in) = mtm
491                    .fade_timings
492                    .get(&(current_channel.get_tag(), channel_tag))
493                    .unwrap_or(&(1.0, 1.0));
494                current_channel.fade_out(*fade_out, None);
495            }
496
497            let now = inner.clock().time();
498
499            let channel = match inner.channels.get_music_channel(channel_tag) {
500                Some(c) => c,
501                None => {
502                    inner.create_music_channel(channel_tag);
503                    inner
504                        .channels
505                        .music
506                        .last_mut()
507                        .expect("We just created this")
508                },
509            };
510
511            let (fade_out, fade_in) = mtm
512                .fade_timings
513                .get(&(channel.get_tag(), channel_tag))
514                .unwrap_or(&(1.0, 0.1));
515            let source = load_ogg(sound, true);
516            channel.stop(Some(*fade_out), None);
517            channel.set_length(length);
518            channel.set_tag(channel_tag);
519            channel.set_loop_data(false, LoopPoint::Start, LoopPoint::End);
520            channel.play(source, now, Some(*fade_in), Some(*fade_out));
521        }
522    }
523
524    /// Turn on or off looping
525    pub fn set_loop(&mut self, channel_tag: MusicChannelTag, sound_loops: bool) {
526        if let Some(inner) = self.inner.as_mut() {
527            let channel = inner.channels.get_music_channel(channel_tag);
528            if let Some(channel) = channel {
529                let loop_data = channel.get_loop_data();
530                channel.set_loop_data(sound_loops, loop_data.1, loop_data.2);
531            }
532        }
533    }
534
535    /// Loops music from start point to end point in seconds
536    pub fn set_loop_points(&mut self, channel_tag: MusicChannelTag, start: f32, end: f32) {
537        if let Some(inner) = self.inner.as_mut() {
538            let channel = inner.channels.get_music_channel(channel_tag);
539            if let Some(channel) = channel {
540                channel.set_loop_data(
541                    true,
542                    LoopPoint::Point(start as f64),
543                    LoopPoint::Point(end as f64),
544                );
545            }
546        }
547    }
548
549    /// Set the cutoff of the filter affecting all ambience
550    pub fn set_ambience_master_filter(&mut self, frequency: u32) {
551        if let Some(inner) = self.inner.as_mut() {
552            inner
553                .effects
554                .ambience
555                .set_cutoff(Value::Fixed(frequency as f64), Tween::default());
556        }
557    }
558
559    /// Find sound based on given trigger_item.
560    /// Randomizes if multiple sounds are found.
561    /// Errors if no sounds are found.
562    /// Returns (file, threshold, subtitle)
563    pub fn get_sfx_file<'a>(
564        trigger_item: Option<(&'a SfxEvent, &'a SfxTriggerItem)>,
565    ) -> Option<(&'a str, f32, Option<&'a str>)> {
566        trigger_item.map(|(event, item)| {
567            let file = match item.files.len() {
568                0 => {
569                    debug!("Sfx event {:?} is missing audio file.", event);
570                    "voxygen.audio.sfx.placeholder"
571                },
572                1 => item
573                    .files
574                    .last()
575                    .expect("Failed to determine sound file for this trigger item."),
576                _ => {
577                    // If more than one file is listed, choose one at random
578                    let rand_step = rand::random::<usize>() % item.files.len();
579                    &item.files[rand_step]
580                },
581            };
582
583            // NOTE: Threshold here is meant to give subtitles some idea of the duration of
584            // the audio, it doesn't have to be perfect but in the future, if possible we
585            // might want to switch it out for the actual duration.
586            (file, item.threshold, item.subtitle.as_deref())
587        })
588    }
589
590    /// Set the cutoff of the filter affecting all spatial sfx
591    pub fn set_sfx_master_filter(&mut self, frequency: u32) {
592        if let Some(inner) = self.inner.as_mut() {
593            inner
594                .effects
595                .sfx
596                .set_cutoff(Value::Fixed(frequency as f64), Tween::default());
597        }
598    }
599
600    /// Play an sfx file given the position and SfxEvent at the given volume
601    /// (default 1.0)
602    pub fn emit_sfx(
603        &mut self,
604        trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
605        position: Vec3<f32>,
606        volume: Option<f32>,
607    ) {
608        if let Some((sfx_file, dur, subtitle)) = Self::get_sfx_file(trigger_item) {
609            self.emit_subtitle(subtitle, Some(position), dur);
610            // Play sound in empty channel at given position
611            if self.sfx_enabled()
612                && let Some(inner) = self.inner.as_mut()
613                && let Some(channel) = inner.channels.get_sfx_channel()
614            {
615                let sound = load_ogg(sfx_file, false);
616                channel.update(position);
617
618                let source = sound.volume(to_decibels(volume.unwrap_or(1.0) * 5.0));
619                channel.play(source);
620            }
621        } else {
622            warn!(
623                "Missing sfx trigger config for sfx event at position: {:?}\n{:#?}",
624                position,
625                backtrace::Backtrace::new(),
626            );
627        }
628    }
629
630    /// Plays a sfx non-spatially at the given volume (default 1.0); doesn't
631    /// need a position
632    pub fn emit_ui_sfx(
633        &mut self,
634        trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
635        volume: Option<f32>,
636    ) {
637        if let Some((sfx_file, dur, subtitle)) = Self::get_sfx_file(trigger_item) {
638            self.emit_subtitle(subtitle, None, dur);
639
640            // Play sound in empty channel
641            if self.sfx_enabled()
642                && let Some(inner) = self.inner.as_mut()
643                && let Some(channel) = inner.channels.get_ui_channel()
644            {
645                let sound = load_ogg(sfx_file, false).volume(to_decibels(volume.unwrap_or(1.0)));
646                channel.play(sound);
647            }
648        } else {
649            warn!("Missing sfx trigger config for ui sfx event.",);
650        }
651    }
652
653    /// Push a subtitle to the subtitle queue
654    pub fn emit_subtitle(
655        &mut self,
656        subtitle: Option<&str>,
657        position: Option<Vec3<f32>>,
658        duration: f32,
659    ) {
660        if self.subtitles_enabled {
661            if let Some(subtitle) = subtitle {
662                self.subtitles.push_back(Subtitle {
663                    localization: subtitle.to_string(),
664                    position,
665                    show_for: duration as f64,
666                });
667                if self.subtitles.len() > 10 {
668                    self.subtitles.pop_front();
669                }
670            }
671        }
672    }
673
674    /// Plays an ambience sound that loops in the channel with a given tag
675    pub fn play_ambience_looping(&mut self, channel_tag: AmbienceChannelTag, sound: &str) {
676        if self.ambience_enabled()
677            && let Some(inner) = self.inner.as_mut()
678            && let Some(channel) = inner.channels.get_ambience_channel(channel_tag)
679        {
680            let source = load_ogg(sound, true).loop_region(0.0..);
681            channel.set_looping(true);
682            channel.play(source, Some(1.0), None);
683        }
684    }
685
686    /// Plays an ambience sound once at the given volume after the given delay.
687    /// Make sure it uses a channel tag that does not change the volume of its
688    /// channel. Currently, ambience oneshots use the Sfx file system
689    pub fn play_ambience_oneshot(
690        &mut self,
691        channel_tag: AmbienceChannelTag,
692        trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
693        volume: Option<f32>,
694        delay: Option<f32>,
695    ) {
696        if self.ambience_enabled()
697            && trigger_item.is_some()
698            && let Some(inner) = self.inner.as_mut()
699            && let Some(channel) = inner.channels.get_ambience_channel(channel_tag)
700        {
701            let sound = AudioFrontend::get_sfx_file(trigger_item)
702                .unwrap_or(("", 0.0, Some("")))
703                .0;
704            let source = load_ogg(sound, false)
705                .loop_region(None)
706                .volume(to_decibels(volume.unwrap_or(1.0)));
707            channel.set_looping(false);
708            channel.fade_to(1.0, 0.0);
709            channel.play(source, None, delay);
710        }
711    }
712
713    pub fn set_listener_pos(&mut self, pos: Vec3<f32>, ori: Vec3<f32>) {
714        if let Some(inner) = self.inner.as_mut() {
715            let tween = Tween {
716                duration: Duration::from_secs_f32(0.01),
717                ..Default::default()
718            };
719
720            inner.listener.pos = pos;
721            inner.listener.ori = ori;
722
723            inner.listener.handle.set_position(pos, tween);
724
725            let ori_quat = Ori::from(ori).to_quat();
726            inner
727                .listener
728                .handle
729                .set_orientation(ori_quat.normalized(), tween);
730        }
731    }
732
733    pub fn get_listener(&mut self) -> Option<&mut ListenerHandle> {
734        self.inner.as_mut().map(|i| &mut i.listener.handle)
735    }
736
737    pub fn get_listener_pos(&self) -> Vec3<f32> {
738        self.inner
739            .as_ref()
740            .map(|i| i.listener.pos)
741            .unwrap_or_default()
742    }
743
744    pub fn get_listener_ori(&self) -> Vec3<f32> {
745        self.inner
746            .as_ref()
747            .map(|i| i.listener.ori)
748            .unwrap_or_else(Vec3::unit_x)
749    }
750
751    /// Switches the playing music to the title music, which is pinned to a
752    /// specific sound file (veloren_title_tune.ogg)
753    pub fn play_title_music(&mut self) {
754        if self.music_enabled() {
755            self.play_music(
756                "voxygen.audio.soundtrack.veloren_title_tune",
757                MusicChannelTag::TitleMusic,
758                43.0,
759            );
760            self.set_loop(MusicChannelTag::TitleMusic, true);
761        }
762    }
763
764    /// Retrieves the current setting for master volume
765    pub fn get_master_volume(&self) -> f32 { self.volumes.master }
766
767    /// Retrieves the current setting for music volume
768    pub fn get_music_volume(&self) -> f32 { self.volumes.music }
769
770    /// Retrieves the current setting for ambience volume
771    pub fn get_ambience_volume(&self) -> f32 { self.volumes.ambience }
772
773    /// Retrieves the current setting for sfx volume
774    pub fn get_sfx_volume(&self) -> f32 { self.volumes.sfx }
775
776    /// Returns false if volume is 0 or the mute is on
777    pub fn music_enabled(&self) -> bool { self.get_music_volume() > 0.0 }
778
779    /// Returns false if volume is 0 or the mute is on
780    pub fn ambience_enabled(&self) -> bool { self.get_ambience_volume() > 0.0 }
781
782    /// Returns false if volume is 0 or the mute is on
783    pub fn sfx_enabled(&self) -> bool { self.get_sfx_volume() > 0.0 }
784
785    pub fn set_music_volume(&mut self, music_volume: f32) {
786        self.volumes.music = music_volume;
787
788        if let Some(inner) = self.inner.as_mut() {
789            inner
790                .tracks
791                .music
792                .set_volume(to_decibels(music_volume), Tween::default())
793        }
794    }
795
796    pub fn set_ambience_volume(&mut self, ambience_volume: f32) {
797        self.volumes.ambience = ambience_volume;
798
799        if let Some(inner) = self.inner.as_mut() {
800            inner
801                .tracks
802                .ambience
803                .set_volume(to_decibels(ambience_volume), Tween::default())
804        }
805    }
806
807    /// Sets the volume for both spatial sfx and UI (might separate these
808    /// controls later)
809    pub fn set_sfx_volume(&mut self, sfx_volume: f32) {
810        self.volumes.sfx = sfx_volume;
811
812        if let Some(inner) = self.inner.as_mut() {
813            inner
814                .tracks
815                .sfx
816                .set_volume(to_decibels(sfx_volume), Tween::default())
817        }
818    }
819
820    pub fn set_music_spacing(&mut self, multiplier: f32) { self.music_spacing = multiplier }
821
822    pub fn set_subtitles(&mut self, enabled: bool) { self.subtitles_enabled = enabled }
823
824    /// Updates volume of the master track
825    pub fn set_master_volume(&mut self, master_volume: f32) {
826        self.volumes.master = master_volume;
827
828        if let Some(inner) = self.inner.as_mut() {
829            inner
830                .manager()
831                .main_track()
832                .set_volume(to_decibels(master_volume), Tween::default());
833        }
834    }
835
836    pub fn stop_all_ambience(&mut self) {
837        if let Some(inner) = self.inner.as_mut() {
838            for channel in &mut inner.channels.ambience {
839                channel.stop(None, None);
840            }
841        }
842    }
843
844    pub fn stop_all_music(&mut self) {
845        if let Some(inner) = self.inner.as_mut() {
846            for channel in &mut inner.channels.music {
847                channel.stop(None, None);
848            }
849        }
850    }
851
852    pub fn stop_all_sfx(&mut self) {
853        if let Some(inner) = self.inner.as_mut() {
854            for channel in &mut inner.channels.sfx {
855                channel.stop();
856            }
857            for channel in &mut inner.channels.ui {
858                channel.stop();
859            }
860        }
861    }
862
863    pub fn set_num_sfx_channels(&mut self, channels: usize) {
864        if let Some(inner) = self.inner.as_mut() {
865            inner.channels.sfx = Vec::new();
866            for _ in 0..channels {
867                if let Ok(channel) =
868                    SfxChannel::new(&mut inner.tracks.sfx, inner.listener.handle.id())
869                {
870                    inner.channels.sfx.push(channel);
871                } else {
872                    warn!("Cannot create sfx channel")
873                }
874            }
875        }
876    }
877
878    pub fn get_num_music_channels(&self) -> usize {
879        self.inner
880            .as_ref()
881            .map(|i| i.channels.music.len())
882            .unwrap_or(0)
883    }
884
885    pub fn get_num_ambience_channels(&self) -> usize {
886        self.inner
887            .as_ref()
888            .map(|i| i.channels.ambience.len())
889            .unwrap_or(0)
890    }
891}