Mercurial > touhou
diff python/src/audio.rs @ 783:ec1e06402a97
Replace SDL2_mixer with the kira crate
| author | Link Mauve <linkmauve@linkmauve.fr> |
|---|---|
| date | Fri, 21 Nov 2025 10:21:59 +0100 |
| parents | |
| children |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/python/src/audio.rs @@ -0,0 +1,101 @@ +use kira::sound::static_sound::StaticSoundData; +use kira::sound::streaming::{StreamingSoundData, StreamingSoundHandle}; +use kira::sound::FromFileError; +use kira::{AudioManager, AudioManagerSettings, Tween}; +use pyo3::prelude::*; +use std::collections::HashMap; +use std::io::Cursor; +use std::path::PathBuf; + +#[pyclass(module = "libtouhou")] +pub struct Audio { + loader: Py<super::Loader>, + manager: AudioManager, + cache: HashMap<String, StaticSoundData>, + bgms: [Option<(String, String)>; 4], + current_music: Option<StreamingSoundHandle<FromFileError>>, +} + +#[pymethods] +impl Audio { + #[new] + fn new(loader: Py<super::Loader>, bgms: [Option<(String, String)>; 4]) -> Audio { + let manager = + AudioManager::<kira::DefaultBackend>::new(AudioManagerSettings::default()).unwrap(); + let cache = HashMap::new(); + Audio { + loader, + manager, + cache, + bgms, + current_music: None, + } + } + + fn play_bgm(&mut self, py: Python, number: usize) { + let Some((_name, filename)) = &self.bgms[number] else { + eprintln!("Unspecified bgm number {number}"); + return; + }; + + // Load the loop points corresponding to this bgm. + let mut filename = PathBuf::from(filename); + filename.set_extension("pos"); + let loader = self.loader.borrow(py); + let loop_points = loader + .get_loop_points(filename.file_name().unwrap().to_str().unwrap()) + .unwrap(); + + // Then try to open the music file. + filename.set_extension("*"); + let path = loader + .game_dir + .clone() + .and_then(|dir| Some(dir.join(&filename))) + .unwrap_or(filename); + let mut music = None; + for path in glob::glob(path.to_str().unwrap()) + .unwrap() + .map(Result::unwrap) + .map(PathBuf::from) + { + match StreamingSoundData::from_file(&path) { + Ok(sound) => { + music = Some(sound); + break; + } + Err(err) => { + eprintln!("Error while opening {path:?} as a music file: {err:?}"); + continue; + } + } + } + let Some(music) = music else { + eprintln!("Unable to find bgm, let’s keep the previous one playing…"); + return; + }; + + // Stop the previous playing one. + if let Some(current_music) = &mut self.current_music { + current_music.stop(Tween::default()); + } + + // And now we can start playing the new one! + let mut current_music = self.manager.play(music).unwrap(); + // TODO: Fetch the sample rate from the file, instead of hardcoding it. + current_music.set_loop_region( + (loop_points.start as f64 / 44100.0)..(loop_points.end as f64 / 44100.0), + ); + self.current_music = Some(current_music); + } + + fn play(&mut self, py: Python, name: &str) { + let sound = self.cache.entry(name.to_string()).or_insert_with(|| { + let loader = self.loader.borrow(py); + let bytes = loader.get_file_internal(name).unwrap(); + let cursor = Cursor::new(bytes); + StaticSoundData::from_cursor(cursor).unwrap() + }); + self.manager.play(sound.clone()).unwrap(); + } +}
