[aud] Add audio
This commit is contained in:
@@ -7,14 +7,14 @@ use std::time::Duration;
|
|||||||
use rand::random;
|
use rand::random;
|
||||||
use crate::device::instruction::Instruction;
|
use crate::device::instruction::Instruction;
|
||||||
use crate::device::keyboard::Keyboard;
|
use crate::device::keyboard::Keyboard;
|
||||||
use crate::device::timer::Timer;
|
use crate::device::timer::TimerManager;
|
||||||
use crate::util::EmulatorResult;
|
use crate::util::EmulatorResult;
|
||||||
|
|
||||||
|
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
pub registers: RegisterFile,
|
pub registers: RegisterFile,
|
||||||
pub memory: Box<[u8; Self::DEVICE_MEMORY_SIZE]>,
|
pub memory: Box<[u8; Self::DEVICE_MEMORY_SIZE]>,
|
||||||
pub timer: Timer,
|
pub timer: TimerManager,
|
||||||
pub stack: Vec<u16>,
|
pub stack: Vec<u16>,
|
||||||
pub frame_buffer: Arc<Mutex<Box<[bool; 64 * 32]>>>,
|
pub frame_buffer: Arc<Mutex<Box<[bool; 64 * 32]>>>,
|
||||||
pub new_chip8_mode: bool,
|
pub new_chip8_mode: bool,
|
||||||
@@ -26,7 +26,7 @@ impl Device {
|
|||||||
pub const FRAME_BUFFER_WIDTH: usize = 64;
|
pub const FRAME_BUFFER_WIDTH: usize = 64;
|
||||||
pub const FRAME_BUFFER_HEIGHT: usize = 32;
|
pub const FRAME_BUFFER_HEIGHT: usize = 32;
|
||||||
pub const FRAME_BUFFER_SIZE: usize = Self::FRAME_BUFFER_WIDTH * Self::FRAME_BUFFER_HEIGHT;
|
pub const FRAME_BUFFER_SIZE: usize = Self::FRAME_BUFFER_WIDTH * Self::FRAME_BUFFER_HEIGHT;
|
||||||
pub fn new(timer: Timer, fb: Arc<Mutex<Box<[bool; Device::FRAME_BUFFER_SIZE]>>>, device_keyboard: Keyboard, new_chip8_mode: bool) -> Device {
|
pub fn new(timer: TimerManager, fb: Arc<Mutex<Box<[bool; Device::FRAME_BUFFER_SIZE]>>>, device_keyboard: Keyboard, new_chip8_mode: bool) -> Device {
|
||||||
let memory = vec![0u8; Self::DEVICE_MEMORY_SIZE].into_boxed_slice().try_into().unwrap();
|
let memory = vec![0u8; Self::DEVICE_MEMORY_SIZE].into_boxed_slice().try_into().unwrap();
|
||||||
log::trace!("Successfully initiated device memory");
|
log::trace!("Successfully initiated device memory");
|
||||||
Device {
|
Device {
|
||||||
@@ -215,8 +215,9 @@ impl Device {
|
|||||||
let delay_timer_val = self.registers.v[x];
|
let delay_timer_val = self.registers.v[x];
|
||||||
self.timer.try_set_timer(delay_timer_val)?;
|
self.timer.try_set_timer(delay_timer_val)?;
|
||||||
}
|
}
|
||||||
Instruction::SetSoundTimer(_) => {
|
Instruction::SetSoundTimer(x) => {
|
||||||
log::warn!("Sound unimplemented, instruction {:?}",instruction);
|
let delay_timer_val = self.registers.v[x];
|
||||||
|
self.timer.try_set_sound(delay_timer_val)?;
|
||||||
}
|
}
|
||||||
Instruction::AddToIndex(x) => {
|
Instruction::AddToIndex(x) => {
|
||||||
let reg_value = self.registers.v[x];
|
let reg_value = self.registers.v[x];
|
||||||
|
@@ -53,16 +53,14 @@ impl Keyboard {
|
|||||||
|
|
||||||
fn update_keyboard_state(&mut self, keyboard_event: KeyboardEvent) {
|
fn update_keyboard_state(&mut self, keyboard_event: KeyboardEvent) {
|
||||||
match keyboard_event {
|
match keyboard_event {
|
||||||
KeyboardEvent::KeyUp(key) => { self.key_up(key) }
|
KeyboardEvent::KeyUp(key) => {
|
||||||
KeyboardEvent::KeyDown(key) => { self.key_down(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);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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::sync::mpsc::SendError;
|
||||||
use std::thread::{JoinHandle, sleep};
|
use std::thread::{JoinHandle, sleep};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use sdl2::audio::AudioQueue;
|
||||||
use crate::util::EmulatorResult;
|
use crate::util::EmulatorResult;
|
||||||
|
|
||||||
pub struct Timer {
|
/// Manages the timer and the sound timer
|
||||||
|
pub struct TimerManager {
|
||||||
timer_left: Arc<Mutex<u8>>,
|
timer_left: Arc<Mutex<u8>>,
|
||||||
|
sound_left: Arc<Mutex<u8>>,
|
||||||
join_handle: Option<(JoinHandle<()>, std::sync::mpsc::Sender<()>)>,
|
join_handle: Option<(JoinHandle<()>, std::sync::mpsc::Sender<()>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Timer {
|
impl TimerManager {
|
||||||
pub const TIMER_THREAD_NAME: &'static str = "Timer";
|
pub const TIMER_THREAD_NAME: &'static str = "Timer";
|
||||||
pub fn new() -> Timer {
|
pub fn new() -> TimerManager {
|
||||||
Timer { timer_left: Arc::new(Mutex::default()), join_handle: None }
|
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<Mutex<u8>> {
|
||||||
let timer_left_ref = self.timer_left.clone();
|
let timer_left_ref = self.timer_left.clone();
|
||||||
|
let sound_timer_ref = self.sound_left.clone();
|
||||||
let (sender, receiver) = std::sync::mpsc::channel();
|
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 || {
|
let res = std::thread::Builder::new().name(Self::TIMER_THREAD_NAME.into()).spawn(move || {
|
||||||
loop {
|
loop {
|
||||||
let val = receiver.try_recv();
|
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 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 {
|
if *timer_lock > 0 {
|
||||||
*timer_lock -= 1;
|
*timer_lock -= 1;
|
||||||
}
|
}
|
||||||
|
if *sound_lock > 0 {
|
||||||
|
log::info!("Beep!");
|
||||||
|
*sound_lock -= 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sleep(Duration::from_secs_f32(1f32 / 60f32));
|
sleep(Duration::from_secs_f32(1f32 / 60f32));
|
||||||
}
|
}
|
||||||
}).expect("Failed to start timer thread");
|
}).expect("Failed to start timer thread");
|
||||||
self.join_handle = Some((res, sender));
|
self.join_handle = Some((res, sender));
|
||||||
|
self.sound_left.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a timer down tick from `val`
|
/// Set a timer down tick from `val`
|
||||||
pub fn try_set_timer(&self, val: u8) -> EmulatorResult<()> {
|
pub fn try_set_timer(&self, val: u8) -> EmulatorResult<()> {
|
||||||
let mut timer_val = self.timer_left.lock()?;
|
let mut timer_val = self.timer_left.lock()?;
|
||||||
@@ -43,6 +61,12 @@ impl Timer {
|
|||||||
Ok(())
|
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<u8> {
|
pub fn poll_value(&self) -> EmulatorResult<u8> {
|
||||||
let res = self.timer_left.lock()?;
|
let res = self.timer_left.lock()?;
|
||||||
Ok(res.clone())
|
Ok(res.clone())
|
||||||
|
48
src/main.rs
48
src/main.rs
@@ -15,12 +15,13 @@ use sdl2::render::BlendMode;
|
|||||||
|
|
||||||
use sdl2::render::WindowCanvas;
|
use sdl2::render::WindowCanvas;
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
use device::timer::Timer;
|
use device::timer::TimerManager;
|
||||||
use crate::args::Porcel8ProgramArgs;
|
use crate::args::Porcel8ProgramArgs;
|
||||||
use crate::device::Device;
|
use crate::device::Device;
|
||||||
use crate::device::keyboard::Keyboard;
|
use crate::device::keyboard::Keyboard;
|
||||||
use crate::util::EmulatorResult;
|
use crate::util::EmulatorResult;
|
||||||
use crate::kb_map::get_key_index;
|
use crate::kb_map::get_key_index;
|
||||||
|
use crate::sdl_audio_adapter::SquareWave;
|
||||||
use crate::sdl_graphics_adapter::SdlGraphicsAdapter;
|
use crate::sdl_graphics_adapter::SdlGraphicsAdapter;
|
||||||
use crate::sdl_keyboard_adapter::SdlKeyboardAdapter;
|
use crate::sdl_keyboard_adapter::SdlKeyboardAdapter;
|
||||||
|
|
||||||
@@ -30,26 +31,29 @@ mod kb_map;
|
|||||||
mod sdl_graphics_adapter;
|
mod sdl_graphics_adapter;
|
||||||
mod util;
|
mod util;
|
||||||
mod sdl_keyboard_adapter;
|
mod sdl_keyboard_adapter;
|
||||||
|
mod sdl_audio_adapter;
|
||||||
|
|
||||||
fn main() -> EmulatorResult<()> {
|
fn main() -> EmulatorResult<()> {
|
||||||
SimpleLogger::new().with_level(LevelFilter::Info).env().init().unwrap();
|
SimpleLogger::new().with_level(LevelFilter::Info).env().init().unwrap();
|
||||||
let Porcel8ProgramArgs { filename, new_chip8_behaviour: new_chip_behaviour,draw_scale } = Porcel8ProgramArgs::parse();
|
let Porcel8ProgramArgs { filename, new_chip8_behaviour: new_chip_behaviour,draw_scale } = Porcel8ProgramArgs::parse();
|
||||||
log::info!("Started emulator");
|
log::info!("Started emulator");
|
||||||
|
|
||||||
let mut timer = Timer::new();
|
let mut timer = TimerManager::new();
|
||||||
timer.start();
|
|
||||||
|
|
||||||
|
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 (frame_buffer_for_display, frame_buffer_for_device) = get_frame_buffer_references();
|
||||||
|
|
||||||
let (sdl_kb_adapter, device_keyboard) = SdlKeyboardAdapter::new_keyboard();
|
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 || {
|
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();
|
let mut sdl_graphics_adapter = SdlGraphicsAdapter::new();
|
||||||
|
|
||||||
@@ -61,7 +65,7 @@ fn main() -> EmulatorResult<()> {
|
|||||||
match event {
|
match event {
|
||||||
Event::Quit { .. } |
|
Event::Quit { .. } |
|
||||||
Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
|
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;
|
break 'running;
|
||||||
}
|
}
|
||||||
Event::KeyDown { keycode: Some(x), repeat: false, .. } => {
|
Event::KeyDown { keycode: Some(x), repeat: false, .. } => {
|
||||||
@@ -96,7 +100,7 @@ fn main() -> EmulatorResult<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_device_loop(mut timer: Timer, frame_buffer: Arc<Mutex<Box<[bool; 2048]>>>, receiver: Receiver<()>, device_keyboard: Keyboard, rom_file_location_option: Option<String>, new_chip_behaviour: bool) {
|
fn do_device_loop(mut timer: TimerManager, frame_buffer: Arc<Mutex<Box<[bool; 2048]>>>, receiver: Receiver<()>, device_keyboard: Keyboard, rom_file_location_option: Option<String>, new_chip_behaviour: bool) {
|
||||||
let mut device = Device::new(timer, frame_buffer, device_keyboard, new_chip_behaviour);
|
let mut device = Device::new(timer, frame_buffer, device_keyboard, new_chip_behaviour);
|
||||||
device.set_default_font();
|
device.set_default_font();
|
||||||
|
|
||||||
@@ -135,17 +139,22 @@ fn load_rom(rom_file_location: String) -> [u8; ROM_SIZE] {
|
|||||||
rom_slice
|
rom_slice
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_initiate_sdl(draw_scale: f32) -> EmulatorResult<(WindowCanvas, EventPump)> {
|
fn try_initiate_sdl(audio_state: Arc<Mutex<u8>>, draw_scale: f32) -> EmulatorResult<(WindowCanvas, EventPump)> {
|
||||||
let sdl_context = sdl2::init().unwrap();
|
let sdl_context = sdl2::init().unwrap();
|
||||||
let video_subsystem = sdl_context.video().unwrap();
|
let video_subsystem = sdl_context.video().unwrap();
|
||||||
// let audio_subsystem = sdl_context.audio().unwrap();
|
let audio_subsystem = sdl_context.audio().unwrap();
|
||||||
// let wanted_spec = AudioSpecDesired {
|
let wanted_spec = AudioSpecDesired {
|
||||||
// channels: Some(1),
|
channels: Some(1),
|
||||||
// samples: Some(256),
|
samples: None,
|
||||||
// freq: Some(15360),
|
freq: Some(44100),
|
||||||
// };
|
};
|
||||||
// let audio_queue = audio_subsystem.open_queue::<u8, _>(None, &wanted_spec).unwrap();
|
|
||||||
// audio_queue.resume();
|
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_width = (Device::FRAME_BUFFER_WIDTH as f32 * draw_scale) as u32;
|
||||||
let window_height = (Device::FRAME_BUFFER_HEIGHT 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();
|
canvas.present();
|
||||||
let event_pump = sdl_context.event_pump()?;
|
let event_pump = sdl_context.event_pump()?;
|
||||||
Ok((canvas, event_pump
|
Ok((canvas, event_pump
|
||||||
// , audio_queue
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
46
src/sdl_audio_adapter.rs
Normal file
46
src/sdl_audio_adapter.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
use log::warn;
|
||||||
|
use sdl2::audio::AudioCallback;
|
||||||
|
|
||||||
|
pub struct SquareWave {
|
||||||
|
sound_timer: Arc<Mutex<u8>>,
|
||||||
|
phase_inc: f32,
|
||||||
|
phase: f32,
|
||||||
|
volume: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SquareWave {
|
||||||
|
pub fn new(sound_timer: Arc<Mutex<u8>>,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user