Skip to main content

siglus_scene_vm/audio/
kira_hub.rs

1use std::fmt;
2
3use anyhow::{anyhow, Context, Result};
4use kira::manager::{backend::DefaultBackend, AudioManager, AudioManagerSettings};
5use kira::sound::static_sound::{StaticSoundData, StaticSoundHandle};
6use kira::track::{TrackBuilder, TrackHandle};
7use kira::tween::Tween;
8use kira::Volume;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum TrackKind {
12    Bgm,
13    Se,
14    Pcm,
15    Koe,
16    Mov,
17}
18
19/// Shared Kira backend.
20///
21/// Kira 0.9 uses `AudioManager::play` plus `StaticSoundData::output_destination(&track)`.
22/// We keep one sub-track per Siglus category (BGM/SE/PCM).
23pub struct AudioHub {
24    manager: Option<AudioManager<DefaultBackend>>,
25    bgm: Option<TrackHandle>,
26    se: Option<TrackHandle>,
27    pcm: Option<TrackHandle>,
28    koe: Option<TrackHandle>,
29    mov: Option<TrackHandle>,
30    bgm_base: u8,
31    se_base: u8,
32    pcm_base: u8,
33    koe_base: u8,
34    mov_base: u8,
35    bgm_master: u8,
36    se_master: u8,
37    pcm_master: u8,
38    koe_master: u8,
39    mov_master: u8,
40}
41
42impl fmt::Debug for AudioHub {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        f.debug_struct("AudioHub")
45            .field("enabled", &self.manager.is_some())
46            .finish()
47    }
48}
49
50impl AudioHub {
51    pub fn new() -> Self {
52        match AudioManager::<DefaultBackend>::new(AudioManagerSettings::default()) {
53            Ok(mut manager) => {
54                let bgm = manager.add_sub_track(TrackBuilder::default()).ok();
55                let se = manager.add_sub_track(TrackBuilder::default()).ok();
56                let pcm = manager.add_sub_track(TrackBuilder::default()).ok();
57                let koe = manager.add_sub_track(TrackBuilder::default()).ok();
58                let mov = manager.add_sub_track(TrackBuilder::default()).ok();
59                Self {
60                    manager: Some(manager),
61                    bgm,
62                    se,
63                    pcm,
64                    koe,
65                    mov,
66                    bgm_base: 255,
67                    se_base: 255,
68                    pcm_base: 255,
69                    koe_base: 255,
70                    mov_base: 255,
71                    bgm_master: 255,
72                    se_master: 255,
73                    pcm_master: 255,
74                    koe_master: 255,
75                    mov_master: 255,
76                }
77            }
78            Err(e) => {
79                eprintln!("kira init failed, audio disabled: {:#}", e);
80                Self {
81                    manager: None,
82                    bgm: None,
83                    se: None,
84                    pcm: None,
85                    koe: None,
86                    mov: None,
87                    bgm_base: 255,
88                    se_base: 255,
89                    pcm_base: 255,
90                    koe_base: 255,
91                    mov_base: 255,
92                    bgm_master: 255,
93                    se_master: 255,
94                    pcm_master: 255,
95                    koe_master: 255,
96                    mov_master: 255,
97                }
98            }
99        }
100    }
101
102    pub fn is_enabled(&self) -> bool {
103        self.manager.is_some()
104    }
105
106    fn track_ref(&self, kind: TrackKind) -> Option<&TrackHandle> {
107        match kind {
108            TrackKind::Bgm => self.bgm.as_ref(),
109            TrackKind::Se => self.se.as_ref(),
110            TrackKind::Pcm => self.pcm.as_ref(),
111            TrackKind::Koe => self.koe.as_ref(),
112            TrackKind::Mov => self.mov.as_ref(),
113        }
114    }
115
116    fn track_mut(&mut self, kind: TrackKind) -> Option<&mut TrackHandle> {
117        match kind {
118            TrackKind::Bgm => self.bgm.as_mut(),
119            TrackKind::Se => self.se.as_mut(),
120            TrackKind::Pcm => self.pcm.as_mut(),
121            TrackKind::Koe => self.koe.as_mut(),
122            TrackKind::Mov => self.mov.as_mut(),
123        }
124    }
125
126    fn track_base_mut(&mut self, kind: TrackKind) -> &mut u8 {
127        match kind {
128            TrackKind::Bgm => &mut self.bgm_base,
129            TrackKind::Se => &mut self.se_base,
130            TrackKind::Pcm => &mut self.pcm_base,
131            TrackKind::Koe => &mut self.koe_base,
132            TrackKind::Mov => &mut self.mov_base,
133        }
134    }
135
136    fn track_master_mut(&mut self, kind: TrackKind) -> &mut u8 {
137        match kind {
138            TrackKind::Bgm => &mut self.bgm_master,
139            TrackKind::Se => &mut self.se_master,
140            TrackKind::Pcm => &mut self.pcm_master,
141            TrackKind::Koe => &mut self.koe_master,
142            TrackKind::Mov => &mut self.mov_master,
143        }
144    }
145
146    fn track_base(&self, kind: TrackKind) -> u8 {
147        match kind {
148            TrackKind::Bgm => self.bgm_base,
149            TrackKind::Se => self.se_base,
150            TrackKind::Pcm => self.pcm_base,
151            TrackKind::Koe => self.koe_base,
152            TrackKind::Mov => self.mov_base,
153        }
154    }
155
156    fn track_master(&self, kind: TrackKind) -> u8 {
157        match kind {
158            TrackKind::Bgm => self.bgm_master,
159            TrackKind::Se => self.se_master,
160            TrackKind::Pcm => self.pcm_master,
161            TrackKind::Koe => self.koe_master,
162            TrackKind::Mov => self.mov_master,
163        }
164    }
165
166    fn apply_track_volume(&mut self, kind: TrackKind) {
167        let base = self.track_base(kind) as f64 / 255.0;
168        let master = self.track_master(kind) as f64 / 255.0;
169        let amp = base * master;
170        let Some(track) = self.track_mut(kind) else {
171            return;
172        };
173        track.set_volume(Volume::Amplitude(amp), Tween::default());
174    }
175
176    fn apply_track_volume_fade(&mut self, kind: TrackKind, fade_ms: i64) {
177        let base = self.track_base(kind) as f64 / 255.0;
178        let master = self.track_master(kind) as f64 / 255.0;
179        let amp = base * master;
180        let Some(track) = self.track_mut(kind) else {
181            return;
182        };
183        let tween = if fade_ms > 0 {
184            Tween {
185                duration: std::time::Duration::from_millis(fade_ms as u64),
186                ..Tween::default()
187            }
188        } else {
189            Tween::default()
190        };
191        track.set_volume(Volume::Amplitude(amp), tween);
192    }
193
194    pub fn play_static(
195        &mut self,
196        kind: TrackKind,
197        data: StaticSoundData,
198    ) -> Result<StaticSoundHandle> {
199        // Decide output destination before taking a mutable borrow of the manager.
200        let data = if let Some(track) = self.track_ref(kind) {
201            data.output_destination(track)
202        } else {
203            data
204        };
205
206        let manager = self
207            .manager
208            .as_mut()
209            .ok_or_else(|| anyhow!("audio disabled"))?;
210
211        manager.play(data).context("kira: play static sound")
212    }
213
214    pub fn set_track_volume_raw(&mut self, kind: TrackKind, volume_raw: u8) {
215        *self.track_base_mut(kind) = volume_raw;
216        self.apply_track_volume(kind);
217    }
218
219    pub fn set_track_master_volume_raw(&mut self, kind: TrackKind, volume_raw: u8) {
220        *self.track_master_mut(kind) = volume_raw;
221        self.apply_track_volume(kind);
222    }
223
224    pub fn set_track_volume_raw_fade(&mut self, kind: TrackKind, volume_raw: u8, fade_ms: i64) {
225        *self.track_base_mut(kind) = volume_raw;
226        self.apply_track_volume_fade(kind, fade_ms);
227    }
228}