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::GameError::AudioError;
use crate::sound::organya::Song;
use crate::sound::playback::PlaybackEngine;
use crate::sound::playback::{PlaybackEngine, SavedPlaybackState};
use crate::sound::wave_bank::SoundBank;
use crate::str;
@ -101,20 +101,39 @@ impl SoundManager {
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(""))?)?;
log::info!("Playing song: {}", song_name);
log::info!("Playing BGM: {}", song_name);
self.current_song_id = song_id;
self.tx.send(PlaybackMessage::PlaySong(org));
self.tx.send(PlaybackMessage::PlaySong(Box::new(org)))?;
}
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 {
Stop,
PlaySong(Song),
PlaySong(Box<Song>),
SaveState,
RestoreState,
}
#[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 channels = config.channels as usize;
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);
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| {
match rx.try_recv() {
Ok(PlaybackMessage::PlaySong(song)) => {
engine.start_song(song, &bank);
engine.start_song(*song, &bank);
for i in &mut buf[0..frames] { *i = 0x8080 };
frames = engine.render_to(&mut buf);
@ -156,6 +175,21 @@ fn run<T>(rx: Receiver<PlaybackMessage>, bank: SoundBank,
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 struct PlaybackState {
pub struct SavedPlaybackState {
song: Organya,
play_pos: i32,
}
impl Clone for SavedPlaybackState {
fn clone(&self) -> SavedPlaybackState {
SavedPlaybackState {
song: self.song.clone(),
play_pos: self.play_pos,
}
}
}
impl PlaybackEngine {
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;
}
pub fn get_state(&self) -> PlaybackState {
PlaybackState {
pub fn get_state(&self) -> SavedPlaybackState {
SavedPlaybackState {
song: self.song.clone(),
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.play_pos = state.play_pos;
}

View File

@ -628,14 +628,25 @@ impl TextScriptVM {
OpCode::CMU => {
let song_id = read_cur_varint(&mut cursor)? as usize;
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);
}
// unimplemented opcodes
// Zero operands
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::RMU | OpCode::SAT | OpCode::SLP | OpCode::SPS |
OpCode::SAT | OpCode::SLP | OpCode::SPS |
OpCode::STC | OpCode::SVP | OpCode::TUR | OpCode::WAS | OpCode::ZAM => {
log::warn!("unimplemented opcode: {:?}", op);
@ -751,7 +762,7 @@ impl TextScript {
/// Compiles a decrypted text script data into internal bytecode.
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 iter = data.iter().copied().peekable();
@ -778,7 +789,6 @@ impl TextScript {
let bytecode = TextScript::compile_event(&mut iter, strict)?;
log::info!("Successfully compiled event #{} ({} bytes generated).", event_num, bytecode.len());
println!("{:x?}", &bytecode);
event_map.insert(event_num, bytecode);
}
b'\r' | b'\n' | b' ' | b'\t' => {