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