Mercurial > touhou
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 782:a30ce01b9154 | 783:ec1e06402a97 |
|---|---|
| 1 use kira::sound::static_sound::StaticSoundData; | |
| 2 use kira::sound::streaming::{StreamingSoundData, StreamingSoundHandle}; | |
| 3 use kira::sound::FromFileError; | |
| 4 use kira::{AudioManager, AudioManagerSettings, Tween}; | |
| 5 use pyo3::prelude::*; | |
| 6 use std::collections::HashMap; | |
| 7 use std::io::Cursor; | |
| 8 use std::path::PathBuf; | |
| 9 | |
| 10 #[pyclass(module = "libtouhou")] | |
| 11 pub struct Audio { | |
| 12 loader: Py<super::Loader>, | |
| 13 manager: AudioManager, | |
| 14 cache: HashMap<String, StaticSoundData>, | |
| 15 bgms: [Option<(String, String)>; 4], | |
| 16 current_music: Option<StreamingSoundHandle<FromFileError>>, | |
| 17 } | |
| 18 | |
| 19 #[pymethods] | |
| 20 impl Audio { | |
| 21 #[new] | |
| 22 fn new(loader: Py<super::Loader>, bgms: [Option<(String, String)>; 4]) -> Audio { | |
| 23 let manager = | |
| 24 AudioManager::<kira::DefaultBackend>::new(AudioManagerSettings::default()).unwrap(); | |
| 25 let cache = HashMap::new(); | |
| 26 Audio { | |
| 27 loader, | |
| 28 manager, | |
| 29 cache, | |
| 30 bgms, | |
| 31 current_music: None, | |
| 32 } | |
| 33 } | |
| 34 | |
| 35 fn play_bgm(&mut self, py: Python, number: usize) { | |
| 36 let Some((_name, filename)) = &self.bgms[number] else { | |
| 37 eprintln!("Unspecified bgm number {number}"); | |
| 38 return; | |
| 39 }; | |
| 40 | |
| 41 // Load the loop points corresponding to this bgm. | |
| 42 let mut filename = PathBuf::from(filename); | |
| 43 filename.set_extension("pos"); | |
| 44 let loader = self.loader.borrow(py); | |
| 45 let loop_points = loader | |
| 46 .get_loop_points(filename.file_name().unwrap().to_str().unwrap()) | |
| 47 .unwrap(); | |
| 48 | |
| 49 // Then try to open the music file. | |
| 50 filename.set_extension("*"); | |
| 51 let path = loader | |
| 52 .game_dir | |
| 53 .clone() | |
| 54 .and_then(|dir| Some(dir.join(&filename))) | |
| 55 .unwrap_or(filename); | |
| 56 let mut music = None; | |
| 57 for path in glob::glob(path.to_str().unwrap()) | |
| 58 .unwrap() | |
| 59 .map(Result::unwrap) | |
| 60 .map(PathBuf::from) | |
| 61 { | |
| 62 match StreamingSoundData::from_file(&path) { | |
| 63 Ok(sound) => { | |
| 64 music = Some(sound); | |
| 65 break; | |
| 66 } | |
| 67 Err(err) => { | |
| 68 eprintln!("Error while opening {path:?} as a music file: {err:?}"); | |
| 69 continue; | |
| 70 } | |
| 71 } | |
| 72 } | |
| 73 let Some(music) = music else { | |
| 74 eprintln!("Unable to find bgm, let’s keep the previous one playing…"); | |
| 75 return; | |
| 76 }; | |
| 77 | |
| 78 // Stop the previous playing one. | |
| 79 if let Some(current_music) = &mut self.current_music { | |
| 80 current_music.stop(Tween::default()); | |
| 81 } | |
| 82 | |
| 83 // And now we can start playing the new one! | |
| 84 let mut current_music = self.manager.play(music).unwrap(); | |
| 85 // TODO: Fetch the sample rate from the file, instead of hardcoding it. | |
| 86 current_music.set_loop_region( | |
| 87 (loop_points.start as f64 / 44100.0)..(loop_points.end as f64 / 44100.0), | |
| 88 ); | |
| 89 self.current_music = Some(current_music); | |
| 90 } | |
| 91 | |
| 92 fn play(&mut self, py: Python, name: &str) { | |
| 93 let sound = self.cache.entry(name.to_string()).or_insert_with(|| { | |
| 94 let loader = self.loader.borrow(py); | |
| 95 let bytes = loader.get_file_internal(name).unwrap(); | |
| 96 let cursor = Cursor::new(bytes); | |
| 97 StaticSoundData::from_cursor(cursor).unwrap() | |
| 98 }); | |
| 99 self.manager.play(sound.clone()).unwrap(); | |
| 100 } | |
| 101 } |
