Add 3 additional audio channels for a total of 4
This commit is contained in:
parent
881b9c1620
commit
ad6f402179
65
src/audio.rs
65
src/audio.rs
|
@ -1,17 +1,70 @@
|
||||||
// audio.rs
|
// audio.rs
|
||||||
|
|
||||||
pub struct Audio {
|
use crate::Interrupt;
|
||||||
pub current_buffer_is_0: bool,
|
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 playing: bool,
|
||||||
pub sample_rate: u32,
|
pub sample_rate: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Audio {
|
impl AudioChannel {
|
||||||
pub fn new() -> Self {
|
pub fn new(id: u8) -> Self {
|
||||||
Audio {
|
AudioChannel {
|
||||||
current_buffer_is_0: true,
|
id,
|
||||||
playing: false,
|
playing: false,
|
||||||
sample_rate: 0,
|
sample_rate: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is terrible
|
||||||
|
pub fn spawn_thread(this: Arc<Mutex<Self>>, interrupt_sender: Sender<Interrupt>, 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<i16> = 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
61
src/bus.rs
61
src/bus.rs
|
@ -1,6 +1,6 @@
|
||||||
// bus.rs
|
// 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::sync::{Arc, Mutex};
|
||||||
use std::io::{Write, stdout};
|
use std::io::{Write, stdout};
|
||||||
|
@ -10,7 +10,10 @@ pub struct Bus {
|
||||||
|
|
||||||
pub disk_controller: DiskController,
|
pub disk_controller: DiskController,
|
||||||
|
|
||||||
pub audio: Arc<Mutex<Audio>>,
|
pub audio_channel_0: Arc<Mutex<AudioChannel>>,
|
||||||
|
pub audio_channel_1: Arc<Mutex<AudioChannel>>,
|
||||||
|
pub audio_channel_2: Arc<Mutex<AudioChannel>>,
|
||||||
|
pub audio_channel_3: Arc<Mutex<AudioChannel>>,
|
||||||
|
|
||||||
pub keyboard: Arc<Mutex<Keyboard>>,
|
pub keyboard: Arc<Mutex<Keyboard>>,
|
||||||
pub mouse: Arc<Mutex<Mouse>>,
|
pub mouse: Arc<Mutex<Mouse>>,
|
||||||
|
@ -84,9 +87,27 @@ impl Bus {
|
||||||
let mut keyboard_lock = self.keyboard.lock().expect("failed to lock the keyboard mutex");
|
let mut keyboard_lock = self.keyboard.lock().expect("failed to lock the keyboard mutex");
|
||||||
keyboard_lock.pop() as u32
|
keyboard_lock.pop() as u32
|
||||||
}
|
}
|
||||||
0x80000600 => { // audio port
|
0x80000600..=0x80000603 => { // audio port
|
||||||
let audio_lock = self.audio.lock().unwrap();
|
let channel = (port & 0x000000FF) as u8;
|
||||||
audio_lock.playing as u32
|
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
|
0x80001000..=0x80002003 => { // disk controller port
|
||||||
let id = port as u8;
|
let id = port as u8;
|
||||||
|
@ -171,11 +192,31 @@ impl Bus {
|
||||||
_ => panic!("invalid mouse control port"),
|
_ => panic!("invalid mouse control port"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x80000600 => { // audio port
|
0x80000600..=0x80000603 => { // audio port
|
||||||
let mut audio_lock = self.audio.lock().unwrap();
|
let channel = (port & 0x000000FF) as u8;
|
||||||
audio_lock.playing = word != 0;
|
match channel {
|
||||||
audio_lock.sample_rate = word;
|
0 => {
|
||||||
audio_lock.current_buffer_is_0 = false; // the first buffer refill interrupt will invert this to true
|
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
|
0x80001000..=0x80005003 => { // disk controller port
|
||||||
let id = port as u8;
|
let id = port as u8;
|
||||||
|
|
52
src/main.rs
52
src/main.rs
|
@ -8,7 +8,7 @@ pub mod keyboard;
|
||||||
pub mod mouse;
|
pub mod mouse;
|
||||||
pub mod disk;
|
pub mod disk;
|
||||||
|
|
||||||
use audio::Audio;
|
use audio::AudioChannel;
|
||||||
use bus::Bus;
|
use bus::Bus;
|
||||||
use cpu::{Cpu, Interrupt};
|
use cpu::{Cpu, Interrupt};
|
||||||
use keyboard::Keyboard;
|
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::sync::{Arc, mpsc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time;
|
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::{File, read};
|
use std::fs::{File, read};
|
||||||
|
@ -26,7 +25,6 @@ use std::fs::{File, read};
|
||||||
use image;
|
use image;
|
||||||
use log::error;
|
use log::error;
|
||||||
use pixels::{Pixels, SurfaceTexture};
|
use pixels::{Pixels, SurfaceTexture};
|
||||||
use rodio::{OutputStream, buffer::SamplesBuffer, Sink};
|
|
||||||
use winit::dpi::LogicalSize;
|
use winit::dpi::LogicalSize;
|
||||||
use winit::event::{ElementState, Event, WindowEvent};
|
use winit::event::{ElementState, Event, WindowEvent};
|
||||||
use winit::event_loop::{ControlFlow, EventLoop};
|
use winit::event_loop::{ControlFlow, EventLoop};
|
||||||
|
@ -37,9 +35,6 @@ const WIDTH: usize = 640;
|
||||||
const HEIGHT: usize = 480;
|
const HEIGHT: usize = 480;
|
||||||
|
|
||||||
const FRAMEBUFFER_ADDRESS: usize = 0x02000000;
|
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 {
|
pub struct Display {
|
||||||
background: Vec<u8>,
|
background: Vec<u8>,
|
||||||
|
@ -84,12 +79,18 @@ fn main() {
|
||||||
let keyboard = Arc::new(Mutex::new(Keyboard::new()));
|
let keyboard = Arc::new(Mutex::new(Keyboard::new()));
|
||||||
let mouse = Arc::new(Mutex::new(Mouse::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 memory = Memory::new(read_rom().as_slice());
|
||||||
let mut bus = Bus {
|
let mut bus = Bus {
|
||||||
memory: memory.clone(),
|
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(),
|
disk_controller: DiskController::new(),
|
||||||
keyboard: keyboard.clone(),
|
keyboard: keyboard.clone(),
|
||||||
mouse: mouse.clone(),
|
mouse: mouse.clone(),
|
||||||
|
@ -104,7 +105,6 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let memory_audio = memory.clone();
|
|
||||||
let memory_cpu = memory.clone();
|
let memory_cpu = memory.clone();
|
||||||
let memory_eventloop = memory.clone();
|
let memory_eventloop = memory.clone();
|
||||||
|
|
||||||
|
@ -145,6 +145,11 @@ fn main() {
|
||||||
let (interrupt_sender, interrupt_receiver) = mpsc::channel::<Interrupt>();
|
let (interrupt_sender, interrupt_receiver) = mpsc::channel::<Interrupt>();
|
||||||
let (exit_sender, exit_receiver) = mpsc::channel::<bool>();
|
let (exit_sender, exit_receiver) = mpsc::channel::<bool>();
|
||||||
|
|
||||||
|
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());
|
let builder = thread::Builder::new().name("cpu".to_string());
|
||||||
builder.spawn({
|
builder.spawn({
|
||||||
move || {
|
move || {
|
||||||
|
@ -180,35 +185,6 @@ fn main() {
|
||||||
}
|
}
|
||||||
}).unwrap();
|
}).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<i16> = 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| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
*control_flow = ControlFlow::Poll;
|
*control_flow = ControlFlow::Poll;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user