2022-05-19 01:51:26 -07:00
|
|
|
#include "sound.h"
|
|
|
|
|
|
|
|
#include "debug.h"
|
|
|
|
#include "memory.h"
|
2022-05-28 14:22:40 -07:00
|
|
|
#include "platform_compat.h"
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
#include <io.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <mmsystem.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2022-05-28 04:45:48 -07:00
|
|
|
#include <algorithm>
|
|
|
|
|
2022-05-19 01:51:26 -07:00
|
|
|
// 0x51D478
|
|
|
|
STRUCT_51D478* _fadeHead = NULL;
|
|
|
|
|
|
|
|
// 0x51D47C
|
|
|
|
STRUCT_51D478* _fadeFreeList = NULL;
|
|
|
|
|
|
|
|
// 0x51D480
|
|
|
|
unsigned int _fadeEventHandle = UINT_MAX;
|
|
|
|
|
|
|
|
// 0x51D488
|
|
|
|
MallocProc* gSoundMallocProc = soundMallocProcDefaultImpl;
|
|
|
|
|
|
|
|
// 0x51D48C
|
|
|
|
ReallocProc* gSoundReallocProc = soundReallocProcDefaultImpl;
|
|
|
|
|
|
|
|
// 0x51D490
|
|
|
|
FreeProc* gSoundFreeProc = soundFreeProcDefaultImpl;
|
|
|
|
|
|
|
|
// 0x51D494
|
|
|
|
SoundFileIO gSoundDefaultFileIO = {
|
|
|
|
open,
|
|
|
|
close,
|
|
|
|
read,
|
|
|
|
write,
|
|
|
|
lseek,
|
|
|
|
tell,
|
2022-05-28 14:22:40 -07:00
|
|
|
compat_filelength,
|
2022-05-19 01:51:26 -07:00
|
|
|
-1,
|
|
|
|
};
|
|
|
|
|
|
|
|
// 0x51D4B4
|
|
|
|
SoundFileNameMangler* gSoundFileNameMangler = soundFileManglerDefaultImpl;
|
|
|
|
|
|
|
|
// 0x51D4B8
|
|
|
|
const char* gSoundErrorDescriptions[SOUND_ERR_COUNT] = {
|
|
|
|
"sound.c: No error",
|
|
|
|
"sound.c: SOS driver not loaded",
|
|
|
|
"sound.c: SOS invalid pointer",
|
|
|
|
"sound.c: SOS detect initialized",
|
|
|
|
"sound.c: SOS fail on file open",
|
|
|
|
"sound.c: SOS memory fail",
|
|
|
|
"sound.c: SOS invalid driver ID",
|
|
|
|
"sound.c: SOS no driver found",
|
|
|
|
"sound.c: SOS detection failure",
|
|
|
|
"sound.c: SOS driver loaded",
|
|
|
|
"sound.c: SOS invalid handle",
|
|
|
|
"sound.c: SOS no handles",
|
|
|
|
"sound.c: SOS paused",
|
|
|
|
"sound.c: SOS not paused",
|
|
|
|
"sound.c: SOS invalid data",
|
|
|
|
"sound.c: SOS drv file fail",
|
|
|
|
"sound.c: SOS invalid port",
|
|
|
|
"sound.c: SOS invalid IRQ",
|
|
|
|
"sound.c: SOS invalid DMA",
|
|
|
|
"sound.c: SOS invalid DMA IRQ",
|
|
|
|
"sound.c: no device",
|
|
|
|
"sound.c: not initialized",
|
|
|
|
"sound.c: no sound",
|
|
|
|
"sound.c: function not supported",
|
|
|
|
"sound.c: no buffers available",
|
|
|
|
"sound.c: file not found",
|
|
|
|
"sound.c: already playing",
|
|
|
|
"sound.c: not playing",
|
|
|
|
"sound.c: already paused",
|
|
|
|
"sound.c: not paused",
|
|
|
|
"sound.c: invalid handle",
|
|
|
|
"sound.c: no memory available",
|
|
|
|
"sound.c: unknown error",
|
|
|
|
};
|
|
|
|
|
|
|
|
// 0x668150
|
|
|
|
int gSoundLastError;
|
|
|
|
|
|
|
|
// 0x668154
|
|
|
|
int _masterVol;
|
|
|
|
|
|
|
|
// 0x668158
|
|
|
|
LPDIRECTSOUNDBUFFER gDirectSoundPrimaryBuffer;
|
|
|
|
|
|
|
|
// 0x66815C
|
|
|
|
int _sampleRate;
|
|
|
|
|
|
|
|
// Number of sounds currently playing.
|
|
|
|
//
|
|
|
|
// 0x668160
|
|
|
|
int _numSounds;
|
|
|
|
|
|
|
|
// 0x668164
|
|
|
|
int _deviceInit;
|
|
|
|
|
|
|
|
// 0x668168
|
|
|
|
int _dataSize;
|
|
|
|
|
|
|
|
// 0x66816C
|
|
|
|
int _numBuffers;
|
|
|
|
|
|
|
|
// 0x668170
|
|
|
|
bool gSoundInitialized;
|
|
|
|
|
|
|
|
// 0x668174
|
|
|
|
Sound* gSoundListHead;
|
|
|
|
|
|
|
|
// 0x668178
|
|
|
|
LPDIRECTSOUND gDirectSound;
|
|
|
|
|
|
|
|
// 0x4AC6F0
|
|
|
|
void* soundMallocProcDefaultImpl(size_t size)
|
|
|
|
{
|
|
|
|
return malloc(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AC6F8
|
|
|
|
void* soundReallocProcDefaultImpl(void* ptr, size_t size)
|
|
|
|
{
|
|
|
|
return realloc(ptr, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AC700
|
|
|
|
void soundFreeProcDefaultImpl(void* ptr)
|
|
|
|
{
|
|
|
|
free(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AC708
|
|
|
|
void soundSetMemoryProcs(MallocProc* mallocProc, ReallocProc* reallocProc, FreeProc* freeProc)
|
|
|
|
{
|
|
|
|
gSoundMallocProc = mallocProc;
|
|
|
|
gSoundReallocProc = reallocProc;
|
|
|
|
gSoundFreeProc = freeProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AC78C
|
|
|
|
char* soundFileManglerDefaultImpl(char* fname)
|
|
|
|
{
|
|
|
|
return fname;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AC790
|
|
|
|
const char* soundGetErrorDescription(int err)
|
|
|
|
{
|
|
|
|
if (err == -1) {
|
|
|
|
err = gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err < 0 || err > SOUND_UNKNOWN_ERROR) {
|
|
|
|
err = SOUND_UNKNOWN_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return gSoundErrorDescriptions[err];
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AC7B0
|
|
|
|
void _refreshSoundBuffers(Sound* sound)
|
|
|
|
{
|
|
|
|
if (sound->field_3C & 0x80) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD readPos;
|
|
|
|
DWORD writePos;
|
|
|
|
HRESULT hr = IDirectSoundBuffer_GetCurrentPosition(sound->directSoundBuffer, &readPos, &writePos);
|
|
|
|
if (hr != DS_OK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (readPos < sound->field_74) {
|
|
|
|
sound->field_64 += readPos + sound->field_78 * sound->field_7C - sound->field_74;
|
|
|
|
} else {
|
|
|
|
sound->field_64 += readPos - sound->field_74;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->field_3C & 0x0100) {
|
|
|
|
if (sound->field_44 & 0x20) {
|
|
|
|
if (sound->field_3C & 0x0200) {
|
|
|
|
sound->field_3C |= 0x80;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (sound->field_60 <= sound->field_64) {
|
|
|
|
sound->field_3C |= 0x0280;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sound->field_74 = readPos;
|
|
|
|
|
|
|
|
if (sound->field_60 < sound->field_64) {
|
|
|
|
int v3;
|
|
|
|
do {
|
|
|
|
v3 = sound->field_64 - sound->field_60;
|
|
|
|
sound->field_64 = v3;
|
|
|
|
} while (v3 > sound->field_60);
|
|
|
|
}
|
|
|
|
|
|
|
|
int v6 = readPos / sound->field_7C;
|
|
|
|
if (sound->field_70 == v6) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int v53;
|
|
|
|
if (sound->field_70 > v6) {
|
|
|
|
v53 = v6 + sound->field_78 - sound->field_70;
|
|
|
|
} else {
|
|
|
|
v53 = v6 - sound->field_70;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->field_7C * v53 >= sound->readLimit) {
|
|
|
|
v53 = (sound->readLimit + sound->field_7C - 1) / sound->field_7C;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (v53 < sound->field_5C) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID* audioPtr1;
|
|
|
|
VOID* audioPtr2;
|
|
|
|
DWORD audioBytes1;
|
|
|
|
DWORD audioBytes2;
|
|
|
|
hr = IDirectSoundBuffer_Lock(sound->directSoundBuffer, sound->field_7C * sound->field_70, sound->field_7C * v53, &audioPtr1, &audioBytes1, &audioPtr2, &audioBytes2, 0);
|
|
|
|
if (hr == DSERR_BUFFERLOST) {
|
|
|
|
IDirectSoundBuffer_Restore(sound->directSoundBuffer);
|
|
|
|
hr = IDirectSoundBuffer_Lock(sound->directSoundBuffer, sound->field_7C * sound->field_70, sound->field_7C * v53, &audioPtr1, &audioBytes1, &audioPtr2, &audioBytes2, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hr != DS_OK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (audioBytes1 + audioBytes2 != sound->field_7C * v53) {
|
|
|
|
debugPrint("locked memory region not big enough, wanted %d (%d * %d), got %d (%d + %d)\n", sound->field_7C * v53, v53, sound->field_7C, audioBytes1 + audioBytes2, audioBytes1, audioBytes2);
|
|
|
|
debugPrint("Resetting readBuffers from %d to %d\n", v53, (audioBytes1 + audioBytes2) / sound->field_7C);
|
|
|
|
|
|
|
|
v53 = (audioBytes1 + audioBytes2) / sound->field_7C;
|
|
|
|
if (v53 < sound->field_5C) {
|
|
|
|
debugPrint("No longer above read buffer size, returning\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
unsigned char* audioPtr = (unsigned char*)audioPtr1;
|
|
|
|
int audioBytes = audioBytes1;
|
|
|
|
while (--v53 != -1) {
|
|
|
|
int bytesRead;
|
|
|
|
if (sound->field_3C & 0x0200) {
|
|
|
|
bytesRead = sound->field_7C;
|
|
|
|
memset(sound->field_20, 0, bytesRead);
|
|
|
|
} else {
|
|
|
|
int bytesToRead = sound->field_7C;
|
|
|
|
if (sound->field_58 != -1) {
|
|
|
|
int pos = sound->io.tell(sound->io.fd);
|
|
|
|
if (bytesToRead + pos > sound->field_58) {
|
|
|
|
bytesToRead = sound->field_58 - pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bytesRead = sound->io.read(sound->io.fd, sound->field_20, bytesToRead);
|
|
|
|
if (bytesRead < sound->field_7C) {
|
|
|
|
if (!(sound->field_3C & 0x20) || (sound->field_3C & 0x0100)) {
|
|
|
|
memset(sound->field_20 + bytesRead, 0, sound->field_7C - bytesRead);
|
|
|
|
sound->field_3C |= 0x0200;
|
|
|
|
bytesRead = sound->field_7C;
|
|
|
|
} else {
|
|
|
|
while (bytesRead < sound->field_7C) {
|
|
|
|
if (sound->field_50 == -1) {
|
|
|
|
sound->io.seek(sound->io.fd, sound->field_54, SEEK_SET);
|
|
|
|
if (sound->callback != NULL) {
|
|
|
|
sound->callback(sound->callbackUserData, 0x0400);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (sound->field_50 <= 0) {
|
|
|
|
sound->field_58 = -1;
|
|
|
|
sound->field_54 = 0;
|
|
|
|
sound->field_50 = 0;
|
|
|
|
sound->field_3C &= ~0x20;
|
|
|
|
bytesRead += sound->io.read(sound->io.fd, sound->field_20 + bytesRead, sound->field_7C - bytesRead);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
sound->field_50--;
|
|
|
|
sound->io.seek(sound->io.fd, sound->field_54, SEEK_SET);
|
|
|
|
|
|
|
|
if (sound->callback != NULL) {
|
|
|
|
sound->callback(sound->callbackUserData, 0x400);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->field_58 == -1) {
|
|
|
|
bytesToRead = sound->field_7C - bytesRead;
|
|
|
|
} else {
|
|
|
|
int pos = sound->io.tell(sound->io.fd);
|
|
|
|
if (sound->field_7C + bytesRead + pos <= sound->field_58) {
|
|
|
|
bytesToRead = sound->field_7C - bytesRead;
|
|
|
|
} else {
|
|
|
|
bytesToRead = sound->field_58 - bytesRead - pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int v20 = sound->io.read(sound->io.fd, sound->field_20 + bytesRead, bytesToRead);
|
|
|
|
bytesRead += v20;
|
|
|
|
if (v20 < bytesToRead) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bytesRead > audioBytes) {
|
|
|
|
if (audioBytes != 0) {
|
|
|
|
memcpy(audioPtr, sound->field_20, audioBytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (audioPtr2 != NULL) {
|
|
|
|
memcpy(audioPtr2, sound->field_20 + audioBytes, bytesRead - audioBytes);
|
|
|
|
audioPtr = (unsigned char*)audioPtr2 + bytesRead - audioBytes;
|
|
|
|
audioBytes = audioBytes2 - bytesRead;
|
|
|
|
} else {
|
|
|
|
debugPrint("Hm, no second write pointer, but buffer not big enough, this shouldn't happen\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
memcpy(audioPtr, sound->field_20, bytesRead);
|
|
|
|
audioPtr += bytesRead;
|
|
|
|
audioBytes -= bytesRead;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
IDirectSoundBuffer_Unlock(sound->directSoundBuffer, audioPtr1, audioBytes1, audioPtr2, audioBytes2);
|
|
|
|
|
|
|
|
sound->field_70 = v6;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4ACC58
|
|
|
|
int soundInit(int a1, int a2, int a3, int a4, int rate)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
DWORD v24;
|
|
|
|
|
|
|
|
if (gDirectSoundCreateProc(0, &gDirectSound, 0) != DS_OK) {
|
|
|
|
gDirectSound = NULL;
|
|
|
|
gSoundLastError = SOUND_SOS_DETECTION_FAILURE;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IDirectSound_SetCooperativeLevel(gDirectSound, gProgramWindow, DSSCL_EXCLUSIVE) != DS_OK) {
|
|
|
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
_sampleRate = rate;
|
|
|
|
_dataSize = a4;
|
|
|
|
_numBuffers = a2;
|
|
|
|
gSoundInitialized = true;
|
|
|
|
_deviceInit = 1;
|
|
|
|
|
|
|
|
DSBUFFERDESC dsbdesc;
|
|
|
|
memset(&dsbdesc, 0, sizeof(dsbdesc));
|
|
|
|
dsbdesc.dwSize = sizeof(dsbdesc);
|
|
|
|
dsbdesc.dwFlags = DSCAPS_PRIMARYMONO;
|
|
|
|
dsbdesc.dwBufferBytes = 0;
|
|
|
|
|
|
|
|
hr = IDirectSound_CreateSoundBuffer(gDirectSound, &dsbdesc, &gDirectSoundPrimaryBuffer, NULL);
|
|
|
|
if (hr != DS_OK) {
|
|
|
|
switch (hr) {
|
|
|
|
case DSERR_ALLOCATED:
|
|
|
|
debugPrint("%s:%s\n", "CreateSoundBuffer", "DSERR_ALLOCATED");
|
|
|
|
break;
|
|
|
|
case DSERR_BADFORMAT:
|
|
|
|
debugPrint("%s:%s\n", "CreateSoundBuffer", "DSERR_BADFORMAT");
|
|
|
|
break;
|
|
|
|
case DSERR_INVALIDPARAM:
|
|
|
|
debugPrint("%s:%s\n", "CreateSoundBuffer", "DSERR_INVALIDPARAM");
|
|
|
|
break;
|
|
|
|
case DSERR_NOAGGREGATION:
|
|
|
|
debugPrint("%s:%s\n", "CreateSoundBuffer", "DSERR_NOAGGREGATION");
|
|
|
|
break;
|
|
|
|
case DSERR_OUTOFMEMORY:
|
|
|
|
debugPrint("%s:%s\n", "CreateSoundBuffer", "DSERR_OUTOFMEMORY");
|
|
|
|
break;
|
|
|
|
case DSERR_UNINITIALIZED:
|
|
|
|
debugPrint("%s:%s\n", "CreateSoundBuffer", "DSERR_UNINITIALIZED");
|
|
|
|
break;
|
|
|
|
case DSERR_UNSUPPORTED:
|
|
|
|
debugPrint("%s:%s\n", "CreateSoundBuffer", "DSERR_UNSUPPORTED");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
WAVEFORMATEX pcmwf;
|
|
|
|
memset(&pcmwf, 0, sizeof(pcmwf));
|
|
|
|
|
|
|
|
DSCAPS dscaps;
|
|
|
|
memset(&dscaps, 0, sizeof(dscaps));
|
|
|
|
dscaps.dwSize = sizeof(dscaps);
|
|
|
|
|
|
|
|
hr = IDirectSound_GetCaps(gDirectSound, &dscaps);
|
|
|
|
if (hr != DS_OK) {
|
|
|
|
debugPrint("soundInit: Error getting primary buffer parameters\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcmwf.nSamplesPerSec = rate;
|
|
|
|
pcmwf.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
|
|
|
|
if (dscaps.dwFlags & DSCAPS_PRIMARY16BIT) {
|
|
|
|
pcmwf.wBitsPerSample = 16;
|
|
|
|
} else {
|
|
|
|
pcmwf.wBitsPerSample = 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcmwf.nChannels = (dscaps.dwFlags & DSCAPS_PRIMARYSTEREO) ? 2 : 1;
|
|
|
|
pcmwf.nBlockAlign = pcmwf.wBitsPerSample * pcmwf.nChannels / 8;
|
|
|
|
pcmwf.nSamplesPerSec = rate;
|
|
|
|
pcmwf.cbSize = 0;
|
|
|
|
pcmwf.nAvgBytesPerSec = pcmwf.nBlockAlign * rate;
|
|
|
|
|
|
|
|
debugPrint("soundInit: Setting primary buffer to: %d bit, %d channels, %d rate\n", pcmwf.wBitsPerSample, pcmwf.nChannels, rate);
|
|
|
|
hr = IDirectSoundBuffer_SetFormat(gDirectSoundPrimaryBuffer, &pcmwf);
|
|
|
|
if (hr != DS_OK) {
|
|
|
|
debugPrint("soundInit: Couldn't change rate to %d\n", rate);
|
|
|
|
|
|
|
|
switch (hr) {
|
|
|
|
case DSERR_BADFORMAT:
|
|
|
|
debugPrint("%s:%s\n", "SetFormat", "DSERR_BADFORMAT");
|
|
|
|
break;
|
|
|
|
case DSERR_INVALIDCALL:
|
|
|
|
debugPrint("%s:%s\n", "SetFormat", "DSERR_INVALIDCALL");
|
|
|
|
break;
|
|
|
|
case DSERR_INVALIDPARAM:
|
|
|
|
debugPrint("%s:%s\n", "SetFormat", "DSERR_INVALIDPARAM");
|
|
|
|
break;
|
|
|
|
case DSERR_OUTOFMEMORY:
|
|
|
|
debugPrint("%s:%s\n", "SetFormat", "DSERR_OUTOFMEMORY");
|
|
|
|
break;
|
|
|
|
case DSERR_PRIOLEVELNEEDED:
|
|
|
|
debugPrint("%s:%s\n", "SetFormat", "DSERR_PRIOLEVELNEEDED");
|
|
|
|
break;
|
|
|
|
case DSERR_UNSUPPORTED:
|
|
|
|
debugPrint("%s:%s\n", "SetFormat", "DSERR_UNSUPPORTED");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = IDirectSoundBuffer_GetFormat(gDirectSoundPrimaryBuffer, &pcmwf, sizeof(WAVEFORMATEX), &v24);
|
|
|
|
if (hr != DS_OK) {
|
|
|
|
debugPrint("soundInit: Couldn't read new settings\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
debugPrint("soundInit: Primary buffer settings set to: %d bit, %d channels, %d rate\n", pcmwf.wBitsPerSample, pcmwf.nChannels, pcmwf.nSamplesPerSec);
|
|
|
|
|
|
|
|
if (dscaps.dwFlags & DSCAPS_EMULDRIVER) {
|
|
|
|
debugPrint("soundInit: using DirectSound emulated drivers\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
_soundSetMasterVolume(VOLUME_MAX);
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AD04C
|
|
|
|
void soundExit()
|
|
|
|
{
|
|
|
|
while (gSoundListHead != NULL) {
|
|
|
|
Sound* next = gSoundListHead->next;
|
|
|
|
soundDelete(gSoundListHead);
|
|
|
|
gSoundListHead = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_fadeEventHandle != -1) {
|
|
|
|
_removeTimedEvent(&_fadeEventHandle);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (_fadeFreeList != NULL) {
|
|
|
|
STRUCT_51D478* next = _fadeFreeList->next;
|
|
|
|
gSoundFreeProc(_fadeFreeList);
|
|
|
|
_fadeFreeList = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gDirectSoundPrimaryBuffer != NULL) {
|
|
|
|
IDirectSoundBuffer_Release(gDirectSoundPrimaryBuffer);
|
|
|
|
gDirectSoundPrimaryBuffer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gDirectSound != NULL) {
|
|
|
|
IDirectSound_Release(gDirectSound);
|
|
|
|
gDirectSound = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
gSoundInitialized = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AD0FC
|
|
|
|
Sound* soundAllocate(int a1, int a2)
|
|
|
|
{
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-05-21 08:22:03 -07:00
|
|
|
Sound* sound = (Sound*)gSoundMallocProc(sizeof(*sound));
|
2022-05-19 01:51:26 -07:00
|
|
|
memset(sound, 0, sizeof(*sound));
|
|
|
|
|
|
|
|
memcpy(&(sound->io), &gSoundDefaultFileIO, sizeof(gSoundDefaultFileIO));
|
|
|
|
|
2022-05-21 08:22:03 -07:00
|
|
|
WAVEFORMATEX* wfxFormat = (WAVEFORMATEX*)gSoundMallocProc(sizeof(*wfxFormat));
|
2022-05-19 01:51:26 -07:00
|
|
|
memset(wfxFormat, 0, sizeof(*wfxFormat));
|
|
|
|
|
|
|
|
wfxFormat->wFormatTag = 1;
|
|
|
|
wfxFormat->nChannels = 1;
|
|
|
|
|
|
|
|
if (a2 & 0x08) {
|
|
|
|
wfxFormat->wBitsPerSample = 16;
|
|
|
|
} else {
|
|
|
|
wfxFormat->wBitsPerSample = 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(a2 & 0x02)) {
|
|
|
|
a2 |= 0x02;
|
|
|
|
}
|
|
|
|
|
|
|
|
wfxFormat->nSamplesPerSec = _sampleRate;
|
|
|
|
wfxFormat->nBlockAlign = wfxFormat->nChannels * (wfxFormat->wBitsPerSample / 8);
|
|
|
|
wfxFormat->cbSize = 0;
|
|
|
|
wfxFormat->nAvgBytesPerSec = wfxFormat->nBlockAlign * wfxFormat->nSamplesPerSec;
|
|
|
|
|
|
|
|
sound->field_3C = a2;
|
|
|
|
sound->field_44 = a1;
|
|
|
|
sound->field_7C = _dataSize;
|
|
|
|
sound->field_64 = 0;
|
|
|
|
sound->directSoundBuffer = 0;
|
|
|
|
sound->field_40 = 0;
|
|
|
|
sound->directSoundBufferDescription.dwSize = sizeof(DSBUFFERDESC);
|
|
|
|
sound->directSoundBufferDescription.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
|
|
|
|
sound->field_78 = _numBuffers;
|
|
|
|
sound->readLimit = sound->field_7C * _numBuffers;
|
|
|
|
|
|
|
|
if (a2 & 0x2) {
|
|
|
|
sound->directSoundBufferDescription.dwFlags |= DSBCAPS_CTRLVOLUME;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a2 & 0x4) {
|
|
|
|
sound->directSoundBufferDescription.dwFlags |= DSBCAPS_CTRLPAN;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a2 & 0x40) {
|
|
|
|
sound->directSoundBufferDescription.dwFlags |= DSBCAPS_CTRLFREQUENCY;
|
|
|
|
}
|
|
|
|
|
|
|
|
sound->directSoundBufferDescription.lpwfxFormat = wfxFormat;
|
|
|
|
|
|
|
|
if (a1 & 0x10) {
|
|
|
|
sound->field_50 = -1;
|
|
|
|
sound->field_3C |= 0x20;
|
|
|
|
}
|
|
|
|
|
|
|
|
sound->field_58 = -1;
|
|
|
|
sound->field_5C = 1;
|
|
|
|
sound->volume = VOLUME_MAX;
|
|
|
|
sound->prev = NULL;
|
|
|
|
sound->field_54 = 0;
|
|
|
|
sound->next = gSoundListHead;
|
|
|
|
|
|
|
|
if (gSoundListHead != NULL) {
|
|
|
|
gSoundListHead->prev = sound;
|
|
|
|
}
|
|
|
|
|
|
|
|
gSoundListHead = sound;
|
|
|
|
|
|
|
|
return sound;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AD308
|
|
|
|
int _preloadBuffers(Sound* sound)
|
|
|
|
{
|
|
|
|
unsigned char* buf;
|
|
|
|
int bytes_read;
|
|
|
|
int result;
|
|
|
|
int v15;
|
|
|
|
unsigned char* v14;
|
|
|
|
int size;
|
|
|
|
|
|
|
|
size = sound->io.filelength(sound->io.fd);
|
|
|
|
sound->field_60 = size;
|
|
|
|
|
|
|
|
if (sound->field_44 & 0x02) {
|
|
|
|
if (!(sound->field_3C & 0x20)) {
|
|
|
|
sound->field_3C |= 0x0120;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->field_78 * sound->field_7C >= size) {
|
|
|
|
if (size / sound->field_7C * sound->field_7C != size) {
|
|
|
|
size = (size / sound->field_7C + 1) * sound->field_7C;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
size = sound->field_78 * sound->field_7C;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sound->field_44 &= ~(0x03);
|
|
|
|
sound->field_44 |= 0x01;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = (unsigned char*)gSoundMallocProc(size);
|
|
|
|
bytes_read = sound->io.read(sound->io.fd, buf, size);
|
|
|
|
if (bytes_read != size) {
|
|
|
|
if (!(sound->field_3C & 0x20) || (sound->field_3C & (0x01 << 8))) {
|
|
|
|
memset(buf + bytes_read, 0, size - bytes_read);
|
|
|
|
} else {
|
|
|
|
v14 = buf + bytes_read;
|
|
|
|
v15 = bytes_read;
|
|
|
|
while (size - v15 > bytes_read) {
|
|
|
|
memcpy(v14, buf, bytes_read);
|
|
|
|
v15 += bytes_read;
|
|
|
|
v14 += bytes_read;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (v15 < size) {
|
|
|
|
memcpy(v14, buf, size - v15);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result = _soundSetData(sound, buf, size);
|
|
|
|
gSoundFreeProc(buf);
|
|
|
|
|
|
|
|
if (sound->field_44 & 0x01) {
|
|
|
|
sound->io.close(sound->io.fd);
|
|
|
|
sound->io.fd = -1;
|
|
|
|
} else {
|
|
|
|
if (sound->field_20 == NULL) {
|
|
|
|
sound->field_20 = (unsigned char*)gSoundMallocProc(sound->field_7C);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AD498
|
|
|
|
int soundLoad(Sound* sound, char* filePath)
|
|
|
|
{
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
sound->io.fd = sound->io.open(gSoundFileNameMangler(filePath), 0x0200);
|
|
|
|
if (sound->io.fd == -1) {
|
|
|
|
gSoundLastError = SOUND_FILE_NOT_FOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
return _preloadBuffers(sound);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AD504
|
|
|
|
int _soundRewind(Sound* sound)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL || sound->directSoundBuffer == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->field_44 & 0x02) {
|
|
|
|
sound->io.seek(sound->io.fd, 0, SEEK_SET);
|
|
|
|
sound->field_70 = 0;
|
|
|
|
sound->field_74 = 0;
|
|
|
|
sound->field_64 = 0;
|
|
|
|
sound->field_3C &= 0xFD7F;
|
|
|
|
hr = IDirectSoundBuffer_SetCurrentPosition(sound->directSoundBuffer, 0);
|
|
|
|
_preloadBuffers(sound);
|
|
|
|
} else {
|
|
|
|
hr = IDirectSoundBuffer_SetCurrentPosition(sound->directSoundBuffer, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hr != DS_OK) {
|
|
|
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
sound->field_40 &= ~(0x01);
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AD5C8
|
|
|
|
int _addSoundData(Sound* sound, unsigned char* buf, int size)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
void* audio_ptr_1;
|
|
|
|
DWORD audio_bytes_1;
|
|
|
|
void* audio_ptr_2;
|
|
|
|
DWORD audio_bytes_2;
|
|
|
|
|
|
|
|
hr = IDirectSoundBuffer_Lock(sound->directSoundBuffer, 0, size, &audio_ptr_1, &audio_bytes_1, &audio_ptr_2, &audio_bytes_2, DSBLOCK_FROMWRITECURSOR);
|
|
|
|
if (hr == DSERR_BUFFERLOST) {
|
|
|
|
IDirectSoundBuffer_Restore(sound->directSoundBuffer);
|
|
|
|
hr = IDirectSoundBuffer_Lock(sound->directSoundBuffer, 0, size, &audio_ptr_1, &audio_bytes_1, &audio_ptr_2, &audio_bytes_2, DSBLOCK_FROMWRITECURSOR);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hr != DS_OK) {
|
|
|
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(audio_ptr_1, buf, audio_bytes_1);
|
|
|
|
|
|
|
|
if (audio_ptr_2 != NULL) {
|
|
|
|
memcpy(audio_ptr_2, buf + audio_bytes_1, audio_bytes_2);
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = IDirectSoundBuffer_Unlock(sound->directSoundBuffer, audio_ptr_1, audio_bytes_1, audio_ptr_2, audio_bytes_2);
|
|
|
|
if (hr != DS_OK) {
|
|
|
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AD6C0
|
|
|
|
int _soundSetData(Sound* sound, unsigned char* buf, int size)
|
|
|
|
{
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->directSoundBuffer == NULL) {
|
|
|
|
sound->directSoundBufferDescription.dwBufferBytes = size;
|
|
|
|
|
|
|
|
if (IDirectSound_CreateSoundBuffer(gDirectSound, &(sound->directSoundBufferDescription), &(sound->directSoundBuffer), NULL) != DS_OK) {
|
|
|
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return _addSoundData(sound, buf, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AD73C
|
|
|
|
int soundPlay(Sound* sound)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
DWORD readPos;
|
|
|
|
DWORD writePos;
|
|
|
|
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL || sound->directSoundBuffer == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Check.
|
|
|
|
if (sound->field_40 & 0x01) {
|
|
|
|
_soundRewind(sound);
|
|
|
|
}
|
|
|
|
|
|
|
|
soundSetVolume(sound, sound->volume);
|
|
|
|
|
|
|
|
hr = IDirectSoundBuffer_Play(sound->directSoundBuffer, 0, 0, sound->field_3C & 0x20 ? DSBPLAY_LOOPING : 0);
|
|
|
|
|
|
|
|
IDirectSoundBuffer_GetCurrentPosition(sound->directSoundBuffer, &readPos, &writePos);
|
|
|
|
sound->field_70 = readPos / sound->field_7C;
|
|
|
|
|
|
|
|
if (hr != DS_OK) {
|
|
|
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
sound->field_40 |= SOUND_FLAG_SOUND_IS_PLAYING;
|
|
|
|
|
|
|
|
++_numSounds;
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AD828
|
|
|
|
int soundStop(Sound* sound)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL || sound->directSoundBuffer == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(sound->field_40 & SOUND_FLAG_SOUND_IS_PLAYING)) {
|
|
|
|
gSoundLastError = SOUND_NOT_PLAYING;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = IDirectSoundBuffer_Stop(sound->directSoundBuffer);
|
|
|
|
if (hr != DS_OK) {
|
|
|
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
sound->field_40 &= ~SOUND_FLAG_SOUND_IS_PLAYING;
|
|
|
|
_numSounds--;
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AD8DC
|
|
|
|
int soundDelete(Sound* sample)
|
|
|
|
{
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sample == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sample->io.fd != -1) {
|
|
|
|
sample->io.close(sample->io.fd);
|
|
|
|
sample->io.fd = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
soundDeleteInternal(sample);
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AD948
|
|
|
|
int soundContinue(Sound* sound)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
DWORD status;
|
|
|
|
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->directSoundBuffer == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(sound->field_40 & SOUND_FLAG_SOUND_IS_PLAYING) || (sound->field_40 & SOUND_FLAG_SOUND_IS_PAUSED)) {
|
|
|
|
gSoundLastError = SOUND_NOT_PLAYING;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->field_40 & 0x01) {
|
|
|
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = IDirectSoundBuffer_GetStatus(sound->directSoundBuffer, &status);
|
|
|
|
if (hr != DS_OK) {
|
|
|
|
debugPrint("Error in soundContinue, %x\n", hr);
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(sound->field_3C & 0x80) && (status & (DSBSTATUS_PLAYING | DSBSTATUS_LOOPING))) {
|
|
|
|
if (!(sound->field_40 & SOUND_FLAG_SOUND_IS_PAUSED) && (sound->field_44 & 0x02)) {
|
|
|
|
_refreshSoundBuffers(sound);
|
|
|
|
}
|
|
|
|
} else if (!(sound->field_40 & SOUND_FLAG_SOUND_IS_PAUSED)) {
|
|
|
|
if (sound->callback != NULL) {
|
|
|
|
sound->callback(sound->callbackUserData, 1);
|
|
|
|
sound->callback = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->field_44 & 0x04) {
|
|
|
|
sound->callback = NULL;
|
|
|
|
soundDelete(sound);
|
|
|
|
} else {
|
|
|
|
sound->field_40 |= 0x01;
|
|
|
|
|
|
|
|
if (sound->field_40 & 0x02) {
|
|
|
|
--_numSounds;
|
|
|
|
}
|
|
|
|
|
|
|
|
soundStop(sound);
|
|
|
|
|
|
|
|
sound->field_40 &= ~(0x03);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4ADA84
|
|
|
|
bool soundIsPlaying(Sound* sound)
|
|
|
|
{
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL || sound->directSoundBuffer == 0) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (sound->field_40 & SOUND_FLAG_SOUND_IS_PLAYING) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4ADAC4
|
|
|
|
bool _soundDone(Sound* sound)
|
|
|
|
{
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL || sound->directSoundBuffer == 0) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sound->field_40 & 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4ADB44
|
|
|
|
bool soundIsPaused(Sound* sound)
|
|
|
|
{
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL || sound->directSoundBuffer == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (sound->field_40 & SOUND_FLAG_SOUND_IS_PAUSED) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4ADBC4
|
|
|
|
int _soundType(Sound* sound, int a2)
|
|
|
|
{
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL || sound->directSoundBuffer == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sound->field_44 & a2;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4ADC04
|
|
|
|
int soundGetDuration(Sound* sound)
|
|
|
|
{
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL || sound->directSoundBuffer == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bytesPerSec = sound->directSoundBufferDescription.lpwfxFormat->nAvgBytesPerSec;
|
|
|
|
int v3 = sound->field_60;
|
|
|
|
int v4 = v3 % bytesPerSec;
|
|
|
|
int result = v3 / bytesPerSec;
|
|
|
|
if (v4 != 0) {
|
|
|
|
result += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4ADD00
|
|
|
|
int soundSetLooping(Sound* sound, int a2)
|
|
|
|
{
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a2) {
|
|
|
|
sound->field_3C |= 0x20;
|
|
|
|
sound->field_50 = a2;
|
|
|
|
} else {
|
|
|
|
sound->field_50 = 0;
|
|
|
|
sound->field_58 = -1;
|
|
|
|
sound->field_54 = 0;
|
|
|
|
sound->field_3C &= ~(0x20);
|
|
|
|
}
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Normalize volume?
|
|
|
|
//
|
|
|
|
// 0x4ADD68
|
|
|
|
int _soundVolumeHMItoDirectSound(int volume)
|
|
|
|
{
|
|
|
|
double normalizedVolume;
|
|
|
|
|
|
|
|
if (volume > VOLUME_MAX) {
|
|
|
|
volume = VOLUME_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (volume != 0) {
|
|
|
|
normalizedVolume = -1000.0 * log2(32767.0 / volume);
|
2022-05-28 04:45:48 -07:00
|
|
|
normalizedVolume = std::clamp(normalizedVolume, -10000.0, 0.0);
|
2022-05-19 01:51:26 -07:00
|
|
|
} else {
|
|
|
|
normalizedVolume = -10000.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (int)normalizedVolume;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4ADE0C
|
|
|
|
int soundSetVolume(Sound* sound, int volume)
|
|
|
|
{
|
|
|
|
int normalizedVolume;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
sound->volume = volume;
|
|
|
|
|
|
|
|
if (sound->directSoundBuffer == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
normalizedVolume = _soundVolumeHMItoDirectSound(_masterVol * volume / VOLUME_MAX);
|
|
|
|
|
|
|
|
hr = IDirectSoundBuffer_SetVolume(sound->directSoundBuffer, normalizedVolume);
|
|
|
|
if (hr != DS_OK) {
|
|
|
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4ADE80
|
|
|
|
int _soundGetVolume(Sound* sound)
|
|
|
|
{
|
|
|
|
long volume;
|
|
|
|
int v13;
|
|
|
|
int v8;
|
|
|
|
int diff;
|
|
|
|
|
|
|
|
if (!_deviceInit) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL || sound->directSoundBuffer == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
IDirectSoundBuffer_GetVolume(sound->directSoundBuffer, &volume);
|
|
|
|
|
|
|
|
if (volume == -10000) {
|
|
|
|
v13 = 0;
|
|
|
|
} else {
|
|
|
|
// TODO: Check.
|
|
|
|
volume = -volume;
|
|
|
|
v13 = (int)(32767.0 / pow(2.0, (volume * 0.001)));
|
|
|
|
}
|
|
|
|
|
|
|
|
v8 = VOLUME_MAX * v13 / _masterVol;
|
|
|
|
diff = abs(v8 - sound->volume);
|
|
|
|
if (diff > 20) {
|
|
|
|
debugPrint("Actual sound volume differs significantly from noted volume actual %x stored %x, diff %d.", v8, sound->volume, diff);
|
|
|
|
}
|
|
|
|
|
|
|
|
return sound->volume;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4ADFF0
|
|
|
|
int soundSetCallback(Sound* sound, SoundCallback* callback, void* userData)
|
|
|
|
{
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
sound->callback = callback;
|
|
|
|
sound->callbackUserData = userData;
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AE02C
|
|
|
|
int soundSetChannels(Sound* sound, int channels)
|
|
|
|
{
|
|
|
|
LPWAVEFORMATEX format;
|
|
|
|
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (channels == 3) {
|
|
|
|
format = sound->directSoundBufferDescription.lpwfxFormat;
|
|
|
|
|
|
|
|
format->nBlockAlign = (2 * format->wBitsPerSample) / 8;
|
|
|
|
format->nChannels = 2;
|
|
|
|
format->nAvgBytesPerSec = format->nBlockAlign * _sampleRate;
|
|
|
|
}
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AE0B0
|
|
|
|
int soundSetReadLimit(Sound* sound, int readLimit)
|
|
|
|
{
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_DEVICE;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
sound->readLimit = readLimit;
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Check, looks like it uses couple of inlined functions.
|
|
|
|
//
|
|
|
|
// 0x4AE0E4
|
|
|
|
int soundPause(Sound* sound)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
DWORD readPos;
|
|
|
|
DWORD writePos;
|
|
|
|
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->directSoundBuffer == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(sound->field_40 & SOUND_FLAG_SOUND_IS_PLAYING)) {
|
|
|
|
gSoundLastError = SOUND_NOT_PLAYING;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->field_40 & SOUND_FLAG_SOUND_IS_PAUSED) {
|
|
|
|
gSoundLastError = SOUND_ALREADY_PAUSED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = IDirectSoundBuffer_GetCurrentPosition(sound->directSoundBuffer, &readPos, &writePos);
|
|
|
|
if (hr != DS_OK) {
|
|
|
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
sound->field_48 = readPos;
|
|
|
|
sound->field_40 |= SOUND_FLAG_SOUND_IS_PAUSED;
|
|
|
|
|
|
|
|
return soundStop(sound);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Check, looks like it uses couple of inlined functions.
|
|
|
|
//
|
|
|
|
// 0x4AE1F0
|
|
|
|
int soundResume(Sound* sound)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL || sound->directSoundBuffer == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((sound->field_40 & SOUND_FLAG_SOUND_IS_PLAYING) != 0) {
|
|
|
|
gSoundLastError = SOUND_NOT_PAUSED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(sound->field_40 & SOUND_FLAG_SOUND_IS_PAUSED)) {
|
|
|
|
gSoundLastError = SOUND_NOT_PAUSED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = IDirectSoundBuffer_SetCurrentPosition(sound->directSoundBuffer, sound->field_48);
|
|
|
|
if (hr != DS_OK) {
|
|
|
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
sound->field_40 &= ~SOUND_FLAG_SOUND_IS_PAUSED;
|
|
|
|
sound->field_48 = 0;
|
|
|
|
|
|
|
|
return soundPlay(sound);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AE2FC
|
|
|
|
int soundSetFileIO(Sound* sound, SoundOpenProc* openProc, SoundCloseProc* closeProc, SoundReadProc* readProc, SoundWriteProc* writeProc, SoundSeekProc* seekProc, SoundTellProc* tellProc, SoundFileLengthProc* fileLengthProc)
|
|
|
|
{
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (openProc != NULL) {
|
|
|
|
sound->io.open = openProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (closeProc != NULL) {
|
|
|
|
sound->io.close = closeProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (readProc != NULL) {
|
|
|
|
sound->io.read = readProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (writeProc != NULL) {
|
|
|
|
sound->io.write = writeProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (seekProc != NULL) {
|
|
|
|
sound->io.seek = seekProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tellProc != NULL) {
|
|
|
|
sound->io.tell = tellProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fileLengthProc != NULL) {
|
|
|
|
sound->io.filelength = fileLengthProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AE378
|
|
|
|
void soundDeleteInternal(Sound* sound)
|
|
|
|
{
|
|
|
|
STRUCT_51D478* curr;
|
|
|
|
Sound* v10;
|
|
|
|
Sound* v11;
|
|
|
|
|
|
|
|
if (sound->field_40 & 0x04) {
|
|
|
|
curr = _fadeHead;
|
|
|
|
|
|
|
|
while (curr != NULL) {
|
|
|
|
if (sound == curr->field_0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
curr = curr->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
_removeFadeSound(curr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->directSoundBuffer != NULL) {
|
|
|
|
// NOTE: Uninline.
|
|
|
|
if (!soundIsPlaying(sound)) {
|
|
|
|
soundStop(sound);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->callback != NULL) {
|
|
|
|
sound->callback(sound->callbackUserData, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
IDirectSoundBuffer_Release(sound->directSoundBuffer);
|
|
|
|
sound->directSoundBuffer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->field_90 != NULL) {
|
|
|
|
sound->field_90(sound->field_8C);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->field_20 != NULL) {
|
|
|
|
gSoundFreeProc(sound->field_20);
|
|
|
|
sound->field_20 = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->directSoundBufferDescription.lpwfxFormat != NULL) {
|
|
|
|
gSoundFreeProc(sound->directSoundBufferDescription.lpwfxFormat);
|
|
|
|
}
|
|
|
|
|
|
|
|
v10 = sound->next;
|
|
|
|
if (v10 != NULL) {
|
|
|
|
v10->prev = sound->prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
v11 = sound->prev;
|
|
|
|
if (v11 != NULL) {
|
|
|
|
v11->next = sound->next;
|
|
|
|
} else {
|
|
|
|
gSoundListHead = sound->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
gSoundFreeProc(sound);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AE578
|
|
|
|
int _soundSetMasterVolume(int volume)
|
|
|
|
{
|
|
|
|
if (volume < VOLUME_MIN || volume > VOLUME_MAX) {
|
|
|
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
_masterVol = volume;
|
|
|
|
|
|
|
|
Sound* curr = gSoundListHead;
|
|
|
|
while (curr != NULL) {
|
|
|
|
soundSetVolume(curr, curr->volume);
|
|
|
|
curr = curr->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AE5C8
|
|
|
|
void CALLBACK _doTimerEvent(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
|
|
|
|
{
|
|
|
|
void (*fn)();
|
|
|
|
|
|
|
|
if (dwUser != 0) {
|
|
|
|
fn = (void (*)())dwUser;
|
|
|
|
fn();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AE614
|
|
|
|
void _removeTimedEvent(unsigned int* timerId)
|
|
|
|
{
|
|
|
|
if (*timerId != -1) {
|
|
|
|
timeKillEvent(*timerId);
|
|
|
|
*timerId = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AE634
|
|
|
|
int _soundGetPosition(Sound* sound)
|
|
|
|
{
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD playPos;
|
|
|
|
DWORD writePos;
|
|
|
|
IDirectSoundBuffer_GetCurrentPosition(sound->directSoundBuffer, &playPos, &writePos);
|
|
|
|
|
|
|
|
if ((sound->field_44 & 0x02) != 0) {
|
|
|
|
if (playPos < sound->field_74) {
|
|
|
|
playPos += sound->field_64 + sound->field_78 * sound->field_7C - sound->field_74;
|
|
|
|
} else {
|
|
|
|
playPos -= sound->field_74 + sound->field_64;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return playPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AE6CC
|
|
|
|
int _soundSetPosition(Sound* sound, int a2)
|
|
|
|
{
|
|
|
|
if (!gSoundInitialized) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->directSoundBuffer == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound->field_44 & 0x02) {
|
|
|
|
int v6 = a2 / sound->field_7C % sound->field_78;
|
|
|
|
|
|
|
|
IDirectSoundBuffer_SetCurrentPosition(sound->directSoundBuffer, v6 * sound->field_7C + a2 % sound->field_7C);
|
|
|
|
|
|
|
|
sound->io.seek(sound->io.fd, v6 * sound->field_7C, SEEK_SET);
|
|
|
|
int bytes_read = sound->io.read(sound->io.fd, sound->field_20, sound->field_7C);
|
|
|
|
if (bytes_read < sound->field_7C) {
|
|
|
|
if (sound->field_44 & 0x02) {
|
|
|
|
sound->io.seek(sound->io.fd, 0, SEEK_SET);
|
|
|
|
sound->io.read(sound->io.fd, sound->field_20 + bytes_read, sound->field_7C - bytes_read);
|
|
|
|
} else {
|
|
|
|
memset(sound->field_20 + bytes_read, 0, sound->field_7C - bytes_read);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int v17 = v6 + 1;
|
|
|
|
sound->field_64 = a2;
|
|
|
|
|
|
|
|
if (v17 < sound->field_78) {
|
|
|
|
sound->field_70 = v17;
|
|
|
|
} else {
|
|
|
|
sound->field_70 = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
soundContinue(sound);
|
|
|
|
} else {
|
|
|
|
IDirectSoundBuffer_SetCurrentPosition(sound->directSoundBuffer, a2);
|
|
|
|
}
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AE830
|
|
|
|
void _removeFadeSound(STRUCT_51D478* a1)
|
|
|
|
{
|
|
|
|
STRUCT_51D478* prev;
|
|
|
|
STRUCT_51D478* next;
|
|
|
|
STRUCT_51D478* tmp;
|
|
|
|
|
|
|
|
if (a1 == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a1->field_0 == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(a1->field_0->field_40 & 0x04)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
prev = a1->prev;
|
|
|
|
if (prev != NULL) {
|
|
|
|
prev->next = a1->next;
|
|
|
|
} else {
|
|
|
|
_fadeHead = a1->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
next = a1->next;
|
|
|
|
if (next != NULL) {
|
|
|
|
next->prev = a1->prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
a1->field_0->field_40 &= ~(0x04);
|
|
|
|
a1->field_0 = NULL;
|
|
|
|
|
|
|
|
tmp = _fadeFreeList;
|
|
|
|
_fadeFreeList = a1;
|
|
|
|
a1->next = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AE8B0
|
|
|
|
void _fadeSounds()
|
|
|
|
{
|
|
|
|
STRUCT_51D478* ptr;
|
|
|
|
|
|
|
|
ptr = _fadeHead;
|
|
|
|
while (ptr != NULL) {
|
|
|
|
if ((ptr->field_10 > ptr->field_8 || ptr->field_10 + ptr->field_4 < ptr->field_8) && (ptr->field_10 < ptr->field_8 || ptr->field_10 + ptr->field_4 > ptr->field_8)) {
|
|
|
|
ptr->field_10 += ptr->field_4;
|
|
|
|
soundSetVolume(ptr->field_0, ptr->field_10);
|
|
|
|
} else {
|
|
|
|
if (ptr->field_8 == 0) {
|
|
|
|
if (ptr->field_14) {
|
|
|
|
soundPause(ptr->field_0);
|
|
|
|
soundSetVolume(ptr->field_0, ptr->field_C);
|
|
|
|
} else {
|
|
|
|
if (ptr->field_0->field_44 & 0x04) {
|
|
|
|
soundDelete(ptr->field_0);
|
|
|
|
} else {
|
|
|
|
soundStop(ptr->field_0);
|
|
|
|
|
|
|
|
ptr->field_C = ptr->field_8;
|
|
|
|
ptr->field_10 = ptr->field_8;
|
|
|
|
ptr->field_4 = 0;
|
|
|
|
|
|
|
|
soundSetVolume(ptr->field_0, ptr->field_8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_removeFadeSound(ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_fadeHead == NULL && _fadeEventHandle != -1) {
|
|
|
|
timeKillEvent(_fadeEventHandle);
|
|
|
|
_fadeEventHandle = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AE988
|
|
|
|
int _internalSoundFade(Sound* sound, int a2, int a3, int a4)
|
|
|
|
{
|
|
|
|
STRUCT_51D478* ptr;
|
|
|
|
|
|
|
|
if (!_deviceInit) {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr = NULL;
|
|
|
|
if (sound->field_40 & 0x04) {
|
|
|
|
ptr = _fadeHead;
|
|
|
|
while (ptr != NULL) {
|
|
|
|
if (ptr->field_0 == sound) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr = ptr->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ptr == NULL) {
|
|
|
|
if (_fadeFreeList != NULL) {
|
|
|
|
ptr = _fadeFreeList;
|
|
|
|
_fadeFreeList = _fadeFreeList->next;
|
|
|
|
} else {
|
|
|
|
ptr = (STRUCT_51D478*)gSoundMallocProc(sizeof(STRUCT_51D478));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ptr != NULL) {
|
|
|
|
if (_fadeHead != NULL) {
|
|
|
|
_fadeHead->prev = ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr->field_0 = sound;
|
|
|
|
ptr->prev = NULL;
|
|
|
|
ptr->next = _fadeHead;
|
|
|
|
_fadeHead = ptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ptr == NULL) {
|
|
|
|
gSoundLastError = SOUND_NO_MEMORY_AVAILABLE;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr->field_8 = a3;
|
|
|
|
ptr->field_C = _soundGetVolume(sound);
|
|
|
|
ptr->field_10 = ptr->field_C;
|
|
|
|
ptr->field_14 = a4;
|
|
|
|
// TODO: Check.
|
|
|
|
ptr->field_4 = 8 * (125 * (a3 - ptr->field_C)) / (40 * a2);
|
|
|
|
|
|
|
|
sound->field_40 |= 0x04;
|
|
|
|
|
|
|
|
bool v14;
|
|
|
|
if (gSoundInitialized) {
|
|
|
|
if (sound->directSoundBuffer != NULL) {
|
|
|
|
v14 = (sound->field_40 & 0x02) == 0;
|
|
|
|
} else {
|
|
|
|
gSoundLastError = SOUND_NO_SOUND;
|
|
|
|
v14 = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
|
|
|
v14 = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (v14) {
|
|
|
|
soundPlay(sound);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_fadeEventHandle != -1) {
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
_fadeEventHandle = timeSetEvent(40, 10, _doTimerEvent, (DWORD_PTR)_fadeSounds, 1);
|
|
|
|
if (_fadeEventHandle == 0) {
|
|
|
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AEB0C
|
|
|
|
int _soundFade(Sound* sound, int a2, int a3)
|
|
|
|
{
|
|
|
|
return _internalSoundFade(sound, a2, a3, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AEB54
|
|
|
|
void soundDeleteAll()
|
|
|
|
{
|
|
|
|
while (gSoundListHead != NULL) {
|
|
|
|
soundDelete(gSoundListHead);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AEBE0
|
|
|
|
void soundContinueAll()
|
|
|
|
{
|
|
|
|
Sound* curr = gSoundListHead;
|
|
|
|
while (curr != NULL) {
|
|
|
|
// Sound can be deallocated in `soundContinue`.
|
|
|
|
Sound* next = curr->next;
|
|
|
|
soundContinue(curr);
|
|
|
|
curr = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AEC00
|
|
|
|
int soundSetDefaultFileIO(SoundOpenProc* openProc, SoundCloseProc* closeProc, SoundReadProc* readProc, SoundWriteProc* writeProc, SoundSeekProc* seekProc, SoundTellProc* tellProc, SoundFileLengthProc* fileLengthProc)
|
|
|
|
{
|
|
|
|
if (openProc != NULL) {
|
|
|
|
gSoundDefaultFileIO.open = openProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (closeProc != NULL) {
|
|
|
|
gSoundDefaultFileIO.close = closeProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (readProc != NULL) {
|
|
|
|
gSoundDefaultFileIO.read = readProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (writeProc != NULL) {
|
|
|
|
gSoundDefaultFileIO.write = writeProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (seekProc != NULL) {
|
|
|
|
gSoundDefaultFileIO.seek = seekProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tellProc != NULL) {
|
|
|
|
gSoundDefaultFileIO.tell = tellProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fileLengthProc != NULL) {
|
|
|
|
gSoundDefaultFileIO.filelength = fileLengthProc;
|
|
|
|
}
|
|
|
|
|
|
|
|
gSoundLastError = SOUND_NO_ERROR;
|
|
|
|
return gSoundLastError;
|
|
|
|
}
|