port(audio): D202 M-67-M-69 runtime probes — GE_VOICEDUMP, [VOICEMAP], timestamped [WIRE]/[EVT]/[STOP-EVT]

Env-gated diagnostics for the D202 per-voice sample-correctness work
(M-67/M-68/M-69), all inert unless the env var is set:

- GE_VOICEDUMP (mixer.c aEnvMixerImpl): per-voice resampled mono stream
  to voicedump.raw, records [stateAddr][nSamples][us][s16 x n]. NB the
  us stamp is mix time — records render ahead, samples END at the stamp.
- [VOICEMAP] (synthesizer.c, under GE_AUDIOTRACE): full PVoice ->
  decoder/resampler/env-state address map at synth init, tying voicedump
  slot addresses to WIRE filter addresses.
- [WIRE] (load.c) now carries a µs timestamp; sndPlaySfx trace gains
  t=µs + wavetable pointer.
- [EVT]/[STOP-EVT] (snd.c): every ALSndpEvent the player processes, plus
  the release ramp a STOP/DEACTIVATE computes (deltaUs).
- audiodebug.ps1: -VoiceDump switch.

With scratchpad/exact_match.py these prove every allocated voice plays
the exact ROM sample at the exact ROM pitch (286/320 CC >= 0.85 on the
M-69 full-corpus run; remaining failures are dropped allocations + wire
attribution, not corruption). Remove once D202 closes.
This commit is contained in:
James Dansereau committed 2026-09-06 08:21:14 -04:00
1 parent 9b31f864d3
commit 4ceb917ad8
5 files changed
+85 -4

No files matched your search

+3 -1
View File
@@ -394,8 +394,10 @@ alLoadParam(void *filter, s32 paramID, void *param)
#if defined(__x86_64__)
if (getenv("GE_AUDIOTRACE")) {
static FILE *tf = NULL;
extern uint64_t sysGetMicroseconds(void); /* port/src/system.c */
if (!tf) { tf = fopen("audiotrace_wire.log", "a"); if (tf) setvbuf(tf, NULL, _IONBF, 0); }
if (tf) fprintf(tf, "[WIRE] filter=%p <- table=%p base=%p len=%d book=%p\n",
if (tf) fprintf(tf, "[WIRE] t=%llu filter=%p <- table=%p base=%p len=%d book=%p\n",
(unsigned long long)sysGetMicroseconds(),
(void *)a, (void *)a->table, (void *)a->table->base,
a->table->len,
(a->table->type == AL_ADPCM_WAVE) ? (void *)a->table->waveInfo.adpcmWave.book : NULL);
+22
View File
@@ -20,6 +20,10 @@
#include "synthInternals.h"
#include "include/assert.h"
#if defined(__x86_64__)
#include <stdio.h>
#include <stdlib.h>
#endif
#ifdef AUD_PROFILE
#include <os.h>
@@ -117,6 +121,24 @@ void alSynNew(ALSynth *drvr, ALSynConfig *c)
alAuxBusParam(drvr->auxBus, AL_FILTER_ADD_SOURCE, &pv->envmixer);
pv->channelKnob = (ALFilter *)&pv->envmixer;
#if defined(__x86_64__)
/* D202/M-69 diag (temporary): GE_AUDIOTRACE=1 logs the full
* PVoice -> decoder/resampler/env-state address map at synth init,
* so offline voicedump slot addresses can be tied to WIRE filter
* addresses (and hence to wave tables / sound indices). */
if (getenv("GE_AUDIOTRACE")) {
static FILE *mf = NULL;
if (!mf) { mf = fopen("audiotrace_wire.log", "a"); if (mf) setvbuf(mf, NULL, _IONBF, 0); }
if (i == 0)
fprintf(mf, "[VOICEMAP] sizes pv=%u dec=%u res=%u env=%u\n",
(unsigned)sizeof(PVoice), (unsigned)sizeof(ALLoadFilter),
(unsigned)sizeof(ALResampler), (unsigned)sizeof(ALEnvMixer));
if (mf) fprintf(mf, "[VOICEMAP] i=%d pv=%p dec=%p res=%p envstate=%p\n",
i, (void *)pv, (void *)&pv->decoder,
(void *)&pv->resampler, (void *)pv->envmixer.state);
}
#endif
}
alSaveParam(save, AL_FILTER_SET_SOURCE, drvr->mainBus);
+33 -2
View File
@@ -292,6 +292,19 @@ void sndHandleEvent(ALSndPlayer *sndp, ALSndpEvent *event) {
soundState = event->common.state;
sound = soundState->sound;
#ifdef PORT
/* D202/M-67 diag (temporary): log every event the player processes,
* so each VOICE- can be attributed to the exact killing event.
* Remove once root-caused. */
if (getenv("GE_AUDIOTRACE")) {
static FILE *fe = NULL;
extern uint64_t sysGetMicroseconds(void);
if (!fe) { fe = fopen("audiotrace.log", "a"); if (fe) setvbuf(fe, NULL, _IONBF, 0); }
if (fe) fprintf(fe, "[EVT] t=%llu type=%d state=%p\n",
(unsigned long long)sysGetMicroseconds(),
(int)event->common.type, (void *)soundState);
}
#endif
if (sound == NULL) {
s16 numFree, numAlloc;
@@ -486,6 +499,18 @@ void sndHandleEvent(ALSndPlayer *sndp, ALSndpEvent *event) {
case SOUND_STATE_PLAYING:
sndRemoveEvents(&sndp->evtq, soundState, AL_SNDP_DECAY_EVT);
delta = sound->envelope->releaseTime / soundState->pitch_28 / soundState->pitch_2c;
#ifdef PORT
/* D202/M-67 diag (temporary): log the release ramp a
* STOP/DEACTIVATE computes; delta==0 means the voice
* is disposed immediately. Remove once root-caused. */
if (getenv("GE_AUDIOTRACE")) {
static FILE *fs = NULL;
if (!fs) { fs = fopen("audiotrace.log", "a"); if (fs) setvbuf(fs, NULL, _IONBF, 0); }
if (fs) fprintf(fs, "[STOP-EVT] type=%d state=%p playingState=%d deltaUs=%d\n",
(int)event->common.type, (void *)soundState,
(int)soundState->playingState, (int)delta);
}
#endif
alSynSetVol(sndp->drvr, &soundState->voice, 0, delta);
if (delta != 0) {
spAC.common.type = AL_SNDP_END_EVT;
@@ -1059,16 +1084,22 @@ ALSoundState *sndPlaySfx(struct ALBankAlt_s *soundBank, s16 soundIndex, ALSoundS
(int)sound->envelope->releaseTime,
(unsigned)sound->envelope->attackVolume,
(unsigned)sound->envelope->decayVolume);
if (f2) fprintf(f2, "[AUDIOTRACE] sndPlaySfx: soundIndex=%d -> newState=%p flags=%d "
if (f2) {
extern uint64_t sysGetMicroseconds(void);
fprintf(f2, "[AUDIOTRACE] sndPlaySfx: t=%llu soundIndex=%d -> newState=%p wavetable=%p flags=%d "
"keyMap=%p velMin(next)=%d velMax(period)=%d keyMin=0x%02x keyMax=0x%02x "
"keyBase=%d detune=%d retrig=%d deltaLoopUs=%d\n",
(int)soundIndex, (void *)newState, newState ? (int)newState->unk3e : -1,
(unsigned long long)sysGetMicroseconds(),
(int)soundIndex, (void *)newState,
(void *)(sound ? sound->wavetable : NULL),
newState ? (int)newState->unk3e : -1,
(void *)kmp,
kmp ? (int)kmp->velocityMin : -1, kmp ? (int)kmp->velocityMax : -1,
kmp ? (unsigned)kmp->keyMin : 0u, kmp ? (unsigned)kmp->keyMax : 0u,
kmp ? (int)kmp->keyBase : -1, kmp ? (int)kmp->detune : -1,
(newState && (newState->unk3e & 0x10)) ? 1 : 0,
kmp ? (int)(kmp->velocityMax * DELTA_33_MS) : -1);
}
/* D202/M-65: only 8 voices exist (MUSIC_SFX_SEQ_MAYBE_MAX_SOUNDS).
* A SOUND_FLAG_LOOPED voice never self-releases and is skipped by
* the preemption scan, so each leaked one permanently costs a