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
|
||||
|
||||
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<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
|
||||
|
||||
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<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 mouse: Arc<Mutex<Mouse>>,
|
||||
|
@ -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;
|
||||
|
|
52
src/main.rs
52
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<u8>,
|
||||
|
@ -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::<Interrupt>();
|
||||
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());
|
||||
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<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| {
|
||||
*control_flow = ControlFlow::Poll;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user