Re-implement custom audio files support. (#379)
* reimplement custom audio loading for `toast` and `key_click` using `from_mp3` * run cargo fmt * `try_bytes_from_config`: output with `log::trace` when file cannot be opened * rewrite `try_bytes_from_config` * use file `read_to_end`, log categories * edit `register_wgui_samples` * rework audio functions for loading custom wgui sounds oh hey, it works! * clean up * allow custom `startup` and `app_start` sounds these sounds are loaded on-demand and never registered; this behaviour is unchanged * edit `bytes_from_config` outputs * use return value of `read_to_end` instead of `len()` for reported file size * implement suggested changes - remove now-unused `register_mp3_sample_from_assets` function - replace implementation of `try_bytes_from_config` - missing the `real_path` context * swap out `context`s for `inspect_err`s with closures to output `real_path` * clean up `audio.rs` - remove unused import - unify capitalisation + quotes in output messages.
This commit is contained in:
@@ -183,10 +183,19 @@ impl<T: 'static> Frontend<T> {
|
|||||||
|
|
||||||
fn play_sound(&mut self, audio_system: &mut audio::AudioSystem, sound_type: SoundType) -> anyhow::Result<()> {
|
fn play_sound(&mut self, audio_system: &mut audio::AudioSystem, sound_type: SoundType) -> anyhow::Result<()> {
|
||||||
let mut assets = self.globals.assets_builtin();
|
let mut assets = self.globals.assets_builtin();
|
||||||
let sample = audio::AudioSample::from_mp3(&assets.load_from_path(match sound_type {
|
|
||||||
|
let path = match sound_type {
|
||||||
SoundType::Startup => "sound/startup.mp3",
|
SoundType::Startup => "sound/startup.mp3",
|
||||||
SoundType::Launch => "sound/app_start.mp3",
|
SoundType::Launch => "sound/app_start.mp3",
|
||||||
})?)?;
|
};
|
||||||
|
|
||||||
|
// try loading a custom sound; if one doesn't exist (or it failed to load), use the built-in asset
|
||||||
|
let sound_bytes = match audio::AudioSample::try_bytes_from_config(path) {
|
||||||
|
Ok(bytes) => bytes,
|
||||||
|
Err(_) => assets.load_from_path(path)?.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let sample = audio::AudioSample::from_mp3(&*sound_bytes)?;
|
||||||
audio_system.play_sample(&sample);
|
audio_system.play_sample(&sample);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,13 +94,19 @@ impl AppState {
|
|||||||
let mut audio_sample_player = audio::SamplePlayer::new();
|
let mut audio_sample_player = audio::SamplePlayer::new();
|
||||||
audio_sample_player.register_sample(
|
audio_sample_player.register_sample(
|
||||||
"key_click",
|
"key_click",
|
||||||
audio::AudioSample::from_mp3(include_bytes!("res/key_click.mp3"))?,
|
audio::AudioSample::from_mp3(&*audio::AudioSample::bytes_from_config_or_default(
|
||||||
);
|
"sound/key_click.mp3",
|
||||||
|
include_bytes!("res/key_click.mp3"),
|
||||||
|
))?,
|
||||||
|
)?;
|
||||||
|
|
||||||
audio_sample_player.register_sample(
|
audio_sample_player.register_sample(
|
||||||
"toast",
|
"toast",
|
||||||
audio::AudioSample::from_mp3(include_bytes!("res/toast.mp3"))?,
|
audio::AudioSample::from_mp3(&*audio::AudioSample::bytes_from_config_or_default(
|
||||||
);
|
"sound/toast.mp3",
|
||||||
|
include_bytes!("res/toast.mp3"),
|
||||||
|
))?,
|
||||||
|
)?;
|
||||||
|
|
||||||
let mut assets = Box::new(gui::asset::GuiAsset {});
|
let mut assets = Box::new(gui::asset::GuiAsset {});
|
||||||
audio_sample_player.register_wgui_samples(assets.as_mut())?;
|
audio_sample_player.register_wgui_samples(assets.as_mut())?;
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
use std::{collections::HashMap, io::Cursor};
|
use std::{collections::HashMap, io::Cursor, rc::Rc};
|
||||||
|
|
||||||
use rodio::Source;
|
use rodio::Source;
|
||||||
use wgui::{assets::AssetProvider, sound::WguiSoundType};
|
use wgui::{assets::AssetProvider, sound::WguiSoundType};
|
||||||
|
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
pub struct AudioSystem {
|
pub struct AudioSystem {
|
||||||
audio_stream: Option<rodio::OutputStream>,
|
audio_stream: Option<rodio::OutputStream>,
|
||||||
first_try: bool,
|
first_try: bool,
|
||||||
@@ -33,32 +35,31 @@ impl SamplePlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_sample(&mut self, sample_name: &str, sample: AudioSample) {
|
pub fn register_sample(&mut self, sample_name: &str, sample: AudioSample) -> anyhow::Result<()> {
|
||||||
log::debug!("registering audio sample \"{sample_name}\"");
|
|
||||||
self.samples.insert(String::from(sample_name), sample);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_mp3_sample_from_assets(
|
|
||||||
&mut self,
|
|
||||||
sample_name: &str,
|
|
||||||
assets: &mut dyn AssetProvider,
|
|
||||||
path: &str,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
// load only once
|
// load only once
|
||||||
if self.samples.contains_key(sample_name) {
|
if self.samples.contains_key(sample_name) {
|
||||||
|
log::debug!("Audio sample '{sample_name}' already exists.");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = assets.load_from_path(path)?;
|
log::debug!("Registering audio sample '{sample_name}'");
|
||||||
self.register_sample(sample_name, AudioSample::from_mp3(&data)?);
|
self.samples.insert(String::from(sample_name), sample);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_wgui_samples(&mut self, assets: &mut dyn AssetProvider) -> anyhow::Result<()> {
|
pub fn register_wgui_samples(&mut self, assets: &mut dyn AssetProvider) -> anyhow::Result<()> {
|
||||||
let mut load = |sound: WguiSoundType| -> anyhow::Result<()> {
|
let mut load = |sound: WguiSoundType| -> anyhow::Result<()> {
|
||||||
let sample_name = get_sample_name_from_wgui_sound_type(sound);
|
let sample_name = get_sample_name_from_wgui_sound_type(sound);
|
||||||
self.register_mp3_sample_from_assets(sample_name, assets, &format!("sound/{}.mp3", sample_name))
|
let path = &format!("sound/{}.mp3", sample_name);
|
||||||
|
|
||||||
|
// try loading a custom sound; if one doesn't exist (or it failed to load), use the built-in asset
|
||||||
|
let sound_bytes = match AudioSample::try_bytes_from_config(path) {
|
||||||
|
Ok(bytes) => bytes,
|
||||||
|
Err(_) => assets.load_from_path(path)?.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.register_sample(sample_name, AudioSample::from_mp3(&*sound_bytes)?)?;
|
||||||
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
load(WguiSoundType::ButtonPress)?;
|
load(WguiSoundType::ButtonPress)?;
|
||||||
@@ -71,7 +72,7 @@ impl SamplePlayer {
|
|||||||
|
|
||||||
pub fn play_sample(&mut self, system: &mut AudioSystem, sample_name: &str) {
|
pub fn play_sample(&mut self, system: &mut AudioSystem, sample_name: &str) {
|
||||||
let Some(sample) = self.samples.get(sample_name) else {
|
let Some(sample) = self.samples.get(sample_name) else {
|
||||||
log::error!("failed to play sample by name {}", sample_name);
|
log::error!("Failed to play sample by name '{}'", sample_name);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -143,4 +144,26 @@ impl AudioSample {
|
|||||||
),
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_bytes_from_config(path: &str) -> anyhow::Result<Rc<[u8]>> {
|
||||||
|
let real_path = crate::config_io::get_config_root().join(&*path);
|
||||||
|
|
||||||
|
let mut file = std::fs::File::open(&real_path)
|
||||||
|
.inspect_err(|e| log::debug!("Could not open file '{}': {e:?}", real_path.display()))?;
|
||||||
|
let mut file_buffer = vec![];
|
||||||
|
file
|
||||||
|
.read_to_end(&mut file_buffer)
|
||||||
|
.inspect_err(|e| log::debug!("Could not read file '{}': {e:?}", real_path.display()))?;
|
||||||
|
Ok(file_buffer.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bytes_from_config_or_default(path: &str, default: &'static [u8]) -> Rc<[u8]> {
|
||||||
|
match AudioSample::try_bytes_from_config(path) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => {
|
||||||
|
log::trace!("File '{}' not found, using default.", path);
|
||||||
|
default.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user