diff --git a/Cargo.toml b/Cargo.toml index 97e19fb..d760ad2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,5 @@ authors = ["Kevin Hamacher "] [dependencies] sdl2 = "0.19.0" -libc = "*" +libc = "0.2.14" +pulse-simple = "1.0.0" diff --git a/src/interconnect.rs b/src/interconnect.rs index f717155..5839190 100644 --- a/src/interconnect.rs +++ b/src/interconnect.rs @@ -43,7 +43,7 @@ pub struct Interconnect { ram: Box<[u8]>, hiram: Box<[u8]>, wram_bank: u8, - sound: sound::Sound, + sound: sound::SoundManager, display: display::Display, interrupt: u8, interrupt_request_flags: u8, @@ -74,7 +74,7 @@ impl Interconnect { ram: vec![0; WRAM_SIZE].into_boxed_slice(), hiram: vec![0; HIRAM_SIZE].into_boxed_slice(), wram_bank: 1, - sound: sound::Sound::new(), + sound: sound::SoundManager::new(), display: display::Display::new(), // Refactor those interrupt_request_flags: 0, @@ -133,9 +133,6 @@ impl Interconnect { pub fn tick(&mut self, cycles: u8) { self.display.tick(cycles as u16); self.timer.tick(cycles as u16); - for _ in 0..cycles { - self.sound.tick(); - } if self.display.vblank_interrupt() { self.interrupt_request_flags |= INTERRUPT_DISPLAY_VBLANK; @@ -238,9 +235,9 @@ impl Interconnect { self.interrupt_request_flags }, 0xFF10 ... 0xFF26 => { - self.sound.read_byte(addr) + self.sound.sound_object.lock().unwrap().read_byte(addr) }, - 0xFF30 ... 0xFF3F => self.sound.read_byte(addr), + 0xFF30 ... 0xFF3F => self.sound.sound_object.lock().unwrap().read_byte(addr), 0xFF40 ... 0xFF4B => { self.display.read_byte(addr) }, @@ -308,9 +305,9 @@ impl Interconnect { self.interrupt_request_flags = val; } 0xFF10 ... 0xFF26 => { - self.sound.write_byte(addr, val); + self.sound.sound_object.lock().unwrap().write_byte(addr, val); }, - 0xFF30 ... 0xFF3F => self.sound.write_byte(addr, val), + 0xFF30 ... 0xFF3F => self.sound.sound_object.lock().unwrap().write_byte(addr, val), // Exclude DMA transfer, we will do this below 0xFF40 ... 0xFF45 | 0xFF47 ... 0xFF4B => { self.display.write_byte(addr, val); diff --git a/src/sound.rs b/src/sound.rs deleted file mode 100644 index 5dbe5d9..0000000 --- a/src/sound.rs +++ /dev/null @@ -1,617 +0,0 @@ -const TICK_RATE: usize = 4194304; -const OUTPUT_SAMPLE_RATE: usize = 32768; -const TICKS_PER_SAMPLE: usize = TICK_RATE / OUTPUT_SAMPLE_RATE; - -#[allow(dead_code)] -#[derive(Debug)] -enum EnvelopeDirection { - Increase, - Decrease -} - -#[derive(Debug,Clone)] -struct VolumeEnvelope(u8); -#[allow(dead_code)] -impl VolumeEnvelope { - fn initial_volume(&self) -> u8 { - self.0 >> 4 - } - - fn envelope_direction(&self) -> EnvelopeDirection { - match self.0 & (1 << 3) { - 0 => EnvelopeDirection::Decrease, - _ => EnvelopeDirection::Increase, - } - } - - fn nr_of_envelope_sweep(&self) -> u8 { - self.0 & 0x7 - } - - fn set_nr_of_envelope_sweep(&mut self, nr: u8) { - self.0 &= !0x7; - self.0 |= nr & 0x7; - } -} - -impl From for VolumeEnvelope { - fn from(val: u8) -> VolumeEnvelope { - VolumeEnvelope(val) - } -} - -impl Into for VolumeEnvelope { - fn into(self) -> u8 { - self.0 - } -} - -impl Default for VolumeEnvelope { - fn default() -> VolumeEnvelope { - VolumeEnvelope(0) - } -} - -trait OutputChannel { - fn sample(&mut self) -> i8; - fn clock_length_counter(&mut self); // 256 Hz - fn clock_volume_envelope(&mut self) -> Option; // 64 Hz - - // A square channel's frequency timer period is set to (2048-frequency)*4. - // Four duty cycles are available, each waveform taking 8 frequency timer clocks to cycle through: -} - -#[derive(Debug)] -enum ChannelMode { - CounterMode, - ConsecutiveMode, -} - -impl Default for ChannelMode { - fn default() -> Self { - ChannelMode::CounterMode - } -} - -// Tone & Sweep -#[derive(Debug, Default)] -struct Channel1 { - // MMaped registers - sweep_register: u8, - sound_length: u8, - volume_envelope: VolumeEnvelope, - active_volume_envelope: VolumeEnvelope, - frequency_low: u8, - frequency_high: u8, - - // Registeres used for sound generation - current_phase: f32, - // Depending on sound_length - //sound_enabled: bool, - // Current envelope volume - envelope_volume: u8, - current_mode: ChannelMode, -} - -impl OutputChannel for Channel1 { - /* - According to some specification page: - gb = 2048 - (131072 / Hz) - Hz = 131072 / (2048 - gb) - */ - fn sample(&mut self) -> i8 { - // Right now just assume that we have 100% on time - // I also don't know right now how to disable channel1 :( - let freq = self.current_frequency(); - // sample rate 128? Dunno! - self.current_phase += 1f32 / (2048f32 * TICKS_PER_SAMPLE as f32); - let sample = 2.0 * 3.1415 * freq * self.current_phase; - let volume = (127f32 * self.envelope_volume as f32 / 16f32) as i8; - - if sample.sin() > 0f32 { - volume - } else { - -1 * volume - } - - //(sample.sin() * volume as f32) as i8 - } - - fn clock_length_counter(&mut self) { - if self.frequency_high & (1 << 6) > 0 { - // Use sound length - let sound_length = self.sound_length & 0x3F; - if sound_length > 0 { - self.sound_length &= 0xC0; - self.sound_length |= sound_length - 1; - // self.sound_enabled = true; - } else { - // self.sound_enabled = false; - } - } - } - fn clock_volume_envelope(&mut self) -> Option { - if self.active_volume_envelope.nr_of_envelope_sweep() > 0 { - let left = self.active_volume_envelope.nr_of_envelope_sweep(); - self.active_volume_envelope.set_nr_of_envelope_sweep(left - 1); - match self.active_volume_envelope.envelope_direction() { - EnvelopeDirection::Decrease => { - if self.envelope_volume > 0 { - self.envelope_volume -= 1; - println!("CH1: Decreased envelope volume to {} (steps left: {})", self.envelope_volume, left-1); - } - }, - EnvelopeDirection::Increase => { - if self.envelope_volume < 0xF { - self.envelope_volume += 1; - println!("CH1: Increased envelope volume to {}", self.envelope_volume); - } - } - } - Some(self.active_volume_envelope.envelope_direction()) - } else { - match self.current_mode { - ChannelMode::CounterMode => { - self.envelope_volume = 0; - } - ChannelMode::ConsecutiveMode => { - - } - }; - None - } - } -} - -impl Channel1 { - fn current_frequency(&self) -> f32 { - let full_freq = ((self.frequency_high as u16 & 0x7) << 8) | self.frequency_low as u16; - 131072f32 / (2048f32 - full_freq as f32) - } - - fn write_byte(&mut self, addr: u16, val: u8) { - match addr { - 0xFF10 => { - println!("WARNING: Sound0 sweep not supported yet"); - self.sweep_register = val - }, - 0xFF11 => { - // println!("Channel1: Sound length set to {} (wave pattern: {})", val & 0x3F, val >> 6); - self.sound_length = val; - }, - 0xFF12 => { - self.volume_envelope = val.into(); - }, - 0xFF13 => self.frequency_low = val, - 0xFF14 => { - self.frequency_high = val; - if val & (1 << 7) > 0 { - println!("CH1: started"); - self.active_volume_envelope = self.volume_envelope.clone(); - self.envelope_volume = self.active_volume_envelope.initial_volume(); - } - - self.current_mode = match val & (1 << 6) { - 0 => ChannelMode::CounterMode, - _ => ChannelMode::ConsecutiveMode, - }; - - // self.dump(); - self.frequency_high &= 0x7; - }, - _ => panic!("Invalid addr for channel1 write: {:04X}", addr), - } - } - - pub fn read_byte(&self, addr: u16) -> u8 { - match addr { - 0xFF10 => self.sweep_register, - 0xFF11 => self.sound_length, - 0xFF12 => self.volume_envelope.0, - // 0xFF13 => self.frequency_low, // R/O - 0xFF14 => self.frequency_high & (1 << 6), // Only one bit readable - _ => panic!("Tried to read invalid register {:04X}", addr) - } - } -} - -// Tone -#[derive(Debug, Default)] -struct Channel2 { - sound_length: u8, - volume_envelope: VolumeEnvelope, - active_volume_envelope: VolumeEnvelope, - frequency_low: u8, - frequency_high: u8, - - // Registeres used for sound generation - current_phase: f32, - // Depending on sound_length - // sound_enabled: bool, - // Current envelope volume - envelope_volume: u8, - current_mode: ChannelMode, -} - -impl OutputChannel for Channel2 { - /* - According to some specification page: - gb = 2048 - (131072 / Hz) - Hz = 131072 / (2048 - gb) - */ - fn sample(&mut self) -> i8 { - // Right now just assume that we have 100% on time - // I also don't know right now how to disable channel1 :( - let freq = self.current_frequency(); - // sample rate 128? Dunno! - self.current_phase += 1f32 / (2048f32 * TICKS_PER_SAMPLE as f32); - let sample = 2.0 * 3.1415 * freq * self.current_phase; - // let volume = (127f32 * self.envelope_volume as f32 / 16f32) as i8; - - if sample.sin() > 0f32 { - 40 - } else { - -40 - } - } - - - fn clock_length_counter(&mut self) { - if self.frequency_high & (1 << 6) > 0 { - // Use sound length - let sound_length = self.sound_length & 0x3F; - if sound_length > 0 { - self.sound_length &= 0xC0; - self.sound_length |= sound_length - 1; - } - } - } - fn clock_volume_envelope(&mut self) -> Option { - if self.active_volume_envelope.nr_of_envelope_sweep() > 0 { - let left = self.active_volume_envelope.nr_of_envelope_sweep(); - self.active_volume_envelope.set_nr_of_envelope_sweep(left - 1); - match self.active_volume_envelope.envelope_direction() { - EnvelopeDirection::Decrease => { - if self.envelope_volume > 0 { - self.envelope_volume -= 1; - println!("CH2: Decreased envelope volume to {} (steps left: {})", self.envelope_volume, left-1); - } - }, - EnvelopeDirection::Increase => { - if self.envelope_volume < 0xF { - self.envelope_volume += 1; - println!("CH2: Increased envelope volume to {}", self.envelope_volume); - } - } - } - Some(self.active_volume_envelope.envelope_direction()) - } else { - match self.current_mode { - ChannelMode::CounterMode => { - self.envelope_volume = 0; - } - ChannelMode::ConsecutiveMode => { - - } - }; - None - } - } -} - -impl Channel2 { - fn current_frequency(&self) -> f32 { - let full_freq = ((self.frequency_high as u16 & 0x7) << 8) | self.frequency_low as u16; - 131072f32 / (2048f32 - full_freq as f32) - } - - fn write_byte(&mut self, addr: u16, val: u8) { - match addr { - 0xFF16 => self.sound_length = val, - 0xFF17 => { - self.volume_envelope = val.into(); - }, - 0xFF18 => self.frequency_low = val, - 0xFF19 => { - self.frequency_high = val; - if self.frequency_high & 0x80 == 0x80 { - println!("CH2: Started"); - self.active_volume_envelope = self.volume_envelope.clone(); - self.envelope_volume = self.active_volume_envelope.initial_volume(); - } - - self.current_mode = match val & (1 << 6) { - 0 => ChannelMode::CounterMode, - _ => ChannelMode::ConsecutiveMode, - }; - self.frequency_high &= 0x7; - }, - _ => panic!("Invalid addr for channel2 write: {:04X}", addr), - } - } - - pub fn read_byte(&self, addr: u16) -> u8 { - match addr { - 0xFF16 => self.sound_length, - 0xFF17 => self.volume_envelope.0, - // 0xFF18 => self.frequency_low, // R/O - 0xFF19 => self.frequency_high & (1 << 6), // Only one bit readable - _ => panic!("Tried to read invalid register {:04X}", addr) - } - } -} - -// Wave output -#[derive(Debug, Default)] -struct Channel3 { - enable: u8, - sound_length: u8, - output_level: u8, - frequency_low: u8, - frequency_high: u8, - - wave_pattern_data: [u8; 16], - - // Registeres used for sound generation - ticks_since_enable: usize, - - current_pattern_offset: usize, -} - -impl OutputChannel for Channel3 { - fn sample(&mut self) -> i8 { - if self.enable > 0 { - self.ticks_since_enable += 1; - let val = self.wave_pattern_data[self.current_pattern_offset/2]; - let samp = if val & 1 == 0 { - val >> 4 - } else { - val & 0x0F - }; - - let freq = self.current_frequency(); - if (TICK_RATE as f32 / self.ticks_since_enable as f32) < (freq as f32) { - self.current_pattern_offset += 1; - self.current_pattern_offset %= 32; - self.ticks_since_enable = 0; - } - - let samp = match self.output_level >> 5 { - 0 => 0, - 1 => samp, - 2 => samp >> 1, - _ => samp >> 2, - }; - - let samp = samp << 4; - - samp as i8 - } else { - 0 - } - } - - fn clock_length_counter(&mut self) { - if self.frequency_high & (1 << 6) > 0 { - // Use sound length - let sound_length = self.sound_length & 0x3F; - if sound_length > 0 { - self.sound_length &= 0xC0; - self.sound_length |= sound_length - 1; - // self.sound_enabled = true; - } else { - // self.sound_enabled = false; - self.enable = 0; - } - } - } - fn clock_volume_envelope(&mut self) -> Option { - None - } -} - - -impl Channel3 { - fn current_frequency(&self) -> f32 { - let full_freq = ((self.frequency_high as u16 & 0x7) << 8) | self.frequency_low as u16; - (2048f32 - full_freq as f32) * 2f32 - } - - fn write_byte(&mut self, addr: u16, val: u8) { - match addr { - 0xFF1A => {self.enable = val; self.ticks_since_enable = 0}, - 0xFF1B => self.sound_length = val, - 0xFF1C => self.output_level = val, - 0xFF1D => self.frequency_low = val, - 0xFF1E => { - self.frequency_high = val; - // TODO: Check for start - println!("Starting sound3?"); - self.frequency_high &= 0x7; - }, - 0xFF30...0xFF3F => { - self.wave_pattern_data[addr as usize - 0xFF30] = val; - } - _ => panic!("Invalid addr for channel3 write: {:04X}", addr), - } - } - - pub fn read_byte(&self, addr: u16) -> u8 { - match addr { - 0xFF1A => self.enable, - 0xFF1B => self.sound_length, - 0xFF1C => self.output_level, - //0xFF1D => self.frequency_low, - 0xFF1E => self.frequency_high & 1 << 6, - 0xFF30...0xFF3F => { - self.wave_pattern_data[addr as usize - 0xFF30] - } - _ => panic!("Tried to read invalid register {:04X}", addr) - } - } -} - -// Noise output -#[derive(Debug, Default)] -struct Channel4 { - sound_length: u8, - volume_envelope: VolumeEnvelope, - polynominal_counter: u8, - counter_mode: u8, -} - -impl Channel4 { - fn write_byte(&mut self, addr: u16, val: u8) { - match addr { - 0xFF20 => self.sound_length = val, - 0xFF21 => self.volume_envelope = val.into(), - 0xFF22 => self.polynominal_counter = val, - 0xFF23 => self.counter_mode = val, - _ => panic!("Invalid addr for channel4 write: {:04X}", addr), - } - } - - pub fn read_byte(&self, addr: u16) -> u8 { - match addr { - 0xFF20 => self.sound_length, - 0xFF21 => self.volume_envelope.0, - 0xFF22 => self.polynominal_counter, - 0xFF23 => self.counter_mode, - _ => panic!("Tried to read invalid register {:04X}", addr) - } - } -} - - -#[derive(Debug, Default)] -pub struct Sound { - enabled: u8, - - channel1: Channel1, - channel2: Channel2, - channel3: Channel3, - channel4: Channel4, - - sound_channel_volume_control: u8, - sound_output_terminal_selector: u8, - - ticks: usize, -} - -impl Sound { - pub fn new() -> Sound { - Sound::default() - } - - pub fn tick(&mut self) { - self.ticks += 1; - // Every 64 Hz volume envelope => all 65536 ticks - // Every 256 Hz length check => all 16384 ticks - - if self.ticks % 65536 == 0 { - self.channel1.clock_volume_envelope(); - self.channel2.clock_volume_envelope(); - } - - if self.ticks % 16384 == 0 { - self.channel1.clock_length_counter(); - self.channel2.clock_length_counter(); - self.channel3.clock_length_counter(); - } - - // How high is our rate? 32768? - if self.ticks % TICKS_PER_SAMPLE == 0 { - // Mix channels. - let mut s01: i16 = 0; - let mut s02: i16 = 0; - - let s01_volume = self.sound_channel_volume_control & 7; - let s02_volume = (self.sound_channel_volume_control >> 4) & 7; - - // Take samples from the channels - let c1_sample = self.channel1.sample(); - let c2_sample = self.channel2.sample(); - // let c3_sample = self.channel3.sample(); - // let c4_sample = self.channel4.sample(); - - //let c1_sample = 0; - //let c2_sample = 0; - let c3_sample = 0; - - // Check S01 - if s01_volume > 0 { - if self.sound_output_terminal_selector & 1 > 0 { - // Output Channel1 - s01 += c1_sample as _; - } - if self.sound_output_terminal_selector & 2 > 0 && false { - // Output Channel2 - s01 += c2_sample as _; - } - if self.sound_output_terminal_selector & 4 > 0 { - // Output Channel3 - s01 += c3_sample as _; - } - if self.sound_output_terminal_selector & 8 > 0 { - // Output Channel4 - } - } - - if s02_volume > 0 { - if self.sound_output_terminal_selector & 0x10 > 0 && false { - // Output Channel1 - s02 += c1_sample as _; - } - if self.sound_output_terminal_selector & 0x20 > 0 { - // Output Channel2 - s02 += c2_sample as _; - } - if self.sound_output_terminal_selector & 0x40 > 0 { - // Output Channel3 - s02 += c3_sample as _; - } - if self.sound_output_terminal_selector & 0x80 > 0 { - // Output Channel4 - } - } - - println!("S:{}:{}", s01 >> 0, s02 >> 0); - } - } - - pub fn write_byte(&mut self, addr: u16, val: u8) { - 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), - 0xFF20...0xFF23 => self.channel4.write_byte(addr, val), - - 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), - - _ => { - panic!("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), - 0xFF20...0xFF23 => self.channel4.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), - } - } -} diff --git a/src/sound/envelope.rs b/src/sound/envelope.rs new file mode 100644 index 0000000..d5d2f5a --- /dev/null +++ b/src/sound/envelope.rs @@ -0,0 +1,111 @@ +// Implements the envelopes +use sound::{AudioComponent, AudioModule}; + +#[derive(Clone, Debug)] +pub enum EnvelopeDirection { + Increase, + Decrease +} + +impl Default for EnvelopeDirection { + fn default() -> Self { + EnvelopeDirection::Decrease + } +} + +/// Represents a volume envelope for the sound channels +#[derive(Default, Debug)] +pub struct VolumeEnvelope{ + /// Accessible via the corresponding memory location, used as template + /// when the sound is enabled + register_value: u8, + + /// Amount of clocks required for one step + clocks_per_step: u8, + + /// Clocks occured since last step + clocks: u8, + + /// Current volume + current_volume: u8, + + /// Envelope direction + direction: EnvelopeDirection, +} + +#[allow(dead_code)] +impl VolumeEnvelope { + /// Returns the intiial volume of an envelope operation + pub fn initial_volume(&self) -> u8 { + self.register_value >> 4 + } + + /// Returns the envelope direction + pub fn envelope_direction(&self) -> EnvelopeDirection { + match self.register_value & (1 << 3) { + 0 => EnvelopeDirection::Decrease, + _ => EnvelopeDirection::Increase, + } + } + + /// Return the number of sweep steps + pub fn envelope_sweep_count(&self) -> u8 { + self.register_value & 7 + } + + pub fn start(&mut self) { + self.current_volume = self.initial_volume(); + self.clocks_per_step = self.envelope_sweep_count(); + self.direction = self.envelope_direction(); + self.clocks = 0; + } + + pub fn set_register(&mut self, register: u8) { + self.register_value = register & !(1 << 6); + if register & (1 << 6) != 0 { + self.start(); + } + } +} + +impl AudioModule for VolumeEnvelope { + fn transform(&self, sample: u8) -> u8 { + // Use a linear volume gain + let volume = 255f32 * self.current_volume as f32 / 15 as f32; + if sample > 0 { + volume as u8 + } else { + 0 + } + } +} + +impl AudioComponent for VolumeEnvelope { + fn clock(&mut self) { + if self.clocks_per_step > 0 { + self.clocks += 1; + if self.clocks == self.clocks_per_step { + self.clocks = 0; + self.current_volume = match self.direction { + EnvelopeDirection::Decrease => { + if self.current_volume > 0 { + self.current_volume - 1 + } else { + self.clocks_per_step = 0; + 0 + } + } + + EnvelopeDirection::Increase => { + if self.current_volume < 15 { + self.current_volume + 1 + } else { + self.clocks_per_step = 0; + 15 + } + } + }; + } + } + } +} diff --git a/src/sound/length.rs b/src/sound/length.rs new file mode 100644 index 0000000..884c9b2 --- /dev/null +++ b/src/sound/length.rs @@ -0,0 +1,67 @@ +// Implement the length counter +use std::fmt::Debug; +use sound::{AudioComponent, AudioModule}; + +#[derive(Default, Debug)] +pub struct LengthCounter where T: Default + Debug { + // Do we need an additional enabled flag? + counter: T, + enabled: bool, +} + +impl LengthCounter where T: Default + Debug { + pub fn set_length(&mut self, value: T) { + self.counter = value; + } + + pub fn set_enabled(&mut self, val: u8) { + self.enabled = val > 0; + } +} + + +impl AudioModule for LengthCounter { + fn transform(&self, sample: u8) -> u8 { + if self.enabled { + if self.counter > 0 { + sample + } else { + 0 + } + } else { + sample + } + } +} + +impl AudioComponent for LengthCounter { + fn clock(&mut self) { + if self.enabled && self.counter > 0 { + self.counter -= 1; + } + } +} + +// TODO: Is the same as above, there is something we should be able to do about +// this +impl AudioModule for LengthCounter { + fn transform(&self, sample: u8) -> u8 { + if self.enabled { + if self.counter > 0 { + sample + } else { + 0 + } + } else { + sample + } + } +} + +impl AudioComponent for LengthCounter { + fn clock(&mut self) { + if self.enabled && self.counter > 0 { + self.counter -= 1; + } + } +} diff --git a/src/sound/mod.rs b/src/sound/mod.rs new file mode 100644 index 0000000..7ffdaa9 --- /dev/null +++ b/src/sound/mod.rs @@ -0,0 +1,381 @@ +extern crate pulse_simple; +mod envelope; +mod square; +mod length; +mod wave; + +use self::pulse_simple::Playback; +use std::sync::{Arc, Mutex}; +use std::thread; + +//use std::f64::consts::PI; + +//const TICK_RATE: usize = 4194304; +const OUTPUT_SAMPLE_RATE: usize = 32768; +//const OUTPUT_SAMPLE_RATE: usize = 1024; +//const TICKS_PER_SAMPLE: usize = TICK_RATE / OUTPUT_SAMPLE_RATE; + +trait AudioModule { + fn transform(&self, sample: T) -> T; +} + +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, +} + +impl Channel1 { + pub fn write_byte(&mut self, addr: u16, val: u8) { + match addr { + 0xFF10 => { + println!("Channel1: Sweep not implemented yet :<"); + } + 0xFF11 => { + self.length_counter.set_length(val & 0x3F); + self.wave_gen.set_duty_cycle(val >> 6); + } + 0xFF12 => { + self.envelope.set_register(val); + } + 0xFF13 => { + // Set lower frequency + self.wave_gen.set_lower_freq(val); + } + 0xFF14 => { + // Set higher + self.wave_gen.set_higher_freq(val); + + 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 { + _ => panic!("Channel1 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(); + // 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, 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); + + 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 { + match addr { + _ => panic!("Channel2 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(); + // 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, Default)] +struct Channel3 { + // Timer -> Wave -> Length Counter -> Volume -> Mixer + wave_gen: wave::WaveGenerator, + length_counter: length::LengthCounter, + // TODO: Volume + + tick_state: u8, +} + +impl Channel3 { + pub fn write_byte(&mut self, addr: u16, val: u8) { + println!("C3 WB: {:04X} = {:02X} ({:08b})", addr, val, val); + match addr { + 0xFF1A => { + self.wave_gen.enabled = (val & (1 << 7)) > 0; + } + 0xFF1B => { + self.length_counter.set_length(val); + } + 0xFF1C => { + // Output volume, TODO. + } + 0xFF1D => { + // Set lower frequency + self.wave_gen.set_lower_freq(val); + } + 0xFF1E => { + // Set higher + self.wave_gen.set_higher_freq(val); + + 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 { + _ => 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(); + // Follow the chain + let input = self.length_counter.transform(input); + // self.envelope.transform(input) + 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>, +} + +impl SoundManager { + pub fn new() -> Self { + let res = SoundManager { + sound_object: Arc::new(Mutex::new(Sound::new())), + }; + + res.launch_thread(); + res + } + + pub fn launch_thread(&self) { + let obj = self.sound_object.clone(); + thread::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; + + loop { + { + let mut c_obj = obj.lock().unwrap(); + + c_obj.channel1.sample_clock(); + c_obj.channel2.sample_clock(); + c_obj.channel3.sample_clock(); + + // let s = c_obj.sample(); + let s1 = c_obj.channel1.sample(); + let s2 = c_obj.channel2.sample(); + let s3 = c_obj.channel3.sample(); + let samps = [[s3 as u8, s3 as u8]]; + playback.write(&samps[..]); + + // Check for 512 Hz timer + if counter >= (OUTPUT_SAMPLE_RATE / 512) { + c_obj.clock(); + counter = 0; + } else { + counter += 1; + } + } + // No sleep needed, it seems like the playback.write is blocking + // thread::sleep(time::Duration::new(0, sleep_duration)); + } + }); + } +} + +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(&self) -> u8 { + // TODO: Mixing + (self.channel1.sample() >> 1) + (self.channel2.sample() >> 1) + (self.channel3.sample() >> 1) + } + + pub fn write_byte(&mut self, addr: u16, val: u8) { + // 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), + + 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), + + _ => { + // panic!("Sound: Write {:02X} to {:04X} unsupported", val, addr); + 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), + } + } +} diff --git a/src/sound/square.rs b/src/sound/square.rs new file mode 100644 index 0000000..51a7949 --- /dev/null +++ b/src/sound/square.rs @@ -0,0 +1,103 @@ +// Square wave generator +use sound::AudioComponent; +use sound::OUTPUT_SAMPLE_RATE; + +#[derive(Debug)] +pub enum DutyCycle { + Duty0, // 12.5% + Duty1, // 25.0% + Duty2, // 50.0% + Duty3, // 75.0% +} + +impl Default for DutyCycle { + fn default() -> Self { + DutyCycle::Duty2 + } +} + +#[derive(Debug, Default)] +pub struct SquareWaveGenerator { + frequency: f32, + time: f32, + duty_cycle: DutyCycle, + + gb_reg_freq: u16, +} + +impl SquareWaveGenerator { + fn update_frequency(&mut self) { + self.frequency = 131072f32 / ((2048 - self.gb_reg_freq) as f32); + } + + pub fn set_lower_freq(&mut self, freq: u8) { + self.gb_reg_freq &= 0xFF00; + self.gb_reg_freq |= freq as u16; + self.update_frequency(); + } + + pub fn set_higher_freq(&mut self, freq: u8) { + self.gb_reg_freq &= 0x00FF; + self.gb_reg_freq |= ((freq & 7) as u16) << 8; + self.update_frequency(); + } + + pub fn reset(&mut self) { + self.time = 0f32; + } + + pub fn sample(&self) -> u8 { + if self.frequency > 0f32 { + let mut temp = self.time * self.frequency; + temp -= temp.trunc(); + + match self.duty_cycle { + DutyCycle::Duty0 => { + if temp < 0.125 { + 255 + } else { + 0 + } + }, + DutyCycle::Duty1 => { + if temp < 0.25 { + 255 + } else { + 0 + } + }, + DutyCycle::Duty2 => { + if temp < 0.5 { + 255 + } else { + 0 + } + }, + DutyCycle::Duty3 => { + if temp < 0.75 { + 255 + } else { + 0 + } + } + } + } else { + 0 + } + } + + pub fn set_duty_cycle(&mut self, bits: u8) { + self.duty_cycle = match bits { + 0 => DutyCycle::Duty0, + 1 => DutyCycle::Duty1, + 2 => DutyCycle::Duty2, + _ => DutyCycle::Duty3, + }; + } +} + +impl AudioComponent for SquareWaveGenerator { + fn clock(&mut self) { + self.time += 1f32 / (OUTPUT_SAMPLE_RATE as f32); + } +} diff --git a/src/sound/wave.rs b/src/sound/wave.rs new file mode 100644 index 0000000..b40821d --- /dev/null +++ b/src/sound/wave.rs @@ -0,0 +1,81 @@ +// Square wave generator +use sound::AudioComponent; +use sound::OUTPUT_SAMPLE_RATE; + +#[derive(Debug, Default)] +struct SamplePair(u8); + +impl SamplePair { + fn first(&self) -> u8 { + self.0 >> 4 + } + + fn second(&self) -> u8 { + self.0 & 0x0F + } +} + +impl From for SamplePair { + fn from(val: u8) -> SamplePair { + SamplePair(val) + } +} + +#[derive(Debug, Default)] +pub struct WaveGenerator { + frequency: f32, + time: f32, + gb_reg_freq: u16, + + pub enabled: bool, + samples: [SamplePair; 16], +} + +impl WaveGenerator { + fn update_frequency(&mut self) { + self.frequency = ((2048 - self.gb_reg_freq) * 2) as _; + self.time = 0f32; + println!("Freq = {}", self.frequency); + } + + pub fn set_lower_freq(&mut self, freq: u8) { + self.gb_reg_freq &= 0xFF00; + self.gb_reg_freq |= freq as u16; + self.update_frequency(); + } + + pub fn set_higher_freq(&mut self, freq: u8) { + self.gb_reg_freq &= 0x00FF; + self.gb_reg_freq |= ((freq & 7) as u16) << 8; + self.update_frequency(); + } + + pub fn reset(&mut self) { + self.time = 0f32; + } + + pub fn sample(&self) -> u8 { + if self.enabled && self.frequency > 0f32 { + let temp = self.time * self.frequency; + let position = temp.trunc() as usize % 32; + + let sample = &self.samples[position / 2]; + match position % 2 { + 0 => sample.first(), + _ => sample.second(), + } + } else { + 0 + } + } + + pub fn set_sample_pair(&mut self, nr: usize, val: u8) { + self.samples[nr] = val.into(); + } +} + +impl AudioComponent for WaveGenerator { + fn clock(&mut self) { + self.time += 1f32 / (OUTPUT_SAMPLE_RATE as f32); + } +}