audio: work out the sample clock ratios once, not after every instruction 9a6dd8288e68
audio_step() runs after every emulated instruction. It used the
*_SAMP_CLKS_PER_CPU_CLK macros, which divide by MHZ, a variable, so each
call did several 64-bit divisions at run time and then divided by the
result as well. They are constants for the run: audio_init() now works
them out once (MHZ is set by then), and the per-instruction division is
replaced by a multiply and compare, dividing only when the YM buffer is
nearly full. min(clocks, room / ratio) is clocks exactly when
clocks * ratio <= room, so the results are the same integers.
perf on narvi (riscv64) put audio.c:242-247 at about 13% of the run
time at the BASIC prompt; integer division is slow there. Measured with
the emulator stepping 600 frames, real-time mode, SDL dummy drivers
(CPU seconds per emulated second, median of 3; % of a real X16):
narvi idle 2.406 -> 2.183 (42% -> 46%)
narvi busy 1.974 -> 1.914 (51% -> 52%)
x86 idle 0.546 -> 0.393
x86 busy 0.574 -> 0.465
Emulation is unchanged: from power-on with -zeroram -nohostfs, three
BASIC workloads (VERA/VIA timing samples, bitmap LINE drawing, FM and
PSG notes), each with sound on and off, give the same registers, RAM
(low and all 64 banks) and screen at frame 900 as before, on x86 and
on riscv64.
- Author
- Andy McCall @andymccall <[email protected]>
- via
- agent
agent:claude@andymccall <[email protected]> · keyless, under your grant for changes,refs,seal · key 3d452845da48
- When
- · 2026-10-21 01:02 UTC
- Signature
- ◈ sealed by agent:claude@andymccall
- Hash
9a6dd8288e688b1c07d0291a355ac1ccee4ba62e289bd7992b925997dad453d3- Depends on
- 03a3459e6372
- Depended on by
- nothing yet
- In bookmarks
- main riscv-1
- Edits
- 4 atoms, 0 tree operations, 1 file
Browse the tree at this change
modified src/audio.c +16 −5
| … | 85 unchanged lines |
||
| 86 | 86 | static uint32_t vera_samps_per_host_samps = 0; |
|
| 87 | 87 | static uint32_t ym_samps_per_host_samps = 0; |
|
| 88 | 88 | static uint32_t fs_samps_per_host_samps = 0; |
|
| 89 | + | // The *_SAMP_CLKS_PER_CPU_CLK ratios, worked out once in audio_init(): |
|
| 90 | + | // MHZ is a variable, so the macros divide at run time, and audio_step() |
|
| 91 | + | // runs after every instruction. |
|
| 92 | + | static uint64_t vera_samp_clks_per_cpu_clk = 0; |
|
| 93 | + | static uint64_t ym_samp_clks_per_cpu_clk = 0; |
|
| 94 | + | static uint64_t fs_samp_clks_per_cpu_clk = 0; |
|
| 89 | 95 | static uint32_t limiter_amp = 0; |
|
| 90 | 96 | ||
| 91 | 97 | static int16_t psg_buf[2 * SAMPLES_PER_BUFFER]; |
|
| … | 100 unchanged lines |
||
| 192 | 198 | vera_samps_per_host_samps = ((25000000ULL << SAMP_POS_FRAC_BITS) / 512 / host_sample_rate); |
|
| 193 | 199 | ym_samps_per_host_samps = ((3579545ULL << SAMP_POS_FRAC_BITS) / 64 / host_sample_rate); |
|
| 194 | 200 | fs_samps_per_host_samps = vera_samps_per_host_samps; |
|
| 201 | + | vera_samp_clks_per_cpu_clk = VERA_SAMP_CLKS_PER_CPU_CLK; |
|
| 202 | + | ym_samp_clks_per_cpu_clk = YM_SAMP_CLKS_PER_CPU_CLK; |
|
| 203 | + | fs_samp_clks_per_cpu_clk = FS_SAMP_CLKS_PER_CPU_CLK; |
|
| 195 | 204 | vera_samp_pos_rd = 0; |
|
| 196 | 205 | vera_samp_pos_wr = 0; |
|
| 197 | 206 | vera_samp_pos_hd = 0; |
|
| … | 41 unchanged lines |
||
| 239 | 248 | ||
| 240 | 249 | while (cpu_clocks > 0) { |
|
| 241 | 250 | // Only the source with the higest sample rate (YM2151) is needed for calculation |
|
| 242 | - | uint32_t max_cpu_clks_ym = ((ym_samp_pos_rd - ym_samp_pos_hd - (1 << SAMP_POS_FRAC_BITS)) & SAMP_POS_MASK_FRAC) / YM_SAMP_CLKS_PER_CPU_CLK; |
|
| 243 | - | uint32_t max_cpu_clks = SDL_min(cpu_clocks, max_cpu_clks_ym); |
|
| 244 | - | vera_samp_pos_hd = (vera_samp_pos_hd + max_cpu_clks * VERA_SAMP_CLKS_PER_CPU_CLK) & SAMP_POS_MASK_FRAC; |
|
| 245 | - | ym_samp_pos_hd = (ym_samp_pos_hd + max_cpu_clks * YM_SAMP_CLKS_PER_CPU_CLK) & SAMP_POS_MASK_FRAC; |
|
| 246 | - | fs_samp_pos_hd = (fs_samp_pos_hd + max_cpu_clks * FS_SAMP_CLKS_PER_CPU_CLK) & SAMP_POS_MASK_FRAC; |
|
| 251 | + | uint32_t ym_room = (ym_samp_pos_rd - ym_samp_pos_hd - (1 << SAMP_POS_FRAC_BITS)) & SAMP_POS_MASK_FRAC; |
|
| 252 | + | // min(cpu_clocks, ym_room / ratio), dividing only when the buffer |
|
| 253 | + | // is nearly full rather than after every instruction |
|
| 254 | + | uint32_t max_cpu_clks = (uint64_t)cpu_clocks * ym_samp_clks_per_cpu_clk <= ym_room ? (uint32_t)cpu_clocks : (uint32_t)(ym_room / ym_samp_clks_per_cpu_clk); |
|
| 255 | + | vera_samp_pos_hd = (vera_samp_pos_hd + max_cpu_clks * vera_samp_clks_per_cpu_clk) & SAMP_POS_MASK_FRAC; |
|
| 256 | + | ym_samp_pos_hd = (ym_samp_pos_hd + max_cpu_clks * ym_samp_clks_per_cpu_clk) & SAMP_POS_MASK_FRAC; |
|
| 257 | + | fs_samp_pos_hd = (fs_samp_pos_hd + max_cpu_clks * fs_samp_clks_per_cpu_clk) & SAMP_POS_MASK_FRAC; |
|
| 247 | 258 | cpu_clocks -= max_cpu_clks; |
|
| 248 | 259 | if (cpu_clocks > 0) audio_render(); |
|
| 249 | 260 | } |
|
| … | 202 unchanged lines |