mirror of
https://github.com/mattrberry/crab.git
synced 2025-01-30 20:34:45 +01:00
move apu to use Int16 rather than Float32 samples
This commit is contained in:
parent
0de7d0c0c5
commit
278a7ff2cb
7 changed files with 51 additions and 71 deletions
|
@ -22,7 +22,7 @@ class APU
|
|||
@sound_enabled : Bool = false
|
||||
@soundbias = Reg::SOUNDBIAS.new 0
|
||||
|
||||
@buffer = Slice(Float32).new BUFFER_SIZE
|
||||
@buffer = Slice(Int16).new BUFFER_SIZE
|
||||
@buffer_pos = 0
|
||||
@frame_sequencer_stage = 0
|
||||
getter first_half_of_length_period = false
|
||||
|
@ -35,7 +35,7 @@ class APU
|
|||
def initialize(@gba : GBA)
|
||||
@audiospec = LibSDL::AudioSpec.new
|
||||
@audiospec.freq = SAMPLE_RATE
|
||||
@audiospec.format = LibSDL::AUDIO_F32SYS
|
||||
@audiospec.format = LibSDL::AUDIO_S16
|
||||
@audiospec.channels = CHANNELS
|
||||
@audiospec.samples = BUFFER_SIZE
|
||||
@audiospec.callback = nil
|
||||
|
@ -101,48 +101,41 @@ class APU
|
|||
|
||||
def get_sample : Nil
|
||||
abort "Prohibited sound 1-4 volume #{@soundcnt_h.sound_volume}" if @soundcnt_h.sound_volume >= 3
|
||||
psg_master_volume = ((100 >> (2 - @soundcnt_h.sound_volume)) / 100).to_f32
|
||||
# Puts PSGs on scale of 0...0x40
|
||||
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)) / 4
|
||||
psg_left = psg_master_volume *
|
||||
(@soundcnt_l.left_volume / 7) *
|
||||
psg_sound
|
||||
psg_right = psg_master_volume *
|
||||
(@soundcnt_l.right_volume / 7) *
|
||||
psg_sound
|
||||
(@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)
|
||||
|
||||
# Gets DMAs on scale of -0x100...0x100
|
||||
dma_a, dma_b = @dma_channels.get_amplitude
|
||||
vol_a = ((100 >> (1 - @soundcnt_h.dma_sound_a_volume)) / 100).to_f32
|
||||
vol_b = ((100 >> (1 - @soundcnt_h.dma_sound_b_volume)) / 100).to_f32
|
||||
dma_left = ((dma_a * @soundcnt_h.dma_sound_a_left * vol_a) +
|
||||
(dma_b * @soundcnt_h.dma_sound_b_left * vol_b)) / 2
|
||||
dma_right = ((dma_a * @soundcnt_h.dma_sound_a_right * vol_a) +
|
||||
(dma_b * @soundcnt_h.dma_sound_b_right * vol_b)) / 2
|
||||
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
|
||||
|
||||
@buffer[@buffer_pos] = (psg_left + 2*dma_left) / 3
|
||||
@buffer[@buffer_pos + 1] = (psg_right + 2*dma_right) / 3
|
||||
bias = 0x200
|
||||
|
||||
if @buffer[@buffer_pos].abs > 1 || @buffer[@buffer_pos + 1].abs > 1
|
||||
STDERR.puts "Left: #{@buffer[@buffer_pos]}"
|
||||
STDERR.puts " PSG: #{psg_left}"
|
||||
STDERR.puts " DMA: #{dma_right}"
|
||||
STDERR.puts "Right: #{@buffer[@buffer_pos + 1]}"
|
||||
STDERR.puts " PSG: #{psg_right}"
|
||||
STDERR.puts " DMA: #{dma_right}"
|
||||
exit 1
|
||||
end
|
||||
total_left = (psg_left + dma_left + bias).clamp(0_i16..0x3FF_i16)
|
||||
total_right = (psg_right + dma_right + bias).clamp(0_i16..0x3FF_i16)
|
||||
|
||||
@buffer[@buffer_pos] = total_left * 32
|
||||
@buffer[@buffer_pos + 1] = total_right * 32
|
||||
@buffer_pos += 2
|
||||
|
||||
# push to SDL if buffer is full
|
||||
if @buffer_pos >= BUFFER_SIZE
|
||||
LibSDL.clear_queued_audio 1 unless @sync
|
||||
while LibSDL.get_queued_audio_size(1) > BUFFER_SIZE * sizeof(Float32) * 2
|
||||
while LibSDL.get_queued_audio_size(1) > BUFFER_SIZE * sizeof(Int16) * 2
|
||||
LibSDL.delay(1)
|
||||
end
|
||||
LibSDL.queue_audio 1, @buffer, BUFFER_SIZE * sizeof(Float32)
|
||||
LibSDL.queue_audio 1, @buffer, BUFFER_SIZE * sizeof(Int16)
|
||||
@buffer_pos = 0
|
||||
end
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ abstract class SoundChannel
|
|||
# Called when @period reaches 0
|
||||
abstract def step_wave_generation : Nil
|
||||
|
||||
abstract def get_amplitude : Float32
|
||||
abstract def get_amplitude : Int16
|
||||
|
||||
abstract def read_io(index : Int) : UInt8
|
||||
abstract def write_io(index : Int, value : UInt8) : Nil
|
||||
|
|
|
@ -57,14 +57,13 @@ class Channel1 < VolumeEnvelopeChannel
|
|||
end
|
||||
end
|
||||
|
||||
def get_amplitude : Float32
|
||||
# Outputs a value 0..0xF
|
||||
def get_amplitude : Int16
|
||||
if @enabled && @dac_enabled
|
||||
dac_input = WAVE_DUTY[@duty][@wave_duty_position] * @current_volume
|
||||
dac_output = (dac_input / 7.5) - 1
|
||||
dac_output
|
||||
WAVE_DUTY[@duty][@wave_duty_position].to_i16 * @current_volume
|
||||
else
|
||||
0
|
||||
end.to_f32
|
||||
0_i16
|
||||
end
|
||||
end
|
||||
|
||||
# Calculate the new shadow frequency, disable channel if overflow 11 bits
|
||||
|
|
|
@ -33,14 +33,13 @@ class Channel2 < VolumeEnvelopeChannel
|
|||
@gba.scheduler.schedule frequency_timer, ->step, Scheduler::EventType::APUChannel2
|
||||
end
|
||||
|
||||
def get_amplitude : Float32
|
||||
# Outputs a value 0..0xF
|
||||
def get_amplitude : Int16
|
||||
if @enabled && @dac_enabled
|
||||
dac_input = WAVE_DUTY[@duty][@wave_duty_position] * @current_volume
|
||||
dac_output = (dac_input / 7.5) - 1
|
||||
dac_output
|
||||
WAVE_DUTY[@duty][@wave_duty_position].to_i16 * @current_volume
|
||||
else
|
||||
0
|
||||
end.to_f32
|
||||
0_i16
|
||||
end
|
||||
end
|
||||
|
||||
def read_io(index : Int) : UInt8
|
||||
|
|
|
@ -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 : UInt8 = 0x00
|
||||
@wave_ram_sample_buffer : Int16 = 0x00
|
||||
|
||||
# NR30
|
||||
@wave_ram_dimension : Bool = false
|
||||
|
@ -21,15 +21,13 @@ class Channel3 < SoundChannel
|
|||
@volume_code : UInt8 = 0x00
|
||||
@volume_force : Bool = false
|
||||
|
||||
@volume_multiplier : Float32 = 0
|
||||
|
||||
# NR33 / NR34
|
||||
@frequency : UInt16 = 0x00
|
||||
|
||||
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]
|
||||
@wave_ram_sample_buffer = @wave_ram[@wave_ram_bank][@wave_ram_position // 2].to_i16
|
||||
end
|
||||
|
||||
def frequency_timer : UInt32
|
||||
|
@ -40,14 +38,14 @@ class Channel3 < SoundChannel
|
|||
@gba.scheduler.schedule frequency_timer, ->step, Scheduler::EventType::APUChannel3
|
||||
end
|
||||
|
||||
def get_amplitude : Float32
|
||||
# Outputs a value 0..0xF
|
||||
def get_amplitude : Int16
|
||||
if @enabled && @dac_enabled
|
||||
dac_input = @volume_multiplier * ((@wave_ram_sample_buffer >> (@wave_ram_position & 1 == 0 ? 4 : 0)) & 0x0F)
|
||||
dac_output = (dac_input / 7.5) - 1
|
||||
dac_output
|
||||
volume_shift = (-1 + @volume_code) % 4
|
||||
(((@wave_ram_sample_buffer >> (@wave_ram_position & 1 == 0 ? 4 : 0)) & 0x0F) >> volume_shift)
|
||||
else
|
||||
0
|
||||
end.to_f32
|
||||
0_i16
|
||||
end
|
||||
end
|
||||
|
||||
def read_io(index : Int) : UInt8
|
||||
|
@ -82,15 +80,6 @@ class Channel3 < SoundChannel
|
|||
when 0x73
|
||||
@volume_code = (value & 0x60) >> 5
|
||||
@volume_force = bit?(value, 7)
|
||||
# Internal values
|
||||
@volume_multiplier = case {@volume_force, @volume_code}
|
||||
when {true, _} then 0.75_f32
|
||||
when {_, 0b00} then 0_f32
|
||||
when {_, 0b01} then 1_f32
|
||||
when {_, 0b10} then 0.5_f32
|
||||
when {_, 0b11} then 0.25_f32
|
||||
else raise "Impossible volume code #{@volume_code}"
|
||||
end
|
||||
when 0x74
|
||||
@frequency = (@frequency & 0x0700) | value
|
||||
when 0x75
|
||||
|
|
|
@ -33,14 +33,13 @@ class Channel4 < VolumeEnvelopeChannel
|
|||
@gba.scheduler.schedule frequency_timer, ->step, Scheduler::EventType::APUChannel4
|
||||
end
|
||||
|
||||
def get_amplitude : Float32
|
||||
# Outputs a value 0..0xF
|
||||
def get_amplitude : Int16
|
||||
if @enabled && @dac_enabled
|
||||
dac_input = (~@lfsr & 1) * @current_volume
|
||||
dac_output = (dac_input / 7.5) - 1
|
||||
dac_output
|
||||
((~@lfsr & 1) * @current_volume).to_i16
|
||||
else
|
||||
0
|
||||
end.to_f32
|
||||
0_i16
|
||||
end
|
||||
end
|
||||
|
||||
def read_io(index : Int) : UInt8
|
||||
|
|
|
@ -5,7 +5,7 @@ class DMAChannels
|
|||
@positions = Array(Int32).new 2, 0
|
||||
@sizes = Array(Int32).new 2, 0
|
||||
@timers : Array(Proc(UInt16))
|
||||
@latches = Array(Float32).new 2, 0
|
||||
@latches = Array(Int16).new 2, 0
|
||||
|
||||
def ===(value) : Bool
|
||||
value.is_a?(Int) && RANGE.includes?(value)
|
||||
|
@ -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]] / 128).to_f32
|
||||
@latches[channel] = @fifos[channel][@positions[channel]].to_i16
|
||||
@positions[channel] = (@positions[channel] + 1) % 32
|
||||
@sizes[channel] -= 1
|
||||
else
|
||||
|
@ -49,7 +49,8 @@ class DMAChannels
|
|||
end
|
||||
end
|
||||
|
||||
def get_amplitude : Tuple(Float32, Float32)
|
||||
# Outputs a value -0x100...0x100
|
||||
def get_amplitude : Tuple(Int16, Int16)
|
||||
{@latches[0], @latches[1]}
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue