989snd: Support version >= 2 sound effects (#1991)

Supports most of the grain types now while maintaining compatibility
with the old stuff (at least the subset of things jak1 uses)

Would benefit from some testing in Jak 1 to make sure I didn't break
anything.

Sorry the git history is a mess, I'll do something about it later.
This commit is contained in:
Ziemas 2022-12-03 00:08:44 +01:00 committed by GitHub
parent 80e9528e4e
commit 5b99929394
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 4689 additions and 479 deletions

View File

@ -64,6 +64,10 @@ static uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len);
static uint32_t FS_SyncRead(); static uint32_t FS_SyncRead();
static uint32_t FS_LoadSoundBank(char*, void*); static uint32_t FS_LoadSoundBank(char*, void*);
static uint32_t FS_LoadMusic(char*, void*); static uint32_t FS_LoadMusic(char*, void*);
static uint32_t FS_LoadSoundBank2(char*, void*);
static uint32_t FS_LoadMusic2(char*, void*);
static void FS_PollDrive(); static void FS_PollDrive();
static void LoadMusicTweaks(); static void LoadMusicTweaks();
@ -84,10 +88,16 @@ void fake_iso_init_globals() {
fake_iso.close = FS_Close; fake_iso.close = FS_Close;
fake_iso.begin_read = FS_BeginRead; fake_iso.begin_read = FS_BeginRead;
fake_iso.sync_read = FS_SyncRead; fake_iso.sync_read = FS_SyncRead;
fake_iso.load_sound_bank = FS_LoadSoundBank;
fake_iso.load_music = FS_LoadMusic;
fake_iso.poll_drive = FS_PollDrive; fake_iso.poll_drive = FS_PollDrive;
if (g_game_version == GameVersion::Jak1) {
fake_iso.load_sound_bank = FS_LoadSoundBank;
fake_iso.load_music = FS_LoadMusic;
} else {
fake_iso.load_sound_bank = FS_LoadSoundBank2;
fake_iso.load_music = FS_LoadMusic2;
}
sReadInfo = nullptr; sReadInfo = nullptr;
} }
@ -361,6 +371,56 @@ uint32_t FS_LoadSoundBank(char* name, void* buffer) {
snd_ResolveBankXREFS(); snd_ResolveBankXREFS();
PrintBankInfo(bank); PrintBankInfo(bank);
bank->bank_handle = handle; bank->bank_handle = handle;
return 0;
}
uint32_t FS_LoadMusic2(char* name, void* buffer) {
FileRecord* file = nullptr;
u32* bank_handle = (u32*)buffer;
char namebuf[16];
char isoname[16];
u32 handle;
strncpy(namebuf, name, 12);
namebuf[8] = 0;
strcat(namebuf, ".mus");
MakeISOName(isoname, namebuf);
file = FS_FindIN(isoname);
if (!file) {
return 6;
}
handle = snd_BankLoadEx(get_file_path(file), 0, 0xcfcc0, 0x61a80);
snd_ResolveBankXREFS();
*bank_handle = handle;
return 0;
}
uint32_t FS_LoadSoundBank2(char* name, void* buffer) {
SoundBank* bank = (SoundBank*)buffer;
FileRecord* file = nullptr;
char namebuf[16];
char isoname[16];
u32 handle;
strncpy(namebuf, name, 12);
namebuf[8] = 0;
strcat(namebuf, ".sbk");
MakeISOName(isoname, namebuf);
file = FS_FindIN(isoname);
if (!file) {
return 6;
}
handle = snd_BankLoadEx(get_file_path(file), 0, bank->spu_loc, bank->spu_size);
snd_ResolveBankXREFS();
bank->bank_handle = handle;
return 0; return 0;
} }

View File

@ -4,25 +4,67 @@
#include "soundcommon.h" #include "soundcommon.h"
constexpr int N_BANKS = 3; #include "common/log/log.h"
SoundBank* gBanks[N_BANKS];
#include "game/runtime.h"
static constexpr int N_BANKS = 6;
SoundBank gCommonBank; SoundBank gCommonBank;
SoundBank gLevelBank[2]; SoundBank gGunBank;
SoundBank gBoardBank;
SoundBank gLevelBanks[3];
SoundBank* gBanks[N_BANKS] = {&gCommonBank, &gGunBank, &gBoardBank,
&gLevelBanks[0], &gLevelBanks[1], &gLevelBanks[2]};
void sbank_init_globals() { void sbank_init_globals() {
gBanks[0] = &gCommonBank;
gBanks[1] = &gLevelBank[0];
gBanks[2] = &gLevelBank[1];
memset((void*)&gCommonBank, 0, sizeof(gCommonBank)); memset((void*)&gCommonBank, 0, sizeof(gCommonBank));
memset((void*)&gLevelBank, 0, sizeof(gLevelBank)); memset((void*)&gGunBank, 0, sizeof(gGunBank));
memset((void*)&gBoardBank, 0, sizeof(gBoardBank));
memset((void*)&gLevelBanks, 0, sizeof(gLevelBanks));
} }
void InitBanks() { void InitBanks() {
for (auto& gBank : gBanks) { for (auto& gBank : gBanks) {
gBank->bank_handle = 0; gBank->bank_handle = 0;
gBank->sound_count = 0; gBank->sound_count = 0;
gBank->in_use = false;
gBank->unk4 = 0;
// paper over bank allocation differences
if (g_game_version == GameVersion::Jak1)
gBank->in_use = 1;
strcpy(gBank->name, "<unused>"); strcpy(gBank->name, "<unused>");
} }
if (g_game_version == GameVersion::Jak2) {
strncpy(gBanks[0]->name, "common", 16);
gBanks[0]->spu_loc = 0x20000;
gBanks[0]->spu_size = 0xAFCC0;
strncpy(gBanks[1]->name, "gun", 16);
gBanks[0]->spu_loc = 0x131740;
gBanks[0]->spu_size = 0;
strncpy(gBanks[2]->name, "board", 16);
gBanks[0]->spu_loc = 0x131740;
gBanks[0]->spu_size = 0;
strncpy(gBanks[3]->name, "level0", 16);
gBanks[0]->spu_loc = 0x131740;
gBanks[0]->spu_size = 0x42800;
strncpy(gBanks[4]->name, "level1", 16);
gBanks[0]->spu_loc = 0x173f40;
gBanks[0]->spu_size = 0x42800;
strncpy(gBanks[5]->name, "level2", 16);
gBanks[0]->spu_loc = 0x1B6740;
gBanks[0]->spu_size = 0x42800;
}
} }
SoundBank* AllocateBank() { SoundBank* AllocateBank() {
@ -50,25 +92,34 @@ SoundBank* AllocateBank() {
return gBanks[idx]; return gBanks[idx];
} }
SoundBank* AllocateBankName(const char* name) {
if ((!strncmp(name, "common", 16) || !strncmp(name, "commonj", 16)) && !gBanks[0]->in_use) {
return gBanks[0];
}
for (int i = 3; i < N_BANKS; i++) {
if (!gBanks[i]->in_use) {
gBanks[i]->bank_handle = 0;
gBanks[i]->unk4 = 0;
return gBanks[i];
}
}
return nullptr;
}
s32 LookupSoundIndex(const char* name, SoundBank** bank_out) { s32 LookupSoundIndex(const char* name, SoundBank** bank_out) {
int idx = 0; for (auto bank : gBanks) {
while (true) { if (!bank->bank_handle) {
if (idx > N_BANKS - 1) { continue;
return -1;
} }
auto& bank = gBanks[idx]; for (int i = 0; i < bank->sound_count; i++) {
if (bank->bank_handle == 0) {
break;
}
for (int i = 0; i < (int)bank->sound_count; i++) {
if (memcmp(bank->sound[i].name, name, 16) == 0) { if (memcmp(bank->sound[i].name, name, 16) == 0) {
*bank_out = bank; *bank_out = bank;
return i; return i;
} }
} }
idx++;
} }
return -1; return -1;
@ -84,7 +135,7 @@ SoundBank* LookupBank(const char* name) {
auto& bank = gBanks[idx]; auto& bank = gBanks[idx];
// they had some weird stuff here that took advantage of the fact that this region was // they had some weird stuff here that took advantage of the fact that this region was
// 16-byte aligned, so it probably wasn't a memcmp, but this is easier. // 16-byte aligned, so it probably wasn't a memcmp, but this is easier.
if (memcmp(bank->name, name, 16) == 0) { if ((memcmp(bank->name, name, 16) == 0) && bank->in_use) {
return bank; return bank;
} }
idx--; idx--;

View File

@ -11,10 +11,20 @@ struct SoundBank {
char name[16]; char name[16];
u32 bank_handle; u32 bank_handle;
u32 sound_count; u32 sound_count;
union { union {
SoundRecord sound[1]; SoundRecord sound[1];
// Needs to fit the biggest bank (common.sbk) // Needs to fit the biggest bank (common.sbk)
u8 buffer[10 * 2048]; u8 buffer[10 * 2048];
// Jak 2 additions go here
struct {
u32 spu_loc;
u32 spu_size;
u32 unk4;
bool in_use;
};
}; };
}; };
@ -24,5 +34,6 @@ void InitBanks();
void ReloadBankInfo(); void ReloadBankInfo();
SoundBank* AllocateBank(); SoundBank* AllocateBank();
SoundBank* AllocateBankName(const char* name);
s32 LookupSoundIndex(const char* name, SoundBank** bank_out); s32 LookupSoundIndex(const char* name, SoundBank** bank_out);
SoundBank* LookupBank(const char* name); SoundBank* LookupBank(const char* name);

View File

@ -1,6 +1,8 @@
#include "soundcommon.h" #include "soundcommon.h"
#include <algorithm>
#include <cstdio> #include <cstdio>
#include <string.h>
#include <string> #include <string>
#include "common/util/Assert.h" #include "common/util/Assert.h"
@ -16,6 +18,17 @@ void ReadBankSoundInfo(SoundBank* bank, SoundBank* unk, s32 unk2) {
ASSERT(false); ASSERT(false);
} }
// I'm not bored enough to reimplement their strcpy
// Only for use with 16 character sound names!
void strcpy_toupper(char* dest, const char* source) {
// clear the dest string
memset(dest, 0, 16);
std::string string(source);
std::transform(string.begin(), string.end(), string.begin(), ::toupper);
std::replace(string.begin(), string.end(), '-', '_');
string.copy(dest, 16);
}
void PrintBankInfo(SoundBank* bank) { void PrintBankInfo(SoundBank* bank) {
// we dont need this and it spams the console too much // we dont need this and it spams the console too much
return; return;

View File

@ -6,6 +6,7 @@
#include "game/overlord/sbank.h" #include "game/overlord/sbank.h"
void strcpy_toupper(char* dest, const char* source);
void PrintBankInfo(SoundBank* buffer); void PrintBankInfo(SoundBank* buffer);
void ReadBankSoundInfo(SoundBank* bank, SoundBank* unk, s32 unk2); void ReadBankSoundInfo(SoundBank* bank, SoundBank* unk, s32 unk2);

View File

@ -8,6 +8,7 @@
#include "ramdisk.h" #include "ramdisk.h"
#include "sbank.h" #include "sbank.h"
#include "common/log/log.h"
#include "common/util/Assert.h" #include "common/util/Assert.h"
#include "common/versions.h" #include "common/versions.h"
@ -15,11 +16,13 @@
#include "game/common/loader_rpc_types.h" #include "game/common/loader_rpc_types.h"
#include "game/common/player_rpc_types.h" #include "game/common/player_rpc_types.h"
#include "game/graphics/gfx.h" #include "game/graphics/gfx.h"
#include "game/overlord/soundcommon.h"
#include "game/runtime.h" #include "game/runtime.h"
#include "game/sce/iop.h" #include "game/sce/iop.h"
#include "game/sound/sndshim.h" #include "game/sound/sndshim.h"
#include "third-party/fmt/core.h" #include "third-party/fmt/core.h"
#include "third-party/magic_enum.hpp"
using namespace iop; using namespace iop;
@ -35,6 +38,7 @@ s32 gMusicTweak = 0x80;
s32 gMusicPause = 0; s32 gMusicPause = 0;
u32 gFreeMem = 0; u32 gFreeMem = 0;
u32 gFrameNum = 0; u32 gFrameNum = 0;
u8 gFPS = 60;
// added // added
u32 gMusicFadeHack = 0; u32 gMusicFadeHack = 0;
@ -57,6 +61,8 @@ void srpc_init_globals() {
} }
void* RPC_Player(unsigned int fno, void* data, int size); void* RPC_Player(unsigned int fno, void* data, int size);
void* RPC_Player2(unsigned int fno, void* data, int size);
PerGameVersion<void* (*)(unsigned int, void*, int)> RPC_Player_Func = {RPC_Player, RPC_Player2};
u32 Thread_Player() { u32 Thread_Player() {
sceSifQueueData dq; sceSifQueueData dq;
@ -66,14 +72,17 @@ u32 Thread_Player() {
CpuDisableIntr(); CpuDisableIntr();
sceSifInitRpc(0); sceSifInitRpc(0);
sceSifSetRpcQueue(&dq, GetThreadId()); sceSifSetRpcQueue(&dq, GetThreadId());
sceSifRegisterRpc(&serve, PLAYER_RPC_ID[g_game_version], RPC_Player, gPlayerBuf, nullptr, nullptr, sceSifRegisterRpc(&serve, PLAYER_RPC_ID[g_game_version], RPC_Player_Func[g_game_version],
&dq); gPlayerBuf, nullptr, nullptr, &dq);
CpuEnableIntr(); CpuEnableIntr();
sceSifRpcLoop(&dq); sceSifRpcLoop(&dq);
return 0; return 0;
} }
void* RPC_Loader(unsigned int fno, void* data, int size); void* RPC_Loader(unsigned int fno, void* data, int size);
void* RPC_Loader2(unsigned int fno, void* data, int size);
PerGameVersion<void* (*)(unsigned int, void*, int)> RPC_Loader_Func = {RPC_Loader, RPC_Loader2};
u32 Thread_Loader() { u32 Thread_Loader() {
sceSifQueueData dq; sceSifQueueData dq;
@ -83,18 +92,14 @@ u32 Thread_Loader() {
CpuDisableIntr(); CpuDisableIntr();
sceSifInitRpc(0); sceSifInitRpc(0);
sceSifSetRpcQueue(&dq, GetThreadId()); sceSifSetRpcQueue(&dq, GetThreadId());
sceSifRegisterRpc(&serve, LOADER_RPC_ID[g_game_version], RPC_Loader, gLoaderBuf, nullptr, nullptr, sceSifRegisterRpc(&serve, LOADER_RPC_ID[g_game_version], RPC_Loader_Func[g_game_version],
&dq); gLoaderBuf, nullptr, nullptr, &dq);
CpuEnableIntr(); CpuEnableIntr();
sceSifRpcLoop(&dq); sceSifRpcLoop(&dq);
return 0; return 0;
} }
void* RPC_Player(unsigned int /*fno*/, void* data, int size) { void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
if (g_game_version == GameVersion::Jak2) {
printf("RPC_Player skip %d\n", (int)((SoundRpcCommand*)data)->command);
return nullptr;
}
if (gSoundEnable) { if (gSoundEnable) {
gFreeMem = QueryTotalFreeMemSize(); gFreeMem = QueryTotalFreeMemSize();
if (!PollSema(gSema)) { if (!PollSema(gSema)) {
@ -124,8 +129,8 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
int n_messages = size / SRPC_MESSAGE_SIZE; int n_messages = size / SRPC_MESSAGE_SIZE;
SoundRpcCommand* cmd = (SoundRpcCommand*)(data); SoundRpcCommand* cmd = (SoundRpcCommand*)(data);
while (n_messages > 0) { while (n_messages > 0) {
switch (cmd->command) { switch (cmd->j1command) {
case SoundCommand::PLAY: { case Jak1SoundCommand::PLAY: {
if (cmd->play.sound_id == 0) { if (cmd->play.sound_id == 0) {
break; break;
} }
@ -217,7 +222,7 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
sound->id = cmd->play.sound_id; sound->id = cmd->play.sound_id;
} }
} break; } break;
case SoundCommand::PAUSE_SOUND: { case Jak1SoundCommand::PAUSE_SOUND: {
Sound* sound = LookupSound(cmd->sound_id.sound_id); Sound* sound = LookupSound(cmd->sound_id.sound_id);
if (sound != nullptr) { if (sound != nullptr) {
snd_PauseSound(sound->sound_handle); snd_PauseSound(sound->sound_handle);
@ -225,7 +230,7 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
PauseVAGStream(); PauseVAGStream();
} }
} break; } break;
case SoundCommand::STOP_SOUND: { case Jak1SoundCommand::STOP_SOUND: {
Sound* sound = LookupSound(cmd->sound_id.sound_id); Sound* sound = LookupSound(cmd->sound_id.sound_id);
if (sound != nullptr) { if (sound != nullptr) {
snd_StopSound(sound->sound_handle); snd_StopSound(sound->sound_handle);
@ -233,7 +238,7 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
StopVAGStream(nullptr, 0); StopVAGStream(nullptr, 0);
} }
} break; } break;
case SoundCommand::CONTINUE_SOUND: { case Jak1SoundCommand::CONTINUE_SOUND: {
Sound* sound = LookupSound(cmd->sound_id.sound_id); Sound* sound = LookupSound(cmd->sound_id.sound_id);
if (sound != nullptr) { if (sound != nullptr) {
snd_ContinueSound(sound->sound_handle); snd_ContinueSound(sound->sound_handle);
@ -241,7 +246,7 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
UnpauseVAGStream(); UnpauseVAGStream();
} }
} break; } break;
case SoundCommand::SET_PARAM: { case Jak1SoundCommand::SET_PARAM: {
Sound* sound = LookupSound(cmd->sound_id.sound_id); Sound* sound = LookupSound(cmd->sound_id.sound_id);
u32 mask = cmd->param.parms.mask; u32 mask = cmd->param.parms.mask;
if (sound != nullptr) { if (sound != nullptr) {
@ -282,7 +287,7 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
SetVAGStreamVolume(cmd->param.parms.volume); SetVAGStreamVolume(cmd->param.parms.volume);
} }
} break; } break;
case SoundCommand::SET_MASTER_VOLUME: { case Jak1SoundCommand::SET_MASTER_VOLUME: {
u32 group = cmd->master_volume.group.group; u32 group = cmd->master_volume.group.group;
for (int i = 0; i < 32; i++) { for (int i = 0; i < 32; i++) {
if (((group >> i) & 1) != 0) { if (((group >> i) & 1) != 0) {
@ -296,7 +301,7 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
} }
} }
} break; } break;
case SoundCommand::PAUSE_GROUP: { case Jak1SoundCommand::PAUSE_GROUP: {
snd_PauseAllSoundsInGroup(cmd->group.group); snd_PauseAllSoundsInGroup(cmd->group.group);
if ((cmd->group.group & 4) != 0) { if ((cmd->group.group & 4) != 0) {
PauseVAGStream(); PauseVAGStream();
@ -305,14 +310,14 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
gMusicPause = 1; gMusicPause = 1;
} }
} break; } break;
case SoundCommand::STOP_GROUP: { case Jak1SoundCommand::STOP_GROUP: {
u8 group = cmd->group.group; u8 group = cmd->group.group;
KillSoundsInGroup(group); KillSoundsInGroup(group);
if ((group & 4) != 0) { if ((group & 4) != 0) {
StopVAGStream(nullptr, 0); StopVAGStream(nullptr, 0);
} }
} break; } break;
case SoundCommand::CONTINUE_GROUP: { case Jak1SoundCommand::CONTINUE_GROUP: {
snd_ContinueAllSoundsInGroup(cmd->group.group); snd_ContinueAllSoundsInGroup(cmd->group.group);
if (cmd->group.group & 4) { if (cmd->group.group & 4) {
UnpauseVAGStream(); UnpauseVAGStream();
@ -322,10 +327,10 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
gMusicPause = 0; gMusicPause = 0;
} }
} break; } break;
case SoundCommand::SET_FALLOFF_CURVE: { case Jak1SoundCommand::SET_FALLOFF_CURVE: {
SetCurve(cmd->fallof_curve.curve, cmd->fallof_curve.falloff, cmd->fallof_curve.ease); SetCurve(cmd->fallof_curve.curve, cmd->fallof_curve.falloff, cmd->fallof_curve.ease);
} break; } break;
case SoundCommand::SET_SOUND_FALLOFF: { case Jak1SoundCommand::SET_SOUND_FALLOFF: {
SoundBank* bank; SoundBank* bank;
s32 idx = LookupSoundIndex(cmd->fallof.name, &bank); s32 idx = LookupSoundIndex(cmd->fallof.name, &bank);
if (idx >= 0) { if (idx >= 0) {
@ -333,20 +338,21 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
(cmd->fallof.curve << 28) | (cmd->fallof.max << 14) | cmd->fallof.min; (cmd->fallof.curve << 28) | (cmd->fallof.max << 14) | cmd->fallof.min;
} }
} break; } break;
case SoundCommand::SET_FLAVA: { case Jak1SoundCommand::SET_FLAVA: {
gFlava = cmd->flava.flava; gFlava = cmd->flava.flava;
} break; } break;
case SoundCommand::SET_EAR_TRANS: { case Jak1SoundCommand::SET_EAR_TRANS: {
SetEarTrans(&cmd->ear_trans.ear_trans, &cmd->ear_trans.cam_trans, SetEarTrans(&cmd->ear_trans.ear_trans, &cmd->ear_trans.ear_trans,
cmd->ear_trans.cam_angle); &cmd->ear_trans.cam_trans, cmd->ear_trans.cam_angle);
} break; } break;
case SoundCommand::SHUTDOWN: { case Jak1SoundCommand::SHUTDOWN: {
gSoundEnable = 0; gSoundEnable = 0;
snd_StopSoundSystem(); snd_StopSoundSystem();
// TODO ShutdownFilingSystem(); // TODO ShutdownFilingSystem();
} break; } break;
default: { default: {
ASSERT_MSG(false, fmt::format("Unhandled RPC Player command {}", (int)cmd->command)); ASSERT_MSG(false, fmt::format("Unhandled RPC Player command {}",
magic_enum::enum_name(cmd->j1command)));
} break; } break;
} }
n_messages--; n_messages--;
@ -356,11 +362,300 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
return nullptr; return nullptr;
} }
void* RPC_Loader(unsigned int /*fno*/, void* data, int size) { void* RPC_Player2(unsigned int /*fno*/, void* data, int size) {
if (g_game_version == GameVersion::Jak2) { if (!gSoundEnable) {
printf("RPC_Loader skip %d\n", (int)((SoundRpcCommand*)data)->command);
return nullptr; return nullptr;
} }
gFreeMem = QueryTotalFreeMemSize();
if (!PollSema(gSema)) {
if (gMusic) {
if (!gMusicPause && !LookupSound(666)) {
Sound* music = AllocateSound();
if (music != nullptr) {
gMusicFade = 0;
gMusicFadeDir = 1;
SetMusicVol();
music->sound_handle = snd_PlaySoundVolPanPMPB(gMusic, 0, 0x400, -1, 0, 0);
music->id = 666;
music->is_music = 1;
}
}
}
SignalSema(gSema);
}
SetMusicVol();
Sound* music = LookupSound(666);
if (music != nullptr) {
snd_SetSoundVolPan(music->sound_handle, 0x7FFFFFFF, 0);
}
int n_messages = size / SRPC_MESSAGE_SIZE;
SoundRpcCommand* cmd = (SoundRpcCommand*)(data);
if (!gSoundEnable) {
return nullptr;
}
while (n_messages > 0) {
switch (cmd->j2command) {
case Jak2SoundCommand::play: {
if (!cmd->play.sound_id) {
break;
}
auto sound = LookupSound(cmd->play.sound_id);
if (sound != nullptr) {
// update
sound->params = cmd->play.parms;
sound->is_music = false;
SFXUserData data{};
s32 found = snd_GetSoundUserData(0, nullptr, -1, sound->name, &data);
if ((sound->params.mask & 0x40) == 0) {
s16 fo_min = 5;
if (found && data.data[0])
fo_min = data.data[0];
sound->params.fo_min = fo_min;
}
if ((sound->params.mask & 0x80) == 0) {
s16 fo_max = 30;
if (found && data.data[1])
fo_max = data.data[1];
sound->params.fo_max = fo_max;
}
if ((sound->params.mask & 0x100) == 0) {
s16 fo_curve = 2;
if (found && data.data[2])
fo_curve = data.data[2];
sound->params.fo_curve = fo_curve;
}
UpdateVolume(sound);
snd_SetSoundPitchModifier(sound->sound_handle, sound->params.pitch_mod);
if (sound->params.mask & 0x4) {
snd_SetSoundPitchBend(sound->sound_handle, sound->params.bend);
}
if (sound->params.mask & 0x800) {
snd_SetSoundReg(sound->sound_handle, 0, sound->params.reg[0]);
}
if (sound->params.mask & 0x1000) {
snd_SetSoundReg(sound->sound_handle, 1, sound->params.reg[1]);
}
if (sound->params.mask & 0x2000) {
snd_SetSoundReg(sound->sound_handle, 2, sound->params.reg[2]);
}
} else {
// new sound
sound = AllocateSound();
if (sound == nullptr) {
// no free sounds
break;
}
strcpy_toupper(sound->name, cmd->play.name);
// TODO update params struct
sound->params = cmd->play.parms;
sound->is_music = false;
sound->bank_entry = nullptr;
SFXUserData data{};
s32 found = snd_GetSoundUserData(0, nullptr, -1, sound->name, &data);
if ((sound->params.mask & 0x40) == 0) {
s16 fo_min = 5;
if (found && data.data[0])
fo_min = data.data[0];
sound->params.fo_min = fo_min;
}
if ((sound->params.mask & 0x80) == 0) {
s16 fo_max = 30;
if (found && data.data[1])
fo_max = data.data[1];
sound->params.fo_max = fo_max;
}
if ((sound->params.mask & 0x100) == 0) {
s16 fo_curve = 2;
if (found && data.data[2])
fo_curve = data.data[2];
sound->params.fo_curve = fo_curve;
}
// lg::warn("RPC: PLAY {} v:{}, p:{}", sound->name, GetVolume(sound), GetPan(sound));
s32 handle = snd_PlaySoundByNameVolPanPMPB(0, nullptr, sound->name, GetVolume(sound),
GetPan(sound), sound->params.pitch_mod,
sound->params.bend);
sound->sound_handle = handle;
if (handle != 0) {
sound->id = cmd->play.sound_id;
if (sound->params.mask & 0x800) {
snd_SetSoundReg(sound->sound_handle, 0, sound->params.reg[0]);
}
if (sound->params.mask & 0x1000) {
snd_SetSoundReg(sound->sound_handle, 1, sound->params.reg[1]);
}
if (sound->params.mask & 0x2000) {
snd_SetSoundReg(sound->sound_handle, 2, sound->params.reg[2]);
}
}
}
} break;
case Jak2SoundCommand::pause_sound: {
Sound* sound = LookupSound(cmd->sound_id.sound_id);
if (sound != nullptr) {
snd_PauseSound(sound->sound_handle);
}
// TODO vag
} break;
case Jak2SoundCommand::stop_sound: {
Sound* sound = LookupSound(cmd->sound_id.sound_id);
if (sound != nullptr) {
snd_StopSound(sound->sound_handle);
}
// TODO vag
} break;
case Jak2SoundCommand::continue_sound: {
Sound* sound = LookupSound(cmd->sound_id.sound_id);
if (sound != nullptr) {
snd_ContinueSound(sound->sound_handle);
}
// TODO vag
} break;
case Jak2SoundCommand::set_param: {
Sound* sound = LookupSound(cmd->sound_id.sound_id);
u32 mask = cmd->param.parms.mask;
if (sound != nullptr) {
if (mask & 1) {
if (mask & 0x10) {
sound->auto_time = cmd->param.auto_time;
sound->new_volume = cmd->param.parms.volume;
} else {
sound->params.volume = cmd->param.parms.volume;
}
}
if (mask & 0x20) {
sound->params.trans = cmd->param.parms.trans;
}
if (mask & 0x21) {
UpdateVolume(sound);
}
if (mask & 2) {
sound->params.pitch_mod = cmd->param.parms.pitch_mod;
if (mask & 0x10) {
snd_AutoPitch(sound->sound_handle, sound->params.pitch_mod, cmd->param.auto_time,
cmd->param.auto_from);
} else {
snd_SetSoundPitchModifier(sound->sound_handle, cmd->param.parms.pitch_mod);
}
}
if (mask & 4) {
sound->params.bend = cmd->param.parms.bend;
if (mask & 0x10) {
snd_AutoPitchBend(sound->sound_handle, sound->params.bend, cmd->param.auto_time,
cmd->param.auto_from);
} else {
snd_SetSoundPitchBend(sound->sound_handle, cmd->param.parms.bend);
}
}
if (mask & 0x400) {
sound->params.priority = cmd->param.parms.priority;
}
if (mask & 0x8) {
sound->params.group = cmd->param.parms.group;
}
if (mask & 0x40) {
sound->params.fo_min = cmd->param.parms.fo_min;
}
if (mask & 0x80) {
sound->params.fo_max = cmd->param.parms.fo_max;
}
if (mask & 0x100) {
sound->params.fo_curve = cmd->param.parms.fo_curve;
}
if (mask & 0x800) {
sound->params.reg[0] = cmd->param.parms.reg[0];
snd_SetSoundReg(sound->sound_handle, 0, cmd->param.parms.reg[0]);
}
if (mask & 0x1000) {
sound->params.reg[1] = cmd->param.parms.reg[1];
snd_SetSoundReg(sound->sound_handle, 1, cmd->param.parms.reg[1]);
}
if (mask & 0x2000) {
sound->params.reg[2] = cmd->param.parms.reg[2];
snd_SetSoundReg(sound->sound_handle, 2, cmd->param.parms.reg[2]);
}
}
// TODO vag
} break;
case Jak2SoundCommand::set_master_volume: {
u32 group = cmd->master_volume.group.group;
// FIXME array of set volumes
for (int i = 0; i < 32; i++) {
if (((group >> i) & 1) != 0) {
if (i == 1) {
gMusicVol = cmd->master_volume.volume;
} else if (i == 2) {
SetDialogVolume(cmd->master_volume.volume);
} else {
snd_SetMasterVolume(i, cmd->master_volume.volume);
}
}
}
} break;
case Jak2SoundCommand::pause_group: {
snd_PauseAllSoundsInGroup(cmd->group.group);
if (cmd->group.group & 2) {
gMusicPause = 1;
}
if (cmd->group.group & 4) {
// TODO vag
}
} break;
case Jak2SoundCommand::stop_group: {
KillSoundsInGroup(cmd->group.group);
} break;
case Jak2SoundCommand::continue_group: {
snd_ContinueAllSoundsInGroup(cmd->group.group);
if (cmd->group.group & 2) {
gMusicPause = 0;
}
if (cmd->group.group & 4) {
// TODO vag
}
} break;
case Jak2SoundCommand::set_midi_reg: {
if (cmd->midi_reg.reg == 16) {
snd_SetGlobalExcite(cmd->midi_reg.value);
} else {
Sound* sound = LookupSound(666);
snd_SetMIDIRegister(sound->sound_handle, cmd->midi_reg.reg, cmd->midi_reg.value);
}
} break;
case Jak2SoundCommand::set_reverb: {
lg::warn("RPC_Player: unimplemented set_reverb");
// TODO reverb
} break;
case Jak2SoundCommand::set_ear_trans: {
SetEarTrans(&cmd->ear_trans_j2.ear_trans1, &cmd->ear_trans_j2.ear_trans2,
&cmd->ear_trans_j2.cam_trans, cmd->ear_trans_j2.cam_angle);
} break;
case Jak2SoundCommand::shutdown: {
gSoundEnable = 0;
} break;
case Jak2SoundCommand::set_fps: {
gFPS = cmd->fps.fps;
} break;
default:
ASSERT_MSG(false, fmt::format("Unhandled RPC Player command {}",
magic_enum::enum_name(cmd->j2command)));
}
n_messages--;
cmd++;
}
return nullptr;
}
void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
int n_messages = size / SRPC_MESSAGE_SIZE; int n_messages = size / SRPC_MESSAGE_SIZE;
SoundRpcCommand* cmd = (SoundRpcCommand*)(data); SoundRpcCommand* cmd = (SoundRpcCommand*)(data);
if (gSoundEnable) { if (gSoundEnable) {
@ -369,8 +664,8 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
ASSERT(false); ASSERT(false);
} }
while (n_messages > 0) { while (n_messages > 0) {
switch (cmd->command) { switch (cmd->j1command) {
case SoundCommand::LOAD_BANK: { case Jak1SoundCommand::LOAD_BANK: {
// see if it's already loaded // see if it's already loaded
auto bank = LookupBank(cmd->load_bank.bank_name); auto bank = LookupBank(cmd->load_bank.bank_name);
if (!bank) { if (!bank) {
@ -382,7 +677,7 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
} }
} }
} break; } break;
case SoundCommand::UNLOAD_BANK: { case Jak1SoundCommand::UNLOAD_BANK: {
SoundBank* bank = LookupBank(cmd->load_bank.bank_name); SoundBank* bank = LookupBank(cmd->load_bank.bank_name);
if (bank != nullptr) { if (bank != nullptr) {
s32 id = bank->bank_handle; s32 id = bank->bank_handle;
@ -391,20 +686,20 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
snd_ResolveBankXREFS(); snd_ResolveBankXREFS();
} }
} break; } break;
case SoundCommand::GET_IRX_VERSION: { case Jak1SoundCommand::GET_IRX_VERSION: {
cmd->irx_version.major = IRX_VERSION_MAJOR; cmd->irx_version.major = IRX_VERSION_MAJOR;
cmd->irx_version.minor = IRX_VERSION_MINOR; cmd->irx_version.minor = IRX_VERSION_MINOR;
gInfoEE = cmd->irx_version.ee_addr; gInfoEE = cmd->irx_version.ee_addr;
return cmd; return cmd;
} break; } break;
case SoundCommand::RELOAD_INFO: { case Jak1SoundCommand::RELOAD_INFO: {
ReloadBankInfo(); ReloadBankInfo();
} break; } break;
case SoundCommand::SET_LANGUAGE: { case Jak1SoundCommand::SET_LANGUAGE: {
gLanguage = languages[cmd->set_language.langauge_id]; gLanguage = languages[cmd->set_language.langauge_id];
printf("IOP language: %s\n", gLanguage); // added. printf("IOP language: %s\n", gLanguage); // added.
} break; } break;
case SoundCommand::LOAD_MUSIC: { case Jak1SoundCommand::LOAD_MUSIC: {
while (WaitSema(gSema)) while (WaitSema(gSema))
; ;
if (gMusic) { if (gMusic) {
@ -419,10 +714,10 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
LoadMusic(cmd->load_bank.bank_name, &gMusic); LoadMusic(cmd->load_bank.bank_name, &gMusic);
SignalSema(gSema); SignalSema(gSema);
} break; } break;
case SoundCommand::LIST_SOUNDS: { case Jak1SoundCommand::LIST_SOUNDS: {
PrintActiveSounds(); PrintActiveSounds();
} break; } break;
case SoundCommand::UNLOAD_MUSIC: { case Jak1SoundCommand::UNLOAD_MUSIC: {
while (WaitSema(gSema)) while (WaitSema(gSema))
; ;
if (gMusic) { if (gMusic) {
@ -437,7 +732,8 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
SignalSema(gSema); SignalSema(gSema);
} break; } break;
default: default:
ASSERT_MSG(false, fmt::format("Unhandled RPC Loader command {}", (int)cmd->command)); ASSERT_MSG(false, fmt::format("Unhandled RPC Loader command {}",
magic_enum::enum_name(cmd->j1command)));
} }
n_messages--; n_messages--;
cmd++; cmd++;
@ -446,6 +742,104 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
return nullptr; return nullptr;
} }
static void UnLoadMusic(s32* handle) {
gMusicFadeDir = -1;
while (gMusicFade)
DelayThread(1000);
snd_UnloadBank(*handle);
snd_ResolveBankXREFS();
*handle = 0;
}
void* RPC_Loader2(unsigned int fno, void* data, int size) {
int n_messages = size / SRPC_MESSAGE_SIZE;
SoundRpcCommand* cmd = (SoundRpcCommand*)(data);
if (!gSoundEnable) {
return nullptr;
}
while (n_messages > 0) {
switch (cmd->j2command) {
case Jak2SoundCommand::load_bank: {
if (LookupBank(cmd->load_bank.bank_name)) {
break;
}
auto bank = AllocateBankName(cmd->load_bank.bank_name);
if (bank == nullptr) {
break;
}
strncpy(bank->name, cmd->load_bank.bank_name, 16);
bank->in_use = true;
bank->unk4 = 0;
LoadSoundBank(cmd->load_bank.bank_name, bank);
} break;
case Jak2SoundCommand::load_music: {
while (WaitSema(gSema))
;
if (gMusic) {
UnLoadMusic(&gMusic);
}
LoadMusic(cmd->load_bank.bank_name, &gMusic);
SignalSema(gSema);
} break;
case Jak2SoundCommand::unload_bank: {
auto bank = LookupBank(cmd->load_bank.bank_name);
if (!bank) {
break;
}
auto handle = bank->bank_handle;
if (!bank->unk4) {
bank->in_use = false;
}
bank->in_use = 0;
snd_UnloadBank(handle);
snd_ResolveBankXREFS();
} break;
case Jak2SoundCommand::get_irx_version: {
cmd->irx_version.major = 4;
cmd->irx_version.minor = 0;
gInfoEE = cmd->irx_version.ee_addr;
return data;
} break;
case Jak2SoundCommand::set_language: {
gLanguage = languages[cmd->set_language.langauge_id];
} break;
case Jak2SoundCommand::list_sounds: {
// Not present in real jak2 overlord
PrintActiveSounds();
} break;
case Jak2SoundCommand::unload_music: {
while (WaitSema(gSema))
;
if (gMusic) {
UnLoadMusic(&gMusic);
}
SignalSema(gSema);
} break;
case Jak2SoundCommand::set_stereo_mode: {
s32 mode = cmd->stereo_mode.stereo_mode;
if (mode == 0) {
snd_SetPlayBackMode(1);
} else if (mode == 1) {
snd_SetPlayBackMode(2);
} else if (mode == 2) {
snd_SetPlayBackMode(0);
}
} break;
default:
ASSERT_MSG(false, fmt::format("Unhandled RPC Loader command {}",
magic_enum::enum_name(cmd->j2command)));
}
n_messages--;
cmd++;
}
return nullptr;
}
static s32 dmaid = 0; static s32 dmaid = 0;
s32 VBlank_Handler(void*) { s32 VBlank_Handler(void*) {

View File

@ -20,7 +20,7 @@ struct MusicTweaks {
} MusicTweak[MUSIC_TWEAK_COUNT]; } MusicTweak[MUSIC_TWEAK_COUNT];
}; };
enum class SoundCommand : u16 { enum class Jak1SoundCommand : u16 {
LOAD_BANK = 0, LOAD_BANK = 0,
LOAD_MUSIC = 1, LOAD_MUSIC = 1,
UNLOAD_BANK = 2, UNLOAD_BANK = 2,
@ -46,6 +46,60 @@ enum class SoundCommand : u16 {
UNLOAD_MUSIC = 22 UNLOAD_MUSIC = 22
}; };
enum class Jak2SoundCommand : u16 {
iop_store = 0,
iop_free = 1,
load_bank = 2,
load_bank_from_iop = 3,
load_bank_from_ee = 4,
load_music = 5,
unload_bank = 6,
play = 7,
pause_sound = 8,
stop_sound = 9,
continue_sound = 10,
set_param = 11,
set_master_volume = 12,
pause_group = 13,
stop_group = 14,
continue_group = 15,
get_irx_version = 16,
set_falloff_curve = 17,
set_sound_falloff = 18,
reload_info = 19,
set_language = 20,
set_flava = 21,
set_midi_reg = 22,
set_reverb = 23,
set_ear_trans = 24,
shutdown = 25,
list_sounds = 26,
unload_music = 27,
set_fps = 28,
boot_load = 29,
game_load = 30,
num_tests = 31,
num_testruns = 32,
num_sectors = 33,
num_streamsectors = 34,
num_streambanks = 35,
track_pitch = 36,
linvel_nom = 37,
linvel_stm = 38,
seek_nom = 39,
seek_stm = 40,
read_seq_nom = 41,
read_seq_stm = 42,
read_spr_nom = 43,
read_spr_stm = 44,
read_spr_strn_nom = 45,
rand_stm_abort = 46,
rand_nom_abort = 47,
iop_mem = 48,
cancel_dgo = 49,
set_stereo_mode = 50,
};
struct SoundRpcGetIrxVersion { struct SoundRpcGetIrxVersion {
u32 major; u32 major;
u32 minor; u32 minor;
@ -96,6 +150,13 @@ struct SoundRpcSetEarTrans {
s32 cam_angle; s32 cam_angle;
}; };
struct SoundRpc2SetEarTrans {
Vec3w ear_trans1;
Vec3w ear_trans2;
Vec3w cam_trans;
s32 cam_angle;
};
struct SoundRpcSetFPSCommand { struct SoundRpcSetFPSCommand {
u8 fps; u8 fps;
}; };
@ -123,9 +184,21 @@ struct SoundRpcMasterVolCommand {
s32 volume; s32 volume;
}; };
struct SoundRpcStereoMode {
s32 stereo_mode;
};
struct SoundRpcSetMidiReg {
s32 reg;
s32 value;
};
struct SoundRpcCommand { struct SoundRpcCommand {
u16 rsvd1; u16 rsvd1;
SoundCommand command; union {
Jak1SoundCommand j1command;
Jak2SoundCommand j2command;
};
union { union {
SoundRpcGetIrxVersion irx_version; SoundRpcGetIrxVersion irx_version;
SoundRpcBankCommand load_bank; SoundRpcBankCommand load_bank;
@ -134,6 +207,7 @@ struct SoundRpcCommand {
SoundRpcSoundIdCommand sound_id; SoundRpcSoundIdCommand sound_id;
SoundRpcSetFPSCommand fps; SoundRpcSetFPSCommand fps;
SoundRpcSetEarTrans ear_trans; SoundRpcSetEarTrans ear_trans;
SoundRpc2SetEarTrans ear_trans_j2;
SoundRpcSetReverb reverb; SoundRpcSetReverb reverb;
SoundRpcSetFallof fallof; SoundRpcSetFallof fallof;
SoundRpcSetFallofCurve fallof_curve; SoundRpcSetFallofCurve fallof_curve;
@ -141,6 +215,8 @@ struct SoundRpcCommand {
SoundRpcSetFlavaCommand flava; SoundRpcSetFlavaCommand flava;
SoundRpcMasterVolCommand master_volume; SoundRpcMasterVolCommand master_volume;
SoundRpcSetParamCommand param; SoundRpcSetParamCommand param;
SoundRpcStereoMode stereo_mode;
SoundRpcSetMidiReg midi_reg;
u8 max_size[0x4C]; // Temporary u8 max_size[0x4C]; // Temporary
}; };
}; };

View File

@ -7,15 +7,16 @@
#include "game/overlord/iso.h" #include "game/overlord/iso.h"
#include "game/overlord/srpc.h" #include "game/overlord/srpc.h"
#include "game/runtime.h"
#include "game/sound/sndshim.h" #include "game/sound/sndshim.h"
using namespace iop; using namespace iop;
Sound gSounds[64]; Sound gSounds[64];
Curve gCurve[8]; // TODO verify count Curve gCurve[12]; // TODO verify count
VolumePair gPanTable[361]; VolumePair gPanTable[361];
Vec3w gEarTrans; Vec3w gEarTrans[2];
Vec3w gCamTrans; Vec3w gCamTrans;
s32 gCamAngle; s32 gCamAngle;
@ -75,18 +76,31 @@ void InitSound_Overlord() {
s.id = 0; s.id = 0;
} }
SetCurve(1, 0, 0); if (g_game_version == GameVersion::Jak1) {
SetCurve(2, 0x1000, 0); SetCurve(1, 0, 0);
SetCurve(3, 0, 0x1000); SetCurve(2, 4096, 0);
SetCurve(4, 0x800, 0); SetCurve(3, 0, 4096);
SetCurve(5, 0x800, 0x800); SetCurve(4, 2048, 0);
SetCurve(6, -0x1000, 0); SetCurve(5, 2048, 2048);
SetCurve(6, -0x800, 0); SetCurve(6, -4096, 0);
SetCurve(7, -2048, 0);
} else {
SetCurve(2, 0, 0);
SetCurve(9, 0, 0);
SetCurve(11, 0, 0);
SetCurve(10, 0, 0);
SetCurve(3, 4096, 0);
SetCurve(4, 0, 4096);
SetCurve(5, 2048, 0);
SetCurve(6, 2048, 2048);
SetCurve(7, -4096, 0);
SetCurve(8, -2048, 0);
}
snd_StartSoundSystem(); snd_StartSoundSystem();
snd_RegisterIOPMemAllocator(SndMemAlloc, SndMemFree); snd_RegisterIOPMemAllocator(SndMemAlloc, SndMemFree);
snd_LockVoiceAllocator(1); snd_LockVoiceAllocator(1);
u32 voice = snd_ExternVoiceVoiceAlloc(2, 0x7f); u32 voice = snd_ExternVoiceAlloc(2, 0x7f);
snd_UnlockVoiceAllocator(); snd_UnlockVoiceAllocator();
// The voice allocator returns a number in the range 0-47 where voices // The voice allocator returns a number in the range 0-47 where voices
@ -216,22 +230,56 @@ Sound* AllocateSound() {
} }
s32 CalculateFallofVolume(Vec3w* pos, s32 volume, s32 fo_curve, s32 fo_min, s32 fo_max) { s32 CalculateFallofVolume(Vec3w* pos, s32 volume, s32 fo_curve, s32 fo_min, s32 fo_max) {
if (fo_curve == 0) { s32 xdiff = 0;
return volume; s32 ydiff = 0;
} s32 zdiff = 0;
s32 xdiff = gEarTrans.x - pos->x; if (g_game_version == GameVersion::Jak1) {
s32 ydiff = gEarTrans.y - pos->y; if (fo_curve == 0) {
s32 zdiff = gEarTrans.z - pos->z; return volume;
}
xdiff = gEarTrans[0].x - pos->x;
ydiff = gEarTrans[0].y - pos->y;
zdiff = gEarTrans[0].z - pos->z;
} else {
if (fo_curve == 1) {
return volume;
}
if (fo_curve < 9) {
xdiff = gEarTrans[0].x - pos->x;
ydiff = gEarTrans[0].y - pos->y;
zdiff = gEarTrans[0].z - pos->z;
}
if (fo_curve == 9) {
xdiff = gEarTrans[1].x - pos->x;
ydiff = gEarTrans[1].y - pos->y;
zdiff = gEarTrans[1].z - pos->z;
}
if (fo_curve == 10) {
xdiff = 0;
ydiff = gEarTrans[0].y - pos->y;
zdiff = 0;
}
if (fo_curve == 11) {
xdiff = gEarTrans[1].x - pos->x;
ydiff = gEarTrans[1].y - pos->y;
zdiff = gEarTrans[1].z - pos->z;
}
}
if (xdiff < 0) { if (xdiff < 0) {
xdiff = pos->x - gEarTrans.x; xdiff = -xdiff;
} }
if (ydiff < 0) { if (ydiff < 0) {
ydiff = pos->y - gEarTrans.y; ydiff = -ydiff;
} }
if (zdiff < 0) { if (zdiff < 0) {
zdiff = pos->z - gEarTrans.z; zdiff = -zdiff;
} }
s32 min = fo_min << 8; s32 min = fo_min << 8;
@ -300,7 +348,12 @@ s32 CalculateFallofVolume(Vec3w* pos, s32 volume, s32 fo_curve, s32 fo_min, s32
factor = 0x10000; factor = 0x10000;
} }
return (factor * volume) >> 16; s32 ret = (factor * volume) >> 16;
if (fo_curve == 11 && ret < 0x180) {
ret = 0x180;
}
return ret;
} }
s32 CalculateAngle(Vec3w* trans) { s32 CalculateAngle(Vec3w* trans) {
@ -368,8 +421,10 @@ static void UpdateLocation(Sound* sound) {
return; return;
} }
if ((sound->bank_entry->fallof_params >> 28) == 0) { if (g_game_version == GameVersion::Jak1) {
return; if ((sound->bank_entry->fallof_params >> 28) == 0) {
return;
}
} }
s32 id = snd_SoundIsStillPlaying(sound->sound_handle); s32 id = snd_SoundIsStillPlaying(sound->sound_handle);
@ -439,12 +494,13 @@ void UpdateVolume(Sound* sound) {
} }
} }
void SetEarTrans(Vec3w* ear_trans, Vec3w* cam_trans, s32 cam_angle) { void SetEarTrans(Vec3w* ear_trans1, Vec3w* ear_trans2, Vec3w* cam_trans, s32 cam_angle) {
s32 tick = snd_GetTick(); s32 tick = snd_GetTick();
u32 delta = tick - sLastTick; u32 delta = tick - sLastTick;
sLastTick = tick; sLastTick = tick;
gEarTrans = *ear_trans; gEarTrans[0] = *ear_trans1;
gEarTrans[1] = *ear_trans2;
gCamTrans = *cam_trans; gCamTrans = *cam_trans;
gCamAngle = cam_angle; gCamAngle = cam_angle;
@ -465,14 +521,24 @@ void PrintActiveSounds() {
for (auto& s : gSounds) { for (auto& s : gSounds) {
if (s.id != 0 && s.is_music == 0) { if (s.id != 0 && s.is_music == 0) {
u32 len = strlen(s.bank_entry->name); if (s.bank_entry != nullptr) {
if (len > 16) { u32 len = strlen(s.bank_entry->name);
len = 16; if (len > 16) {
len = 16;
}
sprintf(string, " : Vol %d", GetVolume(&s));
memcpy(string, s.bank_entry->name, len);
printf("%s\n", string);
} else { // added for printing jak2 sounds
u32 len = strlen(s.name);
if (len > 16) {
len = 16;
}
sprintf(string, " : Vol %d, ID %d, Curve %d", GetVolume(&s), s.id,
s.params.fo_curve);
memcpy(string, s.name, len);
printf("%s\n", string);
} }
s32 volume = GetVolume(&s);
sprintf(string, " : Vol %d", volume);
memcpy(string, s.bank_entry->name, len);
printf("%s\n", string);
} }
} }
} }

View File

@ -36,9 +36,11 @@ struct SoundParams {
s32 volume; s32 volume;
Vec3w trans; Vec3w trans;
u8 group; u8 group;
u8 reg[3];
}; };
struct Sound { struct Sound {
char name[16];
s32 id; s32 id;
s32 sound_handle; s32 sound_handle;
s32 is_music; s32 is_music;
@ -57,7 +59,7 @@ struct Curve {
void InitSound_Overlord(); void InitSound_Overlord();
void SetCurve(s32 curve, s32 fallof, s32 ease); void SetCurve(s32 curve, s32 fallof, s32 ease);
void SetEarTrans(Vec3w* ear_trans, Vec3w* cam_trans, s32 cam_angle); void SetEarTrans(Vec3w* ear_trans1, Vec3w* ear_trans2, Vec3w* cam_trans, s32 cam_angle);
void KillSoundsInGroup(u8 group); void KillSoundsInGroup(u8 group);
void PrintActiveSounds(); void PrintActiveSounds();
void SetMusicVol(); void SetMusicVol();

View File

@ -17,7 +17,7 @@ ame_handler::ame_handler(MultiMIDIBlockHeader* block,
s32 vol, s32 vol,
s32 pan, s32 pan,
locator& loc, locator& loc,
u32 bank) SoundBank& bank)
: m_sound(sound), : m_sound(sound),
m_bank(bank), m_bank(bank),
m_header(block), m_header(block),
@ -75,7 +75,6 @@ void ame_handler::stop_segment(u32 id) {
return; return;
m->second->stop(); m->second->stop();
m_midis.erase(id);
} }
void ame_handler::pause() { void ame_handler::pause() {
@ -115,7 +114,9 @@ void ame_handler::set_vol_pan(s32 vol, s32 pan) {
} }
void ame_handler::set_pmod(s32 mod) { void ame_handler::set_pmod(s32 mod) {
// TODO for (auto& m : m_midis) {
m.second->set_pmod(mod);
}
} }
#define AME_BEGIN(op) \ #define AME_BEGIN(op) \

View File

@ -26,9 +26,9 @@ class ame_handler : public sound_handler {
s32 vol, s32 vol,
s32 pan, s32 pan,
locator& loc, locator& loc,
u32 bank); SoundBank& bank);
bool tick() override; bool tick() override;
u32 bank() override { return m_bank; }; SoundBank& bank() override { return m_bank; };
void pause() override; void pause() override;
void unpause() override; void unpause() override;
@ -61,7 +61,7 @@ class ame_handler : public sound_handler {
std::pair<bool, u8*> run_ame(midi_handler&, u8* stream); std::pair<bool, u8*> run_ame(midi_handler&, u8* stream);
MIDISound& m_sound; MIDISound& m_sound;
u32 m_bank{0}; SoundBank& m_bank;
MultiMIDIBlockHeader* m_header{nullptr}; MultiMIDIBlockHeader* m_header{nullptr};
locator& m_locator; locator& m_locator;

View File

@ -5,10 +5,14 @@
#include "util.h" #include "util.h"
#include "common/log/log.h"
namespace snd { namespace snd {
std::array<s8, 32> g_block_reg{};
void blocksound_handler::init() { void blocksound_handler::init() {
m_next_grain = 0; m_next_grain = 0;
m_countdown = m_sfx.grains[0].Delay; m_countdown = m_sfx.grains[0]->delay();
// if (m_sfx.d.Flags & 2) { // if (m_sfx.d.Flags & 2) {
// fmt::print("solo flag\n"); // fmt::print("solo flag\n");
@ -16,6 +20,12 @@ void blocksound_handler::init() {
// return; // return;
// } // }
int idx = 0;
for (auto& g : m_sfx.grains) {
lg::info("grain {}: {}", idx, g->inspect());
idx++;
}
while (m_countdown <= 0 && !m_done) { while (m_countdown <= 0 && !m_done) {
do_grain(); do_grain();
} }
@ -24,9 +34,21 @@ void blocksound_handler::init() {
bool blocksound_handler::tick() { bool blocksound_handler::tick() {
m_voices.remove_if([](std::weak_ptr<vag_voice>& p) { return p.expired(); }); m_voices.remove_if([](std::weak_ptr<vag_voice>& p) { return p.expired(); });
if (m_done) { for (auto& lfo : m_lfo) {
lfo.tick();
}
for (auto it = m_children.begin(); it != m_children.end();) {
bool done = it->get()->tick();
if (done) {
it = m_children.erase(it);
} else {
++it;
}
}
if (m_done && m_children.empty()) {
if (m_voices.empty()) { if (m_voices.empty()) {
// fmt::print("{}: voices empty\n", (void*)this);
return m_done; return m_done;
} else { } else {
return false; return false;
@ -86,14 +108,10 @@ void blocksound_handler::stop() {
void blocksound_handler::set_vol_pan(s32 vol, s32 pan) { void blocksound_handler::set_vol_pan(s32 vol, s32 pan) {
if (vol >= 0) { if (vol >= 0) {
if (vol != VOLUME_DONT_CHANGE) { if (vol != VOLUME_DONT_CHANGE) {
m_app_volume = (vol * m_sfx.d.Vol) >> 10; m_app_volume = vol;
} }
} else { } else {
m_app_volume = -vol; m_app_volume = -1024 * vol / 127;
}
if (m_app_volume >= 128) {
m_app_volume = 127;
} }
if (pan == PAN_RESET) { if (pan == PAN_RESET) {
@ -102,21 +120,24 @@ void blocksound_handler::set_vol_pan(s32 vol, s32 pan) {
m_app_pan = pan; m_app_pan = pan;
} }
vol = m_app_volume; // + lfo vol s32 new_vol = ((m_app_volume * m_orig_volume) >> 10) + m_lfo_volume;
// TODO LFO logic here new_vol = std::clamp(new_vol, 0, 127);
pan = m_app_pan; // + lfo pan s32 new_pan = m_app_pan + m_lfo_pan;
while (pan >= 360) { while (new_pan >= 360) {
pan -= 360; new_pan -= 360;
}
while (new_pan < 0) {
new_pan += 360;
} }
while (pan < 0) { if (new_pan != m_cur_pan || new_vol != m_cur_volume) {
pan += 360; m_cur_volume = new_vol;
} m_cur_pan = new_pan;
if (pan != m_cur_pan || vol != m_cur_volume) { for (auto& c : m_children) {
m_cur_volume = vol; c->set_vol_pan(m_app_volume * m_orig_volume / 127, pan);
m_cur_pan = pan; }
for (auto& p : m_voices) { for (auto& p : m_voices) {
auto voice = p.lock(); auto voice = p.lock();
@ -135,8 +156,8 @@ void blocksound_handler::set_vol_pan(s32 vol, s32 pan) {
} }
void blocksound_handler::update_pitch() { void blocksound_handler::update_pitch() {
m_cur_pm = m_app_pm; m_cur_pm = m_app_pm + m_lfo_pm;
m_cur_pb = m_app_pb; m_cur_pb = std::clamp<s32>(m_app_pb + m_lfo_pb, INT16_MIN, INT16_MAX);
for (auto& p : m_voices) { for (auto& p : m_voices) {
auto voice = p.lock(); auto voice = p.lock();
@ -147,72 +168,27 @@ void blocksound_handler::update_pitch() {
auto note = pitchbend(voice->tone, m_cur_pb, m_cur_pm, m_note, m_fine); auto note = pitchbend(voice->tone, m_cur_pb, m_cur_pm, m_note, m_fine);
auto pitch = auto pitch =
PS1Note2Pitch(voice->tone.CenterNote, voice->tone.CenterFine, note.first, note.second); PS1Note2Pitch(voice->tone.CenterNote, voice->tone.CenterFine, note.first, note.second);
voice->set_pitch(pitch); voice->set_pitch(pitch);
} }
} }
void blocksound_handler::set_pmod(s32 mod) { void blocksound_handler::set_pmod(s32 mod) {
// TODO update children
m_app_pm = mod; m_app_pm = mod;
update_pitch(); update_pitch();
} }
void blocksound_handler::set_pbend(s32 bend) { void blocksound_handler::set_pbend(s32 bend) {
// TODO update children
m_app_pb = bend; m_app_pb = bend;
update_pitch(); update_pitch();
} }
s32 blocksound_handler::null(SFXGrain& grain) {
return 0;
}
s32 blocksound_handler::play_tone(SFXGrain& grain) {
auto voice = std::make_shared<vag_voice>(grain.GrainParams.tone);
voice->basevol = m_vm.make_volume(127, 0, m_cur_volume, m_cur_pan, grain.GrainParams.tone.Vol,
grain.GrainParams.tone.Pan);
voice->start_note = m_note;
voice->start_fine = m_fine;
voice->group = m_group;
m_vm.start_tone(voice);
m_voices.emplace_front(voice);
return 0;
}
s32 blocksound_handler::rand_play(SFXGrain& grain) {
int options = grain.GrainParams.control.param[0];
int count = grain.GrainParams.control.param[1];
int previous = grain.GrainParams.control.param[2];
int rnd = rand() % options;
if (rnd == previous) {
rnd++;
if (rnd >= options) {
rnd = 0;
}
}
grain.GrainParams.control.param[2] = rnd;
m_next_grain += rnd * count;
m_grains_to_play = count + 1;
m_grains_to_skip = (options - 1 - rnd) * count;
m_skip_grains = true;
return 0;
}
void blocksound_handler::do_grain() { void blocksound_handler::do_grain() {
auto& grain = m_sfx.grains[m_next_grain]; auto& grain = m_sfx.grains[m_next_grain];
auto handler = m_grain_handler.find((grain_type)grain.Type); s32 ret = grain->execute(*this);
if (handler != m_grain_handler.end()) {
(this->*(handler->second))(grain);
} else {
throw std::runtime_error(
fmt::format("{}: Ignoring grain {}, type {}\n", (void*)this, m_next_grain, grain.Type));
}
if (m_skip_grains) { if (m_skip_grains) {
m_grains_to_play--; m_grains_to_play--;
@ -228,7 +204,7 @@ void blocksound_handler::do_grain() {
return; return;
} }
m_countdown = m_sfx.grains[m_next_grain].Delay; m_countdown = m_sfx.grains[m_next_grain]->delay() + ret;
} }
} // namespace snd } // namespace snd

View File

@ -1,44 +1,90 @@
#pragma once #pragma once
#include <unordered_map>
#include "sfxblock.h"
#include "sound_handler.h" #include "sound_handler.h"
#include "vagvoice.h" #include "vagvoice.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "game/sound/989snd/lfo.h"
#include "sfxblock2.h"
namespace snd { namespace snd {
extern std::array<s8, 32> g_block_reg;
class blocksound_handler : public sound_handler { class blocksound_handler : public sound_handler {
public: public:
blocksound_handler(SFX& sfx, voice_manager& vm, s32 vol, s32 pan, s32 pm, s32 pb, u32 bank_id) blocksound_handler(SoundBank& bank,
: m_sfx(sfx), m_vm(vm), m_bank(bank_id) { SFX2& sfx,
vol = (vol * m_sfx.d.Vol) >> 10; voice_manager& vm,
if (vol >= 128) { s32 sfx_vol,
vol = 127; s32 sfx_pan,
SndPlayParams& params)
: m_sfx(sfx), m_vm(vm), m_bank(bank) {
s32 vol, pan, pitch_mod, pitch_bend;
if (sfx_vol == -1) {
sfx_vol = sfx.d.Vol;
}
if (sfx_pan == -1) {
sfx_pan = sfx.d.Pan;
} }
if (pan >= PAN_DONT_CHANGE) { if (params.vol.has_value()) {
pan = m_sfx.d.Pan; vol = params.vol.value();
} else {
vol = 1024;
} }
m_cur_volume = vol; if (params.pan.has_value()) {
pan = params.pan.value();
} else {
pan = -1;
}
if (params.pitch_mod.has_value()) {
pitch_mod = params.pitch_mod.value();
} else {
pitch_mod = 0;
}
if (params.pitch_bend.has_value()) {
pitch_bend = params.pitch_bend.value();
} else {
pitch_bend = 0;
}
if (vol == VOLUME_DONT_CHANGE) {
vol = 1024;
}
s32 play_vol = (sfx_vol * vol) >> 10;
if (play_vol >= 128) {
play_vol = 127;
}
if (pan == PAN_RESET || pan == PAN_DONT_CHANGE) {
pan = sfx_pan;
}
m_orig_volume = sfx_vol;
m_orig_pan = sfx_pan;
m_cur_volume = play_vol;
m_cur_pan = pan; m_cur_pan = pan;
m_cur_pm = pm; m_cur_pb = pitch_bend;
m_cur_pb = pb; m_cur_pm = pitch_mod;
m_app_volume = vol; m_app_volume = vol;
m_app_pan = pan; m_app_pan = pan;
m_app_pm = 0; // why only this one? m_app_pb = pitch_bend;
m_app_pb = pb; m_app_pm = pitch_mod;
m_orig_pan = m_sfx.d.Pan; m_lfo_volume = 0;
m_orig_volume = m_sfx.d.Vol; m_lfo_pan = 0;
m_lfo_pb = 0;
m_lfo_pm = 0;
m_group = sfx.d.VolGroup; if (params.registers.has_value()) {
m_registers = params.registers.value();
m_grain_handler.insert(std::make_pair(grain_type::null, &blocksound_handler::null)); }
m_grain_handler.insert(std::make_pair(grain_type::tone, &blocksound_handler::play_tone));
m_grain_handler.insert(std::make_pair(grain_type::rand_play, &blocksound_handler::rand_play));
} }
~blocksound_handler() override { ~blocksound_handler() override {
@ -51,7 +97,7 @@ class blocksound_handler : public sound_handler {
} }
bool tick() override; bool tick() override;
u32 bank() override { return m_bank; }; SoundBank& bank() override { return m_bank; };
void pause() override; void pause() override;
void unpause() override; void unpause() override;
@ -59,34 +105,15 @@ class blocksound_handler : public sound_handler {
u8 group() override { return m_group; }; u8 group() override { return m_group; };
void set_vol_pan(s32 vol, s32 pan) override; void set_vol_pan(s32 vol, s32 pan) override;
void set_pmod(s32 mod) override; void set_pmod(s32 mod) override;
void set_register(u8 reg, u8 value) override { m_registers.at(reg) = value; };
void set_pbend(s32 bend); // TODO override; void set_pbend(s32 bend); // TODO override;
void init(); void init();
private:
enum class grain_type : u32 {
null = 0,
tone = 1,
xref_id = 2,
xref_num = 3,
lfo_settings = 4,
loop_start = 21,
loop_end = 22,
loop_continue = 23,
rand_play = 25,
rand_delay = 26,
};
void do_grain(); void do_grain();
s32 null(SFXGrain& grain);
s32 play_tone(SFXGrain& grain);
s32 rand_play(SFXGrain& grain);
void update_pitch(); void update_pitch();
using grain_fp = int (blocksound_handler::*)(SFXGrain& grain);
std::unordered_map<grain_type, grain_fp> m_grain_handler;
bool m_paused{false}; bool m_paused{false};
u8 m_group{0}; u8 m_group{0};
@ -96,11 +123,13 @@ class blocksound_handler : public sound_handler {
u32 m_grains_to_skip{0}; u32 m_grains_to_skip{0};
bool m_skip_grains{false}; bool m_skip_grains{false};
SFX& m_sfx; SFX2& m_sfx;
voice_manager& m_vm; voice_manager& m_vm;
std::list<std::weak_ptr<vag_voice>> m_voices; std::list<std::weak_ptr<vag_voice>> m_voices;
std::list<std::unique_ptr<sound_handler>> m_children;
s32 m_current_pb{0}; s32 m_current_pb{0};
s32 m_current_pm{0}; s32 m_current_pm{0};
@ -118,13 +147,15 @@ class blocksound_handler : public sound_handler {
s32 m_lfo_volume{0}; s32 m_lfo_volume{0};
s32 m_lfo_pan{0}; s32 m_lfo_pan{0};
s32 m_lfo_pm{0}; s32 m_lfo_pm{0};
s32 m_lfo_pb{0};
u32 m_bank{0}; SoundBank& m_bank;
u8 m_note{60}; u8 m_note{60};
u8 m_fine{0}; u8 m_fine{0};
std::array<u8, 4> m_registers{}; std::array<s8, 4> m_registers{};
std::array<LFOTracker, 4> m_lfo{{*this, *this, *this, *this}};
// TODO LFO // TODO LFO

140
game/sound/989snd/lfo.cpp Normal file
View File

@ -0,0 +1,140 @@
#include "lfo.h"
#include <array>
#include "blocksound_handler.h"
namespace snd {
#include "lfo_sine.c.inc"
void LFOTracker::init() {
if (m_type == lfo_type::RAND) {
m_state_hold1 = -(rand() & 0x7fff) * (rand() & 1);
m_state_hold2 = 1;
}
calc_depth();
tick();
}
void LFOTracker::calc_depth() {
if (m_target == lfo_target::VOLUME) {
m_range = (m_handler.m_sfx.d.Vol * m_depth) >> 10;
}
if (m_target == lfo_target::PAN) {
m_range = (180 * m_depth) >> 10;
}
if (m_target == lfo_target::PMOD) {
m_range = (6096 * m_depth) >> 10;
}
if (m_target == lfo_target::PBEND) {
m_range = (0x7fff * m_depth) >> 10;
}
m_last_lfo = 0;
}
void LFOTracker::tick() {
m_tick++;
if (m_target == lfo_target::NONE || (m_tick & 1) == 0) {
return;
}
switch (m_target) {
case lfo_target::VOLUME: {
s32 vol = (m_range * (get_lfo(2) - 0x7fff)) >> 16;
if (m_handler.m_lfo_volume != vol) {
m_handler.m_lfo_volume = vol;
m_handler.set_vol_pan(VOLUME_DONT_CHANGE, PAN_DONT_CHANGE);
}
} break;
case lfo_target::PAN: {
s32 pan = (m_range * get_lfo(2)) >> 15;
if (m_handler.m_lfo_pan != pan) {
m_handler.m_lfo_pan = pan;
m_handler.set_vol_pan(VOLUME_DONT_CHANGE, PAN_DONT_CHANGE);
}
} break;
case lfo_target::PMOD: {
s32 pm = (get_lfo(2) * m_range) >> 15;
if (m_handler.m_lfo_pm != pm) {
m_handler.m_lfo_pm = pm;
m_handler.update_pitch();
}
} break;
case lfo_target::PBEND: {
s32 pb = (get_lfo(2) * m_range) >> 15;
if (m_handler.m_lfo_pb != pb) {
m_handler.m_lfo_pb = pb;
m_handler.update_pitch();
}
} break;
case lfo_target::UNK1: {
} break;
case lfo_target::UNK2: {
} break;
default:
break;
}
}
s32 LFOTracker::get_lfo(s32 step_mult) {
s32 step = m_next_step >> 16;
m_next_step += step_mult * m_step_size;
if (m_next_step > 0x7ffffff) {
m_next_step -= 0x8000000;
}
s32 ret = 0;
switch (m_type) {
case lfo_type::OFF:
ret = 0;
break;
case lfo_type::SINE:
ret = gLFO_sine.at(step);
break;
case lfo_type::SQUARE:
if (step >= m_state_hold1) {
ret = -32767;
} else {
ret = 32767;
}
break;
case lfo_type::TRIANGLE:
if (step < 512) {
ret = 0x7fff * step / 512;
} else if (step >= 1536) {
ret = 0x7fff * (step - 1536) / 512 - 0x7fff;
} else {
ret = 0x7fff - 65534 * (step - 512) / 1024;
}
break;
case lfo_type::SAW:
if (step >= 1024) {
ret = 0x7fff * (step - 1024) / 1024 - 0x7fff;
} else {
ret = 0x7fff * step / 1023;
}
break;
case lfo_type::RAND:
if (step >= 1024 && m_state_hold2 == 1) {
m_state_hold2 = 0;
m_state_hold1 = 2 * ((rand() & 0x7fff) - 0x3fff);
} else if (step < 1024 && m_state_hold2 == 0) {
m_state_hold2 = 1;
m_state_hold1 = -(rand() & 0x7fff) * (rand() & 1);
}
ret = m_state_hold1;
break;
}
if ((m_setup_flags & 1) != 0) {
ret = -ret;
}
return ret;
}
} // namespace snd

43
game/sound/989snd/lfo.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef LFO_H_
#define LFO_H_
#include "common/common_types.h"
namespace snd {
enum class lfo_type { OFF, SINE, SQUARE, TRIANGLE, SAW, RAND };
enum class lfo_target { NONE, VOLUME, PAN, PMOD, PBEND, UNK1, UNK2 };
class blocksound_handler;
class LFOTracker {
public:
LFOTracker(blocksound_handler& handler) : m_handler(handler) {}
lfo_type m_type{lfo_type::OFF};
lfo_target m_target{0};
u8 m_target_extra{0};
u8 m_setup_flags{0};
u8 m_running_flags{0};
s16 m_depth{0};
u32 m_orig_depth{0};
s32 m_next_step{0};
u32 m_step_size{0};
u32 m_orig_step_size{0};
s32 m_state_hold1{0};
s32 m_state_hold2{0};
s32 m_range{0};
s32 m_last_lfo{0};
u32 m_tick{0};
void init();
void calc_depth();
void tick();
s32 get_lfo(s32 step_mult);
blocksound_handler& m_handler;
};
} // namespace snd
#endif // LFO_H_

View File

@ -0,0 +1,172 @@
std::array<short, 2048> gLFO_sine = {
{32767, 32766, 32766, 32765, 32764, 32763, 32761, 32759, 32757, 32754, 32751, 32748,
32744, 32740, 32736, 32732, 32727, 32722, 32717, 32711, 32705, 32699, 32692, 32685,
32678, 32670, 32662, 32654, 32646, 32637, 32628, 32618, 32609, 32599, 32588, 32578,
32567, 32556, 32544, 32532, 32520, 32508, 32495, 32482, 32468, 32455, 32441, 32426,
32412, 32397, 32382, 32366, 32350, 32334, 32318, 32301, 32284, 32267, 32249, 32231,
32213, 32194, 32176, 32156, 32137, 32117, 32097, 32077, 32056, 32035, 32014, 31992,
31970, 31948, 31926, 31903, 31880, 31856, 31833, 31809, 31785, 31760, 31735, 31710,
31684, 31659, 31633, 31606, 31580, 31553, 31525, 31498, 31470, 31442, 31413, 31385,
31356, 31326, 31297, 31267, 31236, 31206, 31175, 31144, 31113, 31081, 31049, 31017,
30984, 30951, 30918, 30885, 30851, 30817, 30783, 30748, 30713, 30678, 30643, 30607,
30571, 30535, 30498, 30461, 30424, 30386, 30349, 30311, 30272, 30234, 30195, 30156,
30116, 30076, 30036, 29996, 29955, 29915, 29873, 29832, 29790, 29748, 29706, 29663,
29621, 29577, 29534, 29490, 29446, 29402, 29358, 29313, 29268, 29222, 29177, 29131,
29085, 29038, 28992, 28945, 28897, 28850, 28802, 28754, 28706, 28657, 28608, 28559,
28510, 28460, 28410, 28360, 28309, 28259, 28208, 28156, 28105, 28053, 28001, 27948,
27896, 27843, 27790, 27736, 27683, 27629, 27575, 27520, 27466, 27411, 27355, 27300,
27244, 27188, 27132, 27076, 27019, 26962, 26905, 26847, 26789, 26731, 26673, 26615,
26556, 26497, 26437, 26378, 26318, 26258, 26198, 26137, 26077, 26016, 25954, 25893,
25831, 25769, 25707, 25645, 25582, 25519, 25456, 25392, 25329, 25265, 25201, 25136,
25072, 25007, 24942, 24877, 24811, 24745, 24679, 24613, 24546, 24480, 24413, 24346,
24278, 24211, 24143, 24075, 24006, 23938, 23869, 23800, 23731, 23661, 23592, 23522,
23452, 23382, 23311, 23240, 23169, 23098, 23027, 22955, 22883, 22811, 22739, 22666,
22594, 22521, 22448, 22374, 22301, 22227, 22153, 22079, 22004, 21930, 21855, 21780,
21705, 21629, 21554, 21478, 21402, 21326, 21249, 21173, 21096, 21019, 20942, 20864,
20787, 20709, 20631, 20553, 20474, 20396, 20317, 20238, 20159, 20079, 20000, 19920,
19840, 19760, 19680, 19599, 19519, 19438, 19357, 19276, 19194, 19113, 19031, 18949,
18867, 18785, 18702, 18620, 18537, 18454, 18371, 18287, 18204, 18120, 18036, 17952,
17868, 17784, 17699, 17615, 17530, 17445, 17360, 17274, 17189, 17103, 17017, 16931,
16845, 16759, 16672, 16586, 16499, 16412, 16325, 16238, 16150, 16063, 15975, 15887,
15799, 15711, 15623, 15534, 15446, 15357, 15268, 15179, 15090, 15001, 14911, 14822,
14732, 14642, 14552, 14462, 14372, 14281, 14191, 14100, 14009, 13918, 13827, 13736,
13645, 13553, 13462, 13370, 13278, 13186, 13094, 13002, 12909, 12817, 12724, 12632,
12539, 12446, 12353, 12260, 12166, 12073, 11980, 11886, 11792, 11698, 11604, 11510,
11416, 11322, 11227, 11133, 11038, 10944, 10849, 10754, 10659, 10564, 10469, 10373,
10278, 10182, 10087, 9991, 9895, 9799, 9703, 9607, 9511, 9415, 9319, 9222,
9126, 9029, 8932, 8836, 8739, 8642, 8545, 8448, 8351, 8253, 8156, 8059,
7961, 7864, 7766, 7668, 7571, 7473, 7375, 7277, 7179, 7081, 6982, 6884,
6786, 6688, 6589, 6491, 6392, 6293, 6195, 6096, 5997, 5898, 5799, 5700,
5601, 5502, 5403, 5304, 5205, 5106, 5006, 4907, 4807, 4708, 4608, 4509,
4409, 4310, 4210, 4110, 4011, 3911, 3811, 3711, 3611, 3511, 3411, 3311,
3211, 3111, 3011, 2911, 2811, 2711, 2610, 2510, 2410, 2310, 2209, 2109,
2009, 1908, 1808, 1708, 1607, 1507, 1406, 1306, 1206, 1105, 1005, 904,
804, 703, 603, 502, 402, 301, 201, 100, 0, -100, -201, -301,
-402, -502, -603, -703, -804, -904, -1005, -1105, -1206, -1306, -1406, -1507,
-1607, -1708, -1808, -1908, -2009, -2109, -2209, -2310, -2410, -2510, -2610, -2711,
-2811, -2911, -3011, -3111, -3211, -3311, -3411, -3511, -3611, -3711, -3811, -3911,
-4011, -4110, -4210, -4310, -4409, -4509, -4608, -4708, -4807, -4907, -5006, -5106,
-5205, -5304, -5403, -5502, -5601, -5700, -5799, -5898, -5997, -6096, -6195, -6293,
-6392, -6491, -6589, -6688, -6786, -6884, -6982, -7081, -7179, -7277, -7375, -7473,
-7571, -7668, -7766, -7864, -7961, -8059, -8156, -8253, -8351, -8448, -8545, -8642,
-8739, -8836, -8932, -9029, -9126, -9222, -9319, -9415, -9511, -9607, -9703, -9799,
-9895, -9991, -10087, -10182, -10278, -10373, -10469, -10564, -10659, -10754, -10849, -10944,
-11038, -11133, -11227, -11322, -11416, -11510, -11604, -11698, -11792, -11886, -11980, -12073,
-12166, -12260, -12353, -12446, -12539, -12632, -12724, -12817, -12909, -13002, -13094, -13186,
-13278, -13370, -13462, -13553, -13645, -13736, -13827, -13918, -14009, -14100, -14191, -14281,
-14372, -14462, -14552, -14642, -14732, -14822, -14911, -15001, -15090, -15179, -15268, -15357,
-15446, -15534, -15623, -15711, -15799, -15887, -15975, -16063, -16150, -16238, -16325, -16412,
-16499, -16586, -16672, -16759, -16845, -16931, -17017, -17103, -17189, -17274, -17360, -17445,
-17530, -17615, -17699, -17784, -17868, -17952, -18036, -18120, -18204, -18287, -18371, -18454,
-18537, -18620, -18702, -18785, -18867, -18949, -19031, -19113, -19194, -19276, -19357, -19438,
-19519, -19599, -19680, -19760, -19840, -19920, -20000, -20079, -20159, -20238, -20317, -20396,
-20474, -20553, -20631, -20709, -20787, -20864, -20942, -21019, -21096, -21173, -21249, -21326,
-21402, -21478, -21554, -21629, -21705, -21780, -21855, -21930, -22004, -22079, -22153, -22227,
-22301, -22374, -22448, -22521, -22594, -22666, -22739, -22811, -22883, -22955, -23027, -23098,
-23169, -23240, -23311, -23382, -23452, -23522, -23592, -23661, -23731, -23800, -23869, -23938,
-24006, -24075, -24143, -24211, -24278, -24346, -24413, -24480, -24546, -24613, -24679, -24745,
-24811, -24877, -24942, -25007, -25072, -25136, -25201, -25265, -25329, -25392, -25456, -25519,
-25582, -25645, -25707, -25769, -25831, -25893, -25954, -26016, -26077, -26137, -26198, -26258,
-26318, -26378, -26437, -26497, -26556, -26615, -26673, -26731, -26789, -26847, -26905, -26962,
-27019, -27076, -27132, -27188, -27244, -27300, -27355, -27411, -27466, -27520, -27575, -27629,
-27683, -27736, -27790, -27843, -27896, -27948, -28001, -28053, -28105, -28156, -28208, -28259,
-28309, -28360, -28410, -28460, -28510, -28559, -28608, -28657, -28706, -28754, -28802, -28850,
-28897, -28945, -28992, -29038, -29085, -29131, -29177, -29222, -29268, -29313, -29358, -29402,
-29446, -29490, -29534, -29577, -29621, -29663, -29706, -29748, -29790, -29832, -29873, -29915,
-29955, -29996, -30036, -30076, -30116, -30156, -30195, -30234, -30272, -30311, -30349, -30386,
-30424, -30461, -30498, -30535, -30571, -30607, -30643, -30678, -30713, -30748, -30783, -30817,
-30851, -30885, -30918, -30951, -30984, -31017, -31049, -31081, -31113, -31144, -31175, -31206,
-31236, -31267, -31297, -31326, -31356, -31385, -31413, -31442, -31470, -31498, -31525, -31553,
-31580, -31606, -31633, -31659, -31684, -31710, -31735, -31760, -31785, -31809, -31833, -31856,
-31880, -31903, -31926, -31948, -31970, -31992, -32014, -32035, -32056, -32077, -32097, -32117,
-32137, -32156, -32176, -32194, -32213, -32231, -32249, -32267, -32284, -32301, -32318, -32334,
-32350, -32366, -32382, -32397, -32412, -32426, -32441, -32455, -32468, -32482, -32495, -32508,
-32520, -32532, -32544, -32556, -32567, -32578, -32588, -32599, -32609, -32618, -32628, -32637,
-32646, -32654, -32662, -32670, -32678, -32685, -32692, -32699, -32705, -32711, -32717, -32722,
-32727, -32732, -32736, -32740, -32744, -32748, -32751, -32754, -32757, -32759, -32761, -32763,
-32764, -32765, -32766, -32766, -32767, -32766, -32766, -32765, -32764, -32763, -32761, -32759,
-32757, -32754, -32751, -32748, -32744, -32740, -32736, -32732, -32727, -32722, -32717, -32711,
-32705, -32699, -32692, -32685, -32678, -32670, -32662, -32654, -32646, -32637, -32628, -32618,
-32609, -32599, -32588, -32578, -32567, -32556, -32544, -32532, -32520, -32508, -32495, -32482,
-32468, -32455, -32441, -32426, -32412, -32397, -32382, -32366, -32350, -32334, -32318, -32301,
-32284, -32267, -32249, -32231, -32213, -32194, -32176, -32156, -32137, -32117, -32097, -32077,
-32056, -32035, -32014, -31992, -31970, -31948, -31926, -31903, -31880, -31856, -31833, -31809,
-31785, -31760, -31735, -31710, -31684, -31659, -31633, -31606, -31580, -31553, -31525, -31498,
-31470, -31442, -31413, -31385, -31356, -31326, -31297, -31267, -31236, -31206, -31175, -31144,
-31113, -31081, -31049, -31017, -30984, -30951, -30918, -30885, -30851, -30817, -30783, -30748,
-30713, -30678, -30643, -30607, -30571, -30535, -30498, -30461, -30424, -30386, -30349, -30311,
-30272, -30234, -30195, -30156, -30116, -30076, -30036, -29996, -29955, -29915, -29873, -29832,
-29790, -29748, -29706, -29663, -29621, -29577, -29534, -29490, -29446, -29402, -29358, -29313,
-29268, -29222, -29177, -29131, -29085, -29038, -28992, -28945, -28897, -28850, -28802, -28754,
-28706, -28657, -28608, -28559, -28510, -28460, -28410, -28360, -28309, -28259, -28208, -28156,
-28105, -28053, -28001, -27948, -27896, -27843, -27790, -27736, -27683, -27629, -27575, -27520,
-27466, -27411, -27355, -27300, -27244, -27188, -27132, -27076, -27019, -26962, -26905, -26847,
-26789, -26731, -26673, -26615, -26556, -26497, -26437, -26378, -26318, -26258, -26198, -26137,
-26077, -26016, -25954, -25893, -25831, -25769, -25707, -25645, -25582, -25519, -25456, -25392,
-25329, -25265, -25201, -25136, -25072, -25007, -24942, -24877, -24811, -24745, -24679, -24613,
-24546, -24480, -24413, -24346, -24278, -24211, -24143, -24075, -24006, -23938, -23869, -23800,
-23731, -23661, -23592, -23522, -23452, -23382, -23311, -23240, -23169, -23098, -23027, -22955,
-22883, -22811, -22739, -22666, -22594, -22521, -22448, -22374, -22301, -22227, -22153, -22079,
-22004, -21930, -21855, -21780, -21705, -21629, -21554, -21478, -21402, -21326, -21249, -21173,
-21096, -21019, -20942, -20864, -20787, -20709, -20631, -20553, -20474, -20396, -20317, -20238,
-20159, -20079, -20000, -19920, -19840, -19760, -19680, -19599, -19519, -19438, -19357, -19276,
-19194, -19113, -19031, -18949, -18867, -18785, -18702, -18620, -18537, -18454, -18371, -18287,
-18204, -18120, -18036, -17952, -17868, -17784, -17699, -17615, -17530, -17445, -17360, -17274,
-17189, -17103, -17017, -16931, -16845, -16759, -16672, -16586, -16499, -16412, -16325, -16238,
-16150, -16063, -15975, -15887, -15799, -15711, -15623, -15534, -15446, -15357, -15268, -15179,
-15090, -15001, -14911, -14822, -14732, -14642, -14552, -14462, -14372, -14281, -14191, -14100,
-14009, -13918, -13827, -13736, -13645, -13553, -13462, -13370, -13278, -13186, -13094, -13002,
-12909, -12817, -12724, -12632, -12539, -12446, -12353, -12260, -12166, -12073, -11980, -11886,
-11792, -11698, -11604, -11510, -11416, -11322, -11227, -11133, -11038, -10944, -10849, -10754,
-10659, -10564, -10469, -10373, -10278, -10182, -10087, -9991, -9895, -9799, -9703, -9607,
-9511, -9415, -9319, -9222, -9126, -9029, -8932, -8836, -8739, -8642, -8545, -8448,
-8351, -8253, -8156, -8059, -7961, -7864, -7766, -7668, -7571, -7473, -7375, -7277,
-7179, -7081, -6982, -6884, -6786, -6688, -6589, -6491, -6392, -6293, -6195, -6096,
-5997, -5898, -5799, -5700, -5601, -5502, -5403, -5304, -5205, -5106, -5006, -4907,
-4807, -4708, -4608, -4509, -4409, -4310, -4210, -4110, -4011, -3911, -3811, -3711,
-3611, -3511, -3411, -3311, -3211, -3111, -3011, -2911, -2811, -2711, -2610, -2510,
-2410, -2310, -2209, -2109, -2009, -1908, -1808, -1708, -1607, -1507, -1406, -1306,
-1206, -1105, -1005, -904, -804, -703, -603, -502, -402, -301, -201, -100,
0, 100, 201, 301, 402, 502, 603, 703, 804, 904, 1005, 1105,
1206, 1306, 1406, 1507, 1607, 1708, 1808, 1908, 2009, 2109, 2209, 2310,
2410, 2510, 2610, 2711, 2811, 2911, 3011, 3111, 3211, 3311, 3411, 3511,
3611, 3711, 3811, 3911, 4011, 4110, 4210, 4310, 4409, 4509, 4608, 4708,
4807, 4907, 5006, 5106, 5205, 5304, 5403, 5502, 5601, 5700, 5799, 5898,
5997, 6096, 6195, 6293, 6392, 6491, 6589, 6688, 6786, 6884, 6982, 7081,
7179, 7277, 7375, 7473, 7571, 7668, 7766, 7864, 7961, 8059, 8156, 8253,
8351, 8448, 8545, 8642, 8739, 8836, 8932, 9029, 9126, 9222, 9319, 9415,
9511, 9607, 9703, 9799, 9895, 9991, 10087, 10182, 10278, 10373, 10469, 10564,
10659, 10754, 10849, 10944, 11038, 11133, 11227, 11322, 11416, 11510, 11604, 11698,
11792, 11886, 11980, 12073, 12166, 12260, 12353, 12446, 12539, 12632, 12724, 12817,
12909, 13002, 13094, 13186, 13278, 13370, 13462, 13553, 13645, 13736, 13827, 13918,
14009, 14100, 14191, 14281, 14372, 14462, 14552, 14642, 14732, 14822, 14911, 15001,
15090, 15179, 15268, 15357, 15446, 15534, 15623, 15711, 15799, 15887, 15975, 16063,
16150, 16238, 16325, 16412, 16499, 16586, 16672, 16759, 16845, 16931, 17017, 17103,
17189, 17274, 17360, 17445, 17530, 17615, 17699, 17784, 17868, 17952, 18036, 18120,
18204, 18287, 18371, 18454, 18537, 18620, 18702, 18785, 18867, 18949, 19031, 19113,
19194, 19276, 19357, 19438, 19519, 19599, 19680, 19760, 19840, 19920, 20000, 20079,
20159, 20238, 20317, 20396, 20474, 20553, 20631, 20709, 20787, 20864, 20942, 21019,
21096, 21173, 21249, 21326, 21402, 21478, 21554, 21629, 21705, 21780, 21855, 21930,
22004, 22079, 22153, 22227, 22301, 22374, 22448, 22521, 22594, 22666, 22739, 22811,
22883, 22955, 23027, 23098, 23169, 23240, 23311, 23382, 23452, 23522, 23592, 23661,
23731, 23800, 23869, 23938, 24006, 24075, 24143, 24211, 24278, 24346, 24413, 24480,
24546, 24613, 24679, 24745, 24811, 24877, 24942, 25007, 25072, 25136, 25201, 25265,
25329, 25392, 25456, 25519, 25582, 25645, 25707, 25769, 25831, 25893, 25954, 26016,
26077, 26137, 26198, 26258, 26318, 26378, 26437, 26497, 26556, 26615, 26673, 26731,
26789, 26847, 26905, 26962, 27019, 27076, 27132, 27188, 27244, 27300, 27355, 27411,
27466, 27520, 27575, 27629, 27683, 27736, 27790, 27843, 27896, 27948, 28001, 28053,
28105, 28156, 28208, 28259, 28309, 28360, 28410, 28460, 28510, 28559, 28608, 28657,
28706, 28754, 28802, 28850, 28897, 28945, 28992, 29038, 29085, 29131, 29177, 29222,
29268, 29313, 29358, 29402, 29446, 29490, 29534, 29577, 29621, 29663, 29706, 29748,
29790, 29832, 29873, 29915, 29955, 29996, 30036, 30076, 30116, 30156, 30195, 30234,
30272, 30311, 30349, 30386, 30424, 30461, 30498, 30535, 30571, 30607, 30643, 30678,
30713, 30748, 30783, 30817, 30851, 30885, 30918, 30951, 30984, 31017, 31049, 31081,
31113, 31144, 31175, 31206, 31236, 31267, 31297, 31326, 31356, 31385, 31413, 31442,
31470, 31498, 31525, 31553, 31580, 31606, 31633, 31659, 31684, 31710, 31735, 31760,
31785, 31809, 31833, 31856, 31880, 31903, 31926, 31948, 31970, 31992, 32014, 32035,
32056, 32077, 32097, 32117, 32137, 32156, 32176, 32194, 32213, 32231, 32249, 32267,
32284, 32301, 32318, 32334, 32350, 32366, 32382, 32397, 32412, 32426, 32441, 32455,
32468, 32482, 32495, 32508, 32520, 32532, 32544, 32556, 32567, 32578, 32588, 32599,
32609, 32618, 32628, 32637, 32646, 32654, 32662, 32670, 32678, 32685, 32692, 32699,
32705, 32711, 32717, 32722, 32727, 32732, 32736, 32740, 32744, 32748, 32751, 32754,
32757, 32759, 32761, 32763, 32764, 32765, 32766, 32766}};

View File

@ -6,9 +6,11 @@
#include <optional> #include <optional>
#include "midi_handler.h" #include "midi_handler.h"
#include "sfxblock.h"
#include "common/log/log.h" #include "common/log/log.h"
#include "sfxblock2.h"
#include <third-party/fmt/core.h> #include <third-party/fmt/core.h>
namespace snd { namespace snd {
@ -16,71 +18,6 @@ enum chunk : u32 { bank, samples, midi };
#define FOURCC(a, b, c, d) ((u32)(((d) << 24) | ((c) << 16) | ((b) << 8) | (a))) #define FOURCC(a, b, c, d) ((u32)(((d) << 24) | ((c) << 16) | ((b) << 8) | (a)))
u32 loader::read_music_bank(SoundBankData* data) {
u32 handle = m_id_allocator.get_id();
auto bank = std::make_unique<MusicBank>(*this);
auto sound = (MIDISound*)((uintptr_t)data + data->FirstSound);
for (int i = 0; i < data->NumSounds; i++) {
bank->sounds.emplace_back(sound[i]);
}
auto progdata = (ProgData*)((uintptr_t)data + data->FirstProg);
for (int i = 0; i < data->NumProgs; i++) {
Prog prog;
prog.d = progdata[i];
bank->programs.emplace_back(std::move(prog));
}
for (auto& prog : bank->programs) {
auto tonedata = (Tone*)((uintptr_t)data + prog.d.FirstTone);
for (int i = 0; i < prog.d.NumTones; i++) {
Tone tone = tonedata[i];
tone.BankID = handle;
prog.tones.emplace_back(tone);
}
}
bank->type = BankType::Music;
bank->bank_id = handle;
bank->bank_name = data->BankID;
m_soundbanks.emplace(handle, std::move(bank));
return handle;
}
u32 loader::read_sfx_bank(SFXBlockData* data) {
u32 handle = m_id_allocator.get_id();
auto bank = std::make_unique<SFXBlock>(*this);
auto sounddata = (SFXData*)((uintptr_t)data + data->FirstSound);
for (int i = 0; i < data->NumSounds; i++) {
SFX sound;
sound.d = sounddata[i];
bank->sounds.push_back(sound);
}
for (auto& sound : bank->sounds) {
auto graindata = (SFXGrain*)((uintptr_t)data + data->FirstGrain + sound.d.FirstGrain);
for (int i = 0; i < sound.d.NumGrains; i++) {
SFXGrain grain = graindata[i];
if (grain.Type == 1) {
grain.GrainParams.tone.BankID = handle;
}
sound.grains.push_back(grain);
}
}
bank->type = BankType::SFX;
bank->bank_id = handle;
m_soundbanks.emplace(handle, std::move(bank));
return handle;
}
u32 loader::read_bank(std::fstream& in) { u32 loader::read_bank(std::fstream& in) {
size_t origin = in.tellg(); size_t origin = in.tellg();
FileAttributes<3> attr; FileAttributes<3> attr;
@ -91,27 +28,38 @@ u32 loader::read_bank(std::fstream& in) {
return -1; return -1;
} }
/*
* if there's midi data the pointer to the allocated memory is stored
* just before the sound bank data...
if (attr.num_chunks > 2) { if (attr.num_chunks > 2) {
// Fix for bugged tooling I assume?
attr.where[chunk::bank].size += 4; attr.where[chunk::bank].size += 4;
} }
*/
// auto pos = in.tellg(); // auto pos = in.tellg();
auto bank_buf = std::make_unique<u8[]>(attr.where[chunk::bank].size); auto bank_buf = std::make_unique<u8[]>(attr.where[chunk::bank].size);
in.seekg(origin + attr.where[chunk::bank].offset, std::fstream::beg); in.seekg(origin + attr.where[chunk::bank].offset, std::fstream::beg);
in.read((char*)bank_buf.get(), attr.where[chunk::bank].size); in.read((char*)bank_buf.get(), attr.where[chunk::bank].size);
auto bank = (BankTag*)bank_buf.get(); auto bank_tag = (BankTag*)bank_buf.get();
u32 bank_id = 0; u32 bank_id = m_id_allocator.get_id();
std::unique_ptr<SoundBank> bank;
if (bank->DataID == FOURCC('S', 'B', 'v', '2')) { if (bank_tag->DataID == FOURCC('S', 'B', 'v', '2')) {
bank_id = read_music_bank((SoundBankData*)bank_buf.get()); bank = std::make_unique<MusicBank>(*this, bank_id, bank_tag);
} else if (bank->DataID == FOURCC('S', 'B', 'l', 'k')) { } else if (bank_tag->DataID == FOURCC('S', 'B', 'l', 'k')) {
bank_id = read_sfx_bank((SFXBlockData*)bank_buf.get()); if (bank_tag->Version < 2) {
bank = std::make_unique<SFXBlock>(*this, bank_id, bank_tag);
} else {
bank = std::make_unique<SFXBlock2>(*this, bank_id, bank_tag);
}
} else { } else {
m_id_allocator.free_id(bank_id);
throw std::runtime_error("Unknown bank ID, bad file?"); throw std::runtime_error("Unknown bank ID, bad file?");
} }
m_soundbanks.emplace(bank_id, std::move(bank));
if (attr.num_chunks >= 2) { if (attr.num_chunks >= 2) {
in.seekg(origin + attr.where[chunk::samples].offset, std::fstream::beg); in.seekg(origin + attr.where[chunk::samples].offset, std::fstream::beg);
auto samples = std::make_unique<u8[]>(attr.where[chunk::samples].size); auto samples = std::make_unique<u8[]>(attr.where[chunk::samples].size);
@ -152,7 +100,7 @@ SoundBank* loader::get_bank_by_handle(u32 id) {
return m_soundbanks[id].get(); return m_soundbanks[id].get();
} }
MusicBank* loader::get_bank_by_name(u32 id) { MusicBank* loader::get_bank_by_id(u32 id) {
for (auto& b : m_soundbanks) { for (auto& b : m_soundbanks) {
if (b.second->type == BankType::Music) { if (b.second->type == BankType::Music) {
auto* bank = static_cast<MusicBank*>(b.second.get()); auto* bank = static_cast<MusicBank*>(b.second.get());
@ -165,6 +113,30 @@ MusicBank* loader::get_bank_by_name(u32 id) {
return nullptr; return nullptr;
} }
SoundBank* loader::get_bank_by_name(const char* name) {
for (auto& b : m_soundbanks) {
auto bankname = b.second->get_name();
if (bankname.has_value()) {
if (bankname->compare(name) == 0) {
return b.second.get();
}
}
}
return nullptr;
}
SoundBank* loader::get_bank_with_sound(const char* name) {
for (auto& b : m_soundbanks) {
auto sound = b.second->get_sound_by_name(name);
if (sound.has_value()) {
return b.second.get();
}
}
return nullptr;
}
MIDIBlock* loader::get_midi(u32 id) { MIDIBlock* loader::get_midi(u32 id) {
return m_midi.at(id); return m_midi.at(id);
} }

View File

@ -33,10 +33,13 @@ struct FileAttributes {
class loader : public locator { class loader : public locator {
public: public:
SoundBank* get_bank_by_handle(u32 id) override; SoundBank* get_bank_by_handle(u32 id) override;
MusicBank* get_bank_by_name(u32 id) override; MusicBank* get_bank_by_id(u32 id) override;
MIDIBlock* get_midi(u32 id) override; MIDIBlock* get_midi(u32 id) override;
u8* get_bank_samples(u32 id) override; u8* get_bank_samples(u32 id) override;
SoundBank* get_bank_by_name(const char* name);
SoundBank* get_bank_with_sound(const char* name);
void unload_bank(u32 id); void unload_bank(u32 id);
u32 read_bank(std::fstream& in); u32 read_bank(std::fstream& in);
@ -46,8 +49,6 @@ class loader : public locator {
private: private:
void load_samples(u32 bank, std::unique_ptr<u8[]> samples); void load_samples(u32 bank, std::unique_ptr<u8[]> samples);
u32 read_music_bank(SoundBankData* data);
u32 read_sfx_bank(SFXBlockData* data);
id_allocator m_id_allocator; id_allocator m_id_allocator;
std::unordered_map<u32, std::unique_ptr<SoundBank>> m_soundbanks; std::unordered_map<u32, std::unique_ptr<SoundBank>> m_soundbanks;

View File

@ -12,7 +12,7 @@ class locator {
public: public:
virtual ~locator() = default; virtual ~locator() = default;
virtual SoundBank* get_bank_by_handle(u32 id) = 0; virtual SoundBank* get_bank_by_handle(u32 id) = 0;
virtual MusicBank* get_bank_by_name(u32 id) = 0; virtual MusicBank* get_bank_by_id(u32 id) = 0;
virtual u8* get_bank_samples(u32 id) = 0; virtual u8* get_bank_samples(u32 id) = 0;
virtual MIDIBlock* get_midi(u32 id) = 0; virtual MIDIBlock* get_midi(u32 id) = 0;
}; };

View File

@ -6,6 +6,7 @@
#include "common/log/log.h" #include "common/log/log.h"
#include "game/sound/989snd/util.h"
#include <third-party/fmt/core.h> #include <third-party/fmt/core.h>
namespace snd { namespace snd {
@ -26,7 +27,7 @@ midi_handler::midi_handler(MIDIBlockHeader* block,
s32 vol, s32 vol,
s32 pan, s32 pan,
locator& loc, locator& loc,
u32 bank) SoundBank& bank)
: m_sound(sound), : m_sound(sound),
m_locator(loc), m_locator(loc),
m_repeats(sound.Repeats), m_repeats(sound.Repeats),
@ -57,7 +58,7 @@ midi_handler::midi_handler(MIDIBlockHeader* block,
s32 vol, s32 vol,
s32 pan, s32 pan,
locator& loc, locator& loc,
u32 bank, SoundBank& bank,
std::optional<ame_handler*> parent) std::optional<ame_handler*> parent)
: m_parent(parent), : m_parent(parent),
m_sound(sound), m_sound(sound),
@ -140,7 +141,21 @@ void midi_handler::set_vol_pan(s32 vol, s32 pan) {
} }
void midi_handler::set_pmod(s32 mod) { void midi_handler::set_pmod(s32 mod) {
// TODO m_cur_pm = mod;
for (auto& v : m_voices) {
auto voice = v.lock();
if (voice == nullptr) {
continue;
}
voice->current_pm = m_cur_pm;
auto note = pitchbend(voice->tone, voice->current_pb, voice->current_pm, voice->start_note,
voice->start_fine);
auto pitch =
PS1Note2Pitch(voice->tone.CenterNote, voice->tone.CenterFine, note.first, note.second);
voice->set_pitch(pitch);
}
} }
void midi_handler::mute_channel(u8 channel) { void midi_handler::mute_channel(u8 channel) {
@ -172,8 +187,8 @@ void midi_handler::note_on() {
// velocity); // velocity);
// Key on all the applicable tones for the program // Key on all the applicable tones for the program
auto bank = dynamic_cast<MusicBank*>(m_locator.get_bank_by_name(m_header->BankID)); auto bank = dynamic_cast<MusicBank*>(m_locator.get_bank_by_id(m_header->BankID));
auto& program = bank->programs[m_programs[channel]]; auto& program = bank->m_programs[m_programs[channel]];
for (auto& t : program.tones) { for (auto& t : program.tones) {
if (note >= t.MapLow && note <= t.MapHigh) { if (note >= t.MapLow && note <= t.MapHigh) {
@ -192,12 +207,11 @@ void midi_handler::note_on() {
voice->start_note = note; voice->start_note = note;
voice->start_fine = 0; voice->start_fine = 0;
// TODO voice->current_pm = m_pitch_bend[channel];
// voice->current_pm = 0; voice->current_pb = m_cur_pm;
// voice->current_pb = 0;
voice->group = m_sound.VolGroup; voice->group = m_sound.VolGroup;
m_vm.start_tone(voice); m_vm.start_tone(voice, m_bank.bank_id);
m_voices.emplace_front(voice); m_voices.emplace_front(voice);
} }
} }
@ -260,10 +274,26 @@ void midi_handler::channel_pressure() {
void midi_handler::channel_pitch() { void midi_handler::channel_pitch() {
u8 channel = m_status & 0xF; u8 channel = m_status & 0xF;
u32 pitch = (m_seq_ptr[0] << 7) | m_seq_ptr[1]; s32 pitch = 0xFFFF * ((m_seq_ptr[0] & 0x7f) | ((m_seq_ptr[1] & 0x7f) << 7)) / 0x3FFF;
(void)pitch;
(void)channel;
// lg::debug("{}: pitch ch{:01x} {:04x}", m_time, channel, pitch); // lg::debug("{}: pitch ch{:01x} {:04x}", m_time, channel, pitch);
m_pitch_bend[channel] = pitch + 0x8000;
for (auto& v : m_voices) {
auto voice = v.lock();
if (voice == nullptr) {
continue;
}
if (voice->channel == channel) {
voice->current_pb = m_pitch_bend[channel];
auto note = pitchbend(voice->tone, voice->current_pb, voice->current_pm, voice->start_note,
voice->start_fine);
auto pitch =
PS1Note2Pitch(voice->tone.CenterNote, voice->tone.CenterFine, note.first, note.second);
voice->set_pitch(pitch);
}
}
m_seq_ptr += 2; m_seq_ptr += 2;
} }
@ -289,6 +319,7 @@ void midi_handler::meta_event() {
if (*m_seq_ptr == 0x51) { if (*m_seq_ptr == 0x51) {
m_tempo = (m_seq_ptr[2] << 16) | (m_seq_ptr[3] << 8) | (m_seq_ptr[4]); m_tempo = (m_seq_ptr[2] << 16) | (m_seq_ptr[3] << 8) | (m_seq_ptr[4]);
m_ppt = 100 * mics_per_tick / (m_tempo / m_ppq);
} }
m_seq_ptr += len + 2; m_seq_ptr += len + 2;

View File

@ -45,7 +45,7 @@ class midi_handler : public sound_handler {
s32 vol, s32 vol,
s32 pan, s32 pan,
locator& loc, locator& loc,
u32 bank); SoundBank& bank);
midi_handler(MIDIBlockHeader* block, midi_handler(MIDIBlockHeader* block,
voice_manager& vm, voice_manager& vm,
@ -53,7 +53,7 @@ class midi_handler : public sound_handler {
s32 vol, s32 vol,
s32 pan, s32 pan,
locator& loc, locator& loc,
u32 bank, SoundBank& bank,
std::optional<ame_handler*> parent); std::optional<ame_handler*> parent);
~midi_handler() override { ~midi_handler() override {
@ -69,7 +69,7 @@ class midi_handler : public sound_handler {
bool tick() override; bool tick() override;
void mute_channel(u8 channel); void mute_channel(u8 channel);
void unmute_channel(u8 channel); void unmute_channel(u8 channel);
u32 bank() override { return m_bank; }; SoundBank& bank() override { return m_bank; };
void pause() override; void pause() override;
void stop() override; void stop() override;
@ -98,8 +98,9 @@ class midi_handler : public sound_handler {
locator& m_locator; locator& m_locator;
s32 m_vol{0x7f}; s32 m_vol{0x7f};
s32 m_pan{0}; s32 m_pan{0};
s32 m_cur_pm{0};
s8 m_repeats{0}; s8 m_repeats{0};
u32 m_bank; SoundBank& m_bank;
bool m_paused{false}; bool m_paused{false};
@ -108,6 +109,7 @@ class midi_handler : public sound_handler {
std::array<bool, 16> m_mute_state{}; std::array<bool, 16> m_mute_state{};
std::array<s8, 16> m_chanvol{}; std::array<s8, 16> m_chanvol{};
std::array<s8, 16> m_chanpan{}; std::array<s8, 16> m_chanpan{};
std::array<s16, 16> m_pitch_bend{};
u8* m_sample_data{nullptr}; u8* m_sample_data{nullptr};
u8* m_seq_data_start{nullptr}; u8* m_seq_data_start{nullptr};

View File

@ -6,21 +6,48 @@
#include "../common/synth.h" #include "../common/synth.h"
namespace snd { namespace snd {
MusicBank::MusicBank(locator& loc, u32 id, BankTag* tag)
: SoundBank(id, BankType::Music), m_locator(loc) {
auto data = (SoundBankData*)tag;
auto sound = (MIDISound*)((uintptr_t)data + data->FirstSound);
for (int i = 0; i < data->NumSounds; i++) {
m_sounds.emplace_back(sound[i]);
}
auto progdata = (ProgData*)((uintptr_t)data + data->FirstProg);
for (int i = 0; i < data->NumProgs; i++) {
Prog prog;
prog.d = progdata[i];
m_programs.emplace_back(std::move(prog));
}
for (auto& prog : m_programs) {
auto tonedata = (Tone*)((uintptr_t)data + prog.d.FirstTone);
for (int i = 0; i < prog.d.NumTones; i++) {
prog.tones.emplace_back(tonedata[i]);
}
}
bank_name = data->BankID;
}
std::unique_ptr<sound_handler> MusicBank::make_handler(voice_manager& vm, std::unique_ptr<sound_handler> MusicBank::make_handler(voice_manager& vm,
u32 sound_id, u32 sound_id,
s32 vol, s32 vol,
s32 pan, s32 pan,
s32 pm, s32 pm,
s32 pb) { s32 pb) {
auto& sound = sounds[sound_id]; auto& sound = m_sounds[sound_id];
std::unique_ptr<sound_handler> handler; std::unique_ptr<sound_handler> handler;
if (sound.Type == 4) { // midi if (sound.Type == 4) { // midi
auto midi = static_cast<MIDIBlockHeader*>(m_locator.get_midi(sound.MIDIID)); auto midi = static_cast<MIDIBlockHeader*>(m_locator.get_midi(sound.MIDIID));
handler = std::make_unique<midi_handler>(midi, vm, sound, vol, pan, m_locator, bank_id); handler = std::make_unique<midi_handler>(midi, vm, sound, vol, pan, m_locator, *this);
} else if (sound.Type == 5) { // ame } else if (sound.Type == 5) { // ame
auto midi = static_cast<MultiMIDIBlockHeader*>(m_locator.get_midi(sound.MIDIID)); auto midi = static_cast<MultiMIDIBlockHeader*>(m_locator.get_midi(sound.MIDIID));
handler = std::make_unique<ame_handler>(midi, vm, sound, vol, pan, m_locator, bank_id); handler = std::make_unique<ame_handler>(midi, vm, sound, vol, pan, m_locator, *this);
} else { } else {
// error // error
} }
@ -28,4 +55,12 @@ std::unique_ptr<sound_handler> MusicBank::make_handler(voice_manager& vm,
return handler; return handler;
} }
std::unique_ptr<sound_handler> MusicBank::make_handler(voice_manager& vm,
u32 sound_id,
s32 vol,
s32 pan,
SndPlayParams& params) {
return nullptr;
}
} // namespace snd } // namespace snd

View File

@ -60,7 +60,7 @@ struct MIDISound {
struct Prog; struct Prog;
class MusicBank : public SoundBank { class MusicBank : public SoundBank {
public: public:
MusicBank(locator& loc) : m_locator(loc) {} MusicBank(locator& loc, u32 id, BankTag* tag);
std::unique_ptr<sound_handler> make_handler(voice_manager& vm, std::unique_ptr<sound_handler> make_handler(voice_manager& vm,
u32 sound_id, u32 sound_id,
s32 vol, s32 vol,
@ -68,8 +68,14 @@ class MusicBank : public SoundBank {
s32 pm, s32 pm,
s32 pb) override; s32 pb) override;
std::vector<Prog> programs; std::unique_ptr<sound_handler> make_handler(voice_manager& vm,
std::vector<MIDISound> sounds; u32 sound_id,
s32 vol,
s32 pan,
SndPlayParams& params) override;
std::vector<Prog> m_programs;
std::vector<MIDISound> m_sounds;
private: private:
locator& m_locator; locator& m_locator;

View File

@ -143,6 +143,38 @@ u32 player::play_sound(u32 bank_id, u32 sound_id, s32 vol, s32 pan, s32 pm, s32
return handle; return handle;
} }
u32 player::play_sound_by_name(u32 bank_id,
char* bank_name,
char* sound_name,
s32 vol,
s32 pan,
s32 pm,
s32 pb) {
std::scoped_lock lock(m_ticklock);
SoundBank* bank = nullptr;
if (bank_id == 0 && bank_name != nullptr) {
bank = m_loader.get_bank_by_name(bank_name);
} else if (bank_id != 0) {
bank = m_loader.get_bank_by_handle(bank_id);
} else {
bank = m_loader.get_bank_with_sound(sound_name);
}
if (bank == nullptr) {
//lg::error("play_sound_by_name: failed to find bank for sound {}", sound_name);
return 0;
}
auto sound = bank->get_sound_by_name(sound_name);
if (sound.has_value()) {
return play_sound(bank->bank_id, sound.value(), vol, pan, pm, pb);
}
//lg::error("play_sound_by_name: failed to find sound {}", sound_name);
return 0;
}
void player::stop_sound(u32 sound_id) { void player::stop_sound(u32 sound_id) {
std::scoped_lock lock(m_ticklock); std::scoped_lock lock(m_ticklock);
auto handler = m_handlers.find(sound_id); auto handler = m_handlers.find(sound_id);
@ -155,7 +187,7 @@ void player::stop_sound(u32 sound_id) {
// m_handlers.erase(sound_id); // m_handlers.erase(sound_id);
} }
void player::set_midi_reg(u32 sound_id, u8 reg, u8 value) { void player::set_sound_reg(u32 sound_id, u8 reg, u8 value) {
std::scoped_lock lock(m_ticklock); std::scoped_lock lock(m_ticklock);
if (m_handlers.find(sound_id) == m_handlers.end()) { if (m_handlers.find(sound_id) == m_handlers.end()) {
// fmt::print("set_midi_reg: Handler {} does not exist\n", sound_id); // fmt::print("set_midi_reg: Handler {} does not exist\n", sound_id);
@ -210,7 +242,7 @@ void player::unload_bank(u32 bank_handle) {
return; return;
for (auto it = m_handlers.begin(); it != m_handlers.end();) { for (auto it = m_handlers.begin(); it != m_handlers.end();) {
if (it->second->bank() == bank_handle) { if (it->second->bank().bank_id == bank_handle) {
m_handle_allocator.free_id(it->first); m_handle_allocator.free_id(it->first);
it = m_handlers.erase(it); it = m_handlers.erase(it);
} else { } else {
@ -285,4 +317,54 @@ void player::set_sound_pmod(s32 sound_handle, s32 mod) {
handler->second->set_pmod(mod); handler->second->set_pmod(mod);
} }
void player::stop_all_sounds() {
for (auto it = m_handlers.begin(); it != m_handlers.end();) {
m_handle_allocator.free_id(it->first);
it = m_handlers.erase(it);
}
}
s32 player::get_sound_user_data(s32 block_handle,
char* block_name,
s32 sound_id,
char* sound_name,
SFXUserData* dst) {
std::scoped_lock lock(m_ticklock);
SoundBank* bank = nullptr;
if (block_handle == 0 && block_name != nullptr) {
bank = m_loader.get_bank_by_name(block_name);
} else if (block_handle != 0) {
bank = m_loader.get_bank_by_handle(block_handle);
} else {
bank = m_loader.get_bank_with_sound(sound_name);
}
if (bank == nullptr) {
return 0;
}
if (sound_id == -1) {
auto sound = bank->get_sound_by_name(sound_name);
if (sound.has_value()) {
sound_id = sound.value();
} else {
return 0;
}
}
auto ud = bank->get_sound_user_data(sound_id);
if (ud.has_value()) {
dst->data[0] = ud.value()->data[0];
dst->data[1] = ud.value()->data[1];
dst->data[2] = ud.value()->data[2];
dst->data[3] = ud.value()->data[3];
return 1;
} else {
return 0;
}
return 0;
}
} // namespace snd } // namespace snd

View File

@ -36,7 +36,14 @@ class player {
u32 load_bank(fs::path& path, size_t offset); u32 load_bank(fs::path& path, size_t offset);
u32 play_sound(u32 bank, u32 sound, s32 vol, s32 pan, s32 pm, s32 pb); u32 play_sound(u32 bank, u32 sound, s32 vol, s32 pan, s32 pm, s32 pb);
void set_midi_reg(u32 sound_id, u8 reg, u8 value); u32 play_sound_by_name(u32 bank,
char* bank_name,
char* sound_name,
s32 vol,
s32 pan,
s32 pm,
s32 pb);
void set_sound_reg(u32 sound_id, u8 reg, u8 value);
bool sound_still_active(u32 sound_id); bool sound_still_active(u32 sound_id);
void set_master_volume(u32 group, s32 volume); void set_master_volume(u32 group, s32 volume);
void unload_bank(u32 bank_handle); void unload_bank(u32 bank_handle);
@ -53,6 +60,12 @@ class player {
void init_cubeb(); void init_cubeb();
void destroy_cubeb(); void destroy_cubeb();
s32 get_tick() { return m_tick; }; s32 get_tick() { return m_tick; };
void stop_all_sounds();
s32 get_sound_user_data(s32 block_handle,
char* block_name,
s32 sound_id,
char* sound_name,
SFXUserData* dst);
private: private:
std::recursive_mutex m_ticklock; // TODO does not need to recursive with some light restructuring std::recursive_mutex m_ticklock; // TODO does not need to recursive with some light restructuring

View File

@ -1,25 +1,48 @@
#include "sfxblock.h" #include "sfxblock.h"
#include "blocksound_handler.h" #include "blocksound_handler.h"
#include "sfxgrain.h"
#include "common/log/log.h"
namespace snd { namespace snd {
SFXBlock::SFXBlock(locator& loc, u32 id, BankTag* tag)
: SoundBank(id, BankType::SFX), m_locator(loc) {
auto data = (SFXBlockData*)tag;
auto sounddata = (SFX2Data*)((uintptr_t)data + data->FirstSound);
for (int i = 0; i < data->NumSounds; i++) {
SFX2 sound;
sound.index = i;
sound.d = sounddata[i];
m_sounds.push_back(std::move(sound));
}
for (auto& sound : m_sounds) {
auto graindata = (SFXGrain*)((uintptr_t)data + data->FirstGrain + sound.d.FirstGrain);
for (int i = 0; i < sound.d.NumGrains; i++) {
SFXGrain& grain = graindata[i];
sound.grains.push_back(new_grain((grain_type)grain.Type, grain));
}
}
}
std::unique_ptr<sound_handler> SFXBlock::make_handler(voice_manager& vm, std::unique_ptr<sound_handler> SFXBlock::make_handler(voice_manager& vm,
u32 sound_id, u32 sound_id,
s32 vol, s32 vol,
s32 pan, s32 pan,
s32 pm, SndPlayParams& params) {
s32 pb) { auto& SFX = m_sounds[sound_id];
std::unique_ptr<blocksound_handler> handler;
auto& SFX = sounds[sound_id];
if (SFX.grains.empty()) { if (SFX.grains.empty()) {
// fmt::print("skipping empty sfx\n"); // fmt::print("skipping empty sfx\n");
return nullptr; return nullptr;
} }
handler = std::make_unique<blocksound_handler>(sounds[sound_id], vm, vol, pan, pm, pb, bank_id); auto handler =
std::make_unique<blocksound_handler>(*this, m_sounds[sound_id], vm, vol, pan, params);
handler->init(); handler->init();
return handler; return handler;
} }
} // namespace snd } // namespace snd

View File

@ -1,8 +1,11 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include "sfxgrain.h"
#include "soundbank.h" #include "soundbank.h"
#include "sfxblock2.h"
namespace snd { namespace snd {
struct SFXBlockData : BankTag { struct SFXBlockData : BankTag {
@ -19,76 +22,14 @@ struct SFXBlockData : BankTag {
/* 28 */ u32 VagDataSize; /* 28 */ u32 VagDataSize;
/* 2c */ u32 SRAMAllocSize; /* 2c */ u32 SRAMAllocSize;
/* 30 */ u32 NextBlock; /* 30 */ u32 NextBlock;
/* these last ones are probably not in jak1? */
/* 34 */ u32 BlockNames; /* 34 */ u32 BlockNames;
/* 38 */ u32 SFXUD; /* 38 */ u32 SFXUD;
}; };
static_assert(sizeof(SFXBlockData) == 0x38 + 4); static_assert(sizeof(SFXBlockData) == 0x38 + 4);
struct XREFGrainParams {
/* 0 */ u32 BankID;
/* 4 */ u32 SoundIndex;
/* 8 */ s32 PitchMod;
/* c */ u32 Flags;
};
struct RandDelayParams {
/* 0 */ s32 Amount;
};
struct ControlParams {
/* 0 */ s16 param[4];
};
struct LFOParams {
/* 0 */ u8 which_lfo;
/* 1 */ u8 target;
/* 2 */ u8 target_extra;
/* 3 */ u8 shape;
/* 4 */ u16 duty_cycle;
/* 6 */ u16 depth;
/* 8 */ u16 flags;
/* a */ u16 start_offset;
/* c */ u32 step_size;
};
struct PlaySoundParams {
/* 0 */ s32 vol;
/* 4 */ s32 pan;
/* 8 */ s8 reg_settings[4];
/* c */ s32 sound_id;
/* 10 */ char snd_name[16];
};
struct PluginParams {
/* 0 */ u32 id;
/* 4 */ u32 index;
/* 8 */ u8 data[24];
};
struct LargestGrainParamStruct {
/* 0 */ char blank[32];
};
/*
** Type 1 = Tone
*/
struct SFXGrain {
/* 0 */ u32 Type;
/* 4 */ s32 Delay;
union {
/* 8 */ Tone tone;
/* 8 */ XREFGrainParams xref;
/* 8 */ RandDelayParams delay;
/* 8 */ ControlParams control;
/* 8 */ LFOParams lfo;
/* 8 */ PlaySoundParams play_sound;
/* 8 */ PluginParams plugin_params;
/* 8 */ LargestGrainParamStruct junk;
} GrainParams;
};
struct SFXData { struct SFXData {
/* 0 */ s8 Vol; /* 0 */ s8 Vol;
/* 1 */ s8 VolGroup; /* 1 */ s8 VolGroup;
@ -106,23 +47,21 @@ enum SFXFlags {
struct SFX { struct SFX {
SFXData d; SFXData d;
std::vector<SFXGrain> grains; std::vector<std::unique_ptr<Grain>> grains;
}; };
class SFXBlock : public SoundBank { class SFXBlock : public SoundBank {
public: public:
SFXBlock(locator& loc) : m_locator(loc) {} SFXBlock(locator& loc, u32 handle, BankTag* tag);
std::unique_ptr<sound_handler> make_handler(voice_manager& vm, std::unique_ptr<sound_handler> make_handler(voice_manager& vm,
u32 sound_id, u32 sound_id,
s32 vol, s32 vol,
s32 pan, s32 pan,
s32 pm, SndPlayParams& params) override;
s32 pb) override;
std::vector<SFX> sounds;
private: private:
locator& m_locator; locator& m_locator;
std::vector<SFX2> m_sounds;
}; };
} // namespace snd } // namespace snd

View File

@ -0,0 +1,103 @@
#include "sfxblock2.h"
#include "blocksound_handler.h"
#include "common/log/log.h"
namespace snd {
SFXBlock2::SFXBlock2(locator& loc, u32 id, BankTag* tag)
: SoundBank(id, BankType::SFX), m_locator(loc) {
auto data = (SFXBlockData2*)tag;
auto sounddata = (SFX2Data*)((uintptr_t)data + data->FirstSound);
auto userdata = (SFXUserData*)((uintptr_t)data + data->SFXUD);
for (int i = 0; i < data->NumSounds; i++) {
SFX2 sound;
sound.index = i;
sound.d = sounddata[i];
sound.user_data = userdata[i];
m_sounds.push_back(std::move(sound));
}
for (auto& sound : m_sounds) {
auto graindata = (SFXGrain2*)((uintptr_t)data + data->FirstGrain + sound.d.FirstGrain);
for (int i = 0; i < sound.d.NumGrains; i++) {
SFXGrain2& grain = graindata[i];
sound.grains.push_back(new_grain((grain_type)grain.OpcodeData.type, grain,
(u8*)((uintptr_t)data + data->GrainData)));
}
}
auto names = (SFXBlockNames*)((uintptr_t)data + data->BlockNames);
char buf[8];
strncpy(buf, (char*)names->BlockName, 8);
m_name = buf;
if (names->SFXNameTableOffset != 0) {
// The sound names are hashed and divided up into 32 buckets
// to reduce the number of comparisons needed to search the list.
// An empty name entry signifies the end of each bucket.
// Let's go through all the buckets and collect the names.
auto name_table = (SFXName*)((uintptr_t)names + names->SFXNameTableOffset);
for (int i = 0; i < 32; i++) {
auto name = &name_table[names->SFXHashOffsets[i]];
while (name->Name[0] != 0) {
char buf[16];
strncpy(buf, (char*)name->Name, 16);
std::string str(buf);
m_names[str] = name->Index;
m_sounds.at(name->Index).name = str;
name++;
}
}
}
auto idx = 0;
for (auto& s : m_sounds) {
lg::warn("sound {} : {}", idx, s.name);
idx++;
}
}
std::unique_ptr<sound_handler> SFXBlock2::make_handler(voice_manager& vm,
u32 sound_id,
s32 vol,
s32 pan,
SndPlayParams& params) {
if (sound_id >= m_sounds.size()) {
lg::error("out of bounds sound_id");
return nullptr;
}
auto& SFX = m_sounds[sound_id];
if (SFX.grains.empty()) {
// fmt::print("skipping empty sfx\n");
return nullptr;
}
lg::info("playing sound: {}", SFX.name);
auto handler =
std::make_unique<blocksound_handler>(*this, m_sounds[sound_id], vm, vol, pan, params);
handler->init();
return handler;
}
std::optional<u32> SFXBlock2::get_sound_by_name(const char* name) {
// lg::error("searching for sound {}", name);
// for (auto& s : m_names) {
// lg::error("{}", s.first);
// }
auto sound = m_names.find(name);
if (sound != m_names.end()) {
return sound->second;
}
return std::nullopt;
}
} // namespace snd

View File

@ -0,0 +1,115 @@
#pragma once
#include <vector>
#include "sfxgrain.h"
#include "soundbank.h"
namespace snd {
enum class SFX2BlockFlags : u32 {
HasBlockNames = 1 << 8,
};
struct SFXBlockData2 : BankTag {
/* 10 */ s8 BlockNum;
/* 11 */ s8 pad1;
/* 12 */ s16 pad2;
/* 14 */ s16 pad3;
/* 16 */ s16 NumSounds;
/* 18 */ s16 NumGrains;
/* 1a */ s16 NumVAGs;
/* 1c */ /* SFX2Ptr */ u32 FirstSound;
/* 20 */ /* SFXGrain2Ptr */ u32 FirstGrain;
/* 24 */ u32 VagsInSR;
/* 28 */ u32 VagDataSize;
/* 2c */ u32 SRAMAllocSize;
/* 30 */ u32 NextBlock;
/* 34 */ u32 GrainData; // new
/* 38 */ /* SFXBlockNames* */ u32 BlockNames;
/* 3c */ /* SFXUserData* */ u32 SFXUD;
};
static_assert(sizeof(SFXBlockData2) == 0x3c + 4);
struct SFXUserData {
/* 0 */ u32 data[4];
};
struct SFXName {
/* 0 */ u32 Name[4];
/* 10 */ s16 Index;
/* 12 */ s16 reserved;
};
struct VAGName {
/* 0 */ u32 Name[4];
/* 10 */ u32 Offset;
/* 14 */ u32 res1;
/* 18 */ u32 res2;
};
struct VAGImport {
/* 0 */ u32 BlockName[2];
/* 8 */ u32 VAGName[4];
/* 18 */ u32 VAGLocation;
/* 1c */ u32 VAGSR;
};
struct VAGExport {
/* 0 */ u32 VAGName[4];
/* 10 */ u32 VAGLocation;
/* 14 */ u32 VAGSR;
};
struct SFXBlockNames {
/* 0 */ u32 BlockName[2];
/* 8 */ u32 SFXNameTableOffset;
/* c */ u32 VAGNameTableOffset;
/* 10 */ u32 VAGImportsTableOffset;
/* 14 */ u32 VAGExportsTableOffset;
/* 18 */ s16 SFXHashOffsets[32];
/* 58 */ s16 VAGHashOffsets[32];
};
struct SFX2Data {
/* 0 */ s8 Vol;
/* 1 */ s8 VolGroup;
/* 2 */ s16 Pan;
/* 4 */ s8 NumGrains;
/* 5 */ s8 InstanceLimit;
/* 6 */ u16 Flags;
/* 8 */ u32 FirstGrain;
};
struct SFX2 {
SFX2Data d;
std::string name;
std::vector<std::unique_ptr<Grain>> grains;
SFXUserData user_data;
int index;
};
class SFXBlock2 : public SoundBank {
public:
SFXBlock2(locator& loc, u32 handle, BankTag* tag);
std::unique_ptr<sound_handler> make_handler(voice_manager& vm,
u32 sound_id,
s32 vol,
s32 pan,
SndPlayParams& params) override;
std::optional<std::string_view> get_name() override { return m_name; };
std::optional<u32> get_sound_by_name(const char* name) override;
std::optional<const SFXUserData*> get_sound_user_data(u32 sound_id) override {
return &m_sounds.at(sound_id).user_data;
};
private:
locator& m_locator;
std::string m_name;
std::unordered_map<std::string, u32> m_names;
std::vector<SFX2> m_sounds;
};
} // namespace snd

View File

@ -0,0 +1,614 @@
#include "sfxgrain.h"
#include "blocksound_handler.h"
#include "lfo.h"
#include "common/log/log.h"
namespace snd {
SFXGrain_Tone::SFXGrain_Tone(SFXGrain& grain) : Grain(grain), m_tone(grain.GrainParams.tone) {}
SFXGrain_Tone::SFXGrain_Tone(SFXGrain2& grain, u8* data) : Grain(grain) {
m_tone = *(Tone*)(data + (grain.OpcodeData.Opcode & 0xFFFFFF));
}
s32 SFXGrain_Tone::execute(blocksound_handler& handler) {
handler.m_cur_volume =
((handler.m_app_volume * handler.m_orig_volume) >> 10) + handler.m_lfo_volume;
handler.m_cur_volume = std::clamp<s32>(handler.m_cur_volume, 0, 127);
handler.m_cur_pan = handler.m_app_pan + handler.m_lfo_pan;
while (handler.m_cur_pan >= 360)
handler.m_cur_pan -= 360;
while (handler.m_cur_pan < 0)
handler.m_cur_pan += 360;
if ((m_tone.Flags & 8) != 0) {
// Noise unsupported
return 0;
}
auto voice = std::make_shared<vag_voice>(m_tone);
s32 vol = m_tone.Vol;
if (vol < 0) {
if (vol >= -4) {
vol = handler.m_registers.at(-vol - 1);
} else if (vol == -5) {
vol = rand() % 0x7f;
} else {
vol = g_block_reg.at(-vol - 6);
}
}
vol = std::max(vol, 0);
s32 pan = m_tone.Pan;
if (pan < 0) {
if (pan >= -4) {
pan = 360 * handler.m_registers.at(-pan - 1) / 127;
} else if (pan == -5) {
pan = rand() % 360;
} else {
pan = 360 * g_block_reg.at(-pan - 6) / 127;
}
}
while (pan >= 360)
pan -= 360;
while (pan < 0)
pan += 360;
voice->start_note = handler.m_note;
voice->start_fine = handler.m_fine;
voice->group = handler.m_group;
voice->basevol =
handler.m_vm.make_volume(127, 0, handler.m_cur_volume, handler.m_cur_pan, vol, pan);
handler.m_vm.start_tone(voice, handler.m_bank.bank_id);
handler.m_voices.emplace_front(voice);
return 0;
}
SFXGrain_LfoSettings::SFXGrain_LfoSettings(SFXGrain& grain) : Grain(grain) {
m_lfop = grain.GrainParams.lfo;
}
SFXGrain_LfoSettings::SFXGrain_LfoSettings(SFXGrain2& grain, u8* data) : Grain(grain) {
m_lfop = *(LFOParams*)(data + (grain.OpcodeData.Opcode & 0xFFFFFF));
}
s32 SFXGrain_LfoSettings::execute(blocksound_handler& handler) {
auto& lfo = handler.m_lfo.at(m_lfop.which_lfo);
lfo.m_target = static_cast<lfo_target>(m_lfop.target);
if (lfo.m_target != lfo_target::NONE) {
lfo.m_type = static_cast<lfo_type>(m_lfop.shape);
lfo.m_target_extra = m_lfop.target_extra;
lfo.m_setup_flags = m_lfop.flags;
lfo.m_depth = m_lfop.depth;
lfo.m_orig_depth = m_lfop.depth;
lfo.m_step_size = m_lfop.step_size;
lfo.m_orig_step_size = m_lfop.step_size;
lfo.m_state_hold1 = 0;
lfo.m_last_lfo = 0;
if (lfo.m_type == lfo_type::SQUARE) {
lfo.m_state_hold1 = m_lfop.duty_cycle;
}
lfo.m_state_hold2 = 0;
if ((lfo.m_setup_flags & 2) != 0) {
lfo.m_next_step = (rand() & 0x7ff) << 16;
} else {
lfo.m_next_step = m_lfop.start_offset << 16;
}
lg::info("starting LFO type {} for {}", magic_enum::enum_name(lfo.m_type),
magic_enum::enum_name(lfo.m_target));
lfo.init();
} else {
lfo.m_type = lfo_type::OFF;
}
return 0;
}
SFXGrain_StartChildSound::SFXGrain_StartChildSound(SFXGrain& grain) : Grain(grain) {
m_psp = grain.GrainParams.play_sound;
}
SFXGrain_StartChildSound::SFXGrain_StartChildSound(SFXGrain2& grain, u8* data) : Grain(grain) {
m_psp = *(PlaySoundParams*)(data + (grain.OpcodeData.Opcode & 0xFFFFFF));
}
s32 SFXGrain_StartChildSound::execute(blocksound_handler& handler) {
s32 vol = m_psp.vol;
if (vol < 0) {
if (vol >= -4) {
vol = handler.m_registers.at(-vol - 1);
} else if (vol == -5) {
vol = rand() % 0x7f;
} else {
vol = g_block_reg.at(-vol - 6);
}
}
vol = std::clamp(std::abs(vol), 0, 127);
s32 pan = m_psp.pan;
if (pan < 0) {
if (pan >= -4) {
pan = 360 * std::min(std::abs(handler.m_registers.at(-pan - 1)), 127) / 127;
} else if (pan == -5) {
pan = rand() % 360;
} else {
pan = 360 * std::min(std::abs(g_block_reg.at(-pan - 6)), 127) / 127;
}
}
SndPlayParams params{};
params.vol = handler.m_app_volume * handler.m_orig_volume / 127;
params.pan = handler.m_app_pan;
params.pitch_mod = handler.m_app_pm;
params.pitch_bend = handler.m_app_pb;
params.registers = handler.m_registers;
auto& block = static_cast<SoundBank&>(handler.bank());
s32 index = m_psp.sound_id;
if (index >= 0) {
handler.m_children.emplace_front(block.make_handler(handler.m_vm, index, vol, pan, params));
return 0;
}
lg::error("indirect createchildsound");
return 0;
}
SFXGrain_StopChildSound::SFXGrain_StopChildSound(SFXGrain& grain) : Grain(grain) {
m_psp = grain.GrainParams.play_sound;
}
SFXGrain_StopChildSound::SFXGrain_StopChildSound(SFXGrain2& grain, u8* data) : Grain(grain) {
m_psp = *(PlaySoundParams*)(data + (grain.OpcodeData.Opcode & 0xFFFFFF));
}
s32 SFXGrain_StopChildSound::execute(blocksound_handler& handler) {
if (m_psp.sound_id >= 0) {
for (auto it = handler.m_children.begin(); it != handler.m_children.end();) {
auto* sound = static_cast<blocksound_handler*>(it->get());
if (sound->m_sfx.index == m_psp.sound_id) {
it = handler.m_children.erase(it);
} else {
++it;
}
}
return 0;
}
lg::error("indirect createchildsound");
return 0;
}
SFXGrain_PluginMessage::SFXGrain_PluginMessage(SFXGrain& grain) : Grain(grain) {}
SFXGrain_PluginMessage::SFXGrain_PluginMessage(SFXGrain2& grain, u8* data) : Grain(grain) {}
s32 SFXGrain_PluginMessage::execute(blocksound_handler& handler) {
// lg::warn("plugin message");
// TODO probably used
return 0;
}
SFXGrain_Branch::SFXGrain_Branch(SFXGrain& grain) : Grain(grain) {}
SFXGrain_Branch::SFXGrain_Branch(SFXGrain2& grain, u8* data) : Grain(grain) {}
s32 SFXGrain_Branch::execute(blocksound_handler& handler) {
return 0;
}
SFXGrain_LoopEnd::SFXGrain_LoopEnd(SFXGrain& grain) : Grain(grain) {}
SFXGrain_LoopEnd::SFXGrain_LoopEnd(SFXGrain2& grain, u8* data) : Grain(grain) {}
s32 SFXGrain_LoopEnd::execute(blocksound_handler& handler) {
bool found = false;
for (int i = handler.m_next_grain - 1; i >= 0 && !found; i--) {
if (handler.m_sfx.grains[i]->type() == grain_type::LOOP_START) {
handler.m_next_grain = i - 1;
found = true;
}
}
if (!found) {
lg::error("LOOP_END could not find LOOP_START");
}
return 0;
}
SFXGrain_LoopContinue::SFXGrain_LoopContinue(SFXGrain& grain) : Grain(grain) {}
SFXGrain_LoopContinue::SFXGrain_LoopContinue(SFXGrain2& grain, u8* data) : Grain(grain) {}
s32 SFXGrain_LoopContinue::execute(blocksound_handler& handler) {
bool found = false;
for (int i = handler.m_next_grain + 1; i < handler.m_sfx.grains.size() && !found; i++) {
if (handler.m_sfx.grains[i]->type() == grain_type::LOOP_END) {
handler.m_next_grain = i;
found = true;
}
}
if (!found) {
lg::error("LOOP_CONTINUE could not find LOOP_END");
}
return 0;
}
SFXGrain_Stop::SFXGrain_Stop(SFXGrain& grain) : Grain(grain) {}
SFXGrain_Stop::SFXGrain_Stop(SFXGrain2& grain, u8* data) : Grain(grain) {}
s32 SFXGrain_Stop::execute(blocksound_handler& handler) {
handler.m_done = true;
return 0;
}
SFXGrain_RandPlay::SFXGrain_RandPlay(SFXGrain& grain) : Grain(grain) {
options = grain.GrainParams.control.param[0];
count = grain.GrainParams.control.param[1];
previous = grain.GrainParams.control.param[2];
}
SFXGrain_RandPlay::SFXGrain_RandPlay(SFXGrain2& grain, u8* data) : Grain(grain) {
options = grain.OpcodeData.arg[0];
count = grain.OpcodeData.arg[1];
previous = grain.OpcodeData.arg[2];
}
s32 SFXGrain_RandPlay::execute(blocksound_handler& handler) {
int rnd = rand() % options;
if (rnd == previous) {
rnd++;
if (rnd >= options) {
rnd = 0;
}
}
previous = rnd;
handler.m_next_grain += rnd * count;
handler.m_grains_to_play = count + 1;
handler.m_grains_to_skip = (options - 1 - rnd) * count;
handler.m_skip_grains = true;
return 0;
}
SFXGrain_RandDelay::SFXGrain_RandDelay(SFXGrain& grain) : Grain(grain) {
m_max = grain.GrainParams.delay.Amount;
}
SFXGrain_RandDelay::SFXGrain_RandDelay(SFXGrain2& grain, u8* data) : Grain(grain) {
m_max = (grain.OpcodeData.Opcode & 0xFFFFFF) + 1;
}
s32 SFXGrain_RandDelay::execute(blocksound_handler& handler) {
return rand() % m_max;
}
SFXGrain_RandPB::SFXGrain_RandPB(SFXGrain& grain) : Grain(grain) {
m_pb = grain.GrainParams.control.param[0];
}
SFXGrain_RandPB::SFXGrain_RandPB(SFXGrain2& grain, u8* data) : Grain(grain) {
m_pb = grain.OpcodeData.arg[0];
}
s32 SFXGrain_RandPB::execute(blocksound_handler& handler) {
s32 rnd = rand();
handler.set_pbend(m_pb * ((0xffff * (rnd % 0x7fff)) / 0x7fff - 0x8000) / 100);
return 0;
}
SFXGrain_PB::SFXGrain_PB(SFXGrain& grain) : Grain(grain) {
m_pb = grain.GrainParams.control.param[0];
}
SFXGrain_PB::SFXGrain_PB(SFXGrain2& grain, u8* data) : Grain(grain) {
m_pb = grain.OpcodeData.arg[0];
}
s32 SFXGrain_PB::execute(blocksound_handler& handler) {
if (m_pb >= 0) {
handler.set_pbend(0x7fff * m_pb / 127);
} else {
handler.set_pbend(-0x8000 * m_pb / -128);
}
return 0;
}
SFXGrain_AddPB::SFXGrain_AddPB(SFXGrain& grain) : Grain(grain) {
m_pb = grain.GrainParams.control.param[0];
}
SFXGrain_AddPB::SFXGrain_AddPB(SFXGrain2& grain, u8* data) : Grain(grain) {
m_pb = grain.OpcodeData.arg[0];
}
s32 SFXGrain_AddPB::execute(blocksound_handler& handler) {
s32 new_pb = handler.m_cur_pb + 0x7fff * m_pb / 127;
std::clamp<s32>(new_pb, INT16_MIN, INT16_MAX);
handler.set_pbend(new_pb);
return 0;
}
SFXGrain_SetRegister::SFXGrain_SetRegister(SFXGrain& grain) : Grain(grain) {
m_reg = grain.GrainParams.control.param[0];
m_value = grain.GrainParams.control.param[1];
}
SFXGrain_SetRegister::SFXGrain_SetRegister(SFXGrain2& grain, u8* data) : Grain(grain) {
m_reg = grain.OpcodeData.arg[0];
m_value = grain.OpcodeData.arg[1];
}
s32 SFXGrain_SetRegister::execute(blocksound_handler& handler) {
if (m_reg < 0) {
g_block_reg.at(-m_reg - 1) = m_value;
} else {
handler.m_registers.at(m_reg) = m_value;
}
return 0;
}
SFXGrain_SetRegisterRand::SFXGrain_SetRegisterRand(SFXGrain& grain) : Grain(grain) {
m_reg = grain.GrainParams.control.param[0];
m_lower_bound = grain.GrainParams.control.param[1];
m_upper_bound = grain.GrainParams.control.param[2];
}
SFXGrain_SetRegisterRand::SFXGrain_SetRegisterRand(SFXGrain2& grain, u8* data) : Grain(grain) {
m_reg = grain.OpcodeData.arg[0];
m_lower_bound = grain.OpcodeData.arg[1];
m_upper_bound = grain.OpcodeData.arg[2];
}
s32 SFXGrain_SetRegisterRand::execute(blocksound_handler& handler) {
s32 range = m_upper_bound - m_lower_bound + 1;
s32 rnd = (rand() % range) + m_lower_bound;
if (m_reg < 0) {
g_block_reg.at(-m_reg - 1) = rnd;
} else {
handler.m_registers.at(m_reg) = rnd;
}
return 0;
}
SFXGrain_IncRegister::SFXGrain_IncRegister(SFXGrain& grain) : Grain(grain) {
m_reg = grain.GrainParams.control.param[0];
}
SFXGrain_IncRegister::SFXGrain_IncRegister(SFXGrain2& grain, u8* data) : Grain(grain) {
m_reg = grain.OpcodeData.arg[0];
}
s32 SFXGrain_IncRegister::execute(blocksound_handler& handler) {
if (m_reg < 0) {
s32 new_val = g_block_reg.at(-m_reg - 1) + 1;
g_block_reg.at(-m_reg - 1) = std::clamp<s32>(new_val, INT8_MIN, INT8_MAX);
} else {
s32 new_val = handler.m_registers.at(m_reg) + 1;
handler.m_registers.at(m_reg) = std::clamp<s32>(new_val, INT8_MIN, INT8_MAX);
}
return 0;
}
SFXGrain_DecRegister::SFXGrain_DecRegister(SFXGrain& grain) : Grain(grain) {
m_reg = grain.GrainParams.control.param[0];
}
SFXGrain_DecRegister::SFXGrain_DecRegister(SFXGrain2& grain, u8* data) : Grain(grain) {
m_reg = grain.OpcodeData.arg[0];
}
s32 SFXGrain_DecRegister::execute(blocksound_handler& handler) {
if (m_reg < 0) {
s32 new_val = g_block_reg.at(-m_reg - 1) - 1;
g_block_reg.at(-m_reg - 1) = std::clamp<s32>(new_val, INT8_MIN, INT8_MAX);
} else {
s32 new_val = handler.m_registers.at(m_reg) - 1;
handler.m_registers.at(m_reg) = std::clamp<s32>(new_val, INT8_MIN, INT8_MAX);
}
return 0;
}
SFXGrain_TestRegister::SFXGrain_TestRegister(SFXGrain& grain) : Grain(grain) {
m_reg = grain.GrainParams.control.param[0];
m_action = grain.GrainParams.control.param[1];
m_cmp = grain.GrainParams.control.param[2];
}
SFXGrain_TestRegister::SFXGrain_TestRegister(SFXGrain2& grain, u8* data) : Grain(grain) {
m_reg = grain.OpcodeData.arg[0];
m_action = grain.OpcodeData.arg[1];
m_cmp = grain.OpcodeData.arg[2];
}
s32 SFXGrain_TestRegister::execute(blocksound_handler& handler) {
s32 value;
if (m_reg < 0) {
value = g_block_reg[m_reg - 1];
} else {
value = handler.m_registers.at(m_reg);
}
if (m_action == 0) {
if (value >= m_cmp) {
handler.m_next_grain++;
}
} else if (m_action == 1) {
if (value != m_cmp) {
handler.m_next_grain++;
}
} else if (m_action >= 2) {
if (m_cmp >= value)
handler.m_next_grain++;
}
return 0;
}
SFXGrain_GotoMarker::SFXGrain_GotoMarker(SFXGrain& grain) : Grain(grain) {
m_mark = grain.GrainParams.control.param[0];
}
SFXGrain_GotoMarker::SFXGrain_GotoMarker(SFXGrain2& grain, u8* data) : Grain(grain) {
m_mark = grain.OpcodeData.arg[0];
}
s32 SFXGrain_GotoMarker::execute(blocksound_handler& handler) {
bool found = false;
for (int i = 0; i < handler.m_sfx.grains.size() && !found; i++) {
if (handler.m_sfx.grains.at(i)->type() == grain_type::MARKER) {
if (static_cast<SFXGrain_Marker*>(handler.m_sfx.grains.at(i).get())->marker() == m_mark) {
handler.m_next_grain = i - 1;
found = true;
}
}
}
if (!found) {
lg::error("GOTO_MARKER to non-existing marker");
}
return 0;
}
SFXGrain_GotoRandomMarker::SFXGrain_GotoRandomMarker(SFXGrain& grain) : Grain(grain) {
m_lower_bound = grain.GrainParams.control.param[0];
m_upper_bound = grain.GrainParams.control.param[1];
}
SFXGrain_GotoRandomMarker::SFXGrain_GotoRandomMarker(SFXGrain2& grain, u8* data) : Grain(grain) {
m_lower_bound = grain.OpcodeData.arg[0];
m_upper_bound = grain.OpcodeData.arg[1];
}
s32 SFXGrain_GotoRandomMarker::execute(blocksound_handler& handler) {
bool found = false;
s32 range = m_upper_bound - m_lower_bound + 1;
s32 mark = (rand() % range) + m_lower_bound;
for (int i = 0; i < handler.m_sfx.grains.size() && !found; i++) {
if (handler.m_sfx.grains.at(i)->type() == grain_type::MARKER) {
if (static_cast<SFXGrain_Marker*>(handler.m_sfx.grains.at(i).get())->marker() == mark) {
handler.m_next_grain = i - 1;
found = true;
}
}
}
if (!found) {
lg::error("GOTO_RANDOM_MARKER to non-existing marker");
}
return 0;
}
SFXGrain_WaitForAllVoices::SFXGrain_WaitForAllVoices(SFXGrain& grain) : Grain(grain) {}
SFXGrain_WaitForAllVoices::SFXGrain_WaitForAllVoices(SFXGrain2& grain, u8* data) : Grain(grain) {}
s32 SFXGrain_WaitForAllVoices::execute(blocksound_handler& handler) {
if (!handler.m_voices.empty()) {
handler.m_next_grain--;
return 1;
}
return 0;
}
SFXGrain_PlayCycle::SFXGrain_PlayCycle(SFXGrain& grain) : Grain(grain) {
m_group_size = grain.GrainParams.control.param[0];
m_group_count = grain.GrainParams.control.param[1];
m_index = grain.GrainParams.control.param[2];
}
SFXGrain_PlayCycle::SFXGrain_PlayCycle(SFXGrain2& grain, u8* data) : Grain(grain) {
m_group_size = grain.OpcodeData.arg[0];
m_group_count = grain.OpcodeData.arg[1];
m_index = grain.OpcodeData.arg[2];
}
s32 SFXGrain_PlayCycle::execute(blocksound_handler& handler) {
auto a = m_index++;
if (m_index == m_group_size) {
m_index = 0;
}
handler.m_next_grain += m_group_count * a;
handler.m_grains_to_play = m_group_count + 1;
handler.m_grains_to_skip = (m_group_size - 1 - a) * m_group_count;
handler.m_skip_grains = true;
return 0;
}
SFXGrain_AddRegister::SFXGrain_AddRegister(SFXGrain& grain) : Grain(grain) {
m_val = grain.GrainParams.control.param[0];
m_reg = grain.GrainParams.control.param[1];
}
SFXGrain_AddRegister::SFXGrain_AddRegister(SFXGrain2& grain, u8* data) : Grain(grain) {
m_val = grain.OpcodeData.arg[0];
m_reg = grain.OpcodeData.arg[1];
}
s32 SFXGrain_AddRegister::execute(blocksound_handler& handler) {
if (m_reg < 0) {
s32 new_val = g_block_reg.at(-m_reg - 1) + m_val;
g_block_reg.at(-m_reg - 1) = std::clamp<s32>(new_val, INT8_MIN, INT8_MAX);
} else {
s32 new_val = handler.m_registers.at(m_reg) + m_val;
handler.m_registers.at(m_reg) = std::clamp<s32>(new_val, INT8_MIN, INT8_MAX);
}
return 0;
}
SFXGrain_KeyOffVoices::SFXGrain_KeyOffVoices(SFXGrain& grain) : Grain(grain) {}
SFXGrain_KeyOffVoices::SFXGrain_KeyOffVoices(SFXGrain2& grain, u8* data) : Grain(grain) {}
s32 SFXGrain_KeyOffVoices::execute(blocksound_handler& handler) {
for (auto& p : handler.m_voices) {
auto v = p.lock();
if (v == nullptr) {
continue;
}
v->key_off();
}
return 0;
}
SFXGrain_KillVoices::SFXGrain_KillVoices(SFXGrain& grain) : Grain(grain) {}
SFXGrain_KillVoices::SFXGrain_KillVoices(SFXGrain2& grain, u8* data) : Grain(grain) {}
s32 SFXGrain_KillVoices::execute(blocksound_handler& handler) {
for (auto& p : handler.m_voices) {
auto v = p.lock();
if (v == nullptr) {
continue;
}
v->key_off();
v->set_volume_l(0);
v->set_volume_r(0);
}
return 0;
}
SFXGrain_OnStopMarker::SFXGrain_OnStopMarker(SFXGrain& grain) : Grain(grain) {}
SFXGrain_OnStopMarker::SFXGrain_OnStopMarker(SFXGrain2& grain, u8* data) : Grain(grain) {}
s32 SFXGrain_OnStopMarker::execute(blocksound_handler& handler) {
handler.m_next_grain = handler.m_sfx.grains.size() - 1;
return 0;
}
SFXGrain_CopyRegister::SFXGrain_CopyRegister(SFXGrain& grain) : Grain(grain) {
m_src = grain.GrainParams.control.param[0];
m_dst = grain.GrainParams.control.param[1];
}
SFXGrain_CopyRegister::SFXGrain_CopyRegister(SFXGrain2& grain, u8* data) : Grain(grain) {
m_src = grain.OpcodeData.arg[0];
m_dst = grain.OpcodeData.arg[1];
}
s32 SFXGrain_CopyRegister::execute(blocksound_handler& handler) {
s8 value = 0;
if (m_src < 0) {
value = g_block_reg.at(-m_src - 1);
} else {
value = handler.m_registers.at(m_src);
}
if (m_dst < 0) {
g_block_reg.at(-m_dst - 1) = value;
} else {
handler.m_registers.at(m_dst) = value;
}
return 0;
}
} // namespace snd

View File

@ -0,0 +1,529 @@
#pragma once
#include <map>
#include "common/common_types.h"
#include "common/log/log.h"
#include "game/sound/989snd/vagvoice.h"
#include "third-party/magic_enum.hpp"
namespace snd {
struct XREFGrainParams {
/* 0 */ u32 BankID;
/* 4 */ u32 SoundIndex;
/* 8 */ s32 PitchMod;
/* c */ u32 Flags;
};
struct RandDelayParams {
/* 0 */ s32 Amount;
};
struct ControlParams {
/* 0 */ s16 param[4];
};
struct LFOParams {
/* 0 */ u8 which_lfo;
/* 1 */ u8 target;
/* 2 */ u8 target_extra;
/* 3 */ u8 shape;
/* 4 */ u16 duty_cycle;
/* 6 */ u16 depth;
/* 8 */ u16 flags;
/* a */ u16 start_offset;
/* c */ u32 step_size;
};
struct PlaySoundParams {
/* 0 */ s32 vol;
/* 4 */ s32 pan;
/* 8 */ s8 reg_settings[4];
/* c */ s32 sound_id;
/* 10 */ char snd_name[16];
};
struct PluginParams {
/* 0 */ u32 id;
/* 4 */ u32 index;
/* 8 */ u8 data[24];
};
struct LargestGrainParamStruct {
/* 0 */ char blank[32];
};
struct SFXGrain {
/* 0 */ u32 Type;
/* 4 */ s32 Delay;
union {
/* 8 */ Tone tone;
/* 8 */ XREFGrainParams xref;
/* 8 */ RandDelayParams delay;
/* 8 */ ControlParams control;
/* 8 */ LFOParams lfo;
/* 8 */ PlaySoundParams play_sound;
/* 8 */ PluginParams plugin_params;
/* 8 */ LargestGrainParamStruct junk;
} GrainParams;
};
struct SFXGrain2 {
union {
struct {
s8 arg[3];
u8 type;
};
u32 Opcode;
} OpcodeData;
s32 Delay;
};
enum class grain_type : u32 {
NULL_GRAIN = 0,
TONE = 1,
TONE2 = 9,
XREF_ID = 2,
XREF_NUM = 3,
LFO_SETTINGS = 4,
STARTCHILDSOUND = 5,
STOPCHILDSOUND = 6,
PLUGIN_MESSAGE = 7,
BRANCH = 8,
CONTROL_NULL = 20,
LOOP_START = 21,
LOOP_END = 22,
LOOP_CONTINUE = 23,
STOP = 24,
RAND_PLAY = 25,
RAND_DELAY = 26,
RAND_PB = 27,
PB = 28,
ADD_PB = 29,
SET_REGISTER = 30,
SET_REGISTER_RAND = 31,
INC_REGISTER = 32,
DEC_REGISTER = 33,
TEST_REGISTER = 34,
MARKER = 35,
GOTO_MARKER = 36,
GOTO_RANDOM_MARKER = 37,
WAIT_FOR_ALL_VOICES = 38,
PLAY_CYCLE = 39,
ADD_REGISTER = 40,
KEY_OFF_VOICES = 41,
KILL_VOICES = 42,
ON_STOP_MARKER = 43,
COPY_REGISTER = 44,
};
class blocksound_handler;
class Grain {
public:
Grain(SFXGrain& grain) : m_type((grain_type)grain.Type), m_delay(grain.Delay) {}
Grain(SFXGrain2& grain) : m_type((grain_type)grain.OpcodeData.type), m_delay(grain.Delay) {}
Grain(SFXGrain2& grain, [[maybe_unused]] u8* data)
: m_type((grain_type)grain.OpcodeData.type), m_delay(grain.Delay) {}
virtual ~Grain() = default;
virtual s32 execute(blocksound_handler& handler) { return 0; };
virtual std::string_view inspect() { return magic_enum::enum_name(type()); };
s32 delay() { return m_delay; }
grain_type type() { return m_type; }
std::array<int, 4>& args() { return m_args; }
private:
std::array<int, 4> m_args;
grain_type m_type{0};
s32 m_delay{0};
};
class SFXGrain_Null : public Grain {
public:
SFXGrain_Null(SFXGrain& grain) : Grain(grain){};
SFXGrain_Null(SFXGrain2& grain, [[maybe_unused]] u8* data) : Grain(grain){};
};
class SFXGrain_Tone : public Grain {
public:
SFXGrain_Tone(SFXGrain& grain);
SFXGrain_Tone(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
Tone m_tone;
};
class SFXGrain_XrefID : public Grain {
public:
SFXGrain_XrefID(SFXGrain& grain) : Grain(grain){};
SFXGrain_XrefID(SFXGrain2& grain, [[maybe_unused]] u8* data) : Grain(grain){};
};
class SFXGrain_XrefNum : public Grain {
public:
SFXGrain_XrefNum(SFXGrain& grain) : Grain(grain){};
SFXGrain_XrefNum(SFXGrain2& grain, [[maybe_unused]] u8* data) : Grain(grain){};
};
class SFXGrain_LfoSettings : public Grain {
public:
SFXGrain_LfoSettings(SFXGrain& grain);
SFXGrain_LfoSettings(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
LFOParams m_lfop{};
};
class SFXGrain_StartChildSound : public Grain {
public:
SFXGrain_StartChildSound(SFXGrain& grain);
SFXGrain_StartChildSound(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
PlaySoundParams m_psp{};
};
class SFXGrain_StopChildSound : public Grain {
public:
SFXGrain_StopChildSound(SFXGrain& grain);
SFXGrain_StopChildSound(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
PlaySoundParams m_psp{};
};
class SFXGrain_PluginMessage : public Grain {
public:
SFXGrain_PluginMessage(SFXGrain& grain);
SFXGrain_PluginMessage(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
};
class SFXGrain_Branch : public Grain {
public:
SFXGrain_Branch(SFXGrain& grain);
SFXGrain_Branch(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
};
class SFXGrain_ControlNull : public Grain {
public:
SFXGrain_ControlNull(SFXGrain& grain) : Grain(grain){};
SFXGrain_ControlNull(SFXGrain2& grain, u8* data) : Grain(grain){};
};
class SFXGrain_LoopStart : public Grain {
public:
SFXGrain_LoopStart(SFXGrain& grain) : Grain(grain){};
SFXGrain_LoopStart(SFXGrain2& grain, u8* data) : Grain(grain){};
};
class SFXGrain_LoopEnd : public Grain {
public:
SFXGrain_LoopEnd(SFXGrain& grain);
SFXGrain_LoopEnd(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
};
class SFXGrain_LoopContinue : public Grain {
public:
SFXGrain_LoopContinue(SFXGrain& grain);
SFXGrain_LoopContinue(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
};
class SFXGrain_Stop : public Grain {
public:
SFXGrain_Stop(SFXGrain& grain);
SFXGrain_Stop(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
};
class SFXGrain_RandPlay : public Grain {
public:
SFXGrain_RandPlay(SFXGrain& grain);
SFXGrain_RandPlay(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
int options{0};
int count{0};
int previous{0};
};
class SFXGrain_RandDelay : public Grain {
public:
SFXGrain_RandDelay(SFXGrain& grain);
SFXGrain_RandDelay(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
int m_max{0};
};
class SFXGrain_RandPB : public Grain {
public:
SFXGrain_RandPB(SFXGrain& grain);
SFXGrain_RandPB(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
int m_pb{0};
};
class SFXGrain_PB : public Grain {
public:
SFXGrain_PB(SFXGrain& grain);
SFXGrain_PB(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
int m_pb{0};
};
class SFXGrain_AddPB : public Grain {
public:
SFXGrain_AddPB(SFXGrain& grain);
SFXGrain_AddPB(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
int m_pb{0};
};
class SFXGrain_SetRegister : public Grain {
public:
SFXGrain_SetRegister(SFXGrain& grain);
SFXGrain_SetRegister(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
int m_reg{0};
int m_value{0};
};
class SFXGrain_SetRegisterRand : public Grain {
public:
SFXGrain_SetRegisterRand(SFXGrain& grain);
SFXGrain_SetRegisterRand(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
int m_reg{0};
int m_lower_bound{0};
int m_upper_bound{0};
};
class SFXGrain_IncRegister : public Grain {
public:
SFXGrain_IncRegister(SFXGrain& grain);
SFXGrain_IncRegister(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
int m_reg{0};
};
class SFXGrain_DecRegister : public Grain {
public:
SFXGrain_DecRegister(SFXGrain& grain);
SFXGrain_DecRegister(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
int m_reg{0};
};
class SFXGrain_TestRegister : public Grain {
public:
SFXGrain_TestRegister(SFXGrain& grain);
SFXGrain_TestRegister(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
int m_reg{0};
int m_cmp{0};
int m_action{0};
};
class SFXGrain_Marker : public Grain {
public:
SFXGrain_Marker(SFXGrain& grain) : Grain(grain), m_mark(grain.GrainParams.control.param[0]) {}
SFXGrain_Marker(SFXGrain2& grain, u8* data) : Grain(grain), m_mark(grain.OpcodeData.arg[0]) {}
int marker() { return m_mark; }
private:
int m_mark{0};
};
class SFXGrain_GotoMarker : public Grain {
public:
SFXGrain_GotoMarker(SFXGrain& grain);
SFXGrain_GotoMarker(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
int m_mark{0};
};
class SFXGrain_GotoRandomMarker : public Grain {
public:
SFXGrain_GotoRandomMarker(SFXGrain& grain);
SFXGrain_GotoRandomMarker(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
int m_upper_bound{0};
int m_lower_bound{0};
};
class SFXGrain_WaitForAllVoices : public Grain {
public:
SFXGrain_WaitForAllVoices(SFXGrain& grain);
SFXGrain_WaitForAllVoices(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
};
class SFXGrain_PlayCycle : public Grain {
public:
SFXGrain_PlayCycle(SFXGrain& grain);
SFXGrain_PlayCycle(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
int m_group_size;
int m_group_count;
int m_index;
};
class SFXGrain_AddRegister : public Grain {
public:
SFXGrain_AddRegister(SFXGrain& grain);
SFXGrain_AddRegister(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
int m_val{0};
int m_reg{0};
};
class SFXGrain_KeyOffVoices : public Grain {
public:
SFXGrain_KeyOffVoices(SFXGrain& grain);
SFXGrain_KeyOffVoices(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
};
class SFXGrain_KillVoices : public Grain {
public:
SFXGrain_KillVoices(SFXGrain& grain);
SFXGrain_KillVoices(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
};
class SFXGrain_OnStopMarker : public Grain {
public:
SFXGrain_OnStopMarker(SFXGrain& grain);
SFXGrain_OnStopMarker(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
};
class SFXGrain_CopyRegister : public Grain {
public:
SFXGrain_CopyRegister(SFXGrain& grain);
SFXGrain_CopyRegister(SFXGrain2& grain, u8* data);
s32 execute(blocksound_handler& handler) override;
private:
int m_src{0};
int m_dst{0};
};
template <typename... Args>
std::unique_ptr<Grain> new_grain(grain_type id, Args&&... args) {
switch (id) {
case grain_type::NULL_GRAIN:
return std::make_unique<SFXGrain_Null>(std::forward<Args>(args)...);
case grain_type::TONE:
case grain_type::TONE2:
return std::make_unique<SFXGrain_Tone>(std::forward<Args>(args)...);
case grain_type::XREF_ID:
return std::make_unique<SFXGrain_XrefID>(std::forward<Args>(args)...);
case grain_type::XREF_NUM:
return std::make_unique<SFXGrain_XrefNum>(std::forward<Args>(args)...);
case grain_type::LFO_SETTINGS:
return std::make_unique<SFXGrain_LfoSettings>(std::forward<Args>(args)...);
case grain_type::STARTCHILDSOUND:
return std::make_unique<SFXGrain_StartChildSound>(std::forward<Args>(args)...);
case grain_type::STOPCHILDSOUND:
return std::make_unique<SFXGrain_StopChildSound>(std::forward<Args>(args)...);
case grain_type::PLUGIN_MESSAGE:
return std::make_unique<SFXGrain_PluginMessage>(std::forward<Args>(args)...);
case grain_type::BRANCH:
return std::make_unique<SFXGrain_Branch>(std::forward<Args>(args)...);
case grain_type::CONTROL_NULL:
return std::make_unique<SFXGrain_ControlNull>(std::forward<Args>(args)...);
case grain_type::LOOP_START:
return std::make_unique<SFXGrain_LoopStart>(std::forward<Args>(args)...);
case grain_type::LOOP_END:
return std::make_unique<SFXGrain_LoopEnd>(std::forward<Args>(args)...);
case grain_type::LOOP_CONTINUE:
return std::make_unique<SFXGrain_LoopContinue>(std::forward<Args>(args)...);
case grain_type::STOP:
return std::make_unique<SFXGrain_Stop>(std::forward<Args>(args)...);
case grain_type::RAND_PLAY:
return std::make_unique<SFXGrain_RandPlay>(std::forward<Args>(args)...);
case grain_type::RAND_DELAY:
return std::make_unique<SFXGrain_RandDelay>(std::forward<Args>(args)...);
case grain_type::RAND_PB:
return std::make_unique<SFXGrain_RandPB>(std::forward<Args>(args)...);
case grain_type::PB:
return std::make_unique<SFXGrain_PB>(std::forward<Args>(args)...);
case grain_type::ADD_PB:
return std::make_unique<SFXGrain_AddPB>(std::forward<Args>(args)...);
case grain_type::SET_REGISTER:
return std::make_unique<SFXGrain_SetRegister>(std::forward<Args>(args)...);
case grain_type::SET_REGISTER_RAND:
return std::make_unique<SFXGrain_SetRegisterRand>(std::forward<Args>(args)...);
case grain_type::INC_REGISTER:
return std::make_unique<SFXGrain_IncRegister>(std::forward<Args>(args)...);
case grain_type::DEC_REGISTER:
return std::make_unique<SFXGrain_DecRegister>(std::forward<Args>(args)...);
case grain_type::TEST_REGISTER:
return std::make_unique<SFXGrain_TestRegister>(std::forward<Args>(args)...);
case grain_type::MARKER:
return std::make_unique<SFXGrain_Marker>(std::forward<Args>(args)...);
case grain_type::GOTO_MARKER:
return std::make_unique<SFXGrain_GotoMarker>(std::forward<Args>(args)...);
case grain_type::GOTO_RANDOM_MARKER:
return std::make_unique<SFXGrain_GotoRandomMarker>(std::forward<Args>(args)...);
case grain_type::WAIT_FOR_ALL_VOICES:
return std::make_unique<SFXGrain_WaitForAllVoices>(std::forward<Args>(args)...);
case grain_type::PLAY_CYCLE:
return std::make_unique<SFXGrain_PlayCycle>(std::forward<Args>(args)...);
case grain_type::ADD_REGISTER:
return std::make_unique<SFXGrain_AddRegister>(std::forward<Args>(args)...);
case grain_type::KEY_OFF_VOICES:
return std::make_unique<SFXGrain_KeyOffVoices>(std::forward<Args>(args)...);
case grain_type::KILL_VOICES:
return std::make_unique<SFXGrain_KillVoices>(std::forward<Args>(args)...);
case grain_type::ON_STOP_MARKER:
return std::make_unique<SFXGrain_OnStopMarker>(std::forward<Args>(args)...);
case grain_type::COPY_REGISTER:
return std::make_unique<SFXGrain_CopyRegister>(std::forward<Args>(args)...);
default:
throw std::runtime_error(fmt::format("Unknown grain type {}", id));
}
return nullptr;
}
} // namespace snd

View File

@ -1,3 +1,12 @@
#include <filesystem>
#include <iostream>
#include <sstream>
#ifdef _WIN32
#include <windows.h>
#define sleep(n) Sleep(n * 1000)
#endif
#include "player.h" #include "player.h"
#include "common/log/log.h" #include "common/log/log.h"
@ -7,22 +16,65 @@ int main(int argc, char* argv[]) {
unsigned bankid = 0; unsigned bankid = 0;
fs::path file = argv[1]; fs::path file = argv[1];
bankid = player.load_bank(file, 0);
if (argc > 2) { if (argc > 2) {
bankid = player.load_bank(file, 0);
unsigned sound = player.play_sound(bankid, atoi(argv[2]), 0x400, 0, 0, 0); unsigned sound = player.play_sound(bankid, atoi(argv[2]), 0x400, 0, 0, 0);
lg::info("sound {} started", sound); lg::info("sound {} started", sound);
} }
printf("commands:\n");
printf(" play [id]\n");
printf(" stop\n");
while (true) { while (true) {
#ifdef __linux printf("> ");
timespec rqtp{}, rmtp{}; std::string command;
rqtp.tv_nsec = 0; std::getline(std::cin, command);
rqtp.tv_sec = 1;
if (nanosleep(&rqtp, &rmtp) == -1) { std::stringstream ss(command);
break; std::string tmp;
std::vector<std::string> parts;
while (std::getline(ss, tmp, ' ')) {
parts.push_back(tmp);
}
if (parts[0] == "play") {
if (parts.size() < 2) {
printf("invalid args\n");
} else {
auto id = player.play_sound(bankid, std::atoi(parts[1].c_str()), 0x400, 0, 0, 0);
printf("sound handle %d started\n", id);
}
}
if (parts[0] == "playall") {
auto idx = 0;
auto id = player.play_sound(bankid, idx, 0x400, 0, 0, 0);
while (true) {
if (player.sound_still_active(id)) {
sleep(1);
} else {
idx++;
id = player.play_sound(bankid, idx, 0x400, 0, 0, 0);
}
}
}
if (parts[0] == "setreg") {
if (parts.size() < 3) {
printf("invalid args\n");
} else {
player.set_sound_reg(std::atoi(parts[1].c_str()), std::atoi(parts[2].c_str()),
std::atoi(parts[3].c_str()));
}
}
if (parts[0] == "stop") {
printf("stopping all sounds\n");
player.stop_all_sounds();
} }
#endif
} }
return 0; return 0;

View File

@ -4,16 +4,18 @@
#include "common/common_types.h" #include "common/common_types.h"
namespace snd {
static constexpr int PAN_RESET = -1; static constexpr int PAN_RESET = -1;
static constexpr int PAN_DONT_CHANGE = -2; static constexpr int PAN_DONT_CHANGE = -2;
static constexpr int VOLUME_DONT_CHANGE = 0x7fffffff; static constexpr int VOLUME_DONT_CHANGE = 0x7fffffff;
namespace snd { class SoundBank;
class sound_handler { class sound_handler {
public: public:
virtual ~sound_handler() = default; virtual ~sound_handler() = default;
virtual bool tick() = 0; virtual bool tick() = 0;
virtual u32 bank() = 0; virtual SoundBank& bank() = 0;
virtual void pause() = 0; virtual void pause() = 0;
virtual void unpause() = 0; virtual void unpause() = 0;
virtual u8 group() = 0; virtual u8 group() = 0;

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <optional>
#include "locator.h" #include "locator.h"
#include "sound_handler.h" #include "sound_handler.h"
@ -10,6 +11,15 @@
#include "../common/synth.h" #include "../common/synth.h"
namespace snd { namespace snd {
struct SndPlayParams {
std::optional<s32> vol;
std::optional<s32> pan;
std::optional<s32> pitch_mod;
std::optional<s32> pitch_bend;
std::optional<std::array<s8, 4>> registers;
};
struct BankTag { struct BankTag {
/* 0 */ u32 DataID; /* 0 */ u32 DataID;
/* 4 */ u32 Version; /* 4 */ u32 Version;
@ -22,17 +32,40 @@ enum class BankType {
SFX, SFX,
}; };
struct SFXUserData;
class SoundBank { class SoundBank {
public: public:
SoundBank(u32 id, BankType type) : type(type), bank_id(id){};
virtual ~SoundBank() = default; virtual ~SoundBank() = default;
BankType type;
virtual std::unique_ptr<sound_handler> make_handler(voice_manager& vm, virtual std::unique_ptr<sound_handler> make_handler(voice_manager& vm,
u32 sound_id, u32 sound_id,
s32 vol, s32 vol,
s32 pan, s32 pan,
s32 pm, s32 pm,
s32 pb) = 0; s32 pb) {
SndPlayParams params{};
params.vol = vol;
params.pan = pan;
params.pitch_mod = pm;
params.pitch_bend = pb;
return make_handler(vm, sound_id, -1, -1, params);
};
virtual std::unique_ptr<sound_handler> make_handler(voice_manager& vm,
u32 sound_id,
s32 vol,
s32 pan,
SndPlayParams& params) = 0;
virtual std::optional<std::string_view> get_name() { return std::nullopt; };
virtual std::optional<u32> get_sound_by_name(const char* name) { return std::nullopt; };
virtual std::optional<const SFXUserData*> get_sound_user_data(u32 sound_id) {
return std::nullopt;
};
BankType type;
u32 bank_id; u32 bank_id;
u32 bank_name; u32 bank_name;
std::unique_ptr<u8[]> sampleBuf; std::unique_ptr<u8[]> sampleBuf;

View File

@ -129,12 +129,14 @@ std::pair<s16, s16> pitchbend(Tone& tone,
int current_pm, int current_pm,
int start_note, int start_note,
int start_fine) { int start_fine) {
auto v9 = (start_note << 7) + start_fine + current_pm; s32 v9 = (start_note << 7) + start_fine + current_pm;
u32 v7;
s32 v7;
if (current_pb >= 0) if (current_pb >= 0)
v7 = tone.PBHigh * (current_pb << 7) / 0x7fff + v9; v7 = tone.PBHigh * (current_pb << 7) / 0x7fff + v9;
else else
v7 = tone.PBLow * (current_pb << 7) / 0x7fff + v9; v7 = tone.PBLow * (current_pb << 7) / 0x8000 + v9;
return {v7 / 128, v7 % 128}; return {v7 / 128, v7 % 128};
} }

View File

@ -15,7 +15,7 @@ voice_manager::voice_manager(synth& synth, locator& loc) : m_synth(synth), m_loc
m_group_duck.fill(0x10000); m_group_duck.fill(0x10000);
} }
void voice_manager::start_tone(std::shared_ptr<vag_voice> voice) { void voice_manager::start_tone(std::shared_ptr<vag_voice> voice, u32 bank) {
s16 left = adjust_vol_to_group(voice->basevol.left, voice->group); s16 left = adjust_vol_to_group(voice->basevol.left, voice->group);
s16 right = adjust_vol_to_group(voice->basevol.right, voice->group); s16 right = adjust_vol_to_group(voice->basevol.right, voice->group);
@ -35,7 +35,7 @@ void voice_manager::start_tone(std::shared_ptr<vag_voice> voice) {
voice->set_asdr1(voice->tone.ADSR1); voice->set_asdr1(voice->tone.ADSR1);
voice->set_asdr2(voice->tone.ADSR2); voice->set_asdr2(voice->tone.ADSR2);
u8* sbuf = m_locator.get_bank_samples(voice->tone.BankID); u8* sbuf = m_locator.get_bank_samples(bank);
voice->set_sample((u16*)(sbuf + voice->tone.VAGInSR)); voice->set_sample((u16*)(sbuf + voice->tone.VAGInSR));
voice->key_on(); voice->key_on();

View File

@ -32,11 +32,7 @@ struct Tone {
/* c */ s16 ADSR2; /* c */ s16 ADSR2;
/* e */ s16 Flags; /* e */ s16 Flags;
/* 10 */ /*void**/ u32 VAGInSR; /* 10 */ /*void**/ u32 VAGInSR;
///* 14 */ u32 reserved1; confiscated /* 14 */ u32 reserved1;
// FIXME I'd rather restructure things than mess about like this.
// If we have to edit the structs they should't be loaded like this
/* 14 */ u32 BankID;
}; };
class vag_voice : public voice { class vag_voice : public voice {
@ -55,7 +51,7 @@ class vag_voice : public voice {
class voice_manager { class voice_manager {
public: public:
voice_manager(synth& synth, locator& loc); voice_manager(synth& synth, locator& loc);
void start_tone(std::shared_ptr<vag_voice> voice); void start_tone(std::shared_ptr<vag_voice> voice, u32 bank);
void pause(std::shared_ptr<vag_voice> voice); void pause(std::shared_ptr<vag_voice> voice);
void unpause(std::shared_ptr<vag_voice> voice); void unpause(std::shared_ptr<vag_voice> voice);
void set_pan_table(vol_pair* table) { m_pan_table = table; }; void set_pan_table(vol_pair* table) { m_pan_table = table; };

View File

@ -7,8 +7,11 @@ set(SOUND_SOURCES
989snd/blocksound_handler.cpp 989snd/blocksound_handler.cpp
989snd/musicbank.cpp 989snd/musicbank.cpp
989snd/sfxblock.cpp 989snd/sfxblock.cpp
989snd/sfxblock2.cpp
989snd/sfxgrain.cpp
989snd/loader.cpp 989snd/loader.cpp
989snd/vagvoice.cpp 989snd/vagvoice.cpp
989snd/lfo.cpp
989snd/util.cpp 989snd/util.cpp
common/synth.cpp common/synth.cpp
common/voice.cpp common/voice.cpp
@ -35,4 +38,4 @@ if (NOT WIN32)
-Wno-unused-parameter -Wno-unused-parameter
-Wno-shadow -Wno-shadow
) )
endif() endif()

View File

@ -41,31 +41,32 @@ void snd_RegisterIOPMemAllocator(AllocFun, FreeFun) {
// printf("snd_RegisterIOPMemAllocator\n"); // printf("snd_RegisterIOPMemAllocator\n");
} }
void snd_LockVoiceAllocator(s32) { int snd_LockVoiceAllocator(bool block) {
// printf("snd_LockVoiceAllocator\n"); // printf("snd_LockVoiceAllocator\n");
return 0;
} }
void snd_UnlockVoiceAllocator() { void snd_UnlockVoiceAllocator() {
// printf("snd_UnlockVoiceAllocator\n"); // printf("snd_UnlockVoiceAllocator\n");
} }
s32 snd_ExternVoiceVoiceAlloc(s32, s32) { s32 snd_ExternVoiceAlloc(s32 vol_group, s32 priority) {
// printf("snd_ExternVoiceVoiceAlloc\n"); // printf("snd_ExternVoiceVoiceAlloc\n");
return 0; return 0;
} }
u32 snd_SRAMMalloc(u32) { u32 snd_SRAMMalloc(u32 size) {
// spu memory currently hardcoded // spu memory currently hardcoded
return 0; return 0;
} }
void snd_SetMixerMode(s32, s32) {} void snd_SetMixerMode(s32 channel_mode, s32 reverb_mode) {}
void snd_SetGroupVoiceRange(s32, s32, s32) {} void snd_SetGroupVoiceRange(s32 group, s32 min, s32 max) {}
void snd_SetReverbDepth(s32, s32, s32) {} void snd_SetReverbDepth(s32 core, s32 left, s32 right) {}
void snd_SetReverbType(s32, s32) {} void snd_SetReverbType(s32 core, s32 type) {}
void snd_SetPanTable(s16* table) { void snd_SetPanTable(s16* table) {
if (player) { if (player) {
@ -89,9 +90,9 @@ s32 snd_SoundIsStillPlaying(s32 sound_handle) {
return 0; return 0;
} }
void snd_StopSound(s32 handle) { void snd_StopSound(s32 sound_handle) {
if (player) { if (player) {
player->stop_sound(handle); player->stop_sound(sound_handle);
} }
} }
@ -101,9 +102,9 @@ void snd_SetSoundVolPan(s32 sound_handle, s32 vol, s32 pan) {
} }
} }
void snd_SetMasterVolume(s32 group, s32 volume) { void snd_SetMasterVolume(s32 which, s32 volume) {
if (player) { if (player) {
player->set_master_volume(group, volume); player->set_master_volume(which, volume);
} }
} }
@ -117,39 +118,55 @@ void snd_ResolveBankXREFS() {
// Currently no-op, idk if we'd ever need it // Currently no-op, idk if we'd ever need it
} }
void snd_ContinueAllSoundsInGroup(u8 group) { void snd_ContinueAllSoundsInGroup(u8 groups) {
if (player) { if (player) {
player->continue_all_sounds_in_group(group); player->continue_all_sounds_in_group(groups);
} }
} }
void snd_PauseAllSoundsInGroup(u8 group) { void snd_PauseAllSoundsInGroup(u8 groups) {
if (player) { if (player) {
player->pause_all_sounds_in_group(group); player->pause_all_sounds_in_group(groups);
} }
} }
void snd_SetMIDIRegister(s32 sound_handle, u8 reg, u8 value) { void snd_SetMIDIRegister(s32 sound_handle, u8 reg, u8 value) {
if (player) { if (player) {
player->set_midi_reg(sound_handle, reg, value); player->set_sound_reg(sound_handle, reg, value);
} }
} }
s32 snd_PlaySoundVolPanPMPB(s32 bank, s32 sound, s32 vol, s32 pan, s32 pm, s32 pb) { s32 snd_PlaySoundVolPanPMPB(s32 bank, s32 sound, s32 vol, s32 pan, s32 pitch_mod, s32 pitch_bend) {
if (player) { if (player) {
return player->play_sound(bank, sound, vol, pan, pm, pb); return player->play_sound(bank, sound, vol, pan, pitch_mod, pitch_bend);
} else { } else {
return 0; return 0;
} }
} }
void snd_SetSoundPitchModifier(s32 sound, s32 mod) { s32 snd_PlaySoundByNameVolPanPMPB(s32 bank_handle,
char* bank_name,
char* sound_name,
s32 vol,
s32 pan,
s32 pitch_mod,
s32 pitch_bend) {
if (player) { if (player) {
player->set_sound_pmod(sound, mod); return player->play_sound_by_name(bank_handle, bank_name, sound_name, vol, pan, pitch_mod,
pitch_bend);
} else {
return 0;
} }
} }
void snd_SetSoundPitchBend(s32 sound, s32 bend) { void snd_SetSoundPitchModifier(s32 sound_handle, s32 pitch_mod) {
if (player) {
player->set_sound_pmod(sound_handle, pitch_mod);
}
}
void snd_SetSoundPitchBend(s32 sound_handle, s32 bend) {
// TODO
if (bend != 0) { if (bend != 0) {
} }
} }
@ -166,16 +183,16 @@ void snd_ContinueSound(s32 sound_handle) {
} }
} }
void snd_AutoPitch(s32, s32, s32, s32) { void snd_AutoPitch(s32 sound_handle, s32 pitch, s32 delta_time, s32 delta_from) {
// TODO // TODO
printf("snd_AutoPitch\n"); lg::warn("Unimplemented snd_AutoPitch\n");
} }
void snd_AutoPitchBend(s32, s32, s32, s32) { void snd_AutoPitchBend(s32 sound_handle, s32 pitch, s32 delta_time, s32 delta_from) {
// TODO // TODO
printf("snd_AutoPitchBend\n"); lg::warn("Unimplemented snd_AutoPitchBend\n");
} }
s32 snd_BankLoadEx(const char* filename, s32 offset, s32, s32) { s32 snd_BankLoadEx(const char* filename, s32 offset, u32 spu_mem_loc, u32 spu_mem_size) {
// printf("snd_BankLoadEx\n"); // printf("snd_BankLoadEx\n");
if (player) { if (player) {
fs::path path = filename; fs::path path = filename;
@ -205,3 +222,26 @@ void snd_keyOffVoiceRaw(u32 core, u32 voice_id) {
voice->key_off(); voice->key_off();
} }
} }
s32 snd_GetSoundUserData(s32 block_handle,
char* block_name,
s32 sound_id,
char* sound_name,
SFXUserData* dst) {
if (player) {
return player->get_sound_user_data(block_handle, block_name, sound_id, sound_name,
(snd::SFXUserData*)dst);
}
return 0;
}
void snd_SetSoundReg(s32 sound_handle, s32 which, u8 val) {
if (player) {
player->set_sound_reg(sound_handle, which, val);
}
}
void snd_SetGlobalExcite(u8 value) {
// TODO
lg::warn("Unimplemented snd_SetGlobalExcite\n");
}

View File

@ -7,44 +7,70 @@ constexpr int SND_CORE_0 = 1;
constexpr int SND_CORE_1 = 2; constexpr int SND_CORE_1 = 2;
constexpr int SD_REV_MODE_OFF = 0; constexpr int SD_REV_MODE_OFF = 0;
struct SFXUserData {
u32 data[4];
};
typedef void* (*AllocFun)(); typedef void* (*AllocFun)();
typedef void (*FreeFun)(void*); typedef void (*FreeFun)(void*);
void snd_StartSoundSystem(); void snd_StartSoundSystem();
void snd_StopSoundSystem(); void snd_StopSoundSystem();
s32 snd_GetTick(); s32 snd_GetTick();
void snd_RegisterIOPMemAllocator(AllocFun, FreeFun); void snd_RegisterIOPMemAllocator(AllocFun alloc, FreeFun free);
void snd_LockVoiceAllocator(s32); int snd_LockVoiceAllocator(bool block);
void snd_UnlockVoiceAllocator(); void snd_UnlockVoiceAllocator();
s32 snd_ExternVoiceVoiceAlloc(s32, s32); s32 snd_ExternVoiceAlloc(s32 vol_group, s32 priority);
u32 snd_SRAMMalloc(u32); u32 snd_SRAMMalloc(u32 size);
void snd_SetMixerMode(s32, s32); void snd_SetMixerMode(s32 channel_mode, s32 reverb_mode);
void snd_SetGroupVoiceRange(s32, s32, s32); void snd_SetGroupVoiceRange(s32 group, s32 min, s32 max);
void snd_SetReverbDepth(s32, s32, s32); void snd_SetReverbDepth(s32 core, s32 left, s32 right);
void snd_SetReverbType(s32, s32); void snd_SetReverbType(s32 core, s32 type);
void snd_SetPanTable(s16*); void snd_SetPanTable(s16* table);
void snd_SetPlayBackMode(s32); void snd_SetPlayBackMode(s32 mode);
s32 snd_SoundIsStillPlaying(s32); s32 snd_SoundIsStillPlaying(s32 sound_handle);
void snd_StopSound(s32); void snd_StopSound(s32 sound_handle);
void snd_SetSoundVolPan(s32, s32, s32); void snd_SetSoundVolPan(s32 sound_handle, s32 vol, s32 pan);
void snd_SetMasterVolume(s32, s32); void snd_SetMasterVolume(s32 which, s32 volume);
void snd_UnloadBank(s32); void snd_UnloadBank(s32 bank_handle);
void snd_ResolveBankXREFS(); void snd_ResolveBankXREFS();
void snd_ContinueAllSoundsInGroup(u8); void snd_ContinueAllSoundsInGroup(u8 groups);
void snd_PauseAllSoundsInGroup(u8); void snd_PauseAllSoundsInGroup(u8 groups);
void snd_SetMIDIRegister(s32, u8, u8); void snd_SetMIDIRegister(s32 handle, u8 reg, u8 value);
s32 snd_PlaySoundVolPanPMPB(s32, s32, s32, s32, s32, s32); void snd_SetGlobalExcite(u8 value);
void snd_SetSoundPitchModifier(s32, s32);
void snd_SetSoundPitchBend(s32, s32); s32 snd_PlaySoundVolPanPMPB(s32 bank_handle,
void snd_PauseSound(s32); s32 sound_id,
void snd_ContinueSound(s32); s32 vol,
void snd_AutoPitch(s32, s32, s32, s32); s32 pan,
void snd_AutoPitchBend(s32, s32, s32, s32); s32 pitch_mod,
s32 snd_BankLoadEx(const char* filepath, s32 data_offset, s32 unk1, s32 unk2); s32 pitch_bend);
s32 snd_PlaySoundByNameVolPanPMPB(s32 bank_handle,
char* bank_name,
char* sound_name,
s32 vol,
s32 pan,
s32 pitch_mod,
s32 pitch_bend);
void snd_SetSoundPitchModifier(s32 sound_handle, s32 pitch_mod);
void snd_SetSoundPitchBend(s32 sound_handle, s32 pitch_bend);
void snd_PauseSound(s32 sound_handle);
void snd_ContinueSound(s32 sound_handle);
void snd_AutoPitch(s32 sound_handle, s32 pitch, s32 delta_time, s32 delta_from);
void snd_AutoPitchBend(s32 sound_handle, s32 bend, s32 delta_time, s32 delta_from);
s32 snd_BankLoadEx(const char* filepath, s32 data_offset, u32 spu_mem_loc, u32 spu_mem_size);
s32 snd_GetVoiceStatus(s32 voice); s32 snd_GetVoiceStatus(s32 voice);
s32 snd_GetFreeSPUDMA(); s32 snd_GetFreeSPUDMA();
void snd_FreeSPUDMA(s32 channel); void snd_FreeSPUDMA(s32 channel);
void snd_keyOnVoiceRaw(u32, u32); void snd_keyOnVoiceRaw(u32 core, u32 voice);
void snd_keyOffVoiceRaw(u32, u32); void snd_keyOffVoiceRaw(u32 core, u32 voice);
s32 snd_GetSoundUserData(s32 block_handle,
char* block_name,
s32 sound_id,
char* sound_name,
SFXUserData* dst);
void snd_SetSoundReg(s32 sound_handle, s32 which, u8 val);
#endif // SNDSHIM_H_ #endif // SNDSHIM_H_

View File

@ -644,7 +644,7 @@
; ) ; )
;; send sound commands to IOP ;; send sound commands to IOP
; (swap-sound-buffers (ear-trans 0) (ear-trans 1) (camera-pos) (camera-angle)) (swap-sound-buffers (ear-trans 0) (ear-trans 1) (camera-pos) (camera-angle))
;; advance streaming animation ;; advance streaming animation
; (str-play-kick) ; (str-play-kick)

View File

@ -935,9 +935,6 @@
0 0
) )
;; temporarily removed, will crash on startup
(format 0 "HACK: skipping check-irx-version and common soundbank load.~%")
#|
(check-irx-version) (check-irx-version)
(case (scf-get-territory) (case (scf-get-territory)
@ -958,7 +955,6 @@
) )
) )
) )
|#

View File

@ -166,6 +166,24 @@
:dep stuff) :dep stuff)
) )
(defun copy-iso-file (name subdir ext)
(let* ((path (string-append "$ISO/" subdir name ext))
(out-name (string-append "$OUT/iso/" name ext)))
(defstep :in path
:tool 'copy
:out `(,out-name))
out-name))
(defmacro copy-sbk-files (&rest files)
`(begin ,@(apply (lambda (x) `(set! *all-sbk* (cons (copy-iso-file ,x "SBK/" ".SBK") *all-sbk*))) files)))
(defmacro copy-mus-files (&rest files)
`(begin ,@(apply (lambda (x) `(set! *all-mus* (cons (copy-iso-file ,x "MUS/" ".MUS") *all-mus*))) files)))
(defmacro copy-vag-files (&rest files)
`(begin ,@(apply (lambda (x) `(set! *all-vag* (cons (copy-iso-file "VAGWAD" "VAG/" (string-append "." ,x)) *all-vag*))) files)))
;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;
;; GOAL Kernel ;; GOAL Kernel
;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;
@ -664,6 +682,159 @@
"blocking-plane-ag" "blocking-plane-ag"
) )
(copy-sbk-files "ASHTAN1"
"ASHTAN2"
"ATOLL1"
"ATOLL2"
"ATOLL3"
"ATOLL4"
"BBUSH1"
"BOARD"
"BOMBBOT1"
"CASBOSS1"
"CASBOSS2"
"CASBOSS3"
"CASTLE1"
"CASTLE2"
"CASTLE3"
"COMMON"
"COMMONJ"
"CONSITE1"
"CONSITE2"
"CONSITE3"
"CTYFARM1"
"CTYWIDE1"
"CTYWIDE2"
"CTYWIDE3"
"CTYWIDE4"
"CTYWIDE5"
"DEMO1"
"DIG1"
"DIG2"
"DIG3"
"DIG4"
"DIG5"
"DIG6"
"DIG7"
"DIG8"
"DRILL1"
"DRILL2"
"DRILL3"
"DRILL4"
"DRILL5"
"DRILL6"
"DRILL7"
"DRILL8"
"EMPTY0"
"EMPTY1"
"EMPTY2"
"ERLCHAL1"
"ESCKID1"
"FORDUMP1"
"FORDUMP2"
"FOREST1"
"FOREST2"
"FOREST3"
"FOREST4"
"FOREST5"
"FOREXIT1"
"FOREXIT2"
"FORRESC1"
"FORRESC2"
"GUN"
"GUNGAME1"
"HELLDOG1"
"HIDEOUT1"
"HIPHOG1"
"INTRO1"
"INTRO2"
"INTRO3"
"MECH"
"MECHWAT"
"MEETBRT1"
"MENU1"
"MOUNT1"
"MOUNT2"
"MOUNT3"
"NEST1"
"NEST2"
"NEST3"
"NEST4"
"NEST5"
"NEST6"
"ONIN1"
"ONIN2"
"ORACLE1"
"OUTRO1"
"PALCAB1"
"PALCAB2"
"PALCAB3"
"PALENT1"
"PALENT2"
"PALENT3"
"PALROOF1"
"PALROOF2"
"PALROOF3"
"PORTRUN1"
"PROTECT1"
"RUINS1"
"RUINS2"
"RUINS3"
"SACK1"
"SEWER1"
"SEWER2"
"SEWER3"
"SEWER4"
"SEWER5"
"SEWER6"
"SKATE1"
"STADIUM1"
"STRIP1"
"STRIP2"
"STRIP3"
"TOMB1"
"TOMB2"
"TOMB3"
"TOMB4"
"TOMB5"
"TOMB6"
"TOMB7"
"TOMB8"
"TOMB9"
"UNDER1"
"UNDER2"
"UNDER3"
"UNDER4"
"UNDER5"
"VINROOM1"
)
(copy-mus-files "ATOLL"
"BATTLE"
"CITY1"
"CREDITS"
"DANGER"
"DANGER1"
"DANGER2"
"DANGER3"
"DANGER4"
"DANGER6"
"DANGER7"
"DANGER9"
"DANGER10"
"DANGER11"
"DIG"
"FOREST"
"FORTRESS"
"MOUNTAIN"
"PALCAB"
"RACE"
"RUINS"
"SEWER"
"STRIP"
"TOMB"
"TWEAKVAL")
;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;
;; Text ;; Text
;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;

1387
third-party/magic_enum.hpp generated vendored Normal file

File diff suppressed because it is too large Load Diff