Audio: ENABLE sound -- real OpenAL + in-tree WAV loader; both backend DLLs were no-op STUBS (task #50)

Root cause of "no sound, ever": the two audio backend DLLs shipped in the repo
are fakes. libsndfile-1.dll exports 15 funcs BY ORDINAL ONLY (no names) and
sf_open() always returns NULL; OpenAL32.dll (72KB) imports only KERNEL32 -- no
dsound/wasapi/winmm -- so it is a pure no-op that returns fake handles
(ctx=0x00000001, alGenSources->0) and never touches the hardware. The whole
render->device->buffer->source->play chain ran clean and silent.

Fixes:
- OpenAL32.dll: replace the stub with the real OpenAL Soft 1.25.2 Win32 build
  (imports AVRT/ole32/WINMM, real WASAPI backend). The exe imports the 25 AL
  funcs by NAME so it is a drop-in; alGenSources now yields a live source and
  alSourcePlay reaches AL_PLAYING.
- libsndfile: DROPPED entirely. It is replaced by LoadWavPCM() in L4AUDRES --
  a tiny RIFF/WAVE fmt+data reader that loads our soundbank WAVs (16-bit PCM)
  straight into the AL buffer. Removed the .lib/.dll from the link + copy and
  git-rm'd the stub. (This also kills the "ordinal 50 could not be located in
  libsndfile-1.dll" load-failure popup: adding an sf_strerror import bound to
  an ordinal the 2..16-only stub could not satisfy.)
- Soundbank: 241 samples cracked from AUDIO1/2.RES (SF2 v1.0) by
  tools/sf2extract.py into content/AUDIO/*.wav + the allPresets[2][128] table
  (audiopresets.cpp), replacing the zero-init btstubs stub. All 241 now load
  (alErr=0). BT_AUDIO_TEST plays buffer0 as proof-of-life; BT_AUDIO_LOG traces
  the chain.

Remaining: in-game triggering (AudioEntities on fire/step/engine/explosion)
so PlayNote fires during play -- next audio wave.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-15 18:55:33 -05:00
co-authored by Claude Opus 4.8
parent 52440e13b0
commit 5b46655b82
250 changed files with 1193 additions and 73 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ struct PRESETINFO
bool is3d;
};
extern PRESETINFO allPresets[2][100];
extern PRESETINFO allPresets[2][128];
bool PRESET_isImplemented(int bank, int preset);
int PRESET_getNumSamples(int bank, int preset);
+104 -66
View File
@@ -1,3 +1,6 @@
#include <stdio.h>
#include <direct.h>
#include <string.h>
#include <cstdlib>
#include "mungal4.h"
#pragma hdrstop
@@ -14,11 +17,65 @@
#include "..\munga\app.h"
#include "..\munga\notation.h"
#include "openal\al.h"
#include "sndfile.h"
#include "openal\alc.h"
ALuint *g_buffers;
int g_numBuffers;
//
// Minimal canonical-PCM WAV reader. The repo's libsndfile-1.dll is a STUB
// (exports 15 funcs by ordinal only, no names -- sf_open always returns NULL
// and sf_strerror(NULL) faults), so the original sf_open path loaded nothing.
// Our soundbank samples (AUDIO1/2.RES, cracked by scratchpad/sf2extract.py)
// are written as plain 16-bit mono PCM WAVs, so a tiny RIFF/WAVE fmt+data
// parser loads the real sample data directly into the AL buffer -- no
// external dependency, no stub. Handles 8/16-bit mono/stereo PCM.
//
static bool LoadWavPCM(const char *path, char **outData, int *outBytes,
int *outChannels, int *outBits, int *outRate)
{
FILE *fp = fopen(path, "rb");
if (!fp) return false;
fseek(fp, 0, SEEK_END); long flen = ftell(fp); fseek(fp, 0, SEEK_SET);
if (flen < 44) { fclose(fp); return false; }
unsigned char *buf = new unsigned char[flen];
size_t got = fread(buf, 1, flen, fp);
fclose(fp);
if ((long)got != flen) { delete [] buf; return false; }
if (memcmp(buf, "RIFF", 4) != 0 || memcmp(buf + 8, "WAVE", 4) != 0) { delete [] buf; return false; }
int channels = 0, bits = 0, rate = 0;
char *pcm = 0; int pcmBytes = 0;
bool haveFmt = false;
long p = 12;
while (p + 8 <= flen)
{
const unsigned char *id = buf + p;
unsigned int csz = buf[p+4] | (buf[p+5]<<8) | (buf[p+6]<<16) | ((unsigned)buf[p+7]<<24);
long body = p + 8;
if (memcmp(id, "fmt ", 4) == 0 && csz >= 16)
{
channels = buf[body+2] | (buf[body+3]<<8);
rate = buf[body+4] | (buf[body+5]<<8) | (buf[body+6]<<16) | ((unsigned)buf[body+7]<<24);
bits = buf[body+14] | (buf[body+15]<<8);
haveFmt = true;
}
else if (memcmp(id, "data", 4) == 0)
{
long avail = flen - body;
pcmBytes = ((long)csz <= avail) ? (int)csz : (int)avail;
if (pcmBytes < 0) pcmBytes = 0;
pcm = new char[pcmBytes > 0 ? pcmBytes : 1];
memcpy(pcm, buf + body, pcmBytes);
}
p = body + csz + (csz & 1); // RIFF chunks are word-aligned
}
delete [] buf;
if (!haveFmt || pcm == 0) { delete [] pcm; return false; }
*outData = pcm; *outBytes = pcmBytes;
*outChannels = channels; *outBits = bits; *outRate = rate;
return true;
}
//#############################################################################
//####################### AudioObjectStream #############################
//#############################################################################
@@ -543,7 +600,7 @@ void
for(int i=1; i <= 2; i++)
{
for (int j=0; j < 100; j++)
for (int j=0; j < 128; j++)
{
if (PRESET_isImplemented(i,j))
{
@@ -574,7 +631,7 @@ void
//Load buffers
for(int i=1; i<=2 && bufferInd < g_numBuffers; i++)
{
for(int j=0; j<100 && bufferInd < g_numBuffers; j++)
for(int j=0; j<128 && bufferInd < g_numBuffers; j++)
{
if (PRESET_isImplemented(i,j))
{
@@ -582,84 +639,65 @@ void
{
SAMPLEINFO info = PRESET_getSampleInfo(i,j,k);
//Load WAV to buffer
char *data;
int size;
//Open WAV
char fullname[50];
strcpy_s(fullname,50,"AUDIO\\");
strcpy_s(fullname,50,"AUDIO/");
strcat_s(fullname,50,info.file);
SF_INFO *sfInfo = new SF_INFO[2];
//SF_INFO is working different than expected!
sfInfo->format = 0;
SNDFILE *file = sf_open(fullname,SFM_READ,sfInfo);
if (file == NULL)
char *data = 0; int size = 0;
int wavCh = 0, wavBits = 0, wavRate = 0;
if (!LoadWavPCM(fullname, &data, &size, &wavCh, &wavBits, &wavRate))
{
DEBUG_STREAM << "Failed to open." << std::endl;
if (getenv("BT_AUDIO_LOG") && bufferInd == 0) {
char cwd[512]; cwd[0]=0; _getcwd(cwd, sizeof(cwd));
DEBUG_STREAM << "[audio] DIAG cwd=[" << cwd << "] path=[" << fullname << "] WAV load FAILED\n" << std::flush;
}
DEBUG_STREAM << "[audio] Failed to open AUDIO/" << info.file << " -- skipping\n" << std::flush;
PRESET_setBufferIndex(i,j,k,bufferInd);
bufferInd++;
continue;
}
unsigned long formatBits = 0;
unsigned long sampleRate = sfInfo->samplerate;
size = sfInfo->frames;
bool isMono = (sfInfo->channels == 1);
if (sfInfo->format & SF_FORMAT_PCM_S8)
{
formatBits = 8;
} else if (sfInfo->format & SF_FORMAT_PCM_16)
{
formatBits = 16;
} else
{
DEBUG_STREAM << "BAD FORMAT" << std::endl;
}
ALenum format;
if (formatBits == 8)
{
if (isMono)
{
format = AL_FORMAT_MONO8;
} else
{
size *= 2;
format = AL_FORMAT_STEREO8;
}
} else if (formatBits == 16)
{
size *= 2;
if (isMono)
{
format = AL_FORMAT_MONO16;
} else
{
size *= 2;
format = AL_FORMAT_STEREO16;
}
}
if (wavBits == 8)
format = (wavCh == 1) ? AL_FORMAT_MONO8 : AL_FORMAT_STEREO8;
else
format = (wavCh == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
ALsizei alSampleRate = sampleRate;
//Load size & data
delete [] sfInfo;
data = new char[size];
sf_read_raw(file,data,size);
sf_close(file);
//Feed the buffer
alBufferData(g_buffers[bufferInd],format,data,size,alSampleRate);
//Feed the buffer with the real PCM sample data
alBufferData(g_buffers[bufferInd], format, data, size, wavRate);
if (getenv("BT_AUDIO_LOG") && bufferInd < 3)
DEBUG_STREAM << "[audio] loaded buf" << bufferInd << " " << info.file
<< " ch=" << wavCh << " bits=" << wavBits << " rate=" << wavRate
<< " bytes=" << size << " alErr=" << alGetError() << "\n" << std::flush;
PRESET_setBufferIndex(i,j,k,bufferInd);
bufferInd++;
delete [] data;
delete [] data;
}
}
}
}
if (getenv("BT_AUDIO_TEST") && g_numBuffers > 0)
{
// PROOF OF LIFE: play the first loaded sample so we can confirm the
// SF2 -> WAV -> OpenAL buffer -> speakers path works end to end.
ALCcontext *ctx = alcGetCurrentContext();
alGetError();
ALuint testsrc = 0; alGenSources(1, &testsrc);
ALenum genErr = alGetError();
alSourcei(testsrc, AL_BUFFER, g_buffers[0]);
ALenum bufErr = alGetError();
alSourcei(testsrc, AL_LOOPING, AL_TRUE);
alSourcef(testsrc, AL_GAIN, 1.0f);
alSourcePlay(testsrc);
ALenum playErr = alGetError();
ALint st = 0; alGetSourcei(testsrc, AL_SOURCE_STATE, &st);
DEBUG_STREAM << "[audio] TEST-PLAY buffer0 src=" << testsrc
<< " ctx=" << (void*)ctx
<< " genErr=" << genErr << " bufErr=" << bufErr << " playErr=" << playErr
<< " state=" << st << " playing=" << (int)(st==AL_PLAYING) << "\n" << std::flush;
}
//
//----------------------------------------------------------------------
// Load the sound banks
Binary file not shown.
Binary file not shown.
Binary file not shown.