SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
commit edce971d8cddc48ee94aa2393003575a8b2ab402 parent 6d9ab972d00d5ff66003a091282da91082d80fae Author: Lior Halphon <LIJI32@gmail.com> Date: Wed, 2 Apr 2025 00:27:07 +0300 Optimize SGB FM synthesis, downgrade the sample rate of synthesis when rendering at high rates. Diffstat:
| M | Core/sgb.c | 56 | +++++++++++++++++++++++++++++++++++++++++++++++++------- |
1 file changed, 49 insertions(+), 7 deletions(-)
diff --git a/Core/sgb.c b/Core/sgb.c @@ -831,19 +831,46 @@ void GB_sgb_load_default_data(GB_gameboy_t *gb) gb->sgb->effective_palettes[3] = LE16(built_in_palettes[3]); } +static double fm_sin(double phase) +{ + phase /= 2 * M_PI; + phase = fabs(phase); + phase -= floor(phase); + if (phase > 0.5) { + return -(fm_sin(1 - phase)); + } + if (phase > 0.25) { + return fm_sin(0.5 - phase); + } + + static bool once = false; + static const size_t table_length = 128; + static double table[table_length + 1]; + if (!once) { + for (unsigned i = 0; i < table_length + 1; i++) { + table[i] = sin(i * M_PI / 2 / table_length); + } + once = true; + } + + phase *= 4 * table_length; + double fraction = phase - floor(phase); + return table[(unsigned)floor(phase)] * (1 - fraction) + table[(unsigned)ceil(phase)] * (fraction); +} + static double fm_synth(double phase) { - return (sin(phase * M_PI * 2) + - sin(phase * M_PI * 2 + sin(phase * M_PI * 2)) + - sin(phase * M_PI * 2 + sin(phase * M_PI * 3)) + - sin(phase * M_PI * 2 + sin(phase * M_PI * 4))) / 4; + return (fm_sin(phase * M_PI * 2) + + fm_sin(phase * M_PI * 2 + fm_sin(phase * M_PI * 2)) + + fm_sin(phase * M_PI * 2 + fm_sin(phase * M_PI * 3)) + + fm_sin(phase * M_PI * 2 + fm_sin(phase * M_PI * 4))) / 4; } static double fm_sweep(double phase) { double ret = 0; for (unsigned i = 0; i < 8; i++) { - ret += sin((phase * M_PI * 2 + sin(phase * M_PI * 8) / 4) * pow(1.25, i)) * (8 - i) / 36; + ret += fm_sin((phase * M_PI * 2 + fm_sin(phase * M_PI * 8) / 4) * pow(1.25, i)) * (8 - i) / 36; } return ret; } @@ -882,14 +909,29 @@ static void render_jingle(GB_gameboy_t *gb, size_t count) if (sweep_cutoff_ratio > 1) { sweep_cutoff_ratio = 1; } + + // Render at a lower resolution if our sample rate is too high + uint8_t downsample_mask = 0; + size_t temp_count = count; + while (temp_count > 2048) { + temp_count /= 2; + downsample_mask <<= 1; + downsample_mask |= 1; + sweep_phase_shift *= 2; + sweep_cutoff_ratio *= 2; + } - GB_sample_t stereo; + GB_sample_t stereo = {0, 0}; for (unsigned i = 0; i < count; i++) { + if ((i & downsample_mask)) { + gb->apu_output.sample_callback(gb, &stereo); + continue; + } double sample = 0; for (signed f = 0; f < 7 && f < jingle_stage; f++) { sample += fm_synth(gb->sgb_intro_jingle_phases[f]) * (0.75 * pow(0.5, jingle_stage - f) + 0.25) / 5.0; - gb->sgb_intro_jingle_phases[f] += frequencies[f] / gb->apu_output.sample_rate; + gb->sgb_intro_jingle_phases[f] += (frequencies[f] / gb->apu_output.sample_rate) * (downsample_mask + 1); } if (gb->sgb->intro_animation > 100) { sample *= pow((GB_SGB_INTRO_ANIMATION_LENGTH - gb->sgb->intro_animation) / (GB_SGB_INTRO_ANIMATION_LENGTH - 100.0), 3);
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.