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