From 79ed746d6ccf0d18d2a410e023ede84c422d9edc Mon Sep 17 00:00:00 2001 From: Matthew Berry Date: Sat, 30 Jan 2021 00:26:02 -0800 Subject: [PATCH] proper DMA/PSG balance, use soundbias, remove offset for silence --- src/crab/apu.cr | 20 ++++++++------------ src/crab/apu/channel1.cr | 10 +++++----- src/crab/apu/channel2.cr | 10 +++++----- src/crab/apu/channel3.cr | 10 +++++----- src/crab/apu/channel4.cr | 4 ++-- src/crab/apu/dma_channels.cr | 2 +- 6 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/crab/apu.cr b/src/crab/apu.cr index 4064d28..7aa6c96 100644 --- a/src/crab/apu.cr +++ b/src/crab/apu.cr @@ -20,7 +20,7 @@ class APU @soundcnt_l = Reg::SOUNDCNT_L.new 0 getter soundcnt_h = Reg::SOUNDCNT_H.new 0 @sound_enabled : Bool = false - @soundbias = Reg::SOUNDBIAS.new 0 + @soundbias = Reg::SOUNDBIAS.new 0x3FE @buffer = Slice(Int16).new BUFFER_SIZE @buffer_pos = 0 @@ -101,29 +101,25 @@ class APU def get_sample : Nil abort "Prohibited sound 1-4 volume #{@soundcnt_h.sound_volume}" if @soundcnt_h.sound_volume >= 3 - # Puts PSGs on scale of 0...0x40 + # Gets PSGs on scale of -0x80..0x80 each psg_sound = ((@channel1.get_amplitude * @soundcnt_l.channel_1_left) + (@channel2.get_amplitude * @soundcnt_l.channel_2_left) + (@channel3.get_amplitude * @soundcnt_l.channel_3_left) + - (@channel4.get_amplitude * @soundcnt_l.channel_4_left))# * 2 - 0x80 - # Puts PSGs on scale of 0...0x200 - psg_left = (psg_sound * @soundcnt_l.left_volume) >> (2 - @soundcnt_h.sound_volume) - psg_right = (psg_sound * @soundcnt_l.right_volume) >> (2 - @soundcnt_h.sound_volume) + (@channel4.get_amplitude * @soundcnt_l.channel_4_left)) + # Keep PSGs on scale of -0x200...0x200 (shift by `5 - vol` to account for `*8` from left/right vol) + psg_left = (psg_sound * @soundcnt_l.left_volume) >> (5 - @soundcnt_h.sound_volume) + psg_right = (psg_sound * @soundcnt_l.right_volume) >> (5 - @soundcnt_h.sound_volume) # Gets DMAs on scale of -0x100...0x100 dma_a, dma_b = @dma_channels.get_amplitude - dma_a <<= 1 - dma_b <<= 1 # Puts DMAs on scale of -0x200...0x200 dma_a <<= @soundcnt_h.dma_sound_a_volume dma_b <<= @soundcnt_h.dma_sound_b_volume dma_left = dma_a * @soundcnt_h.dma_sound_a_left + dma_b * @soundcnt_h.dma_sound_b_left dma_right = dma_a * @soundcnt_h.dma_sound_a_right + dma_b * @soundcnt_h.dma_sound_b_right - bias = 0x200 - - total_left = (psg_left + dma_left + bias).clamp(0_i16..0x3FF_i16) - total_right = (psg_right + dma_right + bias).clamp(0_i16..0x3FF_i16) + total_left = (psg_left + dma_left + @soundbias.bias_level).clamp(0_i16..0x3FF_i16) - @soundbias.bias_level + total_right = (psg_right + dma_right + @soundbias.bias_level).clamp(0_i16..0x3FF_i16) - @soundbias.bias_level @buffer[@buffer_pos] = total_left * 32 @buffer[@buffer_pos + 1] = total_right * 32 diff --git a/src/crab/apu/channel1.cr b/src/crab/apu/channel1.cr index eecb34f..93d24bc 100644 --- a/src/crab/apu/channel1.cr +++ b/src/crab/apu/channel1.cr @@ -1,9 +1,9 @@ class Channel1 < VolumeEnvelopeChannel WAVE_DUTY = [ - [0, 0, 0, 0, 0, 0, 0, 1], # 12.5% - [1, 0, 0, 0, 0, 0, 0, 1], # 25% - [1, 0, 0, 0, 0, 1, 1, 1], # 50% - [0, 1, 1, 1, 1, 1, 1, 0], # 75% + [-8, -8, -8, -8, -8, -8, -8, +8], # 12.5% + [+8, -8, -8, -8, -8, -8, -8, +8], # 25% + [+8, -8, -8, -8, -8, +8, +8, +8], # 50% + [-8, +8, +8, +8, +8, +8, +8, -8], # 75% ] RANGE = 0x60..0x67 @@ -57,7 +57,7 @@ class Channel1 < VolumeEnvelopeChannel end end - # Outputs a value 0..0xF + # Outputs a value -0x80..0x80 def get_amplitude : Int16 if @enabled && @dac_enabled WAVE_DUTY[@duty][@wave_duty_position].to_i16 * @current_volume diff --git a/src/crab/apu/channel2.cr b/src/crab/apu/channel2.cr index 867b4cf..8ebcd07 100644 --- a/src/crab/apu/channel2.cr +++ b/src/crab/apu/channel2.cr @@ -1,9 +1,9 @@ class Channel2 < VolumeEnvelopeChannel WAVE_DUTY = [ - [0, 0, 0, 0, 0, 0, 0, 1], # 12.5% - [1, 0, 0, 0, 0, 0, 0, 1], # 25% - [1, 0, 0, 0, 0, 1, 1, 1], # 50% - [0, 1, 1, 1, 1, 1, 1, 0], # 75% + [-8, -8, -8, -8, -8, -8, -8, +8], # 12.5% + [+8, -8, -8, -8, -8, -8, -8, +8], # 25% + [+8, -8, -8, -8, -8, +8, +8, +8], # 50% + [-8, +8, +8, +8, +8, +8, +8, -8], # 75% ] RANGE = 0x68..0x6F @@ -33,7 +33,7 @@ class Channel2 < VolumeEnvelopeChannel @gba.scheduler.schedule frequency_timer, ->step, Scheduler::EventType::APUChannel2 end - # Outputs a value 0..0xF + # Outputs a value -0x80..0x80 def get_amplitude : Int16 if @enabled && @dac_enabled WAVE_DUTY[@duty][@wave_duty_position].to_i16 * @current_volume diff --git a/src/crab/apu/channel3.cr b/src/crab/apu/channel3.cr index 83b5b3d..f87ee46 100644 --- a/src/crab/apu/channel3.cr +++ b/src/crab/apu/channel3.cr @@ -8,7 +8,7 @@ class Channel3 < SoundChannel @wave_ram = Array(Bytes).new 2, Bytes.new(WAVE_RAM_RANGE.size) { |idx| idx & 1 == 0 ? 0x00_u8 : 0xFF_u8 } @wave_ram_position : UInt8 = 0 - @wave_ram_sample_buffer : Int16 = 0x00 + @wave_ram_sample_buffer : UInt8 = 0x00 # NR30 @wave_ram_dimension : Bool = false @@ -27,7 +27,8 @@ class Channel3 < SoundChannel def step_wave_generation : Nil @wave_ram_position = (@wave_ram_position + 1) % (WAVE_RAM_RANGE.size * 2) @wave_ram_bank ^= 1 if @wave_ram_position == 0 && @wave_ram_dimension - @wave_ram_sample_buffer = @wave_ram[@wave_ram_bank][@wave_ram_position // 2].to_i16 + full_sample = @wave_ram[@wave_ram_bank][@wave_ram_position // 2] + @wave_ram_sample_buffer = (full_sample >> (@wave_ram_position & 1 == 0 ? 4 : 0)) & 0xF end def frequency_timer : UInt32 @@ -38,11 +39,10 @@ class Channel3 < SoundChannel @gba.scheduler.schedule frequency_timer, ->step, Scheduler::EventType::APUChannel3 end - # Outputs a value 0..0xF + # Outputs a value -0x80..0x80 def get_amplitude : Int16 if @enabled && @dac_enabled - volume_shift = (-1 + @volume_code) % 4 - (((@wave_ram_sample_buffer >> (@wave_ram_position & 1 == 0 ? 4 : 0)) & 0x0F) >> volume_shift) + (@wave_ram_sample_buffer.to_i16 - 8) * 4 * (@volume_force ? 3 : {0, 4, 2, 1}[@volume_code]) else 0_i16 end diff --git a/src/crab/apu/channel4.cr b/src/crab/apu/channel4.cr index a4d79fd..4e6b468 100644 --- a/src/crab/apu/channel4.cr +++ b/src/crab/apu/channel4.cr @@ -33,10 +33,10 @@ class Channel4 < VolumeEnvelopeChannel @gba.scheduler.schedule frequency_timer, ->step, Scheduler::EventType::APUChannel4 end - # Outputs a value 0..0xF + # Outputs a value -0x80..0x80 def get_amplitude : Int16 if @enabled && @dac_enabled - ((~@lfsr & 1) * @current_volume).to_i16 + ((~@lfsr & 1).to_i16 * 16 - 8) * @current_volume else 0_i16 end diff --git a/src/crab/apu/dma_channels.cr b/src/crab/apu/dma_channels.cr index 853871b..bb8f06c 100644 --- a/src/crab/apu/dma_channels.cr +++ b/src/crab/apu/dma_channels.cr @@ -37,7 +37,7 @@ class DMAChannels if timer == @timers[channel].call if @sizes[channel] > 0 log "Timer overflow good; channel:#{channel}, timer:#{timer}".colorize.fore(:yellow) - @latches[channel] = @fifos[channel][@positions[channel]].to_i16 + @latches[channel] = @fifos[channel][@positions[channel]].to_i16 << 1 # put on scale of -0x100..0x100 @positions[channel] = (@positions[channel] + 1) % 32 @sizes[channel] -= 1 else