mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2025-05-22 06:41:26 +00:00
song state save/restore
This commit is contained in:
parent
a4f0d8dfa4
commit
510439d9ec
|
@ -9,7 +9,7 @@ use crate::engine_constants::EngineConstants;
|
||||||
use crate::ggez::{Context, filesystem, GameResult};
|
use crate::ggez::{Context, filesystem, GameResult};
|
||||||
use crate::ggez::GameError::AudioError;
|
use crate::ggez::GameError::AudioError;
|
||||||
use crate::sound::organya::Song;
|
use crate::sound::organya::Song;
|
||||||
use crate::sound::playback::PlaybackEngine;
|
use crate::sound::playback::{PlaybackEngine, SavedPlaybackState};
|
||||||
use crate::sound::wave_bank::SoundBank;
|
use crate::sound::wave_bank::SoundBank;
|
||||||
use crate::str;
|
use crate::str;
|
||||||
|
|
||||||
|
@ -101,20 +101,39 @@ impl SoundManager {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(song_name) = SONGS.get(song_id) {
|
if song_id == 0 {
|
||||||
|
log::info!("Stopping BGM");
|
||||||
|
|
||||||
|
self.current_song_id = 0;
|
||||||
|
self.tx.send(PlaybackMessage::Stop)?;
|
||||||
|
} else if let Some(song_name) = SONGS.get(song_id) {
|
||||||
let org = organya::Song::load_from(filesystem::open(ctx, ["/base/Org/", &song_name.to_lowercase(), ".org"].join(""))?)?;
|
let org = organya::Song::load_from(filesystem::open(ctx, ["/base/Org/", &song_name.to_lowercase(), ".org"].join(""))?)?;
|
||||||
log::info!("Playing song: {}", song_name);
|
log::info!("Playing BGM: {}", song_name);
|
||||||
|
|
||||||
self.current_song_id = song_id;
|
self.current_song_id = song_id;
|
||||||
self.tx.send(PlaybackMessage::PlaySong(org));
|
self.tx.send(PlaybackMessage::PlaySong(Box::new(org)))?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn save_state(&mut self) -> GameResult {
|
||||||
|
self.tx.send(PlaybackMessage::SaveState)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restore_state(&mut self) -> GameResult {
|
||||||
|
self.tx.send(PlaybackMessage::RestoreState)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PlaybackMessage {
|
enum PlaybackMessage {
|
||||||
Stop,
|
Stop,
|
||||||
PlaySong(Song),
|
PlaySong(Box<Song>),
|
||||||
|
SaveState,
|
||||||
|
RestoreState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
|
@ -130,7 +149,7 @@ fn run<T>(rx: Receiver<PlaybackMessage>, bank: SoundBank,
|
||||||
let sample_rate = config.sample_rate.0 as f32;
|
let sample_rate = config.sample_rate.0 as f32;
|
||||||
let channels = config.channels as usize;
|
let channels = config.channels as usize;
|
||||||
let mut state = PlaybackState::Stopped;
|
let mut state = PlaybackState::Stopped;
|
||||||
let mut new_song: Option<Song> = None;
|
let mut saved_state: Option<SavedPlaybackState> = None;
|
||||||
let mut engine = PlaybackEngine::new(Song::empty(), &bank);
|
let mut engine = PlaybackEngine::new(Song::empty(), &bank);
|
||||||
|
|
||||||
log::info!("Audio format: {} {}", sample_rate, channels);
|
log::info!("Audio format: {} {}", sample_rate, channels);
|
||||||
|
@ -148,7 +167,7 @@ fn run<T>(rx: Receiver<PlaybackMessage>, bank: SoundBank,
|
||||||
move |data: &mut [T], _: &cpal::OutputCallbackInfo| {
|
move |data: &mut [T], _: &cpal::OutputCallbackInfo| {
|
||||||
match rx.try_recv() {
|
match rx.try_recv() {
|
||||||
Ok(PlaybackMessage::PlaySong(song)) => {
|
Ok(PlaybackMessage::PlaySong(song)) => {
|
||||||
engine.start_song(song, &bank);
|
engine.start_song(*song, &bank);
|
||||||
|
|
||||||
for i in &mut buf[0..frames] { *i = 0x8080 };
|
for i in &mut buf[0..frames] { *i = 0x8080 };
|
||||||
frames = engine.render_to(&mut buf);
|
frames = engine.render_to(&mut buf);
|
||||||
|
@ -156,6 +175,21 @@ fn run<T>(rx: Receiver<PlaybackMessage>, bank: SoundBank,
|
||||||
|
|
||||||
state = PlaybackState::Playing;
|
state = PlaybackState::Playing;
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
for i in &mut buf[0..frames] { *i = 0x8080 };
|
||||||
|
frames = engine.render_to(&mut buf);
|
||||||
|
index = 0;
|
||||||
|
|
||||||
|
state = PlaybackState::Playing;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,20 @@ pub struct PlaybackEngine {
|
||||||
pub loops: usize,
|
pub loops: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PlaybackState {
|
pub struct SavedPlaybackState {
|
||||||
song: Organya,
|
song: Organya,
|
||||||
play_pos: i32,
|
play_pos: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for SavedPlaybackState {
|
||||||
|
fn clone(&self) -> SavedPlaybackState {
|
||||||
|
SavedPlaybackState {
|
||||||
|
song: self.song.clone(),
|
||||||
|
play_pos: self.play_pos,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PlaybackEngine {
|
impl PlaybackEngine {
|
||||||
pub fn new(song: Organya, samples: &SoundBank) -> Self {
|
pub fn new(song: Organya, samples: &SoundBank) -> Self {
|
||||||
|
|
||||||
|
@ -98,14 +107,14 @@ impl PlaybackEngine {
|
||||||
self.frames_per_tick = (sample_rate / 1000) * self.song.time.wait as usize;
|
self.frames_per_tick = (sample_rate / 1000) * self.song.time.wait as usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_state(&self) -> PlaybackState {
|
pub fn get_state(&self) -> SavedPlaybackState {
|
||||||
PlaybackState {
|
SavedPlaybackState {
|
||||||
song: self.song.clone(),
|
song: self.song.clone(),
|
||||||
play_pos: self.play_pos,
|
play_pos: self.play_pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_state(&mut self, state: PlaybackState, samples: &SoundBank) {
|
pub fn set_state(&mut self, state: SavedPlaybackState, samples: &SoundBank) {
|
||||||
self.start_song(state.song, samples);
|
self.start_song(state.song, samples);
|
||||||
self.play_pos = state.play_pos;
|
self.play_pos = state.play_pos;
|
||||||
}
|
}
|
||||||
|
|
|
@ -628,14 +628,25 @@ impl TextScriptVM {
|
||||||
OpCode::CMU => {
|
OpCode::CMU => {
|
||||||
let song_id = read_cur_varint(&mut cursor)? as usize;
|
let song_id = read_cur_varint(&mut cursor)? as usize;
|
||||||
state.sound_manager.play_song(song_id, &state.constants, ctx)?;
|
state.sound_manager.play_song(song_id, &state.constants, ctx)?;
|
||||||
|
|
||||||
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
|
}
|
||||||
|
OpCode::FMU => {
|
||||||
|
state.sound_manager.save_state()?;
|
||||||
|
|
||||||
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
|
}
|
||||||
|
OpCode::RMU => {
|
||||||
|
state.sound_manager.restore_state()?;
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
// unimplemented opcodes
|
// unimplemented opcodes
|
||||||
// Zero operands
|
// Zero operands
|
||||||
OpCode::AEp | OpCode::CAT | OpCode::CIL | OpCode::CPS |
|
OpCode::AEp | OpCode::CAT | OpCode::CIL | OpCode::CPS |
|
||||||
OpCode::CRE | OpCode::CSS | OpCode::ESC | OpCode::FLA | OpCode::FMU |
|
OpCode::CRE | OpCode::CSS | OpCode::ESC | OpCode::FLA |
|
||||||
OpCode::INI | OpCode::LDP | OpCode::MLP | OpCode::MNA |
|
OpCode::INI | OpCode::LDP | OpCode::MLP | OpCode::MNA |
|
||||||
OpCode::RMU | OpCode::SAT | OpCode::SLP | OpCode::SPS |
|
OpCode::SAT | OpCode::SLP | OpCode::SPS |
|
||||||
OpCode::STC | OpCode::SVP | OpCode::TUR | OpCode::WAS | OpCode::ZAM => {
|
OpCode::STC | OpCode::SVP | OpCode::TUR | OpCode::WAS | OpCode::ZAM => {
|
||||||
log::warn!("unimplemented opcode: {:?}", op);
|
log::warn!("unimplemented opcode: {:?}", op);
|
||||||
|
|
||||||
|
@ -751,7 +762,7 @@ impl TextScript {
|
||||||
|
|
||||||
/// Compiles a decrypted text script data into internal bytecode.
|
/// Compiles a decrypted text script data into internal bytecode.
|
||||||
pub fn compile(data: &[u8], strict: bool) -> GameResult<TextScript> {
|
pub fn compile(data: &[u8], strict: bool) -> GameResult<TextScript> {
|
||||||
println!("data: {}", String::from_utf8_lossy(data));
|
log::debug!("data: {}", String::from_utf8_lossy(data));
|
||||||
|
|
||||||
let mut event_map = HashMap::new();
|
let mut event_map = HashMap::new();
|
||||||
let mut iter = data.iter().copied().peekable();
|
let mut iter = data.iter().copied().peekable();
|
||||||
|
@ -778,7 +789,6 @@ impl TextScript {
|
||||||
|
|
||||||
let bytecode = TextScript::compile_event(&mut iter, strict)?;
|
let bytecode = TextScript::compile_event(&mut iter, strict)?;
|
||||||
log::info!("Successfully compiled event #{} ({} bytes generated).", event_num, bytecode.len());
|
log::info!("Successfully compiled event #{} ({} bytes generated).", event_num, bytecode.len());
|
||||||
println!("{:x?}", &bytecode);
|
|
||||||
event_map.insert(event_num, bytecode);
|
event_map.insert(event_num, bytecode);
|
||||||
}
|
}
|
||||||
b'\r' | b'\n' | b' ' | b'\t' => {
|
b'\r' | b'\n' | b' ' | b'\t' => {
|
||||||
|
|
Loading…
Reference in a new issue