Sound improvements

This commit is contained in:
Kevin Hamacher 2020-02-14 21:03:36 +01:00
parent 28d792e48d
commit d1b089556e
4 changed files with 175 additions and 98 deletions

View File

@ -61,7 +61,7 @@ impl VolumeEnvelope {
} }
pub fn set_register(&mut self, register: u8) { pub fn set_register(&mut self, register: u8) {
self.register_value = register & !(1 << 6); self.register_value = register; // & !(1 << 6); // TODO: Where does this come from?
if register & (1 << 6) != 0 { if register & (1 << 6) != 0 {
self.start(); self.start();
} }
@ -70,10 +70,8 @@ impl VolumeEnvelope {
impl AudioModule<u8> for VolumeEnvelope { impl AudioModule<u8> for VolumeEnvelope {
fn transform(&self, sample: u8) -> u8 { 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 { if sample > 0 {
volume as u8 self.current_volume
} else { } else {
0 0
} }

View File

@ -57,9 +57,9 @@ impl Channel1 {
0xFF14 => { 0xFF14 => {
self.stored_regs[4] = val; self.stored_regs[4] = val;
// Set higher // Set higher
self.wave_gen.set_higher_freq(val); self.wave_gen.set_higher_freq(val & 0b111);
if val & 1 << 7 != 0 { if val & (1 << 7) != 0 {
self.wave_gen.reset(); self.wave_gen.reset();
self.envelope.start(); self.envelope.start();
} }
@ -138,9 +138,9 @@ impl Channel2 {
} }
0xFF19 => { 0xFF19 => {
// Set higher // Set higher
self.wave_gen.set_higher_freq(val); self.wave_gen.set_higher_freq(val & 0b111);
if val & 1 << 7 != 0 { if val & (1 << 7) != 0 {
self.wave_gen.reset(); self.wave_gen.reset();
self.envelope.start(); self.envelope.start();
} }
@ -152,7 +152,14 @@ impl Channel2 {
} }
pub fn read_byte(&self, addr: u16) -> u8 { pub fn read_byte(&self, addr: u16) -> u8 {
use crate::sound::square::DutyCycle::*;
match addr { match addr {
0xFF16 => match self.wave_gen.duty_cycle {
Duty0 => 0, // 12.5%
Duty1 => 0b0100_0000, // 25.0%
Duty2 => 0b1000_0000, // 50.0%
Duty3 => 0b1100_0000, // 75.0%
},
_ => { _ => {
println!("Channel2 does not support reading ({:04X})", addr); println!("Channel2 does not support reading ({:04X})", addr);
0 0
@ -192,18 +199,56 @@ impl Channel2 {
} }
} }
#[derive(Debug)]
enum Channel3VolumeSetting {
Muted,
Original,
Half,
Quarter,
}
impl From<u8> for Channel3VolumeSetting {
fn from(o: u8) -> Self {
use Channel3VolumeSetting::*;
match o {
0 => Muted,
1 => Original,
2 => Half,
3 => Quarter,
_ => unreachable!("Invalid channel volume setting"),
}
}
}
impl Default for Channel3VolumeSetting {
fn default() -> Self {
Self::Muted
}
}
impl Channel3VolumeSetting {
fn transform(&self, sample: u8) -> u8 {
match self {
Self::Muted => 0,
Self::Original => sample,
Self::Half => sample >> 1,
Self::Quarter => sample >> 2,
}
}
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct Channel3 { struct Channel3 {
// Timer -> Wave -> Length Counter -> Volume -> Mixer // Timer -> Wave -> Length Counter -> Volume -> Mixer
wave_gen: wave::WaveGenerator, wave_gen: wave::WaveGenerator,
length_counter: length::LengthCounter<u8>, length_counter: length::LengthCounter<u8>,
// TODO: Volume volume_ctrl: Channel3VolumeSetting,
tick_state: u8, tick_state: u8,
} }
impl Channel3 { impl Channel3 {
pub fn write_byte(&mut self, addr: u16, val: u8) { pub fn write_byte(&mut self, addr: u16, val: u8) {
// println!("C3 WB: {:04X} = {:02X} ({:08b})", addr, val, val); // OFF flag -> "bit 7 of NR30"
match addr { match addr {
0xFF1A => { 0xFF1A => {
self.wave_gen.enabled = (val & (1 << 7)) > 0; self.wave_gen.enabled = (val & (1 << 7)) > 0;
@ -211,22 +256,20 @@ impl Channel3 {
0xFF1B => { 0xFF1B => {
self.length_counter.set_length(val); self.length_counter.set_length(val);
} }
0xFF1C => { 0xFF1C => self.volume_ctrl = ((val >> 5) & 0b11).into(),
// Output volume, TODO.
}
0xFF1D => { 0xFF1D => {
// Set lower frequency // Set lower frequency
self.wave_gen.set_lower_freq(val); self.wave_gen.set_lower_freq(val);
} }
0xFF1E => { 0xFF1E => {
// Set higher // Set higher
self.wave_gen.set_higher_freq(val); self.wave_gen.set_higher_freq(val & 0b111);
if val & 1 << 7 != 0 { if val & (1 << 7) != 0 {
self.wave_gen.reset(); self.wave_gen.reset();
} }
self.length_counter.set_enabled(val & 1 << 6); self.length_counter.set_enabled(val & (1 << 6));
} }
0xFF30..=0xFF3F => { 0xFF30..=0xFF3F => {
self.wave_gen.set_sample_pair((addr - 0xFF30) as usize, val); self.wave_gen.set_sample_pair((addr - 0xFF30) as usize, val);
@ -237,6 +280,7 @@ impl Channel3 {
pub fn read_byte(&self, addr: u16) -> u8 { pub fn read_byte(&self, addr: u16) -> u8 {
match addr { match addr {
0xFF30..=0xFF3F => self.wave_gen.get_sample_pair((addr - 0xFF30) as usize),
_ => panic!("Channel3 does not support reading ({:04X})", addr), _ => panic!("Channel3 does not support reading ({:04X})", addr),
} }
} }
@ -247,9 +291,9 @@ impl Channel3 {
pub fn sample(&self) -> u8 { pub fn sample(&self) -> u8 {
let input = self.wave_gen.sample(); let input = self.wave_gen.sample();
// Follow the chain // TODO(?): Follow the chain
// let input = self.length_counter.transform(input); // let input = self.length_counter.transform(input);
input self.volume_ctrl.transform(input)
} }
pub fn clock(&mut self) { pub fn clock(&mut self) {
@ -286,56 +330,72 @@ pub struct Sound {
pub struct SoundManager { pub struct SoundManager {
pub sound_object: Arc<Mutex<Sound>>, pub sound_object: Arc<Mutex<Sound>>,
handle: Option<std::thread::JoinHandle<()>>,
do_exit: Arc<Mutex<bool>>,
}
impl Drop for SoundManager {
fn drop(&mut self) {
*self.do_exit.lock().unwrap() = true;
self.handle.take().and_then(|h| h.join().ok());
}
} }
impl SoundManager { impl SoundManager {
pub fn new() -> Self { pub fn new() -> Self {
let res = SoundManager { let mut res = SoundManager {
sound_object: Arc::new(Mutex::new(Sound::new())), sound_object: Arc::new(Mutex::new(Sound::new())),
handle: None,
do_exit: Arc::new(Mutex::new(false)),
}; };
res.launch_thread(); res.launch_thread();
res res
} }
pub fn launch_thread(&self) { fn launch_thread(&mut self) {
let obj = self.sound_object.clone(); let obj = self.sound_object.clone();
if false { if false {
return; return;
} }
thread::Builder::new()
.name("Audio".into())
.spawn(move || {
// PulseAudio playback object
let playback = Playback::new("GBC", "Output", None, (OUTPUT_SAMPLE_RATE) as _);
// Counter, used for calling the 512 Hz timer let do_exit = self.do_exit.clone();
let mut counter = 0; self.handle = Some(
thread::Builder::new()
.name("Audio".into())
.spawn(move || {
// PulseAudio playback object
let playback = Playback::new("GBC", "Output", None, (OUTPUT_SAMPLE_RATE) as _);
loop { // Counter, used for calling the 512 Hz timer
let (s1, s2) = { let mut counter = 0;
let mut c_obj = obj.lock().unwrap();
// Check for 512 Hz timer while !*do_exit.lock().unwrap() {
if counter >= (OUTPUT_SAMPLE_RATE / 512) { let (s1, s2) = {
c_obj.clock(); let mut c_obj = obj.lock().unwrap();
counter = 0;
} else {
counter += 1;
}
// Sample clock // Check for 512 Hz timer
c_obj.sample_clock(); if counter >= (OUTPUT_SAMPLE_RATE / 512) {
c_obj.clock();
counter = 0;
} else {
counter += 1;
}
// Get sample // Sample clock
c_obj.sample() c_obj.sample_clock();
};
let samps = [[s1, s2]]; // Get sample
// No sleep needed, it seems like the playback.write is blocking c_obj.sample()
playback.write(&samps[..]); };
} let samps = [[s1, s2]];
}) //let samps = [[0f32, 0f32]];
.unwrap(); // No sleep needed, it seems like the playback.write is blocking
playback.write(&samps[..]);
}
})
.unwrap(),
);
} }
} }
@ -359,72 +419,82 @@ impl Sound {
pub fn sample(&self) -> (u8, u8) { pub fn sample(&self) -> (u8, u8) {
// Some basic mixing // Some basic mixing
// Output value // Output value
let mut s = [0u16; 2]; let mut s = (0, 0);
let s01_volume = self.sound_channel_volume_control & 7; let s01_volume = self.sound_channel_volume_control & 7;
let s02_volume = (self.sound_channel_volume_control >> 4) & 7; let s02_volume = (self.sound_channel_volume_control >> 4) & 7;
/*
if self.sound_channel_volume_control & 0b1000 != 0 {
VIN!
}
if self.sound_channel_volume_control & 0b1000_0000 != 0 {
VIN!
}
*/
let c1_sample = self.channel1.sample(); let c1_sample = self.channel1.sample();
let c2_sample = self.channel2.sample(); let c2_sample = self.channel2.sample();
let c3_sample = self.channel3.sample(); let c3_sample = self.channel3.sample();
let c4_sample = 0; let c4_sample = 0;
if self.enabled != 0 { if self.enabled != 0 {
if s01_volume > 0 { if self.sound_output_terminal_selector & 1 > 0 {
if self.sound_output_terminal_selector & 1 > 0 { // Output Channel1
// Output Channel1 s.0 += c1_sample;
s[0] += c1_sample as u16; }
} if self.sound_output_terminal_selector & 2 > 0 {
if self.sound_output_terminal_selector & 2 > 0 && false { // Output Channel2
// Output Channel2 s.0 += c2_sample;
s[0] += c2_sample as u16; }
} if self.sound_output_terminal_selector & 4 > 0 {
if self.sound_output_terminal_selector & 4 > 0 { // Output Channel3
// Output Channel3 s.0 += c3_sample;
s[0] += c3_sample as u16; }
} if self.sound_output_terminal_selector & 8 > 0 {
if self.sound_output_terminal_selector & 8 > 0 { // Output Channel4
// Output Channel4 s.0 += c4_sample;
s[0] += c4_sample as u16;
}
} }
if s02_volume > 0 { if self.sound_output_terminal_selector & 0x10 > 0 {
if self.sound_output_terminal_selector & 0x10 > 0 && false { // Output Channel1
// Output Channel1 s.1 += c1_sample;
s[1] += c1_sample as u16; }
} if self.sound_output_terminal_selector & 0x20 > 0 {
if self.sound_output_terminal_selector & 0x20 > 0 { // Output Channel2
// Output Channel2 s.1 += c2_sample;
s[1] += c2_sample as u16; }
} if self.sound_output_terminal_selector & 0x40 > 0 {
if self.sound_output_terminal_selector & 0x40 > 0 { // Output Channel3
// Output Channel3 s.1 += c3_sample;
s[1] += c3_sample as u16; }
} if self.sound_output_terminal_selector & 0x80 > 0 {
if self.sound_output_terminal_selector & 0x80 > 0 { // Output Channel4
// Output Channel4 s.1 += c4_sample;
s[1] += c4_sample as u16;
}
} }
} }
// (self.channel1.sample() >> 1) + (self.channel2.sample() >> 1) + (self.channel3.sample() >> 1)
((s[0] >> 2) as _, (s[1] >> 2) as _) s.0 *= s01_volume + 1;
s.1 *= s02_volume + 1;
s
} }
pub fn write_byte(&mut self, addr: u16, val: u8) { pub fn write_byte(&mut self, addr: u16, val: u8) {
// Bit 7 of NR52 = 0 -> everything reset
// If all sound off no mode register can be set
// println!("Snd: {:04X} = {:02X} ({:08b})", addr, val, val); // println!("Snd: {:04X} = {:02X} ({:08b})", addr, val, val);
match addr { match addr {
0xFF10..=0xFF14 => self.channel1.write_byte(addr, val), 0xFF10..=0xFF14 => self.channel1.write_byte(addr, val),
0xFF16..=0xFF19 => self.channel2.write_byte(addr, val), 0xFF16..=0xFF19 => self.channel2.write_byte(addr, val),
0xFF1A..=0xFF1E => self.channel3.write_byte(addr, val), 0xFF1A..=0xFF1E => self.channel3.write_byte(addr, val),
// TODO: Channel 4
0xFF24 => self.sound_channel_volume_control = val, 0xFF24 => self.sound_channel_volume_control = val,
0xFF25 => self.sound_output_terminal_selector = val, 0xFF25 => self.sound_output_terminal_selector = val,
0xFF26 => self.enabled = val, 0xFF26 => self.enabled = val,
0xFF30..=0xFF3F => self.channel3.write_byte(addr, 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); println!("Sound: Write {:02X} to {:04X} unsupported", val, addr);
} }
} }

View File

@ -20,7 +20,7 @@ impl Default for DutyCycle {
pub struct SquareWaveGenerator { pub struct SquareWaveGenerator {
frequency: f32, frequency: f32,
time: f32, time: f32,
duty_cycle: DutyCycle, pub duty_cycle: DutyCycle,
gb_reg_freq: u16, gb_reg_freq: u16,
@ -66,15 +66,14 @@ impl SquareWaveGenerator {
} }
pub fn sweep_clock(&mut self) { pub fn sweep_clock(&mut self) {
// self.sweep_freq_shadow = self.frequency; // This should be sampled at 128Hz
if self.sweep_change_period > 0 { if self.sweep_change_period > 0
if self.sweep_change > 0 { && self.sweep_clock >= self.sweep_change_period
if self.sweep_clock >= self.sweep_change_period { && self.sweep_change > 0
self.sweep_clock = 0; {
let change = self.frequency / (2f32.powi(self.sweep_change as _)); self.sweep_clock = 0;
self.frequency += if self.sweep_dec { change } else { -change }; let change = self.frequency / (2f32.powi(self.sweep_change as _));
} self.frequency += if self.sweep_dec { -change } else { change };
}
} }
} }

View File

@ -2,7 +2,7 @@
use crate::sound::AudioComponent; use crate::sound::AudioComponent;
use crate::sound::OUTPUT_SAMPLE_RATE; use crate::sound::OUTPUT_SAMPLE_RATE;
#[derive(Debug, Default)] #[derive(Debug, Default, Copy, Clone)]
struct SamplePair(u8); struct SamplePair(u8);
impl SamplePair { impl SamplePair {
@ -21,6 +21,12 @@ impl From<u8> for SamplePair {
} }
} }
impl Into<u8> for SamplePair {
fn into(self: Self) -> u8 {
self.0
}
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct WaveGenerator { pub struct WaveGenerator {
frequency: f32, frequency: f32,
@ -72,6 +78,10 @@ impl WaveGenerator {
pub fn set_sample_pair(&mut self, nr: usize, val: u8) { pub fn set_sample_pair(&mut self, nr: usize, val: u8) {
self.samples[nr] = val.into(); self.samples[nr] = val.into();
} }
pub fn get_sample_pair(&self, nr: usize) -> u8 {
self.samples[nr].into()
}
} }
impl AudioComponent for WaveGenerator { impl AudioComponent for WaveGenerator {