Channel 1 and 2 almost work correctly; Channel 3 is still annoying
This commit is contained in:
parent
9ddd8ac21c
commit
fc15b398ba
@ -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"
|
||||
|
||||
@ -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);
|
||||
|
||||
617
src/sound.rs
617
src/sound.rs
@ -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
111
src/sound/envelope.rs
Normal 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
67
src/sound/length.rs
Normal 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
381
src/sound/mod.rs
Normal 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
103
src/sound/square.rs
Normal 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
81
src/sound/wave.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user