use crate::runtime::{ AudioMixer, FRAME_HEIGHT, FRAME_RGBA_BYTES, FRAME_WIDTH, NesRuntime, RuntimeError, }; use super::clock::{FrameClock, NoopClock, PacingClock}; use super::config::HostConfig; use super::io::{AudioOutput, InputProvider, VideoOutput}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[non_exhaustive] pub struct FrameExecution { pub frame_number: u64, pub audio_samples: usize, } pub struct RuntimeHostLoop { runtime: NesRuntime, mixer: AudioMixer, frame_buffer: Vec, audio_buffer: Vec, clock: C, } impl RuntimeHostLoop { pub fn new(runtime: NesRuntime, sample_rate: u32) -> Self { let mixer = runtime.default_audio_mixer(sample_rate); let clock = PacingClock::from_runtime(&runtime); Self { runtime, mixer, frame_buffer: vec![0; FRAME_RGBA_BYTES], audio_buffer: Vec::new(), clock, } } pub fn with_config( runtime: NesRuntime, config: HostConfig, ) -> RuntimeHostLoop> { let mixer = runtime.default_audio_mixer(config.sample_rate); let clock: Box = if config.pacing { Box::new(PacingClock::from_runtime(&runtime)) } else { Box::new(NoopClock) }; RuntimeHostLoop { runtime, mixer, frame_buffer: vec![0; FRAME_RGBA_BYTES], audio_buffer: Vec::new(), clock, } } } impl RuntimeHostLoop where C: FrameClock, { pub fn with_clock(runtime: NesRuntime, sample_rate: u32, clock: C) -> Self { let mixer = runtime.default_audio_mixer(sample_rate); Self { runtime, mixer, frame_buffer: vec![0; FRAME_RGBA_BYTES], audio_buffer: Vec::new(), clock, } } pub fn runtime(&self) -> &NesRuntime { &self.runtime } pub fn runtime_mut(&mut self) -> &mut NesRuntime { &mut self.runtime } pub fn into_runtime(self) -> NesRuntime { self.runtime } pub fn mixer(&self) -> &AudioMixer { &self.mixer } pub fn mixer_mut(&mut self) -> &mut AudioMixer { &mut self.mixer } pub fn clock(&self) -> &C { &self.clock } pub fn clock_mut(&mut self) -> &mut C { &mut self.clock } pub fn run_frame( &mut self, input: &mut I, video: &mut V, audio: &mut A, ) -> Result where I: InputProvider, V: VideoOutput, A: AudioOutput, { let stats = self.run_frame_unpaced(input, video, audio)?; self.clock.wait_next_frame(); Ok(stats) } pub fn run_frame_unpaced( &mut self, input: &mut I, video: &mut V, audio: &mut A, ) -> Result where I: InputProvider, V: VideoOutput, A: AudioOutput, { self.runtime.set_buttons(input.poll_buttons()); self.audio_buffer.clear(); self.runtime .run_until_frame_complete_with_audio(&mut self.mixer, &mut self.audio_buffer)?; self.runtime.render_frame_rgba(&mut self.frame_buffer)?; audio.push_samples(&self.audio_buffer); video.present_rgba(&self.frame_buffer, FRAME_WIDTH, FRAME_HEIGHT); Ok(FrameExecution { frame_number: self.runtime.frame_number(), audio_samples: self.audio_buffer.len(), }) } pub fn run_frames( &mut self, frames: usize, input: &mut I, video: &mut V, audio: &mut A, ) -> Result where I: InputProvider, V: VideoOutput, A: AudioOutput, { let mut total_samples = 0usize; for _ in 0..frames { total_samples = total_samples.saturating_add(self.run_frame(input, video, audio)?.audio_samples); } Ok(total_samples) } pub fn run_frames_unpaced( &mut self, frames: usize, input: &mut I, video: &mut V, audio: &mut A, ) -> Result where I: InputProvider, V: VideoOutput, A: AudioOutput, { let mut total_samples = 0usize; for _ in 0..frames { total_samples = total_samples .saturating_add(self.run_frame_unpaced(input, video, audio)?.audio_samples); } Ok(total_samples) } }