Files
psxrecomp/tools/_probe_one.py
T
Matthew Stanley f75159c8d6 SPU ADSR + free-voice CURVOL fix: chime fans across all 24 voices
Root cause: BIOS music engine reads voice register 6 (byte offset 0x0C,
CURVOL / ADSR_LEVEL) to find a free voice; recomp returned the never-
written spu_regs value (always 0), so the engine saw every voice as
silent and over-recycled voices 0-3 instead of fanning across all 24.
After fix, recomp KEYON-per-voice counts match Beetle exactly (e.g.
v0 2/2, v18 2/2, v22 1/1).

Also:
- Full ADSR envelope (Attack/Decay/Sustain/Release) ported from Beetle's
  CalcVCDelta/RunEnvelope. KEYOFF now triggers Release-phase decay
  rather than immediate silencing.
- End-without-loop triggers Release (matches RunDecoder), not active=0.
- ENDX latch at 0x1F801D9C/9E now reports actual block-end-reached
  state instead of active-mask.
- SPU event ring (KEYON/KEYOFF/END_STOP/END_LOOP) on both backends with
  frame-stamped per-voice payload; 1 M entries each (32 MB) for chime +
  game-scene headroom. Beetle-side env>0 filter so silent-loop noise
  doesn't bury the audible-voice timeline (matches recomp behavior
  where deactivated voices don't fire further events).
- spu_voices / spu_events / spu_events_reset debug-server commands on
  both ports. send_fmt buffer kept single-shot to avoid extra newlines.
- tools/_spu_ring_diff.py: frame-aligned event-ring diff with per-voice
  count summary; chunk-count cap removed (was aborting reads at 98 KB).
- tools/_spu_capture.py, _spu_probe.py, _probe_one.py: targeted probes
  used during diagnosis.

Local-only beetle-psx change (gitignored): same env>0 filter at
mednafen/psx/spu.cpp around line 345/352. Rebuild beetle-psx static
lib if reapplying.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 19:09:48 -07:00

26 lines
833 B
Python

import socket, json, sys
port = int(sys.argv[1]); cnt = int(sys.argv[2])
s = socket.create_connection(('127.0.0.1', port), timeout=5)
s.settimeout(5)
s.sendall(('{"cmd":"spu_events","id":1,"count":%d}\n' % cnt).encode())
buf = b''
while True:
try:
c = s.recv(1 << 20)
except socket.timeout:
print('TIMEOUT after', len(buf), 'bytes')
break
if not c:
print('EOF after', len(buf), 'bytes')
break
buf += c
if b'\n' in buf:
print('GOT NEWLINE at', buf.index(b'\n'), '/', len(buf), 'bytes total')
break
line = buf.split(b'\n', 1)[0]
try:
j = json.loads(line)
print('parse OK, total=', j['total'], 'count=', j['count'], 'events_len=', len(j['events']))
except Exception as e:
print('parse FAIL:', e, ' line len:', len(line), ' tail-300:', line[-300:])