Channel 1 and 2 almost work correctly; Channel 3 is still annoying

This commit is contained in:
Kevin Hamacher 2017-01-19 00:00:31 +01:00
parent 9ddd8ac21c
commit fc15b398ba
8 changed files with 751 additions and 627 deletions

View File

@ -5,4 +5,5 @@ authors = ["Kevin Hamacher <kevin.hamacher@rub.de>"]
[dependencies]
sdl2 = "0.19.0"
libc = "*"
libc = "0.2.14"
pulse-simple = "1.0.0"

View File

@ -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);

View File

@ -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<u8> for VolumeEnvelope {
fn from(val: u8) -> VolumeEnvelope {
VolumeEnvelope(val)
}
}
impl Into<u8> 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<EnvelopeDirection>; // 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<EnvelopeDirection> {
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<EnvelopeDirection> {
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<EnvelopeDirection> {
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),
}
}
}

111
src/sound/envelope.rs Normal file
View File

@ -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<u8> 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
}
}
};
}
}
}
}

67
src/sound/length.rs Normal file
View File

@ -0,0 +1,67 @@
// Implement the length counter
use std::fmt::Debug;
use sound::{AudioComponent, AudioModule};
#[derive(Default, Debug)]
pub struct LengthCounter<T> where T: Default + Debug {
// Do we need an additional enabled flag?
counter: T,
enabled: bool,
}
impl<T> LengthCounter<T> 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<u8> for LengthCounter<u16> {
fn transform(&self, sample: u8) -> u8 {
if self.enabled {
if self.counter > 0 {
sample
} else {
0
}
} else {
sample
}
}
}
impl AudioComponent for LengthCounter<u16> {
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<u8> for LengthCounter<u8> {
fn transform(&self, sample: u8) -> u8 {
if self.enabled {
if self.counter > 0 {
sample
} else {
0
}
} else {
sample
}
}
}
impl AudioComponent for LengthCounter<u8> {
fn clock(&mut self) {
if self.enabled && self.counter > 0 {
self.counter -= 1;
}
}
}

381
src/sound/mod.rs Normal file
View File

@ -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<T> {
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<u8>,
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<u8>,
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<u8>,
// 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<Mutex<Sound>>,
}
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),
}
}
}

103
src/sound/square.rs Normal file
View File

@ -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);
}
}

81
src/sound/wave.rs Normal file
View File

@ -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<u8> 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);
}
}