Improve GBC sound by fixing its deferred rendering
This fixes many games that use the PSG, particuarly the noise generator. It is very noticeable in explosion/collision sounds with Sonic and Kirby games, where the noise channel is rapidly tweaked.
This commit is contained in:
parent
2391496432
commit
1d972ec7ff
21
gba_memory.c
21
gba_memory.c
|
@ -23,6 +23,7 @@
|
|||
/* Sound */
|
||||
#define gbc_sound_tone_control_low(channel, regn) \
|
||||
{ \
|
||||
render_gbc_sound(); \
|
||||
u32 initial_volume = (value >> 12) & 0x0F; \
|
||||
u32 envelope_ticks = ((value >> 8) & 0x07) * 4; \
|
||||
gbc_sound_channel[channel].length_ticks = 64 - (value & 0x3F); \
|
||||
|
@ -34,12 +35,12 @@
|
|||
gbc_sound_channel[channel].envelope_ticks = envelope_ticks; \
|
||||
gbc_sound_channel[channel].envelope_status = (envelope_ticks != 0); \
|
||||
gbc_sound_channel[channel].envelope_volume = initial_volume; \
|
||||
gbc_sound_update = 1; \
|
||||
write_ioreg(regn, value); \
|
||||
} \
|
||||
|
||||
#define gbc_sound_tone_control_high(channel, regn) \
|
||||
{ \
|
||||
render_gbc_sound(); \
|
||||
u32 rate = value & 0x7FF; \
|
||||
gbc_sound_channel[channel].rate = rate; \
|
||||
gbc_sound_channel[channel].frequency_step = \
|
||||
|
@ -55,12 +56,12 @@
|
|||
gbc_sound_channel[channel].envelope_initial_volume; \
|
||||
} \
|
||||
\
|
||||
gbc_sound_update = 1; \
|
||||
write_ioreg(regn, value & 0x47FF); \
|
||||
} \
|
||||
|
||||
#define gbc_sound_tone_control_sweep() \
|
||||
{ \
|
||||
render_gbc_sound(); \
|
||||
value &= 0x007F; \
|
||||
u32 sweep_ticks = ((value >> 4) & 0x07) * 2; \
|
||||
gbc_sound_channel[0].sweep_shift = value & 0x07; \
|
||||
|
@ -68,19 +69,18 @@
|
|||
gbc_sound_channel[0].sweep_status = (value != 8); \
|
||||
gbc_sound_channel[0].sweep_ticks = sweep_ticks; \
|
||||
gbc_sound_channel[0].sweep_initial_ticks = sweep_ticks; \
|
||||
gbc_sound_update = 1; \
|
||||
write_ioreg(REG_SOUND1CNT_L, value); \
|
||||
} \
|
||||
|
||||
#define gbc_sound_wave_control() \
|
||||
{ \
|
||||
render_gbc_sound(); \
|
||||
gbc_sound_channel[2].wave_type = (value >> 5) & 0x01; \
|
||||
gbc_sound_channel[2].wave_bank = (value >> 6) & 0x01; \
|
||||
gbc_sound_channel[2].master_enable = 0; \
|
||||
if(value & 0x80) \
|
||||
gbc_sound_channel[2].master_enable = 1; \
|
||||
\
|
||||
gbc_sound_update = 1; \
|
||||
write_ioreg(REG_SOUND3CNT_L, value & 0x00E0); \
|
||||
} \
|
||||
|
||||
|
@ -88,18 +88,19 @@ static const u32 gbc_sound_wave_volume[4] = { 0, 16384, 8192, 4096 };
|
|||
|
||||
#define gbc_sound_tone_control_low_wave() \
|
||||
{ \
|
||||
render_gbc_sound(); \
|
||||
gbc_sound_channel[2].length_ticks = 256 - (value & 0xFF); \
|
||||
if((value >> 15) & 0x01) \
|
||||
gbc_sound_channel[2].wave_volume = 12288; \
|
||||
else \
|
||||
gbc_sound_channel[2].wave_volume = \
|
||||
gbc_sound_wave_volume[(value >> 13) & 0x03]; \
|
||||
gbc_sound_update = 1; \
|
||||
write_ioreg(REG_SOUND3CNT_H, value); \
|
||||
} \
|
||||
|
||||
#define gbc_sound_tone_control_high_wave() \
|
||||
{ \
|
||||
render_gbc_sound(); \
|
||||
u32 rate = value & 0x7FF; \
|
||||
gbc_sound_channel[2].rate = rate; \
|
||||
gbc_sound_channel[2].frequency_step = \
|
||||
|
@ -110,7 +111,6 @@ static const u32 gbc_sound_wave_volume[4] = { 0, 16384, 8192, 4096 };
|
|||
gbc_sound_channel[2].sample_index = 0; \
|
||||
gbc_sound_channel[2].active_flag = 1; \
|
||||
} \
|
||||
gbc_sound_update = 1; \
|
||||
write_ioreg(REG_SOUND3CNT_X, value); \
|
||||
} \
|
||||
|
||||
|
@ -118,6 +118,7 @@ static const u32 gbc_sound_wave_volume[4] = { 0, 16384, 8192, 4096 };
|
|||
{ \
|
||||
u32 dividing_ratio = value & 0x07; \
|
||||
u32 frequency_shift = (value >> 4) & 0x0F; \
|
||||
render_gbc_sound(); \
|
||||
if(dividing_ratio == 0) \
|
||||
{ \
|
||||
gbc_sound_channel[3].frequency_step = \
|
||||
|
@ -141,13 +142,13 @@ static const u32 gbc_sound_wave_volume[4] = { 0, 16384, 8192, 4096 };
|
|||
gbc_sound_channel[3].envelope_volume = \
|
||||
gbc_sound_channel[3].envelope_initial_volume; \
|
||||
} \
|
||||
gbc_sound_update = 1; \
|
||||
write_ioreg(REG_SOUND4CNT_H, value & 0x40FF); \
|
||||
} \
|
||||
|
||||
static void gbc_trigger_sound(u32 value)
|
||||
{
|
||||
u32 channel;
|
||||
render_gbc_sound();
|
||||
|
||||
/* Trigger all 4 GBC sound channels */
|
||||
for (channel = 0; channel < 4; channel++)
|
||||
|
@ -162,6 +163,7 @@ static void gbc_trigger_sound(u32 value)
|
|||
|
||||
#define trigger_sound() \
|
||||
{ \
|
||||
render_gbc_sound(); \
|
||||
timer[0].direct_sound_channels = \
|
||||
((((value >> 10) & 0x01) == 0) | ((((value >> 14) & 0x01) == 0) << 1)); \
|
||||
timer[1].direct_sound_channels = \
|
||||
|
@ -181,6 +183,7 @@ static void gbc_trigger_sound(u32 value)
|
|||
|
||||
static void sound_control_x(u32 value)
|
||||
{
|
||||
render_gbc_sound();
|
||||
if (value & 0x80)
|
||||
{
|
||||
if (sound_on != 1)
|
||||
|
@ -375,9 +378,6 @@ RFILE *gamepak_file_large = NULL;
|
|||
// Writes to these respective locations should trigger an update
|
||||
// so the related subsystem may react to it.
|
||||
|
||||
// If GBC audio is written to:
|
||||
u32 gbc_sound_update = 0;
|
||||
|
||||
// If the GBC audio waveform is modified:
|
||||
u32 gbc_sound_wave_update = 0;
|
||||
|
||||
|
@ -885,6 +885,7 @@ cpu_alert_type function_cc write_io_register16(u32 address, u32 value)
|
|||
|
||||
// Sound wave RAM, flag wave table update
|
||||
case REG_SOUNDWAVE_0 ... REG_SOUNDWAVE_7:
|
||||
render_gbc_sound();
|
||||
gbc_sound_wave_update = 1;
|
||||
write_ioreg(ioreg, value);
|
||||
break;
|
||||
|
|
|
@ -226,7 +226,6 @@ void memory_term(void);
|
|||
u8 *load_gamepak_page(u32 physical_index);
|
||||
|
||||
extern u32 oam_update;
|
||||
extern u32 gbc_sound_update;
|
||||
extern u32 gbc_sound_wave_update;
|
||||
extern dma_transfer_type dma[DMA_CHAN_CNT];
|
||||
|
||||
|
|
74
main.c
74
main.c
|
@ -23,9 +23,8 @@
|
|||
timer_type timer[4];
|
||||
|
||||
u32 cpu_ticks = 0;
|
||||
|
||||
u32 execute_cycles = 960;
|
||||
s32 video_count = 960;
|
||||
u32 execute_cycles = 0;
|
||||
s32 video_count = 0;
|
||||
|
||||
u32 last_frame = 0;
|
||||
u32 flush_ram_count = 0;
|
||||
|
@ -96,7 +95,6 @@ void init_main(void)
|
|||
timer[1].direct_sound_channels = TIMER_DS_CHANNEL_NONE;
|
||||
|
||||
cpu_ticks = 0;
|
||||
|
||||
execute_cycles = 960;
|
||||
video_count = 960;
|
||||
|
||||
|
@ -126,82 +124,85 @@ u32 function_cc update_gba(int remaining_cycles)
|
|||
remaining_cycles = 0;
|
||||
reg[COMPLETED_FRAME] = 0;
|
||||
|
||||
if(gbc_sound_update)
|
||||
{
|
||||
gbc_update_count++;
|
||||
update_gbc_sound(cpu_ticks);
|
||||
gbc_sound_update = 0;
|
||||
}
|
||||
|
||||
// Timers can trigger DMA (usually sound) and consume cycles
|
||||
dma_cycles = update_timers(&irq_raised, completed_cycles);
|
||||
|
||||
// Video count tracks the video cycles remaining until the next event
|
||||
video_count -= completed_cycles;
|
||||
|
||||
// Ran out of cycles, move to the next video area
|
||||
if(video_count <= 0)
|
||||
{
|
||||
u32 vcount = read_ioreg(REG_VCOUNT);
|
||||
u32 dispstat = read_ioreg(REG_DISPSTAT);
|
||||
|
||||
if((dispstat & 0x02) == 0)
|
||||
// Check if we are in hrefresh (0) or hblank (1)
|
||||
if ((dispstat & 0x02) == 0)
|
||||
{
|
||||
// Transition from hrefresh to hblank
|
||||
video_count += (272);
|
||||
dispstat |= 0x02;
|
||||
video_count += (272); // hblank duration, 272 cycles
|
||||
|
||||
if((dispstat & 0x01) == 0)
|
||||
// Check if we are drawing (0) or we are in vblank (1)
|
||||
if ((dispstat & 0x01) == 0)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
// Render the scan line
|
||||
if(reg[OAM_UPDATED])
|
||||
oam_update_count++;
|
||||
|
||||
update_scanline();
|
||||
|
||||
// If in visible area also fire HDMA
|
||||
for(i = 0; i < 4; i++)
|
||||
// Trigger the HBlank DMAs if enabled
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if(dma[i].start_type == DMA_START_HBLANK)
|
||||
dma_transfer(i, &dma_cycles);
|
||||
}
|
||||
}
|
||||
|
||||
if(dispstat & 0x10)
|
||||
// Trigger the hblank interrupt, if enabled in DISPSTAT
|
||||
if (dispstat & 0x10)
|
||||
irq_raised |= IRQ_HBLANK;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Transition from hblank to next line
|
||||
// Transition from hblank to the next scan line (vdraw or vblank)
|
||||
video_count += 960;
|
||||
dispstat &= ~0x02;
|
||||
|
||||
vcount++;
|
||||
|
||||
if(vcount == 160)
|
||||
{
|
||||
// Transition from vrefresh to vblank
|
||||
u32 i;
|
||||
|
||||
dispstat |= 0x01;
|
||||
if(dispstat & 0x8)
|
||||
{
|
||||
irq_raised |= IRQ_VBLANK;
|
||||
}
|
||||
|
||||
// Reinit affine transformation counters for the next frame
|
||||
video_reload_counters();
|
||||
|
||||
for(i = 0; i < 4; i++)
|
||||
// Trigger VBlank interrupt if enabled
|
||||
if (dispstat & 0x8)
|
||||
irq_raised |= IRQ_VBLANK;
|
||||
|
||||
// Trigger the VBlank DMAs if enabled
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
if(dma[i].start_type == DMA_START_VBLANK)
|
||||
dma_transfer(i, &dma_cycles);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if(vcount == 228)
|
||||
else if (vcount == 228)
|
||||
{
|
||||
// Transition from vblank to next screen
|
||||
vcount = 0;
|
||||
dispstat &= ~0x01;
|
||||
|
||||
/* If there's no cheat hook, run on vblank! */
|
||||
if (cheat_master_hook == ~0U)
|
||||
process_cheats();
|
||||
|
||||
/* printf("frame update (%x), %d instructions total, %d RAM flushes\n",
|
||||
reg[REG_PC], instruction_count - last_frame, flush_ram_count);
|
||||
last_frame = instruction_count;
|
||||
|
@ -212,26 +213,19 @@ u32 function_cc update_gba(int remaining_cycles)
|
|||
oam_update_count = 0;
|
||||
flush_ram_count = 0;
|
||||
|
||||
update_gbc_sound(cpu_ticks);
|
||||
gbc_sound_update = 0;
|
||||
// Force audio generation. Need to flush samples for this frame.
|
||||
render_gbc_sound();
|
||||
|
||||
/* If there's no cheat hook, run on vblank! */
|
||||
if (cheat_master_hook == ~0U)
|
||||
process_cheats();
|
||||
|
||||
vcount = 0;
|
||||
// We completed a frame, tell the dynarec to exit to the main thread
|
||||
reg[COMPLETED_FRAME] = 1;
|
||||
}
|
||||
|
||||
// Vcount trigger (flag) and IRQ if enabled
|
||||
if(vcount == (dispstat >> 8))
|
||||
{
|
||||
// vcount trigger
|
||||
dispstat |= 0x04;
|
||||
if(dispstat & 0x20)
|
||||
{
|
||||
irq_raised |= IRQ_VCOUNT;
|
||||
}
|
||||
}
|
||||
else
|
||||
dispstat &= ~0x04;
|
||||
|
@ -241,7 +235,7 @@ u32 function_cc update_gba(int remaining_cycles)
|
|||
write_ioreg(REG_DISPSTAT, dispstat);
|
||||
}
|
||||
|
||||
// Flag any V/H blank interrupts.
|
||||
// Flag any V/H blank interrupts, DMA IRQs, Vcount, etc.
|
||||
if (irq_raised)
|
||||
flag_interrupt(irq_raised);
|
||||
|
||||
|
@ -249,6 +243,8 @@ u32 function_cc update_gba(int remaining_cycles)
|
|||
if (check_and_raise_interrupts())
|
||||
changed_pc = 0x80000000;
|
||||
|
||||
// Figure out when we need to stop CPU execution. The next event is
|
||||
// a video event or a timer event, whatever happens first.
|
||||
execute_cycles = MAX(video_count, 0);
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
|
|
2
main.h
2
main.h
|
@ -66,6 +66,8 @@ typedef enum
|
|||
boot_bios
|
||||
} boot_mode;
|
||||
|
||||
extern u32 gbc_update_count;
|
||||
|
||||
extern u32 cpu_ticks;
|
||||
extern u32 execute_cycles;
|
||||
extern u32 skip_next_frame;
|
||||
|
|
|
@ -109,7 +109,6 @@ bool gba_load_state(const void* src)
|
|||
instruction_count = 0;
|
||||
reg[COMPLETED_FRAME] = 0;
|
||||
reg[OAM_UPDATED] = 1;
|
||||
gbc_sound_update = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
18
sound.c
18
sound.c
|
@ -345,12 +345,12 @@ u32 gbc_sound_master_volume;
|
|||
#define get_noise_sample_full() \
|
||||
current_sample = \
|
||||
((s32)(noise_table15[fp16_16_to_u32(sample_index) >> 5] << \
|
||||
(fp16_16_to_u32(sample_index) & 0x1F)) >> 31) & 0x0F \
|
||||
(fp16_16_to_u32(sample_index) & 0x1F)) >> 31) ^ 0x07 \
|
||||
|
||||
#define get_noise_sample_half() \
|
||||
current_sample = \
|
||||
((s32)(noise_table7[fp16_16_to_u32(sample_index) >> 5] << \
|
||||
(fp16_16_to_u32(sample_index) & 0x1F)) >> 31) & 0x0F \
|
||||
(fp16_16_to_u32(sample_index) & 0x1F)) >> 31) ^ 0x07 \
|
||||
|
||||
#define gbc_sound_render_noise(type, noise_type, envelope_op, sweep_op) \
|
||||
for(i = 0; i < buffer_ticks; i++) \
|
||||
|
@ -396,10 +396,8 @@ u32 gbc_sound_master_volume;
|
|||
gs->sample_index = sample_index; \
|
||||
gs->tick_counter = tick_counter; \
|
||||
|
||||
void update_gbc_sound(u32 cpu_ticks)
|
||||
void render_gbc_sound()
|
||||
{
|
||||
fixed16_16 buffer_ticks = float_to_fp16_16((float)(cpu_ticks -
|
||||
gbc_sound_last_cpu_ticks) * sound_frequency / GBC_BASE_RATE);
|
||||
u32 i, i2;
|
||||
gbc_sound_struct *gs = gbc_sound_channel;
|
||||
fixed16_16 sample_index, frequency_step;
|
||||
|
@ -410,9 +408,13 @@ void update_gbc_sound(u32 cpu_ticks)
|
|||
s32 current_sample;
|
||||
u16 sound_status = read_ioreg(REG_SOUNDCNT_X) & 0xFFF0;
|
||||
const s8 *sample_data;
|
||||
s8 *wave_bank;
|
||||
u8 *wave_ram = ((u8 *)io_registers) + 0x90;
|
||||
u32 tick_delta = cpu_ticks - gbc_sound_last_cpu_ticks;
|
||||
fixed16_16 buffer_ticks = float_to_fp16_16((float)(tick_delta) *
|
||||
sound_frequency / GBC_BASE_RATE);
|
||||
if (!tick_delta)
|
||||
return;
|
||||
|
||||
gbc_update_count++;
|
||||
gbc_sound_partial_ticks += fp16_16_fractional_part(buffer_ticks);
|
||||
buffer_ticks = fp16_16_to_u32(buffer_ticks);
|
||||
|
||||
|
@ -424,6 +426,7 @@ void update_gbc_sound(u32 cpu_ticks)
|
|||
|
||||
if(sound_on == 1)
|
||||
{
|
||||
s8 *wave_bank;
|
||||
gs = gbc_sound_channel + 0;
|
||||
if(gs->active_flag)
|
||||
{
|
||||
|
@ -446,6 +449,7 @@ void update_gbc_sound(u32 cpu_ticks)
|
|||
if(gbc_sound_wave_update)
|
||||
{
|
||||
unsigned bank = (gs->wave_bank == 1) ? 1 : 0;
|
||||
u8 *wave_ram = ((u8 *)io_registers) + 0x90;
|
||||
|
||||
wave_bank = wave_samples + (bank * 32);
|
||||
for(i = 0, i2 = 0; i < 16; i++, i2 += 2)
|
||||
|
|
2
sound.h
2
sound.h
|
@ -100,7 +100,7 @@ extern u32 sound_on;
|
|||
void sound_timer_queue32(u32 channel, u32 value);
|
||||
unsigned sound_timer(fixed8_24 frequency_step, u32 channel);
|
||||
void sound_reset_fifo(u32 channel);
|
||||
void update_gbc_sound(u32 cpu_ticks);
|
||||
void render_gbc_sound();
|
||||
void init_sound();
|
||||
unsigned sound_write_savestate(u8 *dst);
|
||||
bool sound_read_savestate(const u8 *src);
|
||||
|
|
Loading…
Reference in New Issue