diff --git a/src/audio.rs b/src/audio.rs index cdfca02..aaf05c2 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -1,17 +1,70 @@ // audio.rs -pub struct Audio { - pub current_buffer_is_0: bool, +use crate::Interrupt; +use crate::memory::Memory; + +use std::sync::{Arc, Mutex}; +use std::sync::mpsc::Sender; +use std::{thread, time}; +use rodio::{OutputStream, buffer::SamplesBuffer, Sink}; + +const AUDIO_BUFFER_0_ADDRESS: usize = 0x0212C000; +const AUDIO_BUFFER_1_ADDRESS: usize = 0x02134000; +const AUDIO_BUFFER_2_ADDRESS: usize = 0x02290000; +const AUDIO_BUFFER_3_ADDRESS: usize = 0x02298000; +const AUDIO_BUFFER_SIZE: usize = 0x8000; + +const AUDIO_CHANNEL_0_INTERRUPT: u8 = 0xFE; +const AUDIO_CHANNEL_1_INTERRUPT: u8 = 0xFD; +const AUDIO_CHANNEL_2_INTERRUPT: u8 = 0xFC; +const AUDIO_CHANNEL_3_INTERRUPT: u8 = 0xFB; + +pub struct AudioChannel { + pub id: u8, pub playing: bool, pub sample_rate: u32, } -impl Audio { - pub fn new() -> Self { - Audio { - current_buffer_is_0: true, +impl AudioChannel { + pub fn new(id: u8) -> Self { + AudioChannel { + id, playing: false, sample_rate: 0, } } + + // this is terrible + pub fn spawn_thread(this: Arc>, interrupt_sender: Sender, memory: Memory) { + let (audio_buffer_address, audio_channel_interrupt) = match this.lock().unwrap().id { + 0 => (AUDIO_BUFFER_0_ADDRESS, AUDIO_CHANNEL_0_INTERRUPT), + 1 => (AUDIO_BUFFER_1_ADDRESS, AUDIO_CHANNEL_1_INTERRUPT), + 2 => (AUDIO_BUFFER_2_ADDRESS, AUDIO_CHANNEL_2_INTERRUPT), + 3 => (AUDIO_BUFFER_3_ADDRESS, AUDIO_CHANNEL_3_INTERRUPT), + _ => unreachable!(), + }; + let builder = thread::Builder::new().name("audio".to_string()); + builder.spawn({ + move || { + let (_stream, stream_handle) = OutputStream::try_default().unwrap(); + let channel = Sink::try_new(&stream_handle).unwrap(); + loop { + // every `sleep` number of ms, play what is in the audio buffer + let self_lock = this.lock().unwrap(); + if self_lock.playing { + let audio_buffer: Vec = memory.ram()[audio_buffer_address..audio_buffer_address+AUDIO_BUFFER_SIZE] + .to_vec() + .chunks_exact(2) + .map(|x| ((x[1] as i16) << 8) | x[0] as i16) + .collect(); + // 1 Hz = 1000 ms + let sleep: f32 = (1000 as f32 / self_lock.sample_rate as f32) * (AUDIO_BUFFER_SIZE as f32 / 2.0); + channel.append(SamplesBuffer::new(1, self_lock.sample_rate, audio_buffer)); + interrupt_sender.send(Interrupt::Request(audio_channel_interrupt)).unwrap(); + thread::sleep(time::Duration::from_millis(sleep as u64)); + } + } + } + }).unwrap(); + } } diff --git a/src/bus.rs b/src/bus.rs index 2f934f9..486b899 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -1,6 +1,6 @@ // bus.rs -use crate::{Memory, Audio, DiskController, Keyboard, Mouse, Overlay}; +use crate::{Memory, AudioChannel, DiskController, Keyboard, Mouse, Overlay}; use std::sync::{Arc, Mutex}; use std::io::{Write, stdout}; @@ -10,7 +10,10 @@ pub struct Bus { pub disk_controller: DiskController, - pub audio: Arc>, + pub audio_channel_0: Arc>, + pub audio_channel_1: Arc>, + pub audio_channel_2: Arc>, + pub audio_channel_3: Arc>, pub keyboard: Arc>, pub mouse: Arc>, @@ -84,9 +87,27 @@ impl Bus { let mut keyboard_lock = self.keyboard.lock().expect("failed to lock the keyboard mutex"); keyboard_lock.pop() as u32 } - 0x80000600 => { // audio port - let audio_lock = self.audio.lock().unwrap(); - audio_lock.playing as u32 + 0x80000600..=0x80000603 => { // audio port + let channel = (port & 0x000000FF) as u8; + match channel { + 0 => { + let audio_lock = self.audio_channel_0.lock().unwrap(); + audio_lock.sample_rate + }, + 1 => { + let audio_lock = self.audio_channel_1.lock().unwrap(); + audio_lock.sample_rate + }, + 2 => { + let audio_lock = self.audio_channel_2.lock().unwrap(); + audio_lock.sample_rate + }, + 3 => { + let audio_lock = self.audio_channel_3.lock().unwrap(); + audio_lock.sample_rate + }, + _ => panic!("invalid audio channel"), + } } 0x80001000..=0x80002003 => { // disk controller port let id = port as u8; @@ -171,11 +192,31 @@ impl Bus { _ => panic!("invalid mouse control port"), } } - 0x80000600 => { // audio port - let mut audio_lock = self.audio.lock().unwrap(); - audio_lock.playing = word != 0; - audio_lock.sample_rate = word; - audio_lock.current_buffer_is_0 = false; // the first buffer refill interrupt will invert this to true + 0x80000600..=0x80000603 => { // audio port + let channel = (port & 0x000000FF) as u8; + match channel { + 0 => { + let mut audio_lock = self.audio_channel_0.lock().unwrap(); + audio_lock.playing = word != 0; + audio_lock.sample_rate = word; + }, + 1 => { + let mut audio_lock = self.audio_channel_1.lock().unwrap(); + audio_lock.playing = word != 0; + audio_lock.sample_rate = word; + }, + 2 => { + let mut audio_lock = self.audio_channel_2.lock().unwrap(); + audio_lock.playing = word != 0; + audio_lock.sample_rate = word; + }, + 3 => { + let mut audio_lock = self.audio_channel_3.lock().unwrap(); + audio_lock.playing = word != 0; + audio_lock.sample_rate = word; + }, + _ => panic!("invalid audio channel"), + }; } 0x80001000..=0x80005003 => { // disk controller port let id = port as u8; diff --git a/src/main.rs b/src/main.rs index c3b47fb..b08b7fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ pub mod keyboard; pub mod mouse; pub mod disk; -use audio::Audio; +use audio::AudioChannel; use bus::Bus; use cpu::{Cpu, Interrupt}; use keyboard::Keyboard; @@ -18,7 +18,6 @@ use memory::{MEMORY_RAM_START, MEMORY_ROM_START, MemoryRam, Memory}; use std::sync::{Arc, mpsc, Mutex}; use std::thread; -use std::time; use std::process::exit; use std::env; use std::fs::{File, read}; @@ -26,7 +25,6 @@ use std::fs::{File, read}; use image; use log::error; use pixels::{Pixels, SurfaceTexture}; -use rodio::{OutputStream, buffer::SamplesBuffer, Sink}; use winit::dpi::LogicalSize; use winit::event::{ElementState, Event, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; @@ -37,9 +35,6 @@ const WIDTH: usize = 640; const HEIGHT: usize = 480; const FRAMEBUFFER_ADDRESS: usize = 0x02000000; -const AUDIO_BUFFER_0_ADDRESS: usize = 0x0212C000; -const AUDIO_BUFFER_1_ADDRESS: usize = 0x02134000; -const AUDIO_BUFFER_SIZE: usize = 0x8000; pub struct Display { background: Vec, @@ -84,12 +79,18 @@ fn main() { let keyboard = Arc::new(Mutex::new(Keyboard::new())); let mouse = Arc::new(Mutex::new(Mouse::new())); - let audio = Arc::new(Mutex::new(Audio::new())); + let audio_channel_0 = Arc::new(Mutex::new(AudioChannel::new(0))); + let audio_channel_1 = Arc::new(Mutex::new(AudioChannel::new(1))); + let audio_channel_2 = Arc::new(Mutex::new(AudioChannel::new(2))); + let audio_channel_3 = Arc::new(Mutex::new(AudioChannel::new(3))); let memory = Memory::new(read_rom().as_slice()); let mut bus = Bus { memory: memory.clone(), - audio: audio.clone(), + audio_channel_0: audio_channel_0.clone(), + audio_channel_1: audio_channel_1.clone(), + audio_channel_2: audio_channel_2.clone(), + audio_channel_3: audio_channel_3.clone(), disk_controller: DiskController::new(), keyboard: keyboard.clone(), mouse: mouse.clone(), @@ -104,7 +105,6 @@ fn main() { } } - let memory_audio = memory.clone(); let memory_cpu = memory.clone(); let memory_eventloop = memory.clone(); @@ -145,6 +145,11 @@ fn main() { let (interrupt_sender, interrupt_receiver) = mpsc::channel::(); let (exit_sender, exit_receiver) = mpsc::channel::(); + AudioChannel::spawn_thread(audio_channel_0, interrupt_sender.clone(), memory.clone()); + AudioChannel::spawn_thread(audio_channel_1, interrupt_sender.clone(), memory.clone()); + AudioChannel::spawn_thread(audio_channel_2, interrupt_sender.clone(), memory.clone()); + AudioChannel::spawn_thread(audio_channel_3, interrupt_sender.clone(), memory.clone()); + let builder = thread::Builder::new().name("cpu".to_string()); builder.spawn({ move || { @@ -180,35 +185,6 @@ fn main() { } }).unwrap(); - let interrupt_sender_audio = interrupt_sender.clone(); - let builder = thread::Builder::new().name("audio".to_string()); - builder.spawn({ - move || { - let (_stream, stream_handle) = OutputStream::try_default().unwrap(); - let sink = Sink::try_new(&stream_handle).unwrap(); - loop { - // every `sleep` number of ms, play what is in the audio buffer and tell fox32 to swap them - let mut audio_lock = audio.lock().unwrap(); - if audio_lock.playing { - let current_buffer: Vec = if audio_lock.current_buffer_is_0 { - audio_lock.current_buffer_is_0 = false; - memory_audio.ram()[AUDIO_BUFFER_0_ADDRESS..AUDIO_BUFFER_0_ADDRESS+AUDIO_BUFFER_SIZE].to_vec().chunks_exact(2).map(|x| ((x[1] as i16) << 8) | x[0] as i16).collect() - } else { - audio_lock.current_buffer_is_0 = true; - memory_audio.ram()[AUDIO_BUFFER_1_ADDRESS..AUDIO_BUFFER_1_ADDRESS+AUDIO_BUFFER_SIZE].to_vec().chunks_exact(2).map(|x| ((x[1] as i16) << 8) | x[0] as i16).collect() - }; - let buffer = SamplesBuffer::new(1, audio_lock.sample_rate, current_buffer); - // 1 Hz = 1000 ms - let sleep: f32 = (1000 as f32 / audio_lock.sample_rate as f32) * (AUDIO_BUFFER_SIZE as f32 / 2.0); - sink.append(buffer); - drop(audio_lock); - interrupt_sender_audio.send(Interrupt::Request(0xFE)).unwrap(); // audio interrupt, swap audio buffers - thread::sleep(time::Duration::from_millis(sleep as u64)); - } - } - } - }).unwrap(); - event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Poll;