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, Ron},
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<Ron<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 && let Ok(default_output_config) = device.default_output_config()
361 {
362 info!(
363 "Current default samplerate: {:?}",
364 default_output_config.sample_rate().0
365 );
366 samplerate = default_output_config.sample_rate().0;
367 if samplerate > 48000 && set_samplerate.is_none() {
368 warn!(
369 "Current default samplerate is higher than 48000; attempting to lower \
370 samplerate"
371 );
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 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 = best_config.try_with_sample_rate(SampleRate(44100));
382 }
383 if supported_config.is_none() {
384 warn!("Could not change samplerate, using default")
385 }
386 }
387 }
388 } else if let Some(set_samplerate) = set_samplerate {
389 let supported_configs = device.supported_output_configs();
390 if let Ok(supported_configs) = supported_configs {
391 let best_config = supported_configs
392 .max_by(SupportedStreamConfigRange::cmp_default_heuristics);
393 if let Some(best_config) = best_config {
394 warn!("Attempting to force samplerate to {:?}", set_samplerate);
395 supported_config =
396 best_config.try_with_sample_rate(SampleRate(set_samplerate));
397 if supported_config.is_none() {
398 error!(
399 "Could not set samplerate to {:?}, falling back to default.",
400 set_samplerate
401 );
402 }
403 }
404 }
405 }
406 }
407 let mut config = None;
408 if let Some(supported_config) = supported_config {
409 info!(
410 "Samplerate is {:?}",
411 supported_config.config().sample_rate.0
412 );
413 config = Some(supported_config.config())
414 } else {
415 info!("Samplerate is {:?}", samplerate)
416 }
417 let inner = AudioFrontendInner::new(
418 num_sfx_channels,
419 num_ui_channels,
420 buffer_size,
421 device,
422 config,
423 )
424 .inspect_err(|err| match err {
425 AudioCreationError::Manager(e) => {
426 #[cfg(unix)]
427 error!(
428 ?e,
429 "failed to construct audio frontend manager. Is `pulseaudio-alsa` installed?"
430 );
431 #[cfg(not(unix))]
432 error!(?e, "failed to construct audio frontend manager.");
433 },
434 AudioCreationError::Clock(e) => {
435 error!(?e, "Failed to construct audio frontend clock.")
436 },
437 AudioCreationError::Track(e) => {
438 error!(?e, "Failed to construct audio frontend track.")
439 },
440 AudioCreationError::Listener(e) => {
441 error!(?e, "Failed to construct audio frontend listener.")
442 },
443 })
444 .ok();
445
446 if let Some(inner) = inner {
447 Self {
448 inner: Some(inner),
449 volumes: Volumes::default(),
450 music_spacing: 1.0,
451 mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"),
452 subtitles: VecDeque::new(),
453 subtitles_enabled: subtitles,
454 combat_music_enabled,
455 }
456 } else {
457 Self {
458 inner: None,
459 volumes: Volumes::default(),
460 music_spacing: 1.0,
461 mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"),
462 subtitles: VecDeque::new(),
463 subtitles_enabled: subtitles,
464 combat_music_enabled,
465 }
466 }
467 }
468
469 fn channels_mut(&mut self) -> Option<&mut Channels> { Some(&mut self.inner.as_mut()?.channels) }
470
471 pub fn no_audio() -> Self {
473 Self {
474 inner: None,
475 music_spacing: 1.0,
476 volumes: Volumes::default(),
477 mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"),
478 subtitles: VecDeque::new(),
479 subtitles_enabled: false,
480 combat_music_enabled: false,
481 }
482 }
483
484 pub fn maintain(&mut self) {
487 if let Some(inner) = &mut self.inner {
488 inner.channels.music.retain(|c| !c.is_done());
489 inner.channels.ambience.retain(|c| !c.is_stopped());
490 inner.channels.sfx.iter_mut().for_each(|c| {
493 if c.is_done() {
494 c.set_volume(0.0);
495 }
496 });
497 inner.channels.ui.iter_mut().for_each(|c| {
498 if c.is_done() {
499 c.tag = None
500 }
501 });
502 }
503 }
504
505 pub fn get_clock(&self) -> Option<&ClockHandle> { self.inner.as_ref().map(|i| i.clock()) }
506
507 pub fn get_clock_time(&self) -> Option<ClockTime> { self.get_clock().map(|clock| clock.time()) }
508
509 pub fn get_num_active_channels(&self) -> ActiveChannels {
511 self.inner
512 .as_ref()
513 .map(|i| i.channels.count_active())
514 .unwrap_or_default()
515 }
516
517 pub fn get_cpu_usage(&mut self) -> f32 {
518 if let Some(inner) = self.inner.as_mut() {
519 inner.manager.backend_mut().pop_cpu_usage().unwrap_or(0.0)
520 } else {
521 0.0
522 }
523 }
524
525 fn play_music(&mut self, sound: &str, channel_tag: MusicChannelTag, length: f32) {
528 if self.music_enabled()
529 && let Some(inner) = &mut self.inner
530 {
531 let mtm = self.mtm.read();
532
533 if let Some(current_channel) = inner.channels.music.iter_mut().find(|c| !c.is_done()) {
534 let (fade_out, _fade_in) = mtm
535 .0
536 .fade_timings
537 .get(&(current_channel.get_tag(), channel_tag))
538 .unwrap_or(&(1.0, 1.0));
539 current_channel.fade_out(*fade_out, None);
540 }
541
542 let now = inner.clock().time();
543
544 let channel = match inner.channels.get_music_channel(channel_tag) {
545 Some(c) => c,
546 None => {
547 inner.create_music_channel(channel_tag);
548 inner
549 .channels
550 .music
551 .last_mut()
552 .expect("We just created this")
553 },
554 };
555
556 let (fade_out, fade_in) = mtm
557 .0
558 .fade_timings
559 .get(&(channel.get_tag(), channel_tag))
560 .unwrap_or(&(1.0, 0.1));
561 let source = load_ogg(sound, true);
562 channel.stop(Some(*fade_out), None);
563 channel.set_length(length);
564 channel.set_tag(channel_tag);
565 channel.set_loop_data(false, LoopPoint::Start, LoopPoint::End);
566 channel.play(source, now, Some(*fade_in), Some(*fade_out));
567 }
568 }
569
570 pub fn set_loop(&mut self, channel_tag: MusicChannelTag, sound_loops: bool) {
572 if let Some(inner) = self.inner.as_mut() {
573 let channel = inner.channels.get_music_channel(channel_tag);
574 if let Some(channel) = channel {
575 let loop_data = channel.get_loop_data();
576 channel.set_loop_data(sound_loops, loop_data.1, loop_data.2);
577 }
578 }
579 }
580
581 pub fn set_loop_points(&mut self, channel_tag: MusicChannelTag, start: f32, end: f32) {
583 if let Some(inner) = self.inner.as_mut() {
584 let channel = inner.channels.get_music_channel(channel_tag);
585 if let Some(channel) = channel {
586 channel.set_loop_data(
587 true,
588 LoopPoint::Point(start as f64),
589 LoopPoint::Point(end as f64),
590 );
591 }
592 }
593 }
594
595 pub fn get_sfx_file<'a>(
600 trigger_item: Option<(&'a SfxEvent, &'a SfxTriggerItem)>,
601 ) -> Option<(&'a str, f32, Option<&'a str>)> {
602 trigger_item.map(|(event, item)| {
603 let file = match item.files.len() {
604 0 => {
605 debug!("Sfx event {:?} is missing audio file.", event);
606 "voxygen.audio.sfx.placeholder"
607 },
608 1 => item
609 .files
610 .last()
611 .expect("Failed to determine sound file for this trigger item."),
612 _ => {
613 let rand_step = (rand::random::<u64>() as usize) % item.files.len();
615 &item.files[rand_step]
616 },
617 };
618
619 (file, item.threshold, item.subtitle.as_deref())
623 })
624 }
625
626 pub fn set_sfx_master_filter(&mut self, frequency: u32) {
628 if let Some(inner) = self.inner.as_mut() {
629 inner
630 .effects
631 .sfx
632 .set_cutoff(Value::Fixed(frequency as f64), Tween::default());
633 }
634 }
635
636 pub fn emit_sfx(
639 &mut self,
640 trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
641 emitter_pos: Vec3<f32>,
642 volume: Option<f32>,
643 player_pos: Vec3<f32>,
644 ) {
645 self.emit_sfx_ext(trigger_item, emitter_pos, volume, player_pos);
646 }
647
648 pub fn emit_sfx_ext(
649 &mut self,
650 trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
651 emitter_pos: Vec3<f32>,
652 volume: Option<f32>,
653 player_pos: Vec3<f32>,
654 ) -> Option<SfxHandle> {
655 if let Some((sfx_file, dur, subtitle)) = Self::get_sfx_file(trigger_item) {
656 self.emit_subtitle(subtitle, Some(emitter_pos), dur);
657 if self.sfx_enabled()
659 && let Some(inner) = self.inner.as_mut()
660 && let Some((channel_idx, channel)) = inner.channels.get_empty_sfx_channel()
661 {
662 let sound = load_ogg(sfx_file, false);
663 channel.set_pos(emitter_pos);
664 channel.update(player_pos);
665
666 let source = sound.volume(to_decibels(volume.unwrap_or(1.0) * 5.0));
667 Some(SfxHandle {
668 channel_idx,
669 play_id: channel.play(source),
670 })
671 } else {
672 None
673 }
674 } else {
675 warn!(
676 "Missing sfx trigger config for sfx event: {:?}; {:?}",
677 trigger_item,
678 backtrace::Backtrace::new(),
679 );
680 None
681 }
682 }
683
684 pub fn emit_ui_sfx(
687 &mut self,
688 trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
689 volume: Option<f32>,
690 tag: Option<channel::UiChannelTag>,
691 ) {
692 if let Some((sfx_file, dur, subtitle)) = Self::get_sfx_file(trigger_item) {
693 self.emit_subtitle(subtitle, None, dur);
694
695 if self.sfx_enabled()
697 && let Some(inner) = self.inner.as_mut()
698 && !inner
699 .channels
700 .ui
701 .iter()
702 .any(|c| tag.is_some() && c.tag == tag)
703 && let Some(channel) = inner.channels.get_ui_channel()
704 {
705 let sound = load_ogg(sfx_file, false).volume(to_decibels(volume.unwrap_or(1.0)));
706 channel.play(sound, tag);
707 }
708 } else {
709 warn!("Missing sfx trigger config for ui sfx event.",);
710 }
711 }
712
713 pub fn emit_subtitle(
715 &mut self,
716 subtitle: Option<&str>,
717 position: Option<Vec3<f32>>,
718 duration: f32,
719 ) {
720 if self.subtitles_enabled
721 && let Some(subtitle) = subtitle
722 {
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 pub fn set_ambience_master_filter(&mut self, frequency: u32) {
736 if let Some(inner) = self.inner.as_mut() {
737 inner
738 .effects
739 .ambience
740 .set_cutoff(Value::Fixed(frequency as f64), Tween::default());
741 }
742 }
743
744 pub fn play_ambience_looping(
746 &mut self,
747 channel_tag: AmbienceChannelTag,
748 sound: &str,
749 start: usize,
750 end: usize,
751 ) {
752 if self.ambience_enabled()
753 && let Some(inner) = self.inner.as_mut()
754 && let Some(channel) = inner.channels.get_ambience_channel(channel_tag)
755 {
756 let source = load_ogg(sound, true).loop_region(
757 kira::sound::PlaybackPosition::Samples(start)
758 ..kira::sound::PlaybackPosition::Samples(end),
759 );
760 channel.play(source, Some(1.0), None);
761 }
762 }
763
764 pub fn play_ambience_oneshot(
768 &mut self,
769 channel_tag: AmbienceChannelTag,
770 trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>,
771 volume: Option<f32>,
772 delay: Option<f32>,
773 ) {
774 if self.ambience_enabled()
775 && trigger_item.is_some()
776 && let Some(inner) = self.inner.as_mut()
777 && let Some(channel) = inner.channels.get_ambience_channel(channel_tag)
778 {
779 let sound = AudioFrontend::get_sfx_file(trigger_item)
780 .unwrap_or(("", 0.0, Some("")))
781 .0;
782 let source = load_ogg(sound, false)
783 .loop_region(None)
784 .volume(to_decibels(volume.unwrap_or(1.0)));
785 channel.fade_to(1.0, 0.0);
786 channel.play(source, None, delay);
787 }
788 }
789
790 pub fn set_listener_pos(&mut self, pos: Vec3<f32>, ori: Vec3<f32>) {
791 if let Some(inner) = self.inner.as_mut() {
792 let tween = Tween {
793 duration: Duration::from_secs_f32(0.01),
794 ..Default::default()
795 };
796
797 inner.listener.pos = pos;
798 inner.listener.ori = ori;
799
800 inner.listener.handle.set_position(pos, tween);
801
802 let ori_quat = Ori::from(ori).to_quat();
803 inner
804 .listener
805 .handle
806 .set_orientation(ori_quat.normalized(), tween);
807 }
808 }
809
810 pub fn get_listener(&mut self) -> Option<&mut ListenerHandle> {
811 self.inner.as_mut().map(|i| &mut i.listener.handle)
812 }
813
814 pub fn get_listener_pos(&self) -> Vec3<f32> {
815 self.inner
816 .as_ref()
817 .map(|i| i.listener.pos)
818 .unwrap_or_default()
819 }
820
821 pub fn get_listener_ori(&self) -> Vec3<f32> {
822 self.inner
823 .as_ref()
824 .map(|i| i.listener.ori)
825 .unwrap_or_else(Vec3::unit_x)
826 }
827
828 pub fn play_title_music(&mut self) {
831 if self.music_enabled() {
832 self.play_music(
833 "voxygen.audio.soundtrack.veloren_title_tune",
834 MusicChannelTag::TitleMusic,
835 43.0,
836 );
837 self.set_loop(MusicChannelTag::TitleMusic, true);
838 }
839 }
840
841 pub fn get_master_volume(&self) -> f32 { self.volumes.master }
843
844 pub fn get_music_volume(&self) -> f32 { self.volumes.music }
846
847 pub fn get_ambience_volume(&self) -> f32 { self.volumes.ambience }
849
850 pub fn get_sfx_volume(&self) -> f32 { self.volumes.sfx }
852
853 pub fn music_enabled(&self) -> bool { self.get_music_volume() > 0.0 }
855
856 pub fn ambience_enabled(&self) -> bool { self.get_ambience_volume() > 0.0 }
858
859 pub fn sfx_enabled(&self) -> bool { self.get_sfx_volume() > 0.0 }
861
862 pub fn set_music_volume(&mut self, music_volume: f32) {
863 self.volumes.music = music_volume;
864
865 if let Some(inner) = self.inner.as_mut() {
866 inner
867 .tracks
868 .music
869 .set_volume(to_decibels(music_volume), Tween::default())
870 }
871 }
872
873 pub fn set_ambience_volume(&mut self, ambience_volume: f32) {
874 self.volumes.ambience = ambience_volume;
875
876 if let Some(inner) = self.inner.as_mut() {
877 inner
878 .tracks
879 .ambience
880 .set_volume(to_decibels(ambience_volume), Tween::default())
881 }
882 }
883
884 pub fn set_sfx_volume(&mut self, sfx_volume: f32) {
887 self.volumes.sfx = sfx_volume;
888
889 if let Some(inner) = self.inner.as_mut() {
890 inner
891 .tracks
892 .sfx
893 .set_volume(to_decibels(sfx_volume), Tween::default())
894 }
895 }
896
897 pub fn set_music_spacing(&mut self, multiplier: f32) { self.music_spacing = multiplier }
898
899 pub fn set_subtitles(&mut self, enabled: bool) { self.subtitles_enabled = enabled }
900
901 pub fn set_master_volume(&mut self, master_volume: f32) {
903 self.volumes.master = master_volume;
904
905 if let Some(inner) = self.inner.as_mut() {
906 inner
907 .manager()
908 .main_track()
909 .set_volume(to_decibels(master_volume), Tween::default());
910 }
911 }
912
913 pub fn stop_all_ambience(&mut self) {
914 if let Some(inner) = self.inner.as_mut() {
915 for channel in &mut inner.channels.ambience {
916 channel.stop(None, None);
917 }
918 }
919 }
920
921 pub fn stop_all_music(&mut self) {
922 if let Some(inner) = self.inner.as_mut() {
923 for channel in &mut inner.channels.music {
924 channel.stop(None, None);
925 }
926 }
927 }
928
929 pub fn stop_all_sfx(&mut self) {
930 if let Some(inner) = self.inner.as_mut() {
931 for channel in &mut inner.channels.sfx {
932 channel.stop();
933 }
934 for channel in &mut inner.channels.ui {
935 channel.stop();
936 }
937 }
938 }
939
940 pub fn set_num_sfx_channels(&mut self, channels: usize) {
941 if let Some(inner) = self.inner.as_mut() {
942 inner.channels.sfx = Vec::new();
943 for _ in 0..channels {
944 if let Ok(channel) =
945 SfxChannel::new(&mut inner.tracks.sfx, inner.listener.handle.id())
946 {
947 inner.channels.sfx.push(channel);
948 } else {
949 warn!("Cannot create sfx channel")
950 }
951 }
952 }
953 }
954
955 pub fn get_num_music_channels(&self) -> usize {
956 self.inner
957 .as_ref()
958 .map(|i| i.channels.music.len())
959 .unwrap_or(0)
960 }
961
962 pub fn get_num_ambience_channels(&self) -> usize {
963 self.inner
964 .as_ref()
965 .map(|i| i.channels.ambience.len())
966 .unwrap_or(0)
967 }
968}