doukutsu-rs/src/sound/mod.rs

294 lines
8.9 KiB
Rust
Raw Normal View History

2020-09-03 12:04:54 +00:00
use std::sync::mpsc;
2020-09-02 22:58:11 +00:00
use std::sync::mpsc::{Receiver, Sender};
use bitflags::_core::time::Duration;
2020-09-16 13:21:30 +00:00
use cpal::Sample;
2020-09-02 22:58:11 +00:00
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use crate::engine_constants::EngineConstants;
use crate::ggez::{Context, filesystem, GameResult};
use crate::ggez::GameError::{AudioError, InvalidValue, ResourceLoadError};
2020-09-02 22:58:11 +00:00
use crate::sound::organya::Song;
2020-09-16 13:21:30 +00:00
use crate::sound::pixtone::PixTonePlayback;
2020-09-03 11:48:56 +00:00
use crate::sound::playback::{PlaybackEngine, SavedPlaybackState};
2020-09-02 22:58:11 +00:00
use crate::sound::wave_bank::SoundBank;
use crate::str;
2020-08-18 16:46:07 +00:00
pub mod pixtone;
2020-09-02 22:58:11 +00:00
mod wave_bank;
mod organya;
mod playback;
mod stuff;
mod wav;
2020-08-18 16:46:07 +00:00
pub struct SoundManager {
2020-09-02 22:58:11 +00:00
tx: Sender<PlaybackMessage>,
2020-09-05 02:08:59 +00:00
prev_song_id: usize,
2020-09-02 22:58:11 +00:00
current_song_id: usize,
2020-08-18 16:46:07 +00:00
}
2020-09-11 16:30:18 +00:00
static SONGS: [&str; 43] = [
"xxxx",
"wanpaku",
"anzen",
"gameover",
"gravity",
"weed",
"mdown2",
"fireeye",
"vivi",
"mura",
"fanfale1",
"ginsuke",
"cemetery",
"plant",
"kodou",
"fanfale3",
"fanfale2",
"dr",
"escape",
"jenka",
"maze",
"access",
"ironh",
"grand",
"curly",
"oside",
"requiem",
"wanpak2",
"quiet",
"lastcave",
"balcony",
"lastbtl",
"lastbt3",
"ending",
"zonbie",
"bdown",
"hell",
"jenka2",
"marine",
"ballos",
"toroko",
"white",
"kaze"
2020-09-02 22:58:11 +00:00
];
2020-08-19 19:11:32 +00:00
2020-08-18 16:46:07 +00:00
impl SoundManager {
2020-09-02 22:58:11 +00:00
pub fn new(ctx: &mut Context) -> GameResult<SoundManager> {
let (tx, rx): (Sender<PlaybackMessage>, Receiver<PlaybackMessage>) = mpsc::channel();
let host = cpal::default_host();
let device = host.default_output_device().ok_or_else(|| AudioError(str!("Error initializing audio device.")))?;
let config = device.default_output_config()?;
let bnk = wave_bank::SoundBank::load_from(filesystem::open(ctx, "/builtin/pixtone.pcm")?)?;
std::thread::spawn(move || {
if let Err(err) = match config.sample_format() {
cpal::SampleFormat::F32 => run::<f32>(rx, bnk, &device, &config.into()),
cpal::SampleFormat::I16 => run::<i16>(rx, bnk, &device, &config.into()),
cpal::SampleFormat::U16 => run::<u16>(rx, bnk, &device, &config.into()),
} {
log::error!("Something went wrong in audio thread: {}", err);
}
});
Ok(SoundManager {
tx: tx.clone(),
2020-09-05 02:08:59 +00:00
prev_song_id: 0,
2020-09-02 22:58:11 +00:00
current_song_id: 0,
})
2020-08-18 16:46:07 +00:00
}
2020-09-16 13:21:30 +00:00
pub fn play_sfx(&mut self, id: u8) {
self.tx.send(PlaybackMessage::PlaySample(id));
}
2020-09-02 22:58:11 +00:00
pub fn play_song(&mut self, song_id: usize, constants: &EngineConstants, ctx: &mut Context) -> GameResult {
if self.current_song_id == song_id {
return Ok(());
}
2020-08-18 16:46:07 +00:00
2020-09-03 11:48:56 +00:00
if song_id == 0 {
log::info!("Stopping BGM");
self.prev_song_id = self.current_song_id;
2020-09-03 11:48:56 +00:00
self.current_song_id = 0;
self.tx.send(PlaybackMessage::SaveState)?;
2020-09-03 11:48:56 +00:00
self.tx.send(PlaybackMessage::Stop)?;
} else if let Some(song_name) = SONGS.get(song_id) {
let path = constants.organya_paths
.iter()
.map(|prefix| [prefix, &song_name.to_lowercase(), ".org"].join(""))
.find(|path| filesystem::exists(ctx, path))
.ok_or_else(|| ResourceLoadError(format!("BGM {:?} does not exist.", song_name)))?;
let org = organya::Song::load_from(filesystem::open(ctx, path)?)?;
2020-09-03 11:48:56 +00:00
log::info!("Playing BGM: {}", song_name);
2020-08-18 16:46:07 +00:00
2020-09-05 02:08:59 +00:00
self.prev_song_id = self.current_song_id;
2020-09-02 22:58:11 +00:00
self.current_song_id = song_id;
2020-09-05 01:36:19 +00:00
self.tx.send(PlaybackMessage::SaveState)?;
2020-09-03 11:48:56 +00:00
self.tx.send(PlaybackMessage::PlaySong(Box::new(org)))?;
2020-09-02 22:58:11 +00:00
}
2020-08-18 16:46:07 +00:00
Ok(())
}
2020-09-03 11:48:56 +00:00
pub fn save_state(&mut self) -> GameResult {
self.tx.send(PlaybackMessage::SaveState)?;
2020-09-05 02:08:59 +00:00
self.prev_song_id = self.current_song_id;
2020-09-03 11:48:56 +00:00
Ok(())
}
pub fn restore_state(&mut self) -> GameResult {
self.tx.send(PlaybackMessage::RestoreState)?;
2020-09-05 02:08:59 +00:00
self.current_song_id = self.prev_song_id;
2020-09-03 11:48:56 +00:00
Ok(())
}
2020-09-04 12:00:09 +00:00
pub fn set_speed(&mut self, speed: f32) -> GameResult {
if speed <= 0.0 {
return Err(InvalidValue(str!("Speed must be bigger than 0.0!")));
}
self.tx.send(PlaybackMessage::SetSpeed(speed))?;
Ok(())
}
2020-08-18 16:46:07 +00:00
}
2020-09-02 22:58:11 +00:00
enum PlaybackMessage {
Stop,
2020-09-03 11:48:56 +00:00
PlaySong(Box<Song>),
2020-09-16 13:21:30 +00:00
PlaySample(u8),
2020-09-04 12:00:09 +00:00
SetSpeed(f32),
2020-09-03 11:48:56 +00:00
SaveState,
RestoreState,
2020-09-02 22:58:11 +00:00
}
#[derive(PartialEq, Eq)]
enum PlaybackState {
Stopped,
Playing,
}
fn run<T>(rx: Receiver<PlaybackMessage>, bank: SoundBank,
device: &cpal::Device, config: &cpal::StreamConfig) -> GameResult where
T: cpal::Sample,
{
let sample_rate = config.sample_rate.0 as f32;
let channels = config.channels as usize;
let mut state = PlaybackState::Stopped;
2020-09-03 11:48:56 +00:00
let mut saved_state: Option<SavedPlaybackState> = None;
2020-09-16 13:21:30 +00:00
let mut speed = 1.0;
2020-09-02 22:58:11 +00:00
let mut engine = PlaybackEngine::new(Song::empty(), &bank);
2020-09-16 13:21:30 +00:00
let mut pixtone = PixTonePlayback::new();
pixtone.create_samples();
2020-09-02 22:58:11 +00:00
log::info!("Audio format: {} {}", sample_rate, channels);
engine.set_sample_rate(sample_rate as usize);
engine.loops = usize::MAX;
2020-09-16 13:21:30 +00:00
let mut org_buf = vec![0x8080; 441];
let mut pxt_buf = vec![0x8000; 441];
let mut org_index = 0;
let mut pxt_index = 0;
let mut frames = engine.render_to(&mut org_buf);
pixtone.mix(&mut pxt_buf, sample_rate);
2020-09-02 22:58:11 +00:00
let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
let stream = device.build_output_stream(
config,
move |data: &mut [T], _: &cpal::OutputCallbackInfo| {
2020-09-16 13:21:30 +00:00
loop {
match rx.try_recv() {
Ok(PlaybackMessage::PlaySong(song)) => {
engine.start_song(*song, &bank);
2020-09-02 22:58:11 +00:00
2020-09-16 13:21:30 +00:00
for i in &mut org_buf[0..frames] { *i = 0x8080 };
frames = engine.render_to(&mut org_buf);
org_index = 0;
2020-09-02 22:58:11 +00:00
2020-09-16 13:21:30 +00:00
state = PlaybackState::Playing;
}
Ok(PlaybackMessage::PlaySample(id)) => {
pixtone.play_sfx(id);
}
Ok(PlaybackMessage::Stop) => {
state = PlaybackState::Stopped;
}
Ok(PlaybackMessage::SetSpeed(new_speed)) => {
assert!(new_speed > 0.0);
speed = new_speed;
engine.set_sample_rate((sample_rate / new_speed) as usize);
}
Ok(PlaybackMessage::SaveState) => {
saved_state = Some(engine.get_state());
}
Ok(PlaybackMessage::RestoreState) => {
if saved_state.is_some() {
engine.set_state(saved_state.clone().unwrap(), &bank);
saved_state = None;
2020-09-03 11:48:56 +00:00
2020-09-16 13:21:30 +00:00
if state == PlaybackState::Stopped {
engine.set_position(0);
}
2020-09-16 13:21:30 +00:00
for i in &mut org_buf[0..frames] { *i = 0x8080 };
frames = engine.render_to(&mut org_buf);
org_index = 0;
2020-09-03 11:48:56 +00:00
2020-09-16 13:21:30 +00:00
state = PlaybackState::Playing;
}
2020-09-03 11:48:56 +00:00
}
2020-09-16 13:21:30 +00:00
Err(_) => { break; }
2020-09-03 11:48:56 +00:00
}
2020-09-02 22:58:11 +00:00
}
for frame in data.chunks_mut(channels) {
2020-09-16 13:21:30 +00:00
let org_sample: u16 = {
2020-09-02 22:58:11 +00:00
if state == PlaybackState::Stopped {
0x8000
2020-09-16 13:21:30 +00:00
} else if org_index < frames {
let sample = org_buf[org_index];
org_index += 1;
if org_index & 1 == 0 { (sample & 0xff) << 8 } else { sample & 0xff00 }
2020-09-02 22:58:11 +00:00
} else {
2020-09-16 13:21:30 +00:00
for i in &mut org_buf[0..frames] { *i = 0x8080 };
frames = engine.render_to(&mut org_buf);
org_index = 0;
let sample = org_buf[0];
2020-09-02 22:58:11 +00:00
(sample & 0xff) << 8
}
};
2020-09-16 13:21:30 +00:00
let pxt_sample: u16 = pxt_buf[pxt_index] ^ 0x8000;
if pxt_index < (pxt_buf.len() - 1) {
pxt_index += 1;
} else {
pxt_index = 0;
for i in pxt_buf.iter_mut() { *i = 0x8000 };
pixtone.mix(&mut pxt_buf, sample_rate / speed);
}
let sample = org_sample.wrapping_add(pxt_sample);
2020-09-02 22:58:11 +00:00
2020-09-04 12:00:09 +00:00
let value: T = Sample::from::<u16>(&sample);
2020-09-02 22:58:11 +00:00
for sample in frame.iter_mut() {
*sample = value;
}
}
},
err_fn,
)?;
stream.play()?;
loop {
2020-09-16 13:21:30 +00:00
std::thread::sleep(Duration::from_millis(4));
2020-09-02 22:58:11 +00:00
}
}