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) {
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
236
src/sound/mod.rs
236
src/sound/mod.rs
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 };
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user