song state save/restore

This commit is contained in:
Alula 2020-09-03 13:48:56 +02:00
parent a4f0d8dfa4
commit 510439d9ec
No known key found for this signature in database
GPG Key ID: 3E00485503A1D8BA
3 changed files with 68 additions and 15 deletions

View File

@ -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;
}
}
_ => {} _ => {}
} }

View File

@ -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;
} }

View File

@ -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' => {