mirror of
https://github.com/mattrberry/crab.git
synced 2025-01-19 10:26:44 +01:00
handle volume and channel enable properly for gba
This commit is contained in:
parent
ac8319b19c
commit
9f65505731
1 changed files with 82 additions and 49 deletions
131
src/crab/apu.cr
131
src/crab/apu.cr
|
@ -17,25 +17,53 @@ class APU
|
|||
FRAME_SEQUENCER_RATE = 512 # Hz
|
||||
FRAME_SEQUENCER_PERIOD = CPU::CLOCK_SPEED // FRAME_SEQUENCER_RATE
|
||||
|
||||
class SOUNDCNT_L < BitField(UInt16)
|
||||
num channel_4_left, 1
|
||||
num channel_3_left, 1
|
||||
num channel_2_left, 1
|
||||
num channel_1_left, 1
|
||||
num channel_4_right, 1
|
||||
num channel_3_right, 1
|
||||
num channel_2_right, 1
|
||||
num channel_1_right, 1
|
||||
bool not_used_1, lock: true
|
||||
num left_volume, 3
|
||||
bool not_used_2, lock: true
|
||||
num right_volume, 3
|
||||
end
|
||||
|
||||
class SOUNDCNT_H < BitField(UInt16)
|
||||
bool dma_sound_b_reset, lock: true
|
||||
num dma_sound_b_timer, 1
|
||||
bool dma_sound_b_left
|
||||
bool dma_sound_b_right
|
||||
bool dma_sound_a_reset, lock: true
|
||||
num dma_sound_a_timer, 1
|
||||
bool dma_sound_a_left
|
||||
bool dma_sound_a_right
|
||||
num not_used, 4, lock: true
|
||||
bool dma_sound_b_volume
|
||||
bool dma_sound_a_volume
|
||||
num sound_volume, 2
|
||||
end
|
||||
|
||||
class SOUNDBIAS < BitField(UInt16)
|
||||
num amplitude_resolution, 2
|
||||
num not_used_1, 4
|
||||
num bias_level, 9
|
||||
bool not_used_2
|
||||
end
|
||||
|
||||
@soundcnt_l = SOUNDCNT_L.new 0
|
||||
@soundcnt_h = SOUNDCNT_H.new 0
|
||||
@sound_enabled : Bool = false
|
||||
@soundbias = SOUNDBIAS.new 0
|
||||
|
||||
@buffer = Slice(Float32).new BUFFER_SIZE
|
||||
@buffer_pos = 0
|
||||
@frame_sequencer_stage = 0
|
||||
getter first_half_of_length_period = false
|
||||
|
||||
# @left_enable = false
|
||||
# @left_volume = 0_u8
|
||||
# @right_enable = false
|
||||
# @right_volume = 0_u8
|
||||
@left_enable = true
|
||||
@left_volume = 7_u8
|
||||
@right_enable = true
|
||||
@right_volume = 7_u8
|
||||
|
||||
# @nr51 : UInt8 = 0x00
|
||||
@nr51 : UInt8 = 0xFF
|
||||
|
||||
@audiospec : LibSDL::AudioSpec
|
||||
@obtained_spec : LibSDL::AudioSpec
|
||||
|
||||
|
@ -106,16 +134,16 @@ class APU
|
|||
channel2_amp = @channel2.get_amplitude
|
||||
channel3_amp = @channel3.get_amplitude
|
||||
channel4_amp = @channel4.get_amplitude
|
||||
@buffer[@buffer_pos] = (@left_volume / 7).to_f32 *
|
||||
((@nr51 & 0x80 > 0 ? channel4_amp : 0) +
|
||||
(@nr51 & 0x40 > 0 ? channel3_amp : 0) +
|
||||
(@nr51 & 0x20 > 0 ? channel2_amp : 0) +
|
||||
(@nr51 & 0x10 > 0 ? channel1_amp : 0)) / 4
|
||||
@buffer[@buffer_pos + 1] = (@right_volume / 7).to_f32 *
|
||||
((@nr51 & 0x08 > 0 ? channel4_amp : 0) +
|
||||
(@nr51 & 0x04 > 0 ? channel3_amp : 0) +
|
||||
(@nr51 & 0x02 > 0 ? channel2_amp : 0) +
|
||||
(@nr51 & 0x01 > 0 ? channel1_amp : 0)) / 4
|
||||
@buffer[@buffer_pos] = (@soundcnt_l.left_volume / 7).to_f32 *
|
||||
((channel4_amp * @soundcnt_l.channel_4_left) +
|
||||
(channel3_amp * @soundcnt_l.channel_3_left) +
|
||||
(channel2_amp * @soundcnt_l.channel_2_left) +
|
||||
(channel1_amp * @soundcnt_l.channel_1_left)) / 4
|
||||
@buffer[@buffer_pos + 1] = (@soundcnt_l.right_volume).to_f32 *
|
||||
((channel4_amp * @soundcnt_l.channel_4_right) +
|
||||
(channel3_amp * @soundcnt_l.channel_3_right) +
|
||||
(channel2_amp * @soundcnt_l.channel_2_right) +
|
||||
(channel1_amp * @soundcnt_l.channel_1_right)) / 4
|
||||
@buffer_pos += 2
|
||||
|
||||
# push to SDL if buffer is full
|
||||
|
@ -130,41 +158,42 @@ class APU
|
|||
@gba.scheduler.schedule SAMPLE_PERIOD, ->get_sample
|
||||
end
|
||||
|
||||
def read_io(index : Int) : UInt8
|
||||
case index
|
||||
when @channel1 then @channel1.read_io index
|
||||
when @channel2 then @channel2.read_io index
|
||||
when @channel3 then @channel3.read_io index
|
||||
when @channel4 then @channel4.read_io index
|
||||
when 0xFF24
|
||||
((@left_enable ? 0b10000000 : 0) | (@left_volume << 4) |
|
||||
(@right_enable ? 0b00001000 : 0) | @right_volume).to_u8
|
||||
when 0xFF25 then @nr51
|
||||
when 0xFF26
|
||||
0x70 |
|
||||
def read_io(io_addr : Int) : UInt8
|
||||
case io_addr
|
||||
when @channel1 then @channel1.read_io io_addr
|
||||
when @channel2 then @channel2.read_io io_addr
|
||||
when @channel3 then @channel3.read_io io_addr
|
||||
when @channel4 then @channel4.read_io io_addr
|
||||
when 0x80 then @soundcnt_l.value.to_u8
|
||||
when 0x81 then (@soundcnt_l.value >> 8).to_u8
|
||||
when 0x82 then @soundcnt_h.value.to_u8
|
||||
when 0x83 then (@soundcnt_h.value >> 8).to_u8
|
||||
when 0x84
|
||||
0x70_u8 |
|
||||
(@sound_enabled ? 0x80 : 0) |
|
||||
(@channel4.enabled ? 0b1000 : 0) |
|
||||
(@channel3.enabled ? 0b0100 : 0) |
|
||||
(@channel2.enabled ? 0b0010 : 0) |
|
||||
(@channel1.enabled ? 0b0001 : 0)
|
||||
else 0xFF
|
||||
end.to_u8
|
||||
when 0x85 then 0_u8 # unused
|
||||
when 0x88 then @soundbias.value.to_u8
|
||||
when 0x89 then (@soundbias.value >> 8).to_u8
|
||||
else abort "Unmapped APU read ~ addr:#{hex_str io_addr.to_u8}"
|
||||
end
|
||||
end
|
||||
|
||||
# write to apu memory
|
||||
def write_io(index : Int, value : UInt8) : Nil
|
||||
return unless @sound_enabled || index == 0x84 || Channel3::WAVE_RAM_RANGE.includes?(index)
|
||||
case index
|
||||
when @channel1 then @channel1.write_io index, value
|
||||
when @channel2 then @channel2.write_io index, value
|
||||
when @channel3 then @channel3.write_io index, value
|
||||
when @channel4 then @channel4.write_io index, value
|
||||
when 0xFF24
|
||||
@left_enable = value & 0b10000000 > 0
|
||||
@left_volume = (value & 0b01110000) >> 4
|
||||
@right_enable = value & 0b00001000 > 0
|
||||
@right_volume = value & 0b00000111
|
||||
when 0xFF25 then @nr51 = value
|
||||
def write_io(io_addr : Int, value : UInt8) : Nil
|
||||
return unless @sound_enabled || io_addr == 0x84 || Channel3::WAVE_RAM_RANGE.includes?(io_addr)
|
||||
case io_addr
|
||||
when @channel1 then @channel1.write_io io_addr, value
|
||||
when @channel2 then @channel2.write_io io_addr, value
|
||||
when @channel3 then @channel3.write_io io_addr, value
|
||||
when @channel4 then @channel4.write_io io_addr, value
|
||||
when 0x80 then @soundcnt_l.value = (@soundcnt_l.value & 0xFF00) | value
|
||||
when 0x81 then @soundcnt_l.value = (@soundcnt_l.value & 0x00FF) | value.to_u16 << 8
|
||||
when 0x82 then @soundcnt_h.value = (@soundcnt_h.value & 0xFF00) | value
|
||||
when 0x83 then @soundcnt_h.value = (@soundcnt_h.value & 0x00FF) | value.to_u16 << 8
|
||||
when 0x84
|
||||
if value & 0x80 == 0 && @sound_enabled
|
||||
(0xFF10..0xFF25).each { |addr| self.write_io addr, 0x00 }
|
||||
|
@ -177,6 +206,10 @@ class APU
|
|||
@channel3.length_counter = 0
|
||||
@channel4.length_counter = 0
|
||||
end
|
||||
when 0x85 # unused
|
||||
when 0x88 then @soundbias.value = (@soundbias.value & 0xFF00) | value
|
||||
when 0x89 then @soundbias.value = (@soundbias.value & 0x00FF) | value.to_u16 << 8
|
||||
else puts "Unmapped APU write ~ addr:#{hex_str io_addr.to_u8}, val:#{value}".colorize(:yellow)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue