extern crate pulse_simple; mod envelope; mod length; mod square; mod wave; use self::pulse_simple::Playback; use std::sync::{ atomic::{AtomicBool, Ordering}, Arc, Mutex, }; use std::thread; const OUTPUT_SAMPLE_RATE: usize = 48100; //const OUTPUT_SAMPLE_RATE: usize = 32768; /// Represents an 'audio module', transforming an input sample to an output sample trait AudioModule { fn transform(&self, sample: T) -> T; } /// Clockable audio components trait AudioComponent { fn clock(&mut self); } #[derive(Debug, Default)] struct Channel1 { // Square 1: Sweep -> Timer -> Duty -> Length Counter -> Envelope -> Mixer wave_gen: square::SquareWaveGenerator, length_counter: length::LengthCounter, envelope: envelope::VolumeEnvelope, tick_state: u8, stored_regs: [u8; 5], } impl Channel1 { pub fn write_byte(&mut self, addr: u16, val: u8) { match addr { 0xFF10 => { self.wave_gen.set_sweep_reg(val); self.stored_regs[0] = val; } 0xFF11 => { self.length_counter.set_length(val & 0x3F); self.wave_gen.set_duty_cycle(val >> 6); self.stored_regs[1] = val; } 0xFF12 => { self.envelope.set_register(val); self.stored_regs[2] = val; } 0xFF13 => { // Set lower frequency self.wave_gen.set_lower_freq(val); self.stored_regs[3] = val; } 0xFF14 => { self.stored_regs[4] = val; // Set higher self.wave_gen.set_higher_freq(val & 0b111); if val & (1 << 7) != 0 { self.wave_gen.reset(); self.envelope.start(); } self.length_counter.set_enabled(val & 1 << 6); } _ => panic!("Channel1: Write not supported: {:04X} = {:02X}", addr, val), } } pub fn read_byte(&self, addr: u16) -> u8 { match addr { 0xFF10..=0xFF14 => self.stored_regs[(addr - 0xFF10) as usize], _ => { println!("Channel1 does not support reading ({:04X})", addr); 0 } } } pub fn sample_clock(&mut self) { self.wave_gen.clock(); } pub fn sample(&self) -> u8 { let input = self.wave_gen.sample(); // Follow the chain let input = self.length_counter.transform(input); self.envelope.transform(input) } pub fn clock(&mut self) { if self.tick_state % 2 == 0 { self.length_counter.clock(); } if self.tick_state == 2 || self.tick_state == 6 { self.wave_gen.sweep_clock(); } if self.tick_state == 7 { self.envelope.clock(); } // TODO: Sweep // TODO: Sweep in adition mode + overflow? stop. self.tick_state += 1; if self.tick_state == 8 { self.tick_state = 0; } } } #[derive(Debug, Default)] struct Channel2 { // Square 2: Timer -> Duty -> Length Counter -> Envelope -> Mixer wave_gen: square::SquareWaveGenerator, length_counter: length::LengthCounter, envelope: envelope::VolumeEnvelope, tick_state: u8, } impl Channel2 { pub fn write_byte(&mut self, addr: u16, val: u8) { match addr { 0xFF16 => { self.length_counter.set_length(val & 0x3F); self.wave_gen.set_duty_cycle(val >> 6); } 0xFF17 => { self.envelope.set_register(val); } 0xFF18 => { // Set lower frequency self.wave_gen.set_lower_freq(val); } 0xFF19 => { // Set higher self.wave_gen.set_higher_freq(val & 0b111); if val & (1 << 7) != 0 { self.wave_gen.reset(); self.envelope.start(); } self.length_counter.set_enabled(val & 1 << 6); } _ => panic!("Channel2: Write not supported: {:04X} = {:02X}", addr, val), } } pub fn read_byte(&self, addr: u16) -> u8 { use crate::sound::square::DutyCycle::*; match addr { 0xFF16 => match self.wave_gen.duty_cycle { Duty0 => 0, // 12.5% Duty1 => 0b0100_0000, // 25.0% Duty2 => 0b1000_0000, // 50.0% Duty3 => 0b1100_0000, // 75.0% }, _ => { println!("Channel2 does not support reading ({:04X})", addr); 0 } } } pub fn sample_clock(&mut self) { self.wave_gen.clock(); } pub fn sample(&self) -> u8 { let input = self.wave_gen.sample(); // Follow the chain let input = self.length_counter.transform(input); self.envelope.transform(input) } pub fn clock(&mut self) { if self.tick_state % 2 == 0 { self.length_counter.clock(); } if self.tick_state == 2 || self.tick_state == 6 { // TODO: Sweep } if self.tick_state == 7 { self.envelope.clock(); } self.tick_state += 1; if self.tick_state == 8 { self.tick_state = 0; } } } #[derive(Debug)] enum Channel3VolumeSetting { Muted, Original, Half, Quarter, } impl From for Channel3VolumeSetting { fn from(o: u8) -> Self { use Channel3VolumeSetting::*; match o { 0 => Muted, 1 => Original, 2 => Half, 3 => Quarter, _ => unreachable!("Invalid channel volume setting"), } } } impl Default for Channel3VolumeSetting { fn default() -> Self { Self::Muted } } impl Channel3VolumeSetting { fn transform(&self, sample: u8) -> u8 { match self { Self::Muted => 0, Self::Original => sample, Self::Half => sample >> 1, Self::Quarter => sample >> 2, } } } #[derive(Debug, Default)] struct Channel3 { // Timer -> Wave -> Length Counter -> Volume -> Mixer wave_gen: wave::WaveGenerator, length_counter: length::LengthCounter, volume_ctrl: Channel3VolumeSetting, tick_state: u8, } impl Channel3 { pub fn write_byte(&mut self, addr: u16, val: u8) { // OFF flag -> "bit 7 of NR30" match addr { 0xFF1A => { self.wave_gen.enabled = (val & (1 << 7)) > 0; } 0xFF1B => { self.length_counter.set_length(val); } 0xFF1C => self.volume_ctrl = ((val >> 5) & 0b11).into(), 0xFF1D => { // Set lower frequency self.wave_gen.set_lower_freq(val); } 0xFF1E => { // Set higher self.wave_gen.set_higher_freq(val & 0b111); if val & (1 << 7) != 0 { self.wave_gen.reset(); } self.length_counter.set_enabled(val & (1 << 6)); } 0xFF30..=0xFF3F => { self.wave_gen.set_sample_pair((addr - 0xFF30) as usize, val); } _ => panic!("Channel3: Write not supported: {:04X} = {:02X}", addr, val), } } pub fn read_byte(&self, addr: u16) -> u8 { match addr { 0xFF30..=0xFF3F => self.wave_gen.get_sample_pair((addr - 0xFF30) as usize), _ => panic!("Channel3 does not support reading ({:04X})", addr), } } pub fn sample_clock(&mut self) { self.wave_gen.clock(); } pub fn sample(&self) -> u8 { let input = self.wave_gen.sample(); // TODO(?): Follow the chain // let input = self.length_counter.transform(input); self.volume_ctrl.transform(input) } pub fn clock(&mut self) { if self.tick_state % 2 == 0 { self.length_counter.clock(); } if self.tick_state == 2 || self.tick_state == 6 { // TODO: Sweep } if self.tick_state == 7 { // self.envelope.clock(); } self.tick_state += 1; if self.tick_state == 8 { self.tick_state = 0; } } } #[derive(Default)] pub struct Sound { channel1: Channel1, channel2: Channel2, channel3: Channel3, sound_channel_volume_control: u8, sound_output_terminal_selector: u8, enabled: u8, } pub struct SoundManager { pub sound_object: Arc>, handle: Option>, do_exit: Arc, } impl Drop for SoundManager { fn drop(&mut self) { self.do_exit.store(true, Ordering::Relaxed); self.handle.take().and_then(|h| h.join().ok()); } } impl SoundManager { pub fn new() -> Self { let mut res = SoundManager { sound_object: Arc::new(Mutex::new(Sound::new())), handle: None, do_exit: Arc::new(AtomicBool::new(false)), }; res.launch_thread(); res } fn launch_thread(&mut self) { let obj = self.sound_object.clone(); if false { return; } let do_exit = self.do_exit.clone(); self.handle = Some( thread::Builder::new() .name("Audio".into()) .spawn(move || { // PulseAudio playback object let playback = Playback::new("GBC", "Output", None, (OUTPUT_SAMPLE_RATE) as _); // Counter, used for calling the 512 Hz timer let mut counter = 0; while !do_exit.load(Ordering::Relaxed) { for _ in 0..100 { let (s1, s2) = { let mut c_obj = obj.lock().unwrap(); // Check for 512 Hz timer if counter >= (OUTPUT_SAMPLE_RATE / 512) { c_obj.clock(); counter = 0; } else { counter += 1; } // Sample clock c_obj.sample_clock(); // Get sample c_obj.sample() }; let samps = [[s1, s2]]; playback.write(&samps[..]); } std::thread::sleep(std::time::Duration::from_millis(1)); } }) .unwrap(), ); } } impl Sound { pub fn new() -> Sound { Sound::default() } pub fn clock(&mut self) { self.channel1.clock(); self.channel2.clock(); self.channel3.clock(); } pub fn sample_clock(&mut self) { self.channel1.sample_clock(); self.channel2.sample_clock(); self.channel3.sample_clock(); } pub fn sample(&self) -> (u8, u8) { // Some basic mixing // Output value let mut s = (0, 0); let s01_volume = self.sound_channel_volume_control & 7; let s02_volume = (self.sound_channel_volume_control >> 4) & 7; /* if self.sound_channel_volume_control & 0b1000 != 0 { VIN! } if self.sound_channel_volume_control & 0b1000_0000 != 0 { VIN! } */ let c1_sample = self.channel1.sample(); let c2_sample = self.channel2.sample(); let c3_sample = self.channel3.sample(); let c4_sample = 0; if self.enabled != 0 { if self.sound_output_terminal_selector & 1 > 0 { // Output Channel1 s.0 += c1_sample; } if self.sound_output_terminal_selector & 2 > 0 { // Output Channel2 s.0 += c2_sample; } if self.sound_output_terminal_selector & 4 > 0 { // Output Channel3 s.0 += c3_sample; } if self.sound_output_terminal_selector & 8 > 0 { // Output Channel4 s.0 += c4_sample; } if self.sound_output_terminal_selector & 0x10 > 0 { // Output Channel1 s.1 += c1_sample; } if self.sound_output_terminal_selector & 0x20 > 0 { // Output Channel2 s.1 += c2_sample; } if self.sound_output_terminal_selector & 0x40 > 0 { // Output Channel3 s.1 += c3_sample; } if self.sound_output_terminal_selector & 0x80 > 0 { // Output Channel4 s.1 += c4_sample; } } s.0 *= s01_volume + 1; s.1 *= s02_volume + 1; s } pub fn write_byte(&mut self, addr: u16, val: u8) { // Bit 7 of NR52 = 0 -> everything reset // If all sound off no mode register can be set // println!("Snd: {:04X} = {:02X} ({:08b})", addr, val, val); match addr { 0xFF10..=0xFF14 => self.channel1.write_byte(addr, val), 0xFF16..=0xFF19 => self.channel2.write_byte(addr, val), 0xFF1A..=0xFF1E => self.channel3.write_byte(addr, val), // TODO: Channel 4 0xFF24 => self.sound_channel_volume_control = val, 0xFF25 => self.sound_output_terminal_selector = val, 0xFF26 => self.enabled = val, 0xFF30..=0xFF3F => self.channel3.write_byte(addr, val), _ => { println!("Sound: Write {:02X} to {:04X} unsupported", val, addr); } } } pub fn read_byte(&self, addr: u16) -> u8 { // println!("RD {:04X}", addr); match addr { 0xFF10..=0xFF14 => self.channel1.read_byte(addr), 0xFF16..=0xFF19 => self.channel2.read_byte(addr), 0xFF1A..=0xFF1E => self.channel3.read_byte(addr), 0xFF24 => self.sound_channel_volume_control, 0xFF25 => self.sound_output_terminal_selector, 0xFF26 => self.enabled, 0xFF30..=0xFF3F => self.channel3.read_byte(addr), _ => panic!("Sound: Read from {:04X} unsupported", addr), } } }