diff --git a/src/device/device.rs b/src/device/device.rs index 8929f38..1ae43df 100644 --- a/src/device/device.rs +++ b/src/device/device.rs @@ -7,14 +7,14 @@ use std::time::Duration; use rand::random; use crate::device::instruction::Instruction; use crate::device::keyboard::Keyboard; -use crate::device::timer::Timer; +use crate::device::timer::TimerManager; use crate::util::EmulatorResult; pub struct Device { pub registers: RegisterFile, pub memory: Box<[u8; Self::DEVICE_MEMORY_SIZE]>, - pub timer: Timer, + pub timer: TimerManager, pub stack: Vec, pub frame_buffer: Arc>>, pub new_chip8_mode: bool, @@ -26,7 +26,7 @@ impl Device { pub const FRAME_BUFFER_WIDTH: usize = 64; pub const FRAME_BUFFER_HEIGHT: usize = 32; pub const FRAME_BUFFER_SIZE: usize = Self::FRAME_BUFFER_WIDTH * Self::FRAME_BUFFER_HEIGHT; - pub fn new(timer: Timer, fb: Arc>>, device_keyboard: Keyboard, new_chip8_mode: bool) -> Device { + pub fn new(timer: TimerManager, fb: Arc>>, device_keyboard: Keyboard, new_chip8_mode: bool) -> Device { let memory = vec![0u8; Self::DEVICE_MEMORY_SIZE].into_boxed_slice().try_into().unwrap(); log::trace!("Successfully initiated device memory"); Device { @@ -215,8 +215,9 @@ impl Device { let delay_timer_val = self.registers.v[x]; self.timer.try_set_timer(delay_timer_val)?; } - Instruction::SetSoundTimer(_) => { - log::warn!("Sound unimplemented, instruction {:?}",instruction); + Instruction::SetSoundTimer(x) => { + let delay_timer_val = self.registers.v[x]; + self.timer.try_set_sound(delay_timer_val)?; } Instruction::AddToIndex(x) => { let reg_value = self.registers.v[x]; diff --git a/src/device/keyboard.rs b/src/device/keyboard.rs index b597f82..bc44001 100644 --- a/src/device/keyboard.rs +++ b/src/device/keyboard.rs @@ -53,16 +53,14 @@ impl Keyboard { fn update_keyboard_state(&mut self, keyboard_event: KeyboardEvent) { match keyboard_event { - KeyboardEvent::KeyUp(key) => { self.key_up(key) } - KeyboardEvent::KeyDown(key) => { self.key_down(key) } + KeyboardEvent::KeyUp(key) => { + log::debug!("Key Up - state {}",self.bitflags); + self.bitflags &= !((1 << key) as u16); + } + KeyboardEvent::KeyDown(key) => { + log::debug!("Key Down - state {}",self.bitflags); + self.bitflags |= 1 << key; + } } } - fn key_down(&mut self, x: u8) { - self.bitflags |= 1 << x; - log::trace!("Key Down - state {}",self.bitflags); - } - fn key_up(&mut self, x: u8) { - self.bitflags &= !((1 << x) as u16); - log::debug!("Key Up - state {}",self.bitflags); - } } \ No newline at end of file diff --git a/src/device/timer.rs b/src/device/timer.rs index 6e210c5..2e0f382 100644 --- a/src/device/timer.rs +++ b/src/device/timer.rs @@ -1,22 +1,33 @@ -use std::sync::{Arc, Mutex}; +use std::f32::consts::PI; +use std::sync::{Arc, Mutex, RwLock}; use std::sync::mpsc::SendError; use std::thread::{JoinHandle, sleep}; use std::time::Duration; +use sdl2::audio::AudioQueue; use crate::util::EmulatorResult; -pub struct Timer { +/// Manages the timer and the sound timer +pub struct TimerManager { timer_left: Arc>, + sound_left: Arc>, join_handle: Option<(JoinHandle<()>, std::sync::mpsc::Sender<()>)>, } -impl Timer { +impl TimerManager { pub const TIMER_THREAD_NAME: &'static str = "Timer"; - pub fn new() -> Timer { - Timer { timer_left: Arc::new(Mutex::default()), join_handle: None } + pub fn new() -> TimerManager { + TimerManager { + timer_left: Arc::new(Mutex::default()), + sound_left: Arc::new(Mutex::default()), + join_handle: None, + } } - pub fn start(&mut self) { + pub fn start(&mut self) -> Arc> { let timer_left_ref = self.timer_left.clone(); + let sound_timer_ref = self.sound_left.clone(); let (sender, receiver) = std::sync::mpsc::channel(); + // A 1/60th second + let res = std::thread::Builder::new().name(Self::TIMER_THREAD_NAME.into()).spawn(move || { loop { let val = receiver.try_recv(); @@ -27,15 +38,22 @@ impl Timer { } { let mut timer_lock = timer_left_ref.lock().expect("Failed to lock"); + let mut sound_lock = sound_timer_ref.lock().expect("Failed to lock"); if *timer_lock > 0 { *timer_lock -= 1; } + if *sound_lock > 0 { + log::info!("Beep!"); + *sound_lock -= 1; + } } sleep(Duration::from_secs_f32(1f32 / 60f32)); } }).expect("Failed to start timer thread"); self.join_handle = Some((res, sender)); + self.sound_left.clone() } + /// Set a timer down tick from `val` pub fn try_set_timer(&self, val: u8) -> EmulatorResult<()> { let mut timer_val = self.timer_left.lock()?; @@ -43,6 +61,12 @@ impl Timer { Ok(()) } + pub fn try_set_sound(&self, val: u8) -> EmulatorResult<()> { + let mut timer_val = self.sound_left.lock()?; + *timer_val = val; + Ok(()) + } + pub fn poll_value(&self) -> EmulatorResult { let res = self.timer_left.lock()?; Ok(res.clone()) diff --git a/src/main.rs b/src/main.rs index 6274c83..69065e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,12 +15,13 @@ use sdl2::render::BlendMode; use sdl2::render::WindowCanvas; use simple_logger::SimpleLogger; -use device::timer::Timer; +use device::timer::TimerManager; use crate::args::Porcel8ProgramArgs; use crate::device::Device; use crate::device::keyboard::Keyboard; use crate::util::EmulatorResult; use crate::kb_map::get_key_index; +use crate::sdl_audio_adapter::SquareWave; use crate::sdl_graphics_adapter::SdlGraphicsAdapter; use crate::sdl_keyboard_adapter::SdlKeyboardAdapter; @@ -30,26 +31,29 @@ mod kb_map; mod sdl_graphics_adapter; mod util; mod sdl_keyboard_adapter; +mod sdl_audio_adapter; fn main() -> EmulatorResult<()> { SimpleLogger::new().with_level(LevelFilter::Info).env().init().unwrap(); let Porcel8ProgramArgs { filename, new_chip8_behaviour: new_chip_behaviour,draw_scale } = Porcel8ProgramArgs::parse(); log::info!("Started emulator"); - let mut timer = Timer::new(); - timer.start(); + let mut timer = TimerManager::new(); + + let audio_state = timer.start(); + let (mut canvas, mut event_pump) = try_initiate_sdl(audio_state,draw_scale)?; + + + let (frame_buffer_for_display, frame_buffer_for_device) = get_frame_buffer_references(); - let (sdl_kb_adapter, device_keyboard) = SdlKeyboardAdapter::new_keyboard(); - let (termination_signal_sender, termination_signal_sender_receiver) = std::sync::mpsc::channel(); - + let (device_termination_signal_sender, device_termination_signal_sender_receiver) = std::sync::mpsc::channel(); let compute_handle = thread::Builder::new().name("Compute".to_string()).spawn(move || { - do_device_loop(timer, frame_buffer_for_device, termination_signal_sender_receiver, device_keyboard, filename, new_chip_behaviour); + do_device_loop(timer, frame_buffer_for_device, device_termination_signal_sender_receiver, device_keyboard, filename, new_chip_behaviour); })?; - let (mut canvas, mut event_pump) = try_initiate_sdl(draw_scale)?; let mut sdl_graphics_adapter = SdlGraphicsAdapter::new(); @@ -61,7 +65,7 @@ fn main() -> EmulatorResult<()> { match event { Event::Quit { .. } | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => { - termination_signal_sender.send(()).expect("Could not send"); + device_termination_signal_sender.send(()).expect("Could not send"); break 'running; } Event::KeyDown { keycode: Some(x), repeat: false, .. } => { @@ -96,7 +100,7 @@ fn main() -> EmulatorResult<()> { Ok(()) } -fn do_device_loop(mut timer: Timer, frame_buffer: Arc>>, receiver: Receiver<()>, device_keyboard: Keyboard, rom_file_location_option: Option, new_chip_behaviour: bool) { +fn do_device_loop(mut timer: TimerManager, frame_buffer: Arc>>, receiver: Receiver<()>, device_keyboard: Keyboard, rom_file_location_option: Option, new_chip_behaviour: bool) { let mut device = Device::new(timer, frame_buffer, device_keyboard, new_chip_behaviour); device.set_default_font(); @@ -135,17 +139,22 @@ fn load_rom(rom_file_location: String) -> [u8; ROM_SIZE] { rom_slice } -fn try_initiate_sdl(draw_scale: f32) -> EmulatorResult<(WindowCanvas, EventPump)> { +fn try_initiate_sdl(audio_state: Arc>, draw_scale: f32) -> EmulatorResult<(WindowCanvas, EventPump)> { let sdl_context = sdl2::init().unwrap(); let video_subsystem = sdl_context.video().unwrap(); - // let audio_subsystem = sdl_context.audio().unwrap(); - // let wanted_spec = AudioSpecDesired { - // channels: Some(1), - // samples: Some(256), - // freq: Some(15360), - // }; - // let audio_queue = audio_subsystem.open_queue::(None, &wanted_spec).unwrap(); - // audio_queue.resume(); + let audio_subsystem = sdl_context.audio().unwrap(); + let wanted_spec = AudioSpecDesired { + channels: Some(1), + samples: None, + freq: Some(44100), + }; + + let device = audio_subsystem.open_playback(None, &wanted_spec, |spec| { + // initialize the audio callback + SquareWave ::new(audio_state,440.0/spec.freq as f32,0.5) + }).unwrap(); + device.resume(); + let window_width = (Device::FRAME_BUFFER_WIDTH as f32 * draw_scale) as u32; let window_height = (Device::FRAME_BUFFER_HEIGHT as f32 * draw_scale) as u32; @@ -161,7 +170,6 @@ fn try_initiate_sdl(draw_scale: f32) -> EmulatorResult<(WindowCanvas, EventPump) canvas.present(); let event_pump = sdl_context.event_pump()?; Ok((canvas, event_pump - // , audio_queue )) } diff --git a/src/sdl_audio_adapter.rs b/src/sdl_audio_adapter.rs new file mode 100644 index 0000000..8e80252 --- /dev/null +++ b/src/sdl_audio_adapter.rs @@ -0,0 +1,46 @@ +use std::sync::{Arc, Mutex, RwLock}; +use log::warn; +use sdl2::audio::AudioCallback; + +pub struct SquareWave { + sound_timer: Arc>, + phase_inc: f32, + phase: f32, + volume: f32, +} + +impl SquareWave { + pub fn new(sound_timer: Arc>, + phase_inc: f32, + volume: f32)->SquareWave { + SquareWave { + sound_timer, + phase: 0f32, + phase_inc, + volume, + } + } +} + +impl AudioCallback for SquareWave { + type Channel = f32; + + fn callback(&mut self, out: &mut [f32]) { + let sound_timer = self.sound_timer.lock().expect("Could not lock to play audio"); + let sound_timer = sound_timer.clone(); + // log::info!("Processing audio buffer length {}",out.len()); + // Generate a square wave + for x in out.iter_mut() { + *x = if sound_timer > 0 { + if self.phase <= 0.5 { + self.volume + } else { + -self.volume + } + } else { + 0f32 + }; + self.phase = (self.phase + self.phase_inc) % 1.0; + } + } +} \ No newline at end of file