Improve Sound readability
This commit is contained in:
parent
e11f1af9ae
commit
b8dea116ee
|
@ -1681,21 +1681,21 @@ void soundEffectCallback(void* userData, int a2)
|
||||||
// 0x451ADC
|
// 0x451ADC
|
||||||
int _gsound_background_allocate(Sound** soundPtr, int a2, int a3)
|
int _gsound_background_allocate(Sound** soundPtr, int a2, int a3)
|
||||||
{
|
{
|
||||||
int v5 = 10;
|
int soundFlags = SOUND_FLAG_0x02 | SOUND_16BIT;
|
||||||
int v6 = 0;
|
int type = 0;
|
||||||
if (a2 == 13) {
|
if (a2 == 13) {
|
||||||
v6 |= 0x01;
|
type |= SOUND_TYPE_MEMORY;
|
||||||
} else if (a2 == 14) {
|
} else if (a2 == 14) {
|
||||||
v6 |= 0x02;
|
type |= SOUND_TYPE_STREAMING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a3 == 15) {
|
if (a3 == 15) {
|
||||||
v6 |= 0x04;
|
type |= SOUND_TYPE_FIRE_AND_FORGET;
|
||||||
} else if (a3 == 16) {
|
} else if (a3 == 16) {
|
||||||
v5 = 42;
|
soundFlags |= SOUND_LOOPING;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sound* sound = soundAllocate(v6, v5);
|
Sound* sound = soundAllocate(type, soundFlags);
|
||||||
if (sound == NULL) {
|
if (sound == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -2006,7 +2006,7 @@ Sound* _gsound_get_sound_ready_for_effect()
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
Sound* sound = soundAllocate(5, 10);
|
Sound* sound = soundAllocate(SOUND_TYPE_MEMORY | SOUND_TYPE_FIRE_AND_FORGET, SOUND_FLAG_0x02 | SOUND_16BIT);
|
||||||
if (sound == NULL) {
|
if (sound == NULL) {
|
||||||
if (gGameSoundDebugEnabled) {
|
if (gGameSoundDebugEnabled) {
|
||||||
debugPrint(" Can't allocate sound for effect. ");
|
debugPrint(" Can't allocate sound for effect. ");
|
||||||
|
|
|
@ -1872,32 +1872,29 @@ static int intLibSoundDelete(int value)
|
||||||
// 0x466110
|
// 0x466110
|
||||||
static int intLibSoundPlay(char* fileName, int mode)
|
static int intLibSoundPlay(char* fileName, int mode)
|
||||||
{
|
{
|
||||||
int v3 = 1;
|
int type = SOUND_TYPE_MEMORY;
|
||||||
int v5 = 0;
|
int soundFlags = 0;
|
||||||
|
|
||||||
if (mode & 0x01) {
|
if (mode & 0x01) {
|
||||||
// looping
|
soundFlags |= SOUND_LOOPING;
|
||||||
v5 |= 0x20;
|
|
||||||
} else {
|
} else {
|
||||||
v3 = 5;
|
type |= SOUND_TYPE_FIRE_AND_FORGET;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode & 0x02) {
|
if (mode & 0x02) {
|
||||||
v5 |= 0x08;
|
soundFlags |= SOUND_16BIT;
|
||||||
} else {
|
} else {
|
||||||
v5 |= 0x10;
|
soundFlags |= SOUND_8BIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode & 0x0100) {
|
if (mode & 0x0100) {
|
||||||
// memory
|
type &= ~(SOUND_TYPE_MEMORY | SOUND_TYPE_STREAMING);
|
||||||
v3 &= ~0x03;
|
type |= SOUND_TYPE_MEMORY;
|
||||||
v3 |= 0x01;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode & 0x0200) {
|
if (mode & 0x0200) {
|
||||||
// streamed
|
type &= ~(SOUND_TYPE_MEMORY | SOUND_TYPE_STREAMING);
|
||||||
v3 &= ~0x03;
|
type |= SOUND_TYPE_STREAMING;
|
||||||
v3 |= 0x02;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int index;
|
int index;
|
||||||
|
@ -1911,7 +1908,7 @@ static int intLibSoundPlay(char* fileName, int mode)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sound* sound = gIntLibSounds[index] = soundAllocate(v3, v5);
|
Sound* sound = gIntLibSounds[index] = soundAllocate(type, soundFlags);
|
||||||
if (sound == NULL) {
|
if (sound == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -2009,7 +2006,7 @@ static int intLibSoundPause(int value)
|
||||||
}
|
}
|
||||||
|
|
||||||
int rc;
|
int rc;
|
||||||
if (_soundType(sound, 0x01)) {
|
if (_soundType(sound, SOUND_TYPE_MEMORY)) {
|
||||||
rc = soundStop(sound);
|
rc = soundStop(sound);
|
||||||
} else {
|
} else {
|
||||||
rc = soundPause(sound);
|
rc = soundPause(sound);
|
||||||
|
@ -2061,7 +2058,7 @@ static int intLibSoundResume(int value)
|
||||||
}
|
}
|
||||||
|
|
||||||
int rc;
|
int rc;
|
||||||
if (_soundType(sound, 0x01)) {
|
if (_soundType(sound, SOUND_TYPE_MEMORY)) {
|
||||||
rc = soundPlay(sound);
|
rc = soundPlay(sound);
|
||||||
} else {
|
} else {
|
||||||
rc = soundResume(sound);
|
rc = soundResume(sound);
|
||||||
|
|
|
@ -413,7 +413,7 @@ static int _lips_make_speech()
|
||||||
gLipsData.sound = NULL;
|
gLipsData.sound = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
gLipsData.sound = soundAllocate(1, 8);
|
gLipsData.sound = soundAllocate(SOUND_TYPE_MEMORY, SOUND_16BIT);
|
||||||
if (gLipsData.sound == NULL) {
|
if (gLipsData.sound == NULL) {
|
||||||
debugPrint("\nsoundAllocate falied in lips_make_speech!");
|
debugPrint("\nsoundAllocate falied in lips_make_speech!");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
453
src/sound.cc
453
src/sound.cc
|
@ -22,10 +22,12 @@
|
||||||
|
|
||||||
namespace fallout {
|
namespace fallout {
|
||||||
|
|
||||||
#define SOUND_FLAG_SOUND_IS_DONE (0x01)
|
typedef enum SoundStatusFlags {
|
||||||
#define SOUND_FLAG_SOUND_IS_PLAYING (0x02)
|
SOUND_STATUS_DONE = 0x01,
|
||||||
#define SOUND_FLAG_SOUND_IS_FADING (0x04)
|
SOUND_STATUS_IS_PLAYING = 0x02,
|
||||||
#define SOUND_FLAG_SOUND_IS_PAUSED (0x08)
|
SOUND_STATUS_IS_FADING = 0x04,
|
||||||
|
SOUND_STATUS_IS_PAUSED = 0x08,
|
||||||
|
} SoundStatusFlags;
|
||||||
|
|
||||||
typedef char*(SoundFileNameMangler)(char*);
|
typedef char*(SoundFileNameMangler)(char*);
|
||||||
|
|
||||||
|
@ -35,7 +37,7 @@ typedef struct FadeSound {
|
||||||
int targetVolume;
|
int targetVolume;
|
||||||
int initialVolume;
|
int initialVolume;
|
||||||
int currentVolume;
|
int currentVolume;
|
||||||
int field_14;
|
int pause;
|
||||||
struct FadeSound* prev;
|
struct FadeSound* prev;
|
||||||
struct FadeSound* next;
|
struct FadeSound* next;
|
||||||
} FadeSound;
|
} FadeSound;
|
||||||
|
@ -56,7 +58,7 @@ static Uint32 _doTimerEvent(Uint32 interval, void* param);
|
||||||
static void _removeTimedEvent(SDL_TimerID* timerId);
|
static void _removeTimedEvent(SDL_TimerID* timerId);
|
||||||
static void _removeFadeSound(FadeSound* fadeSound);
|
static void _removeFadeSound(FadeSound* fadeSound);
|
||||||
static void _fadeSounds();
|
static void _fadeSounds();
|
||||||
static int _internalSoundFade(Sound* sound, int duration, int targetVolume, int a4);
|
static int _internalSoundFade(Sound* sound, int duration, int targetVolume, bool pause);
|
||||||
|
|
||||||
// 0x51D478
|
// 0x51D478
|
||||||
static FadeSound* _fadeHead = NULL;
|
static FadeSound* _fadeHead = NULL;
|
||||||
|
@ -205,7 +207,7 @@ const char* soundGetErrorDescription(int err)
|
||||||
// 0x4AC7B0
|
// 0x4AC7B0
|
||||||
void _refreshSoundBuffers(Sound* sound)
|
void _refreshSoundBuffers(Sound* sound)
|
||||||
{
|
{
|
||||||
if (sound->field_3C & 0x80) {
|
if ((sound->soundFlags & SOUND_FLAG_0x80) != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,50 +218,50 @@ void _refreshSoundBuffers(Sound* sound)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (readPos < sound->field_74) {
|
if (readPos < sound->lastPosition) {
|
||||||
sound->field_64 += readPos + sound->field_78 * sound->field_7C - sound->field_74;
|
sound->numBytesRead += readPos + sound->numBuffers * sound->dataSize - sound->lastPosition;
|
||||||
} else {
|
} else {
|
||||||
sound->field_64 += readPos - sound->field_74;
|
sound->numBytesRead += readPos - sound->lastPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sound->field_3C & 0x0100) {
|
if ((sound->soundFlags & SOUND_FLAG_0x100) != 0) {
|
||||||
if (sound->field_44 & 0x20) {
|
if ((sound->type & SOUND_TYPE_0x20) != 0) {
|
||||||
if (sound->field_3C & 0x0200) {
|
if ((sound->soundFlags & SOUND_FLAG_0x200) != 0) {
|
||||||
sound->field_3C |= 0x80;
|
sound->soundFlags |= SOUND_FLAG_0x80;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (sound->field_60 <= sound->field_64) {
|
if (sound->fileSize <= sound->numBytesRead) {
|
||||||
sound->field_3C |= 0x0280;
|
sound->soundFlags |= SOUND_FLAG_0x200 | SOUND_FLAG_0x80;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sound->field_74 = readPos;
|
sound->lastPosition = readPos;
|
||||||
|
|
||||||
if (sound->field_60 < sound->field_64) {
|
if (sound->fileSize < sound->numBytesRead) {
|
||||||
int v3;
|
int v3;
|
||||||
do {
|
do {
|
||||||
v3 = sound->field_64 - sound->field_60;
|
v3 = sound->numBytesRead - sound->fileSize;
|
||||||
sound->field_64 = v3;
|
sound->numBytesRead = v3;
|
||||||
} while (v3 > sound->field_60);
|
} while (v3 > sound->fileSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
int v6 = readPos / sound->field_7C;
|
int v6 = readPos / sound->dataSize;
|
||||||
if (sound->field_70 == v6) {
|
if (sound->lastUpdate == v6) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int v53;
|
int v53;
|
||||||
if (sound->field_70 > v6) {
|
if (sound->lastUpdate > v6) {
|
||||||
v53 = v6 + sound->field_78 - sound->field_70;
|
v53 = v6 + sound->numBuffers - sound->lastUpdate;
|
||||||
} else {
|
} else {
|
||||||
v53 = v6 - sound->field_70;
|
v53 = v6 - sound->lastUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sound->field_7C * v53 >= sound->readLimit) {
|
if (sound->dataSize * v53 >= sound->readLimit) {
|
||||||
v53 = (sound->readLimit + sound->field_7C - 1) / sound->field_7C;
|
v53 = (sound->readLimit + sound->dataSize - 1) / sound->dataSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v53 < sound->field_5C) {
|
if (v53 < sound->minReadBuffer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,17 +269,17 @@ void _refreshSoundBuffers(Sound* sound)
|
||||||
void* audioPtr2;
|
void* audioPtr2;
|
||||||
unsigned int audioBytes1;
|
unsigned int audioBytes1;
|
||||||
unsigned int audioBytes2;
|
unsigned int audioBytes2;
|
||||||
hr = audioEngineSoundBufferLock(sound->soundBuffer, sound->field_7C * sound->field_70, sound->field_7C * v53, &audioPtr1, &audioBytes1, &audioPtr2, &audioBytes2, 0);
|
hr = audioEngineSoundBufferLock(sound->soundBuffer, sound->dataSize * sound->lastUpdate, sound->dataSize * v53, &audioPtr1, &audioBytes1, &audioPtr2, &audioBytes2, 0);
|
||||||
if (!hr) {
|
if (!hr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audioBytes1 + audioBytes2 != sound->field_7C * v53) {
|
if (audioBytes1 + audioBytes2 != sound->dataSize * 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("locked memory region not big enough, wanted %d (%d * %d), got %d (%d + %d)\n", sound->dataSize * v53, v53, sound->dataSize, audioBytes1 + audioBytes2, audioBytes1, audioBytes2);
|
||||||
debugPrint("Resetting readBuffers from %d to %d\n", v53, (audioBytes1 + audioBytes2) / sound->field_7C);
|
debugPrint("Resetting readBuffers from %d to %d\n", v53, (audioBytes1 + audioBytes2) / sound->dataSize);
|
||||||
|
|
||||||
v53 = (audioBytes1 + audioBytes2) / sound->field_7C;
|
v53 = (audioBytes1 + audioBytes2) / sound->dataSize;
|
||||||
if (v53 < sound->field_5C) {
|
if (v53 < sound->minReadBuffer) {
|
||||||
debugPrint("No longer above read buffer size, returning\n");
|
debugPrint("No longer above read buffer size, returning\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -286,11 +288,11 @@ void _refreshSoundBuffers(Sound* sound)
|
||||||
int audioBytes = audioBytes1;
|
int audioBytes = audioBytes1;
|
||||||
while (--v53 != -1) {
|
while (--v53 != -1) {
|
||||||
int bytesRead;
|
int bytesRead;
|
||||||
if (sound->field_3C & 0x0200) {
|
if ((sound->soundFlags & SOUND_FLAG_0x200) != 0) {
|
||||||
bytesRead = sound->field_7C;
|
bytesRead = sound->dataSize;
|
||||||
memset(sound->field_20, 0, bytesRead);
|
memset(sound->data, 0, bytesRead);
|
||||||
} else {
|
} else {
|
||||||
int bytesToRead = sound->field_7C;
|
int bytesToRead = sound->dataSize;
|
||||||
if (sound->field_58 != -1) {
|
if (sound->field_58 != -1) {
|
||||||
int pos = sound->io.tell(sound->io.fd);
|
int pos = sound->io.tell(sound->io.fd);
|
||||||
if (bytesToRead + pos > sound->field_58) {
|
if (bytesToRead + pos > sound->field_58) {
|
||||||
|
@ -298,30 +300,30 @@ void _refreshSoundBuffers(Sound* sound)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bytesRead = sound->io.read(sound->io.fd, sound->field_20, bytesToRead);
|
bytesRead = sound->io.read(sound->io.fd, sound->data, bytesToRead);
|
||||||
if (bytesRead < sound->field_7C) {
|
if (bytesRead < sound->dataSize) {
|
||||||
if (!(sound->field_3C & 0x20) || (sound->field_3C & 0x0100)) {
|
if ((sound->soundFlags & SOUND_LOOPING) == 0 || (sound->soundFlags & SOUND_FLAG_0x100) != 0) {
|
||||||
memset(sound->field_20 + bytesRead, 0, sound->field_7C - bytesRead);
|
memset(sound->data + bytesRead, 0, sound->dataSize - bytesRead);
|
||||||
sound->field_3C |= 0x0200;
|
sound->soundFlags |= SOUND_FLAG_0x200;
|
||||||
bytesRead = sound->field_7C;
|
bytesRead = sound->dataSize;
|
||||||
} else {
|
} else {
|
||||||
while (bytesRead < sound->field_7C) {
|
while (bytesRead < sound->dataSize) {
|
||||||
if (sound->field_50 == -1) {
|
if (sound->loops == -1) {
|
||||||
sound->io.seek(sound->io.fd, sound->field_54, SEEK_SET);
|
sound->io.seek(sound->io.fd, sound->field_54, SEEK_SET);
|
||||||
if (sound->callback != NULL) {
|
if (sound->callback != NULL) {
|
||||||
sound->callback(sound->callbackUserData, 0x0400);
|
sound->callback(sound->callbackUserData, 0x0400);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (sound->field_50 <= 0) {
|
if (sound->loops <= 0) {
|
||||||
sound->field_58 = -1;
|
sound->field_58 = -1;
|
||||||
sound->field_54 = 0;
|
sound->field_54 = 0;
|
||||||
sound->field_50 = 0;
|
sound->loops = 0;
|
||||||
sound->field_3C &= ~0x20;
|
sound->soundFlags &= ~SOUND_LOOPING;
|
||||||
bytesRead += sound->io.read(sound->io.fd, sound->field_20 + bytesRead, sound->field_7C - bytesRead);
|
bytesRead += sound->io.read(sound->io.fd, sound->data + bytesRead, sound->dataSize - bytesRead);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
sound->field_50--;
|
sound->loops--;
|
||||||
sound->io.seek(sound->io.fd, sound->field_54, SEEK_SET);
|
sound->io.seek(sound->io.fd, sound->field_54, SEEK_SET);
|
||||||
|
|
||||||
if (sound->callback != NULL) {
|
if (sound->callback != NULL) {
|
||||||
|
@ -330,17 +332,17 @@ void _refreshSoundBuffers(Sound* sound)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sound->field_58 == -1) {
|
if (sound->field_58 == -1) {
|
||||||
bytesToRead = sound->field_7C - bytesRead;
|
bytesToRead = sound->dataSize - bytesRead;
|
||||||
} else {
|
} else {
|
||||||
int pos = sound->io.tell(sound->io.fd);
|
int pos = sound->io.tell(sound->io.fd);
|
||||||
if (sound->field_7C + bytesRead + pos <= sound->field_58) {
|
if (sound->dataSize + bytesRead + pos <= sound->field_58) {
|
||||||
bytesToRead = sound->field_7C - bytesRead;
|
bytesToRead = sound->dataSize - bytesRead;
|
||||||
} else {
|
} else {
|
||||||
bytesToRead = sound->field_58 - bytesRead - pos;
|
bytesToRead = sound->field_58 - bytesRead - pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int v20 = sound->io.read(sound->io.fd, sound->field_20 + bytesRead, bytesToRead);
|
int v20 = sound->io.read(sound->io.fd, sound->data + bytesRead, bytesToRead);
|
||||||
bytesRead += v20;
|
bytesRead += v20;
|
||||||
if (v20 < bytesToRead) {
|
if (v20 < bytesToRead) {
|
||||||
break;
|
break;
|
||||||
|
@ -352,18 +354,18 @@ void _refreshSoundBuffers(Sound* sound)
|
||||||
|
|
||||||
if (bytesRead > audioBytes) {
|
if (bytesRead > audioBytes) {
|
||||||
if (audioBytes != 0) {
|
if (audioBytes != 0) {
|
||||||
memcpy(audioPtr, sound->field_20, audioBytes);
|
memcpy(audioPtr, sound->data, audioBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audioPtr2 != NULL) {
|
if (audioPtr2 != NULL) {
|
||||||
memcpy(audioPtr2, sound->field_20 + audioBytes, bytesRead - audioBytes);
|
memcpy(audioPtr2, sound->data + audioBytes, bytesRead - audioBytes);
|
||||||
audioPtr = (unsigned char*)audioPtr2 + bytesRead - audioBytes;
|
audioPtr = (unsigned char*)audioPtr2 + bytesRead - audioBytes;
|
||||||
audioBytes = audioBytes2 - bytesRead;
|
audioBytes = audioBytes2 - bytesRead;
|
||||||
} else {
|
} else {
|
||||||
debugPrint("Hm, no second write pointer, but buffer not big enough, this shouldn't happen\n");
|
debugPrint("Hm, no second write pointer, but buffer not big enough, this shouldn't happen\n");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
memcpy(audioPtr, sound->field_20, bytesRead);
|
memcpy(audioPtr, sound->data, bytesRead);
|
||||||
audioPtr += bytesRead;
|
audioPtr += bytesRead;
|
||||||
audioBytes -= bytesRead;
|
audioBytes -= bytesRead;
|
||||||
}
|
}
|
||||||
|
@ -371,11 +373,11 @@ void _refreshSoundBuffers(Sound* sound)
|
||||||
|
|
||||||
audioEngineSoundBufferUnlock(sound->soundBuffer, audioPtr1, audioBytes1, audioPtr2, audioBytes2);
|
audioEngineSoundBufferUnlock(sound->soundBuffer, audioPtr1, audioBytes1, audioPtr2, audioBytes2);
|
||||||
|
|
||||||
sound->field_70 = v6;
|
sound->lastUpdate = v6;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x4ACC58
|
// 0x4ACC58
|
||||||
int soundInit(int a1, int a2, int a3, int a4, int rate)
|
int soundInit(int a1, int numBuffers, int a3, int dataSize, int rate)
|
||||||
{
|
{
|
||||||
if (!audioEngineInit()) {
|
if (!audioEngineInit()) {
|
||||||
debugPrint("soundInit: Unable to init audio engine\n");
|
debugPrint("soundInit: Unable to init audio engine\n");
|
||||||
|
@ -385,8 +387,8 @@ int soundInit(int a1, int a2, int a3, int a4, int rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
_sampleRate = rate;
|
_sampleRate = rate;
|
||||||
_dataSize = a4;
|
_dataSize = dataSize;
|
||||||
_numBuffers = a2;
|
_numBuffers = numBuffers;
|
||||||
gSoundInitialized = true;
|
gSoundInitialized = true;
|
||||||
_deviceInit = 1;
|
_deviceInit = 1;
|
||||||
|
|
||||||
|
@ -422,7 +424,7 @@ void soundExit()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x4AD0FC
|
// 0x4AD0FC
|
||||||
Sound* soundAllocate(int a1, int a2)
|
Sound* soundAllocate(int type, int soundFlags)
|
||||||
{
|
{
|
||||||
if (!gSoundInitialized) {
|
if (!gSoundInitialized) {
|
||||||
gSoundLastError = SOUND_NOT_INITIALIZED;
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
||||||
|
@ -434,30 +436,30 @@ Sound* soundAllocate(int a1, int a2)
|
||||||
|
|
||||||
memcpy(&(sound->io), &gSoundDefaultFileIO, sizeof(gSoundDefaultFileIO));
|
memcpy(&(sound->io), &gSoundDefaultFileIO, sizeof(gSoundDefaultFileIO));
|
||||||
|
|
||||||
if (!(a2 & 0x02)) {
|
if ((soundFlags & SOUND_FLAG_0x02) == 0) {
|
||||||
a2 |= 0x02;
|
soundFlags |= SOUND_FLAG_0x02;
|
||||||
}
|
}
|
||||||
|
|
||||||
sound->bitsPerSample = (a2 & 0x08) != 0 ? 16 : 8;
|
sound->bitsPerSample = (soundFlags & SOUND_16BIT) != 0 ? 16 : 8;
|
||||||
sound->channels = 1;
|
sound->channels = 1;
|
||||||
sound->rate = _sampleRate;
|
sound->rate = _sampleRate;
|
||||||
|
|
||||||
sound->field_3C = a2;
|
sound->soundFlags = soundFlags;
|
||||||
sound->field_44 = a1;
|
sound->type = type;
|
||||||
sound->field_7C = _dataSize;
|
sound->dataSize = _dataSize;
|
||||||
sound->field_64 = 0;
|
sound->numBytesRead = 0;
|
||||||
sound->soundBuffer = -1;
|
sound->soundBuffer = -1;
|
||||||
sound->field_40 = 0;
|
sound->statusFlags = 0;
|
||||||
sound->field_78 = _numBuffers;
|
sound->numBuffers = _numBuffers;
|
||||||
sound->readLimit = sound->field_7C * _numBuffers;
|
sound->readLimit = sound->dataSize * _numBuffers;
|
||||||
|
|
||||||
if (a1 & 0x10) {
|
if ((type & SOUND_TYPE_INFINITE) != 0) {
|
||||||
sound->field_50 = -1;
|
sound->loops = -1;
|
||||||
sound->field_3C |= 0x20;
|
sound->soundFlags |= SOUND_LOOPING;
|
||||||
}
|
}
|
||||||
|
|
||||||
sound->field_58 = -1;
|
sound->field_58 = -1;
|
||||||
sound->field_5C = 1;
|
sound->minReadBuffer = 1;
|
||||||
sound->volume = VOLUME_MAX;
|
sound->volume = VOLUME_MAX;
|
||||||
sound->prev = NULL;
|
sound->prev = NULL;
|
||||||
sound->field_54 = 0;
|
sound->field_54 = 0;
|
||||||
|
@ -483,29 +485,29 @@ int _preloadBuffers(Sound* sound)
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
size = sound->io.filelength(sound->io.fd);
|
size = sound->io.filelength(sound->io.fd);
|
||||||
sound->field_60 = size;
|
sound->fileSize = size;
|
||||||
|
|
||||||
if (sound->field_44 & 0x02) {
|
if ((sound->type & SOUND_TYPE_STREAMING) != 0) {
|
||||||
if (!(sound->field_3C & 0x20)) {
|
if ((sound->soundFlags & SOUND_LOOPING) == 0) {
|
||||||
sound->field_3C |= 0x0120;
|
sound->soundFlags |= SOUND_FLAG_0x100 | SOUND_LOOPING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sound->field_78 * sound->field_7C >= size) {
|
if (sound->numBuffers * sound->dataSize >= size) {
|
||||||
if (size / sound->field_7C * sound->field_7C != size) {
|
if (size / sound->dataSize * sound->dataSize != size) {
|
||||||
size = (size / sound->field_7C + 1) * sound->field_7C;
|
size = (size / sound->dataSize + 1) * sound->dataSize;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
size = sound->field_78 * sound->field_7C;
|
size = sound->numBuffers * sound->dataSize;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sound->field_44 &= ~(0x03);
|
sound->type &= ~(SOUND_TYPE_MEMORY | SOUND_TYPE_STREAMING);
|
||||||
sound->field_44 |= 0x01;
|
sound->type |= SOUND_TYPE_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = (unsigned char*)gSoundMallocProc(size);
|
buf = (unsigned char*)gSoundMallocProc(size);
|
||||||
bytes_read = sound->io.read(sound->io.fd, buf, size);
|
bytes_read = sound->io.read(sound->io.fd, buf, size);
|
||||||
if (bytes_read != size) {
|
if (bytes_read != size) {
|
||||||
if (!(sound->field_3C & 0x20) || (sound->field_3C & (0x01 << 8))) {
|
if ((sound->soundFlags & SOUND_LOOPING) == 0 || (sound->soundFlags & SOUND_FLAG_0x100) != 0) {
|
||||||
memset(buf + bytes_read, 0, size - bytes_read);
|
memset(buf + bytes_read, 0, size - bytes_read);
|
||||||
} else {
|
} else {
|
||||||
v14 = buf + bytes_read;
|
v14 = buf + bytes_read;
|
||||||
|
@ -525,12 +527,12 @@ int _preloadBuffers(Sound* sound)
|
||||||
result = _soundSetData(sound, buf, size);
|
result = _soundSetData(sound, buf, size);
|
||||||
gSoundFreeProc(buf);
|
gSoundFreeProc(buf);
|
||||||
|
|
||||||
if (sound->field_44 & 0x01) {
|
if ((sound->type & SOUND_TYPE_MEMORY) != 0) {
|
||||||
sound->io.close(sound->io.fd);
|
sound->io.close(sound->io.fd);
|
||||||
sound->io.fd = -1;
|
sound->io.fd = -1;
|
||||||
} else {
|
} else {
|
||||||
if (sound->field_20 == NULL) {
|
if (sound->data == NULL) {
|
||||||
sound->field_20 = (unsigned char*)gSoundMallocProc(sound->field_7C);
|
sound->data = (unsigned char*)gSoundMallocProc(sound->dataSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,12 +576,12 @@ int _soundRewind(Sound* sound)
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sound->field_44 & 0x02) {
|
if ((sound->type & SOUND_TYPE_STREAMING) != 0) {
|
||||||
sound->io.seek(sound->io.fd, 0, SEEK_SET);
|
sound->io.seek(sound->io.fd, 0, SEEK_SET);
|
||||||
sound->field_70 = 0;
|
sound->lastUpdate = 0;
|
||||||
sound->field_74 = 0;
|
sound->lastPosition = 0;
|
||||||
sound->field_64 = 0;
|
sound->numBytesRead = 0;
|
||||||
sound->field_3C &= 0xFD7F;
|
sound->soundFlags &= ~(SOUND_FLAG_0x200 | SOUND_FLAG_0x80);
|
||||||
hr = audioEngineSoundBufferSetCurrentPosition(sound->soundBuffer, 0);
|
hr = audioEngineSoundBufferSetCurrentPosition(sound->soundBuffer, 0);
|
||||||
_preloadBuffers(sound);
|
_preloadBuffers(sound);
|
||||||
} else {
|
} else {
|
||||||
|
@ -591,7 +593,7 @@ int _soundRewind(Sound* sound)
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
sound->field_40 &= ~SOUND_FLAG_SOUND_IS_DONE;
|
sound->statusFlags &= ~SOUND_STATUS_DONE;
|
||||||
|
|
||||||
gSoundLastError = SOUND_NO_ERROR;
|
gSoundLastError = SOUND_NO_ERROR;
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
|
@ -669,24 +671,23 @@ int soundPlay(Sound* sound)
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check.
|
if ((sound->statusFlags & SOUND_STATUS_DONE) != 0) {
|
||||||
if (sound->field_40 & SOUND_FLAG_SOUND_IS_DONE) {
|
|
||||||
_soundRewind(sound);
|
_soundRewind(sound);
|
||||||
}
|
}
|
||||||
|
|
||||||
soundSetVolume(sound, sound->volume);
|
soundSetVolume(sound, sound->volume);
|
||||||
|
|
||||||
hr = audioEngineSoundBufferPlay(sound->soundBuffer, sound->field_3C & 0x20 ? AUDIO_ENGINE_SOUND_BUFFER_PLAY_LOOPING : 0);
|
hr = audioEngineSoundBufferPlay(sound->soundBuffer, sound->soundFlags & SOUND_LOOPING ? AUDIO_ENGINE_SOUND_BUFFER_PLAY_LOOPING : 0);
|
||||||
|
|
||||||
audioEngineSoundBufferGetCurrentPosition(sound->soundBuffer, &readPos, &writePos);
|
audioEngineSoundBufferGetCurrentPosition(sound->soundBuffer, &readPos, &writePos);
|
||||||
sound->field_70 = readPos / sound->field_7C;
|
sound->lastUpdate = readPos / sound->dataSize;
|
||||||
|
|
||||||
if (!hr) {
|
if (!hr) {
|
||||||
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
sound->field_40 |= SOUND_FLAG_SOUND_IS_PLAYING;
|
sound->statusFlags |= SOUND_STATUS_IS_PLAYING;
|
||||||
|
|
||||||
++_numSounds;
|
++_numSounds;
|
||||||
|
|
||||||
|
@ -709,7 +710,7 @@ int soundStop(Sound* sound)
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(sound->field_40 & SOUND_FLAG_SOUND_IS_PLAYING)) {
|
if ((sound->statusFlags & SOUND_STATUS_IS_PLAYING) == 0) {
|
||||||
gSoundLastError = SOUND_NOT_PLAYING;
|
gSoundLastError = SOUND_NOT_PLAYING;
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
@ -720,7 +721,7 @@ int soundStop(Sound* sound)
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
sound->field_40 &= ~SOUND_FLAG_SOUND_IS_PLAYING;
|
sound->statusFlags &= ~SOUND_STATUS_IS_PLAYING;
|
||||||
_numSounds--;
|
_numSounds--;
|
||||||
|
|
||||||
gSoundLastError = SOUND_NO_ERROR;
|
gSoundLastError = SOUND_NO_ERROR;
|
||||||
|
@ -772,12 +773,17 @@ int soundContinue(Sound* sound)
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(sound->field_40 & SOUND_FLAG_SOUND_IS_PLAYING) || (sound->field_40 & SOUND_FLAG_SOUND_IS_PAUSED)) {
|
if ((sound->statusFlags & SOUND_STATUS_IS_PLAYING) == 0) {
|
||||||
gSoundLastError = SOUND_NOT_PLAYING;
|
gSoundLastError = SOUND_NOT_PLAYING;
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sound->field_40 & SOUND_FLAG_SOUND_IS_DONE) {
|
if ((sound->statusFlags & SOUND_STATUS_IS_PAUSED) != 0) {
|
||||||
|
gSoundLastError = SOUND_NOT_PLAYING;
|
||||||
|
return gSoundLastError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sound->statusFlags & SOUND_STATUS_DONE) != 0) {
|
||||||
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
@ -790,29 +796,29 @@ int soundContinue(Sound* sound)
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(sound->field_3C & 0x80) && (status & (AUDIO_ENGINE_SOUND_BUFFER_STATUS_PLAYING | AUDIO_ENGINE_SOUND_BUFFER_STATUS_LOOPING))) {
|
if ((sound->soundFlags & SOUND_FLAG_0x80) == 0 && (status & (AUDIO_ENGINE_SOUND_BUFFER_STATUS_PLAYING | AUDIO_ENGINE_SOUND_BUFFER_STATUS_LOOPING)) != 0) {
|
||||||
if (!(sound->field_40 & SOUND_FLAG_SOUND_IS_PAUSED) && (sound->field_44 & 0x02)) {
|
if ((sound->statusFlags & SOUND_STATUS_IS_PAUSED) == 0 && (sound->type & SOUND_TYPE_STREAMING) != 0) {
|
||||||
_refreshSoundBuffers(sound);
|
_refreshSoundBuffers(sound);
|
||||||
}
|
}
|
||||||
} else if (!(sound->field_40 & SOUND_FLAG_SOUND_IS_PAUSED)) {
|
} else if ((sound->statusFlags & SOUND_STATUS_IS_PAUSED) == 0) {
|
||||||
if (sound->callback != NULL) {
|
if (sound->callback != NULL) {
|
||||||
sound->callback(sound->callbackUserData, 1);
|
sound->callback(sound->callbackUserData, 1);
|
||||||
sound->callback = NULL;
|
sound->callback = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sound->field_44 & 0x04) {
|
if ((sound->type & SOUND_TYPE_FIRE_AND_FORGET) != 0) {
|
||||||
sound->callback = NULL;
|
sound->callback = NULL;
|
||||||
soundDelete(sound);
|
soundDelete(sound);
|
||||||
} else {
|
} else {
|
||||||
sound->field_40 |= SOUND_FLAG_SOUND_IS_DONE;
|
sound->statusFlags |= SOUND_STATUS_DONE;
|
||||||
|
|
||||||
if (sound->field_40 & SOUND_FLAG_SOUND_IS_PLAYING) {
|
if ((sound->statusFlags & SOUND_STATUS_IS_PLAYING) != 0) {
|
||||||
--_numSounds;
|
--_numSounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
soundStop(sound);
|
soundStop(sound);
|
||||||
|
|
||||||
sound->field_40 &= ~(SOUND_FLAG_SOUND_IS_DONE | SOUND_FLAG_SOUND_IS_PLAYING);
|
sound->statusFlags &= ~(SOUND_STATUS_DONE | SOUND_STATUS_IS_PLAYING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -833,7 +839,7 @@ bool soundIsPlaying(Sound* sound)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (sound->field_40 & SOUND_FLAG_SOUND_IS_PLAYING) != 0;
|
return (sound->statusFlags & SOUND_STATUS_IS_PLAYING) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x4ADAC4
|
// 0x4ADAC4
|
||||||
|
@ -849,7 +855,7 @@ bool _soundDone(Sound* sound)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sound->field_40 & 1;
|
return (sound->statusFlags & SOUND_STATUS_DONE) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x4ADB44
|
// 0x4ADB44
|
||||||
|
@ -865,11 +871,11 @@ bool soundIsPaused(Sound* sound)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (sound->field_40 & SOUND_FLAG_SOUND_IS_PAUSED) != 0;
|
return (sound->statusFlags & SOUND_STATUS_IS_PAUSED) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x4ADBC4
|
// 0x4ADBC4
|
||||||
int _soundType(Sound* sound, int a2)
|
int _soundType(Sound* sound, int type)
|
||||||
{
|
{
|
||||||
if (!gSoundInitialized) {
|
if (!gSoundInitialized) {
|
||||||
gSoundLastError = SOUND_NOT_INITIALIZED;
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
||||||
|
@ -881,7 +887,7 @@ int _soundType(Sound* sound, int a2)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sound->field_44 & a2;
|
return sound->type & type;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x4ADC04
|
// 0x4ADC04
|
||||||
|
@ -898,7 +904,7 @@ int soundGetDuration(Sound* sound)
|
||||||
}
|
}
|
||||||
|
|
||||||
int bytesPerSec = sound->bitsPerSample / 8 * sound->rate;
|
int bytesPerSec = sound->bitsPerSample / 8 * sound->rate;
|
||||||
int v3 = sound->field_60;
|
int v3 = sound->fileSize;
|
||||||
int v4 = v3 % bytesPerSec;
|
int v4 = v3 % bytesPerSec;
|
||||||
int result = v3 / bytesPerSec;
|
int result = v3 / bytesPerSec;
|
||||||
if (v4 != 0) {
|
if (v4 != 0) {
|
||||||
|
@ -909,7 +915,7 @@ int soundGetDuration(Sound* sound)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x4ADD00
|
// 0x4ADD00
|
||||||
int soundSetLooping(Sound* sound, int a2)
|
int soundSetLooping(Sound* sound, int loops)
|
||||||
{
|
{
|
||||||
if (!gSoundInitialized) {
|
if (!gSoundInitialized) {
|
||||||
gSoundLastError = SOUND_NOT_INITIALIZED;
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
||||||
|
@ -921,14 +927,14 @@ int soundSetLooping(Sound* sound, int a2)
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a2) {
|
if (loops != 0) {
|
||||||
sound->field_3C |= 0x20;
|
sound->soundFlags |= SOUND_LOOPING;
|
||||||
sound->field_50 = a2;
|
sound->loops = loops;
|
||||||
} else {
|
} else {
|
||||||
sound->field_50 = 0;
|
sound->loops = 0;
|
||||||
sound->field_58 = -1;
|
sound->field_58 = -1;
|
||||||
sound->field_54 = 0;
|
sound->field_54 = 0;
|
||||||
sound->field_3C &= ~(0x20);
|
sound->soundFlags &= ~SOUND_LOOPING;
|
||||||
}
|
}
|
||||||
|
|
||||||
gSoundLastError = SOUND_NO_ERROR;
|
gSoundLastError = SOUND_NO_ERROR;
|
||||||
|
@ -1085,12 +1091,12 @@ int soundPause(Sound* sound)
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(sound->field_40 & SOUND_FLAG_SOUND_IS_PLAYING)) {
|
if ((sound->statusFlags & SOUND_STATUS_IS_PLAYING) == 0) {
|
||||||
gSoundLastError = SOUND_NOT_PLAYING;
|
gSoundLastError = SOUND_NOT_PLAYING;
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sound->field_40 & SOUND_FLAG_SOUND_IS_PAUSED) {
|
if ((sound->statusFlags & SOUND_STATUS_IS_PAUSED) != 0) {
|
||||||
gSoundLastError = SOUND_ALREADY_PAUSED;
|
gSoundLastError = SOUND_ALREADY_PAUSED;
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
@ -1101,8 +1107,8 @@ int soundPause(Sound* sound)
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
sound->field_48 = readPos;
|
sound->pausePos = readPos;
|
||||||
sound->field_40 |= SOUND_FLAG_SOUND_IS_PAUSED;
|
sound->statusFlags |= SOUND_STATUS_IS_PAUSED;
|
||||||
|
|
||||||
return soundStop(sound);
|
return soundStop(sound);
|
||||||
}
|
}
|
||||||
|
@ -1124,24 +1130,24 @@ int soundResume(Sound* sound)
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((sound->field_40 & SOUND_FLAG_SOUND_IS_PLAYING) != 0) {
|
if ((sound->statusFlags & SOUND_STATUS_IS_PLAYING) != 0) {
|
||||||
gSoundLastError = SOUND_NOT_PAUSED;
|
gSoundLastError = SOUND_NOT_PAUSED;
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(sound->field_40 & SOUND_FLAG_SOUND_IS_PAUSED)) {
|
if ((sound->statusFlags & SOUND_STATUS_IS_PAUSED) == 0) {
|
||||||
gSoundLastError = SOUND_NOT_PAUSED;
|
gSoundLastError = SOUND_NOT_PAUSED;
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = audioEngineSoundBufferSetCurrentPosition(sound->soundBuffer, sound->field_48);
|
hr = audioEngineSoundBufferSetCurrentPosition(sound->soundBuffer, sound->pausePos);
|
||||||
if (!hr) {
|
if (!hr) {
|
||||||
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
gSoundLastError = SOUND_UNKNOWN_ERROR;
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
sound->field_40 &= ~SOUND_FLAG_SOUND_IS_PAUSED;
|
sound->statusFlags &= ~SOUND_STATUS_IS_PAUSED;
|
||||||
sound->field_48 = 0;
|
sound->pausePos = 0;
|
||||||
|
|
||||||
return soundPlay(sound);
|
return soundPlay(sound);
|
||||||
}
|
}
|
||||||
|
@ -1194,22 +1200,21 @@ int soundSetFileIO(Sound* sound, SoundOpenProc* openProc, SoundCloseProc* closeP
|
||||||
// 0x4AE378
|
// 0x4AE378
|
||||||
void soundDeleteInternal(Sound* sound)
|
void soundDeleteInternal(Sound* sound)
|
||||||
{
|
{
|
||||||
FadeSound* curr;
|
Sound* next;
|
||||||
Sound* v10;
|
Sound* prev;
|
||||||
Sound* v11;
|
|
||||||
|
|
||||||
if (sound->field_40 & SOUND_FLAG_SOUND_IS_FADING) {
|
if ((sound->statusFlags & SOUND_STATUS_IS_FADING) != 0) {
|
||||||
curr = _fadeHead;
|
FadeSound* fadeSound = _fadeHead;
|
||||||
|
|
||||||
while (curr != NULL) {
|
while (fadeSound != NULL) {
|
||||||
if (sound == curr->sound) {
|
if (sound == fadeSound->sound) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
curr = curr->next;
|
fadeSound = fadeSound->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
_removeFadeSound(curr);
|
_removeFadeSound(fadeSound);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sound->soundBuffer != -1) {
|
if (sound->soundBuffer != -1) {
|
||||||
|
@ -1226,23 +1231,23 @@ void soundDeleteInternal(Sound* sound)
|
||||||
sound->soundBuffer = -1;
|
sound->soundBuffer = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sound->field_90 != NULL) {
|
if (sound->deleteCallback != NULL) {
|
||||||
sound->field_90(sound->field_8C);
|
sound->deleteCallback(sound->deleteUserData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sound->field_20 != NULL) {
|
if (sound->data != NULL) {
|
||||||
gSoundFreeProc(sound->field_20);
|
gSoundFreeProc(sound->data);
|
||||||
sound->field_20 = NULL;
|
sound->data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
v10 = sound->next;
|
next = sound->next;
|
||||||
if (v10 != NULL) {
|
if (next != NULL) {
|
||||||
v10->prev = sound->prev;
|
next->prev = sound->prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
v11 = sound->prev;
|
prev = sound->prev;
|
||||||
if (v11 != NULL) {
|
if (prev != NULL) {
|
||||||
v11->next = sound->next;
|
prev->next = sound->next;
|
||||||
} else {
|
} else {
|
||||||
gSoundListHead = sound->next;
|
gSoundListHead = sound->next;
|
||||||
}
|
}
|
||||||
|
@ -1305,23 +1310,23 @@ int _soundGetPosition(Sound* sound)
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int playPos;
|
unsigned int readPos;
|
||||||
unsigned int writePos;
|
unsigned int writePos;
|
||||||
audioEngineSoundBufferGetCurrentPosition(sound->soundBuffer, &playPos, &writePos);
|
audioEngineSoundBufferGetCurrentPosition(sound->soundBuffer, &readPos, &writePos);
|
||||||
|
|
||||||
if ((sound->field_44 & 0x02) != 0) {
|
if ((sound->type & SOUND_TYPE_STREAMING) != 0) {
|
||||||
if (playPos < sound->field_74) {
|
if (readPos < sound->lastPosition) {
|
||||||
playPos += sound->field_64 + sound->field_78 * sound->field_7C - sound->field_74;
|
readPos += sound->numBytesRead + sound->numBuffers * sound->dataSize - sound->lastPosition;
|
||||||
} else {
|
} else {
|
||||||
playPos -= sound->field_74 + sound->field_64;
|
readPos -= sound->lastPosition + sound->numBytesRead;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return playPos;
|
return readPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x4AE6CC
|
// 0x4AE6CC
|
||||||
int _soundSetPosition(Sound* sound, int a2)
|
int _soundSetPosition(Sound* sound, int pos)
|
||||||
{
|
{
|
||||||
if (!gSoundInitialized) {
|
if (!gSoundInitialized) {
|
||||||
gSoundLastError = SOUND_NOT_INITIALIZED;
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
||||||
|
@ -1338,34 +1343,34 @@ int _soundSetPosition(Sound* sound, int a2)
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sound->field_44 & 0x02) {
|
if ((sound->type & SOUND_TYPE_STREAMING) != 0) {
|
||||||
int v6 = a2 / sound->field_7C % sound->field_78;
|
int section = pos / sound->dataSize % sound->numBuffers;
|
||||||
|
|
||||||
audioEngineSoundBufferSetCurrentPosition(sound->soundBuffer, v6 * sound->field_7C + a2 % sound->field_7C);
|
audioEngineSoundBufferSetCurrentPosition(sound->soundBuffer, section * sound->dataSize + pos % sound->dataSize);
|
||||||
|
|
||||||
sound->io.seek(sound->io.fd, v6 * sound->field_7C, SEEK_SET);
|
sound->io.seek(sound->io.fd, section * sound->dataSize, SEEK_SET);
|
||||||
int bytes_read = sound->io.read(sound->io.fd, sound->field_20, sound->field_7C);
|
int bytesRead = sound->io.read(sound->io.fd, sound->data, sound->dataSize);
|
||||||
if (bytes_read < sound->field_7C) {
|
if (bytesRead < sound->dataSize) {
|
||||||
if (sound->field_44 & 0x02) {
|
if ((sound->type & SOUND_TYPE_STREAMING) != 0) {
|
||||||
sound->io.seek(sound->io.fd, 0, SEEK_SET);
|
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);
|
sound->io.read(sound->io.fd, sound->data + bytesRead, sound->dataSize - bytesRead);
|
||||||
} else {
|
} else {
|
||||||
memset(sound->field_20 + bytes_read, 0, sound->field_7C - bytes_read);
|
memset(sound->data + bytesRead, 0, sound->dataSize - bytesRead);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int v17 = v6 + 1;
|
int nextSection = section + 1;
|
||||||
sound->field_64 = a2;
|
sound->numBytesRead = pos;
|
||||||
|
|
||||||
if (v17 < sound->field_78) {
|
if (nextSection < sound->numBuffers) {
|
||||||
sound->field_70 = v17;
|
sound->lastUpdate = nextSection;
|
||||||
} else {
|
} else {
|
||||||
sound->field_70 = 0;
|
sound->lastUpdate = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
soundContinue(sound);
|
soundContinue(sound);
|
||||||
} else {
|
} else {
|
||||||
audioEngineSoundBufferSetCurrentPosition(sound->soundBuffer, a2);
|
audioEngineSoundBufferSetCurrentPosition(sound->soundBuffer, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
gSoundLastError = SOUND_NO_ERROR;
|
gSoundLastError = SOUND_NO_ERROR;
|
||||||
|
@ -1387,7 +1392,7 @@ void _removeFadeSound(FadeSound* fadeSound)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(fadeSound->sound->field_40 & SOUND_FLAG_SOUND_IS_FADING)) {
|
if ((fadeSound->sound->statusFlags & SOUND_STATUS_IS_FADING) == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1403,7 +1408,7 @@ void _removeFadeSound(FadeSound* fadeSound)
|
||||||
next->prev = fadeSound->prev;
|
next->prev = fadeSound->prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
fadeSound->sound->field_40 &= ~SOUND_FLAG_SOUND_IS_FADING;
|
fadeSound->sound->statusFlags &= ~SOUND_STATUS_IS_FADING;
|
||||||
fadeSound->sound = NULL;
|
fadeSound->sound = NULL;
|
||||||
|
|
||||||
tmp = _fadeFreeList;
|
tmp = _fadeFreeList;
|
||||||
|
@ -1414,34 +1419,34 @@ void _removeFadeSound(FadeSound* fadeSound)
|
||||||
// 0x4AE8B0
|
// 0x4AE8B0
|
||||||
void _fadeSounds()
|
void _fadeSounds()
|
||||||
{
|
{
|
||||||
FadeSound* ptr;
|
FadeSound* fadeSound;
|
||||||
|
|
||||||
ptr = _fadeHead;
|
fadeSound = _fadeHead;
|
||||||
while (ptr != NULL) {
|
while (fadeSound != NULL) {
|
||||||
if ((ptr->currentVolume > ptr->targetVolume || ptr->currentVolume + ptr->deltaVolume < ptr->targetVolume) && (ptr->currentVolume < ptr->targetVolume || ptr->currentVolume + ptr->deltaVolume > ptr->targetVolume)) {
|
if ((fadeSound->currentVolume > fadeSound->targetVolume || fadeSound->currentVolume + fadeSound->deltaVolume < fadeSound->targetVolume) && (fadeSound->currentVolume < fadeSound->targetVolume || fadeSound->currentVolume + fadeSound->deltaVolume > fadeSound->targetVolume)) {
|
||||||
ptr->currentVolume += ptr->deltaVolume;
|
fadeSound->currentVolume += fadeSound->deltaVolume;
|
||||||
soundSetVolume(ptr->sound, ptr->currentVolume);
|
soundSetVolume(fadeSound->sound, fadeSound->currentVolume);
|
||||||
} else {
|
} else {
|
||||||
if (ptr->targetVolume == 0) {
|
if (fadeSound->targetVolume == 0) {
|
||||||
if (ptr->field_14) {
|
if (fadeSound->pause) {
|
||||||
soundPause(ptr->sound);
|
soundPause(fadeSound->sound);
|
||||||
soundSetVolume(ptr->sound, ptr->initialVolume);
|
soundSetVolume(fadeSound->sound, fadeSound->initialVolume);
|
||||||
} else {
|
} else {
|
||||||
if (ptr->sound->field_44 & 0x04) {
|
if ((fadeSound->sound->type & SOUND_TYPE_FIRE_AND_FORGET) != 0) {
|
||||||
soundDelete(ptr->sound);
|
soundDelete(fadeSound->sound);
|
||||||
} else {
|
} else {
|
||||||
soundStop(ptr->sound);
|
soundStop(fadeSound->sound);
|
||||||
|
|
||||||
ptr->initialVolume = ptr->targetVolume;
|
fadeSound->initialVolume = fadeSound->targetVolume;
|
||||||
ptr->currentVolume = ptr->targetVolume;
|
fadeSound->currentVolume = fadeSound->targetVolume;
|
||||||
ptr->deltaVolume = 0;
|
fadeSound->deltaVolume = 0;
|
||||||
|
|
||||||
soundSetVolume(ptr->sound, ptr->targetVolume);
|
soundSetVolume(fadeSound->sound, fadeSound->targetVolume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_removeFadeSound(ptr);
|
_removeFadeSound(fadeSound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1452,9 +1457,9 @@ void _fadeSounds()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x4AE988
|
// 0x4AE988
|
||||||
int _internalSoundFade(Sound* sound, int duration, int targetVolume, int a4)
|
int _internalSoundFade(Sound* sound, int duration, int targetVolume, bool pause)
|
||||||
{
|
{
|
||||||
FadeSound* ptr;
|
FadeSound* fadeSound;
|
||||||
|
|
||||||
if (!_deviceInit) {
|
if (!_deviceInit) {
|
||||||
gSoundLastError = SOUND_NOT_INITIALIZED;
|
gSoundLastError = SOUND_NOT_INITIALIZED;
|
||||||
|
@ -1466,56 +1471,56 @@ int _internalSoundFade(Sound* sound, int duration, int targetVolume, int a4)
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr = NULL;
|
fadeSound = NULL;
|
||||||
if (sound->field_40 & SOUND_FLAG_SOUND_IS_FADING) {
|
if ((sound->statusFlags & SOUND_STATUS_IS_FADING) != 0) {
|
||||||
ptr = _fadeHead;
|
fadeSound = _fadeHead;
|
||||||
while (ptr != NULL) {
|
while (fadeSound != NULL) {
|
||||||
if (ptr->sound == sound) {
|
if (fadeSound->sound == sound) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr = ptr->next;
|
fadeSound = fadeSound->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ptr == NULL) {
|
if (fadeSound == NULL) {
|
||||||
if (_fadeFreeList != NULL) {
|
if (_fadeFreeList != NULL) {
|
||||||
ptr = _fadeFreeList;
|
fadeSound = _fadeFreeList;
|
||||||
_fadeFreeList = _fadeFreeList->next;
|
_fadeFreeList = _fadeFreeList->next;
|
||||||
} else {
|
} else {
|
||||||
ptr = (FadeSound*)gSoundMallocProc(sizeof(FadeSound));
|
fadeSound = (FadeSound*)gSoundMallocProc(sizeof(FadeSound));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ptr != NULL) {
|
if (fadeSound != NULL) {
|
||||||
if (_fadeHead != NULL) {
|
if (_fadeHead != NULL) {
|
||||||
_fadeHead->prev = ptr;
|
_fadeHead->prev = fadeSound;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr->sound = sound;
|
fadeSound->sound = sound;
|
||||||
ptr->prev = NULL;
|
fadeSound->prev = NULL;
|
||||||
ptr->next = _fadeHead;
|
fadeSound->next = _fadeHead;
|
||||||
_fadeHead = ptr;
|
_fadeHead = fadeSound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ptr == NULL) {
|
if (fadeSound == NULL) {
|
||||||
gSoundLastError = SOUND_NO_MEMORY_AVAILABLE;
|
gSoundLastError = SOUND_NO_MEMORY_AVAILABLE;
|
||||||
return gSoundLastError;
|
return gSoundLastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr->targetVolume = targetVolume;
|
fadeSound->targetVolume = targetVolume;
|
||||||
ptr->initialVolume = _soundGetVolume(sound);
|
fadeSound->initialVolume = _soundGetVolume(sound);
|
||||||
ptr->currentVolume = ptr->initialVolume;
|
fadeSound->currentVolume = fadeSound->initialVolume;
|
||||||
ptr->field_14 = a4;
|
fadeSound->pause = pause;
|
||||||
// TODO: Check.
|
// TODO: Check.
|
||||||
ptr->deltaVolume = 8 * (125 * (targetVolume - ptr->initialVolume)) / (40 * duration);
|
fadeSound->deltaVolume = 8 * (125 * (targetVolume - fadeSound->initialVolume)) / (40 * duration);
|
||||||
|
|
||||||
sound->field_40 |= SOUND_FLAG_SOUND_IS_FADING;
|
sound->statusFlags |= SOUND_STATUS_IS_FADING;
|
||||||
|
|
||||||
bool shouldPlay;
|
bool shouldPlay;
|
||||||
if (gSoundInitialized) {
|
if (gSoundInitialized) {
|
||||||
if (sound->soundBuffer != -1) {
|
if (sound->soundBuffer != -1) {
|
||||||
shouldPlay = (sound->field_40 & SOUND_FLAG_SOUND_IS_PLAYING) == 0;
|
shouldPlay = (sound->statusFlags & SOUND_STATUS_IS_PLAYING) == 0;
|
||||||
} else {
|
} else {
|
||||||
gSoundLastError = SOUND_NO_SOUND;
|
gSoundLastError = SOUND_NO_SOUND;
|
||||||
shouldPlay = true;
|
shouldPlay = true;
|
||||||
|
@ -1547,7 +1552,7 @@ int _internalSoundFade(Sound* sound, int duration, int targetVolume, int a4)
|
||||||
// 0x4AEB0C
|
// 0x4AEB0C
|
||||||
int _soundFade(Sound* sound, int duration, int targetVolume)
|
int _soundFade(Sound* sound, int duration, int targetVolume)
|
||||||
{
|
{
|
||||||
return _internalSoundFade(sound, duration, targetVolume, 0);
|
return _internalSoundFade(sound, duration, targetVolume, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x4AEB54
|
// 0x4AEB54
|
||||||
|
|
66
src/sound.h
66
src/sound.h
|
@ -65,50 +65,66 @@ typedef struct SoundFileIO {
|
||||||
int fd;
|
int fd;
|
||||||
} SoundFileIO;
|
} SoundFileIO;
|
||||||
|
|
||||||
|
typedef enum SoundType {
|
||||||
|
SOUND_TYPE_MEMORY = 0x01,
|
||||||
|
SOUND_TYPE_STREAMING = 0x02,
|
||||||
|
SOUND_TYPE_FIRE_AND_FORGET = 0x04,
|
||||||
|
SOUND_TYPE_INFINITE = 0x10,
|
||||||
|
SOUND_TYPE_0x20 = 0x20,
|
||||||
|
} SoundType;
|
||||||
|
|
||||||
|
typedef enum SoundFlags {
|
||||||
|
SOUND_FLAG_0x02 = 0x02,
|
||||||
|
SOUND_FLAG_0x04 = 0x04,
|
||||||
|
SOUND_16BIT = 0x08,
|
||||||
|
SOUND_8BIT = 0x10,
|
||||||
|
SOUND_LOOPING = 0x20,
|
||||||
|
SOUND_FLAG_0x80 = 0x80,
|
||||||
|
SOUND_FLAG_0x100 = 0x100,
|
||||||
|
SOUND_FLAG_0x200 = 0x200,
|
||||||
|
} SoundFlags;
|
||||||
|
|
||||||
typedef void SoundCallback(void* userData, int a2);
|
typedef void SoundCallback(void* userData, int a2);
|
||||||
|
typedef void SoundDeleteCallback(void* userData);
|
||||||
|
|
||||||
typedef struct Sound {
|
typedef struct Sound {
|
||||||
SoundFileIO io;
|
SoundFileIO io;
|
||||||
unsigned char* field_20;
|
unsigned char* data;
|
||||||
int soundBuffer;
|
int soundBuffer;
|
||||||
int bitsPerSample;
|
int bitsPerSample;
|
||||||
int channels;
|
int channels;
|
||||||
int rate;
|
int rate;
|
||||||
int field_3C;
|
int soundFlags;
|
||||||
// flags
|
int statusFlags;
|
||||||
int field_40;
|
int type;
|
||||||
int field_44;
|
int pausePos;
|
||||||
// pause pos
|
|
||||||
int field_48;
|
|
||||||
int volume;
|
int volume;
|
||||||
int field_50;
|
int loops;
|
||||||
int field_54;
|
int field_54;
|
||||||
int field_58;
|
int field_58;
|
||||||
int field_5C;
|
int minReadBuffer;
|
||||||
// file size
|
int fileSize;
|
||||||
int field_60;
|
int numBytesRead;
|
||||||
int field_64;
|
|
||||||
int field_68;
|
int field_68;
|
||||||
int readLimit;
|
int readLimit;
|
||||||
int field_70;
|
int lastUpdate;
|
||||||
unsigned int field_74;
|
unsigned int lastPosition;
|
||||||
int field_78;
|
int numBuffers;
|
||||||
int field_7C;
|
int dataSize;
|
||||||
int field_80;
|
void* userData;
|
||||||
// callback data
|
|
||||||
void* callbackUserData;
|
void* callbackUserData;
|
||||||
SoundCallback* callback;
|
SoundCallback* callback;
|
||||||
int field_8C;
|
void* deleteUserData;
|
||||||
void (*field_90)(int);
|
SoundDeleteCallback* deleteCallback;
|
||||||
struct Sound* next;
|
struct Sound* next;
|
||||||
struct Sound* prev;
|
struct Sound* prev;
|
||||||
} Sound;
|
} Sound;
|
||||||
|
|
||||||
void soundSetMemoryProcs(MallocProc* mallocProc, ReallocProc* reallocProc, FreeProc* freeProc);
|
void soundSetMemoryProcs(MallocProc* mallocProc, ReallocProc* reallocProc, FreeProc* freeProc);
|
||||||
const char* soundGetErrorDescription(int err);
|
const char* soundGetErrorDescription(int err);
|
||||||
int soundInit(int a1, int a2, int a3, int a4, int rate);
|
int soundInit(int a1, int numBuffers, int a3, int dataSize, int rate);
|
||||||
void soundExit();
|
void soundExit();
|
||||||
Sound* soundAllocate(int a1, int a2);
|
Sound* soundAllocate(int type, int soundFlags);
|
||||||
int soundLoad(Sound* sound, char* filePath);
|
int soundLoad(Sound* sound, char* filePath);
|
||||||
int soundPlay(Sound* sound);
|
int soundPlay(Sound* sound);
|
||||||
int soundStop(Sound* sound);
|
int soundStop(Sound* sound);
|
||||||
|
@ -116,9 +132,9 @@ int soundDelete(Sound* sound);
|
||||||
bool soundIsPlaying(Sound* sound);
|
bool soundIsPlaying(Sound* sound);
|
||||||
bool _soundDone(Sound* sound);
|
bool _soundDone(Sound* sound);
|
||||||
bool soundIsPaused(Sound* sound);
|
bool soundIsPaused(Sound* sound);
|
||||||
int _soundType(Sound* sound, int a2);
|
int _soundType(Sound* sound, int type);
|
||||||
int soundGetDuration(Sound* sound);
|
int soundGetDuration(Sound* sound);
|
||||||
int soundSetLooping(Sound* sound, int a2);
|
int soundSetLooping(Sound* sound, int loops);
|
||||||
int _soundVolumeHMItoDirectSound(int a1);
|
int _soundVolumeHMItoDirectSound(int a1);
|
||||||
int soundSetVolume(Sound* sound, int volume);
|
int soundSetVolume(Sound* sound, int volume);
|
||||||
int soundSetCallback(Sound* sound, SoundCallback* callback, void* userData);
|
int soundSetCallback(Sound* sound, SoundCallback* callback, void* userData);
|
||||||
|
@ -129,7 +145,7 @@ int soundResume(Sound* sound);
|
||||||
int soundSetFileIO(Sound* sound, SoundOpenProc* openProc, SoundCloseProc* closeProc, SoundReadProc* readProc, SoundWriteProc* writeProc, SoundSeekProc* seekProc, SoundTellProc* tellProc, SoundFileLengthProc* fileLengthProc);
|
int soundSetFileIO(Sound* sound, SoundOpenProc* openProc, SoundCloseProc* closeProc, SoundReadProc* readProc, SoundWriteProc* writeProc, SoundSeekProc* seekProc, SoundTellProc* tellProc, SoundFileLengthProc* fileLengthProc);
|
||||||
int _soundSetMasterVolume(int value);
|
int _soundSetMasterVolume(int value);
|
||||||
int _soundGetPosition(Sound* sound);
|
int _soundGetPosition(Sound* sound);
|
||||||
int _soundSetPosition(Sound* sound, int a2);
|
int _soundSetPosition(Sound* sound, int pos);
|
||||||
int _soundFade(Sound* sound, int duration, int targetVolume);
|
int _soundFade(Sound* sound, int duration, int targetVolume);
|
||||||
void soundDeleteAll();
|
void soundDeleteAll();
|
||||||
void soundContinueAll();
|
void soundContinueAll();
|
||||||
|
|
Loading…
Reference in New Issue