Sound improvements
This commit is contained in:
parent
28d792e48d
commit
d1b089556e
@ -61,7 +61,7 @@ impl VolumeEnvelope {
|
||||
}
|
||||
|
||||
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 {
|
||||
self.start();
|
||||
}
|
||||
@ -70,10 +70,8 @@ impl VolumeEnvelope {
|
||||
|
||||
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
|
||||
self.current_volume
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
236
src/sound/mod.rs
236
src/sound/mod.rs
@ -57,9 +57,9 @@ impl Channel1 {
|
||||
0xFF14 => {
|
||||
self.stored_regs[4] = val;
|
||||
// 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.envelope.start();
|
||||
}
|
||||
@ -138,9 +138,9 @@ impl Channel2 {
|
||||
}
|
||||
0xFF19 => {
|
||||
// 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.envelope.start();
|
||||
}
|
||||
@ -152,7 +152,14 @@ impl Channel2 {
|
||||
}
|
||||
|
||||
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||
use crate::sound::square::DutyCycle::*;
|
||||
match addr {
|
||||
0xFF16 => match self.wave_gen.duty_cycle {
|
||||
Duty0 => 0, // 12.5%
|
||||
Duty1 => 0b0100_0000, // 25.0%
|
||||
Duty2 => 0b1000_0000, // 50.0%
|
||||
Duty3 => 0b1100_0000, // 75.0%
|
||||
},
|
||||
_ => {
|
||||
println!("Channel2 does not support reading ({:04X})", addr);
|
||||
0
|
||||
@ -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)]
|
||||
struct Channel3 {
|
||||
// Timer -> Wave -> Length Counter -> Volume -> Mixer
|
||||
wave_gen: wave::WaveGenerator,
|
||||
length_counter: length::LengthCounter<u8>,
|
||||
// TODO: Volume
|
||||
volume_ctrl: Channel3VolumeSetting,
|
||||
tick_state: u8,
|
||||
}
|
||||
|
||||
impl Channel3 {
|
||||
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 {
|
||||
0xFF1A => {
|
||||
self.wave_gen.enabled = (val & (1 << 7)) > 0;
|
||||
@ -211,22 +256,20 @@ impl Channel3 {
|
||||
0xFF1B => {
|
||||
self.length_counter.set_length(val);
|
||||
}
|
||||
0xFF1C => {
|
||||
// Output volume, TODO.
|
||||
}
|
||||
0xFF1C => self.volume_ctrl = ((val >> 5) & 0b11).into(),
|
||||
0xFF1D => {
|
||||
// Set lower frequency
|
||||
self.wave_gen.set_lower_freq(val);
|
||||
}
|
||||
0xFF1E => {
|
||||
// Set higher
|
||||
self.wave_gen.set_higher_freq(val);
|
||||
self.wave_gen.set_higher_freq(val & 0b111);
|
||||
|
||||
if val & 1 << 7 != 0 {
|
||||
if val & (1 << 7) != 0 {
|
||||
self.wave_gen.reset();
|
||||
}
|
||||
|
||||
self.length_counter.set_enabled(val & 1 << 6);
|
||||
self.length_counter.set_enabled(val & (1 << 6));
|
||||
}
|
||||
0xFF30..=0xFF3F => {
|
||||
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 {
|
||||
match addr {
|
||||
0xFF30..=0xFF3F => self.wave_gen.get_sample_pair((addr - 0xFF30) as usize),
|
||||
_ => panic!("Channel3 does not support reading ({:04X})", addr),
|
||||
}
|
||||
}
|
||||
@ -247,9 +291,9 @@ impl Channel3 {
|
||||
|
||||
pub fn sample(&self) -> u8 {
|
||||
let input = self.wave_gen.sample();
|
||||
// Follow the chain
|
||||
// TODO(?): Follow the chain
|
||||
// let input = self.length_counter.transform(input);
|
||||
input
|
||||
self.volume_ctrl.transform(input)
|
||||
}
|
||||
|
||||
pub fn clock(&mut self) {
|
||||
@ -286,56 +330,72 @@ pub struct Sound {
|
||||
|
||||
pub struct SoundManager {
|
||||
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 {
|
||||
pub fn new() -> Self {
|
||||
let res = SoundManager {
|
||||
let mut res = SoundManager {
|
||||
sound_object: Arc::new(Mutex::new(Sound::new())),
|
||||
handle: None,
|
||||
do_exit: Arc::new(Mutex::new(false)),
|
||||
};
|
||||
|
||||
res.launch_thread();
|
||||
res
|
||||
}
|
||||
|
||||
pub fn launch_thread(&self) {
|
||||
fn launch_thread(&mut self) {
|
||||
let obj = self.sound_object.clone();
|
||||
if false {
|
||||
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 mut counter = 0;
|
||||
let do_exit = self.do_exit.clone();
|
||||
self.handle = Some(
|
||||
thread::Builder::new()
|
||||
.name("Audio".into())
|
||||
.spawn(move || {
|
||||
// PulseAudio playback object
|
||||
let playback = Playback::new("GBC", "Output", None, (OUTPUT_SAMPLE_RATE) as _);
|
||||
|
||||
loop {
|
||||
let (s1, s2) = {
|
||||
let mut c_obj = obj.lock().unwrap();
|
||||
// Counter, used for calling the 512 Hz timer
|
||||
let mut counter = 0;
|
||||
|
||||
// Check for 512 Hz timer
|
||||
if counter >= (OUTPUT_SAMPLE_RATE / 512) {
|
||||
c_obj.clock();
|
||||
counter = 0;
|
||||
} else {
|
||||
counter += 1;
|
||||
}
|
||||
while !*do_exit.lock().unwrap() {
|
||||
let (s1, s2) = {
|
||||
let mut c_obj = obj.lock().unwrap();
|
||||
|
||||
// Sample clock
|
||||
c_obj.sample_clock();
|
||||
// Check for 512 Hz timer
|
||||
if counter >= (OUTPUT_SAMPLE_RATE / 512) {
|
||||
c_obj.clock();
|
||||
counter = 0;
|
||||
} else {
|
||||
counter += 1;
|
||||
}
|
||||
|
||||
// Get sample
|
||||
c_obj.sample()
|
||||
};
|
||||
let samps = [[s1, s2]];
|
||||
// No sleep needed, it seems like the playback.write is blocking
|
||||
playback.write(&samps[..]);
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
// Sample clock
|
||||
c_obj.sample_clock();
|
||||
|
||||
// Get sample
|
||||
c_obj.sample()
|
||||
};
|
||||
let samps = [[s1, s2]];
|
||||
//let samps = [[0f32, 0f32]];
|
||||
// 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) {
|
||||
// Some basic mixing
|
||||
// Output value
|
||||
let mut s = [0u16; 2];
|
||||
let mut s = (0, 0);
|
||||
let s01_volume = self.sound_channel_volume_control & 7;
|
||||
let s02_volume = (self.sound_channel_volume_control >> 4) & 7;
|
||||
|
||||
/*
|
||||
if self.sound_channel_volume_control & 0b1000 != 0 {
|
||||
|
||||
VIN!
|
||||
}
|
||||
|
||||
if self.sound_channel_volume_control & 0b1000_0000 != 0 {
|
||||
VIN!
|
||||
}
|
||||
*/
|
||||
|
||||
let c1_sample = self.channel1.sample();
|
||||
let c2_sample = self.channel2.sample();
|
||||
let c3_sample = self.channel3.sample();
|
||||
let c4_sample = 0;
|
||||
|
||||
if self.enabled != 0 {
|
||||
if s01_volume > 0 {
|
||||
if self.sound_output_terminal_selector & 1 > 0 {
|
||||
// Output Channel1
|
||||
s[0] += c1_sample as u16;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 2 > 0 && false {
|
||||
// Output Channel2
|
||||
s[0] += c2_sample as u16;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 4 > 0 {
|
||||
// Output Channel3
|
||||
s[0] += c3_sample as u16;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 8 > 0 {
|
||||
// Output Channel4
|
||||
s[0] += c4_sample as u16;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 1 > 0 {
|
||||
// Output Channel1
|
||||
s.0 += c1_sample;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 2 > 0 {
|
||||
// Output Channel2
|
||||
s.0 += c2_sample;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 4 > 0 {
|
||||
// Output Channel3
|
||||
s.0 += c3_sample;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 8 > 0 {
|
||||
// Output Channel4
|
||||
s.0 += c4_sample;
|
||||
}
|
||||
|
||||
if s02_volume > 0 {
|
||||
if self.sound_output_terminal_selector & 0x10 > 0 && false {
|
||||
// Output Channel1
|
||||
s[1] += c1_sample as u16;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 0x20 > 0 {
|
||||
// Output Channel2
|
||||
s[1] += c2_sample as u16;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 0x40 > 0 {
|
||||
// Output Channel3
|
||||
s[1] += c3_sample as u16;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 0x80 > 0 {
|
||||
// Output Channel4
|
||||
s[1] += c4_sample as u16;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 0x10 > 0 {
|
||||
// Output Channel1
|
||||
s.1 += c1_sample;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 0x20 > 0 {
|
||||
// Output Channel2
|
||||
s.1 += c2_sample;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 0x40 > 0 {
|
||||
// Output Channel3
|
||||
s.1 += c3_sample;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 0x80 > 0 {
|
||||
// Output Channel4
|
||||
s.1 += c4_sample;
|
||||
}
|
||||
}
|
||||
// (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) {
|
||||
// Bit 7 of NR52 = 0 -> everything reset
|
||||
// If all sound off no mode register can be set
|
||||
// println!("Snd: {:04X} = {:02X} ({:08b})", addr, val, val);
|
||||
match addr {
|
||||
0xFF10..=0xFF14 => self.channel1.write_byte(addr, val),
|
||||
0xFF16..=0xFF19 => self.channel2.write_byte(addr, val),
|
||||
0xFF1A..=0xFF1E => self.channel3.write_byte(addr, val),
|
||||
|
||||
// TODO: Channel 4
|
||||
0xFF24 => self.sound_channel_volume_control = val,
|
||||
0xFF25 => self.sound_output_terminal_selector = val,
|
||||
0xFF26 => self.enabled = val,
|
||||
0xFF30..=0xFF3F => self.channel3.write_byte(addr, val),
|
||||
|
||||
_ => {
|
||||
// panic!("Sound: Write {:02X} to {:04X} unsupported", val, addr);
|
||||
println!("Sound: Write {:02X} to {:04X} unsupported", val, addr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ impl Default for DutyCycle {
|
||||
pub struct SquareWaveGenerator {
|
||||
frequency: f32,
|
||||
time: f32,
|
||||
duty_cycle: DutyCycle,
|
||||
pub duty_cycle: DutyCycle,
|
||||
|
||||
gb_reg_freq: u16,
|
||||
|
||||
@ -66,15 +66,14 @@ impl SquareWaveGenerator {
|
||||
}
|
||||
|
||||
pub fn sweep_clock(&mut self) {
|
||||
// self.sweep_freq_shadow = self.frequency;
|
||||
if self.sweep_change_period > 0 {
|
||||
if self.sweep_change > 0 {
|
||||
if self.sweep_clock >= self.sweep_change_period {
|
||||
self.sweep_clock = 0;
|
||||
let change = self.frequency / (2f32.powi(self.sweep_change as _));
|
||||
self.frequency += if self.sweep_dec { change } else { -change };
|
||||
}
|
||||
}
|
||||
// This should be sampled at 128Hz
|
||||
if self.sweep_change_period > 0
|
||||
&& 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.frequency += if self.sweep_dec { -change } else { change };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
use crate::sound::AudioComponent;
|
||||
use crate::sound::OUTPUT_SAMPLE_RATE;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
struct SamplePair(u8);
|
||||
|
||||
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)]
|
||||
pub struct WaveGenerator {
|
||||
frequency: f32,
|
||||
@ -72,6 +78,10 @@ impl WaveGenerator {
|
||||
pub fn set_sample_pair(&mut self, nr: usize, val: u8) {
|
||||
self.samples[nr] = val.into();
|
||||
}
|
||||
|
||||
pub fn get_sample_pair(&self, nr: usize) -> u8 {
|
||||
self.samples[nr].into()
|
||||
}
|
||||
}
|
||||
|
||||
impl AudioComponent for WaveGenerator {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user