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:
co-authored by
Claude Opus 4.8
parent
52440e13b0
commit
5b46655b82
@@ -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
@@ -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.
Reference in New Issue
Block a user