1use std::fmt;
2
3#[cfg(any(target_os = "horizon", target_os = "vita"))]
4use super::switch_backend::SwitchBackend as PlatformBackend;
5use anyhow::{Context, Result, anyhow};
6use kira::Volume;
7#[cfg(not(any(target_os = "horizon", target_os = "vita")))]
8use kira::manager::backend::DefaultBackend as PlatformBackend;
9use kira::manager::{AudioManager, AudioManagerSettings};
10use kira::sound::static_sound::{StaticSoundData, StaticSoundHandle};
11#[cfg(not(target_arch = "wasm32"))]
12use kira::sound::streaming::{StreamingSoundData, StreamingSoundHandle};
13use kira::track::{TrackBuilder, TrackHandle};
14use kira::tween::Tween;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum TrackKind {
18 Bgm,
19 Se,
20 Pcm,
21 Koe,
22 Mov,
23}
24
25pub struct AudioHub {
30 manager: Option<AudioManager<PlatformBackend>>,
31 bgm: Option<TrackHandle>,
32 se: Option<TrackHandle>,
33 pcm: Option<TrackHandle>,
34 koe: Option<TrackHandle>,
35 mov: Option<TrackHandle>,
36 bgm_base: u8,
37 se_base: u8,
38 pcm_base: u8,
39 koe_base: u8,
40 mov_base: u8,
41 bgm_master: u8,
42 se_master: u8,
43 pcm_master: u8,
44 koe_master: u8,
45 mov_master: u8,
46}
47
48impl fmt::Debug for AudioHub {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 f.debug_struct("AudioHub")
51 .field("enabled", &self.manager.is_some())
52 .finish()
53 }
54}
55
56impl Default for AudioHub {
57 fn default() -> Self {
58 Self::new()
59 }
60}
61
62impl AudioHub {
63 pub fn new() -> Self {
64 let manager = if cfg!(feature = "virtual-clock") {
67 Err(anyhow!("virtual clock: audio off"))
68 } else {
69 AudioManager::<PlatformBackend>::new(AudioManagerSettings::default())
70 .map_err(|e| anyhow!("{e}"))
71 };
72 match manager {
73 Ok(mut manager) => {
74 let bgm = manager.add_sub_track(TrackBuilder::default()).ok();
75 let se = manager.add_sub_track(TrackBuilder::default()).ok();
76 let pcm = manager.add_sub_track(TrackBuilder::default()).ok();
77 let koe = manager.add_sub_track(TrackBuilder::default()).ok();
78 let mov = manager.add_sub_track(TrackBuilder::default()).ok();
79 Self {
80 manager: Some(manager),
81 bgm,
82 se,
83 pcm,
84 koe,
85 mov,
86 bgm_base: 255,
87 se_base: 255,
88 pcm_base: 255,
89 koe_base: 255,
90 mov_base: 255,
91 bgm_master: 255,
92 se_master: 255,
93 pcm_master: 255,
94 koe_master: 255,
95 mov_master: 255,
96 }
97 }
98 Err(e) => {
99 eprintln!("kira init failed, audio disabled: {:#}", e);
100 Self {
101 manager: None,
102 bgm: None,
103 se: None,
104 pcm: None,
105 koe: None,
106 mov: None,
107 bgm_base: 255,
108 se_base: 255,
109 pcm_base: 255,
110 koe_base: 255,
111 mov_base: 255,
112 bgm_master: 255,
113 se_master: 255,
114 pcm_master: 255,
115 koe_master: 255,
116 mov_master: 255,
117 }
118 }
119 }
120 }
121
122 pub fn is_enabled(&self) -> bool {
123 self.manager.is_some()
124 }
125
126 fn track_ref(&self, kind: TrackKind) -> Option<&TrackHandle> {
127 match kind {
128 TrackKind::Bgm => self.bgm.as_ref(),
129 TrackKind::Se => self.se.as_ref(),
130 TrackKind::Pcm => self.pcm.as_ref(),
131 TrackKind::Koe => self.koe.as_ref(),
132 TrackKind::Mov => self.mov.as_ref(),
133 }
134 }
135
136 fn track_mut(&mut self, kind: TrackKind) -> Option<&mut TrackHandle> {
137 match kind {
138 TrackKind::Bgm => self.bgm.as_mut(),
139 TrackKind::Se => self.se.as_mut(),
140 TrackKind::Pcm => self.pcm.as_mut(),
141 TrackKind::Koe => self.koe.as_mut(),
142 TrackKind::Mov => self.mov.as_mut(),
143 }
144 }
145
146 fn track_base_mut(&mut self, kind: TrackKind) -> &mut u8 {
147 match kind {
148 TrackKind::Bgm => &mut self.bgm_base,
149 TrackKind::Se => &mut self.se_base,
150 TrackKind::Pcm => &mut self.pcm_base,
151 TrackKind::Koe => &mut self.koe_base,
152 TrackKind::Mov => &mut self.mov_base,
153 }
154 }
155
156 fn track_master_mut(&mut self, kind: TrackKind) -> &mut u8 {
157 match kind {
158 TrackKind::Bgm => &mut self.bgm_master,
159 TrackKind::Se => &mut self.se_master,
160 TrackKind::Pcm => &mut self.pcm_master,
161 TrackKind::Koe => &mut self.koe_master,
162 TrackKind::Mov => &mut self.mov_master,
163 }
164 }
165
166 fn track_base(&self, kind: TrackKind) -> u8 {
167 match kind {
168 TrackKind::Bgm => self.bgm_base,
169 TrackKind::Se => self.se_base,
170 TrackKind::Pcm => self.pcm_base,
171 TrackKind::Koe => self.koe_base,
172 TrackKind::Mov => self.mov_base,
173 }
174 }
175
176 fn track_master(&self, kind: TrackKind) -> u8 {
177 match kind {
178 TrackKind::Bgm => self.bgm_master,
179 TrackKind::Se => self.se_master,
180 TrackKind::Pcm => self.pcm_master,
181 TrackKind::Koe => self.koe_master,
182 TrackKind::Mov => self.mov_master,
183 }
184 }
185
186 fn apply_track_volume(&mut self, kind: TrackKind) {
187 let base = self.track_base(kind) as f64 / 255.0;
188 let master = self.track_master(kind) as f64 / 255.0;
189 let amp = base * master;
190 let Some(track) = self.track_mut(kind) else {
191 return;
192 };
193 track.set_volume(Volume::Amplitude(amp), Tween::default());
194 }
195
196 fn apply_track_volume_fade(&mut self, kind: TrackKind, fade_ms: i64) {
197 let base = self.track_base(kind) as f64 / 255.0;
198 let master = self.track_master(kind) as f64 / 255.0;
199 let amp = base * master;
200 let Some(track) = self.track_mut(kind) else {
201 return;
202 };
203 let tween = if fade_ms > 0 {
204 Tween {
205 duration: std::time::Duration::from_millis(fade_ms as u64),
206 ..Tween::default()
207 }
208 } else {
209 Tween::default()
210 };
211 track.set_volume(Volume::Amplitude(amp), tween);
212 }
213
214 pub fn play_static(
215 &mut self,
216 kind: TrackKind,
217 data: StaticSoundData,
218 ) -> Result<StaticSoundHandle> {
219 let data = if let Some(track) = self.track_ref(kind) {
221 data.output_destination(track)
222 } else {
223 data
224 };
225
226 let manager = self
227 .manager
228 .as_mut()
229 .ok_or_else(|| anyhow!("audio disabled"))?;
230
231 manager.play(data).context("kira: play static sound")
232 }
233
234 #[cfg(not(target_arch = "wasm32"))]
235 pub fn play_streaming<E: Send + 'static>(
236 &mut self,
237 kind: TrackKind,
238 data: StreamingSoundData<E>,
239 ) -> Result<StreamingSoundHandle<E>> {
240 let data = if let Some(track) = self.track_ref(kind) {
241 data.output_destination(track)
242 } else {
243 data
244 };
245 let manager = self
246 .manager
247 .as_mut()
248 .ok_or_else(|| anyhow!("audio disabled"))?;
249 manager
250 .play(data)
251 .map_err(|_| anyhow!("kira: play streaming sound failed"))
252 }
253
254 pub fn set_track_volume_raw(&mut self, kind: TrackKind, volume_raw: u8) {
255 *self.track_base_mut(kind) = volume_raw;
256 self.apply_track_volume(kind);
257 }
258
259 pub fn set_track_master_volume_raw(&mut self, kind: TrackKind, volume_raw: u8) {
260 *self.track_master_mut(kind) = volume_raw;
261 self.apply_track_volume(kind);
262 }
263
264 pub fn set_track_volume_raw_fade(&mut self, kind: TrackKind, volume_raw: u8, fade_ms: i64) {
265 *self.track_base_mut(kind) = volume_raw;
266 self.apply_track_volume_fade(kind, fade_ms);
267 }
268}