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