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