mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-23 06:09:57 +00:00
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:
parent
80e9528e4e
commit
5b99929394
@ -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_LoadSoundBank(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 LoadMusicTweaks();
|
||||
|
||||
@ -84,10 +88,16 @@ void fake_iso_init_globals() {
|
||||
fake_iso.close = FS_Close;
|
||||
fake_iso.begin_read = FS_BeginRead;
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -361,6 +371,56 @@ uint32_t FS_LoadSoundBank(char* name, void* buffer) {
|
||||
snd_ResolveBankXREFS();
|
||||
PrintBankInfo(bank);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -4,25 +4,67 @@
|
||||
|
||||
#include "soundcommon.h"
|
||||
|
||||
constexpr int N_BANKS = 3;
|
||||
SoundBank* gBanks[N_BANKS];
|
||||
#include "common/log/log.h"
|
||||
|
||||
#include "game/runtime.h"
|
||||
|
||||
static constexpr int N_BANKS = 6;
|
||||
|
||||
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() {
|
||||
gBanks[0] = &gCommonBank;
|
||||
gBanks[1] = &gLevelBank[0];
|
||||
gBanks[2] = &gLevelBank[1];
|
||||
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() {
|
||||
for (auto& gBank : gBanks) {
|
||||
gBank->bank_handle = 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>");
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -50,25 +92,34 @@ SoundBank* AllocateBank() {
|
||||
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) {
|
||||
int idx = 0;
|
||||
while (true) {
|
||||
if (idx > N_BANKS - 1) {
|
||||
return -1;
|
||||
for (auto bank : gBanks) {
|
||||
if (!bank->bank_handle) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& bank = gBanks[idx];
|
||||
if (bank->bank_handle == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)bank->sound_count; i++) {
|
||||
for (int i = 0; i < bank->sound_count; i++) {
|
||||
if (memcmp(bank->sound[i].name, name, 16) == 0) {
|
||||
*bank_out = bank;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
@ -84,7 +135,7 @@ SoundBank* LookupBank(const char* name) {
|
||||
auto& bank = gBanks[idx];
|
||||
// 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.
|
||||
if (memcmp(bank->name, name, 16) == 0) {
|
||||
if ((memcmp(bank->name, name, 16) == 0) && bank->in_use) {
|
||||
return bank;
|
||||
}
|
||||
idx--;
|
||||
|
@ -11,10 +11,20 @@ struct SoundBank {
|
||||
char name[16];
|
||||
u32 bank_handle;
|
||||
u32 sound_count;
|
||||
|
||||
union {
|
||||
SoundRecord sound[1];
|
||||
|
||||
// Needs to fit the biggest bank (common.sbk)
|
||||
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();
|
||||
|
||||
SoundBank* AllocateBank();
|
||||
SoundBank* AllocateBankName(const char* name);
|
||||
s32 LookupSoundIndex(const char* name, SoundBank** bank_out);
|
||||
SoundBank* LookupBank(const char* name);
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "soundcommon.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
@ -16,6 +18,17 @@ void ReadBankSoundInfo(SoundBank* bank, SoundBank* unk, s32 unk2) {
|
||||
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) {
|
||||
// we dont need this and it spams the console too much
|
||||
return;
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "game/overlord/sbank.h"
|
||||
|
||||
void strcpy_toupper(char* dest, const char* source);
|
||||
void PrintBankInfo(SoundBank* buffer);
|
||||
void ReadBankSoundInfo(SoundBank* bank, SoundBank* unk, s32 unk2);
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "ramdisk.h"
|
||||
#include "sbank.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
#include "common/versions.h"
|
||||
|
||||
@ -15,11 +16,13 @@
|
||||
#include "game/common/loader_rpc_types.h"
|
||||
#include "game/common/player_rpc_types.h"
|
||||
#include "game/graphics/gfx.h"
|
||||
#include "game/overlord/soundcommon.h"
|
||||
#include "game/runtime.h"
|
||||
#include "game/sce/iop.h"
|
||||
#include "game/sound/sndshim.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "third-party/magic_enum.hpp"
|
||||
|
||||
using namespace iop;
|
||||
|
||||
@ -35,6 +38,7 @@ s32 gMusicTweak = 0x80;
|
||||
s32 gMusicPause = 0;
|
||||
u32 gFreeMem = 0;
|
||||
u32 gFrameNum = 0;
|
||||
u8 gFPS = 60;
|
||||
|
||||
// added
|
||||
u32 gMusicFadeHack = 0;
|
||||
@ -57,6 +61,8 @@ void srpc_init_globals() {
|
||||
}
|
||||
|
||||
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() {
|
||||
sceSifQueueData dq;
|
||||
@ -66,14 +72,17 @@ u32 Thread_Player() {
|
||||
CpuDisableIntr();
|
||||
sceSifInitRpc(0);
|
||||
sceSifSetRpcQueue(&dq, GetThreadId());
|
||||
sceSifRegisterRpc(&serve, PLAYER_RPC_ID[g_game_version], RPC_Player, gPlayerBuf, nullptr, nullptr,
|
||||
&dq);
|
||||
sceSifRegisterRpc(&serve, PLAYER_RPC_ID[g_game_version], RPC_Player_Func[g_game_version],
|
||||
gPlayerBuf, nullptr, nullptr, &dq);
|
||||
CpuEnableIntr();
|
||||
sceSifRpcLoop(&dq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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() {
|
||||
sceSifQueueData dq;
|
||||
@ -83,18 +92,14 @@ u32 Thread_Loader() {
|
||||
CpuDisableIntr();
|
||||
sceSifInitRpc(0);
|
||||
sceSifSetRpcQueue(&dq, GetThreadId());
|
||||
sceSifRegisterRpc(&serve, LOADER_RPC_ID[g_game_version], RPC_Loader, gLoaderBuf, nullptr, nullptr,
|
||||
&dq);
|
||||
sceSifRegisterRpc(&serve, LOADER_RPC_ID[g_game_version], RPC_Loader_Func[g_game_version],
|
||||
gLoaderBuf, nullptr, nullptr, &dq);
|
||||
CpuEnableIntr();
|
||||
sceSifRpcLoop(&dq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
gFreeMem = QueryTotalFreeMemSize();
|
||||
if (!PollSema(gSema)) {
|
||||
@ -124,8 +129,8 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
|
||||
int n_messages = size / SRPC_MESSAGE_SIZE;
|
||||
SoundRpcCommand* cmd = (SoundRpcCommand*)(data);
|
||||
while (n_messages > 0) {
|
||||
switch (cmd->command) {
|
||||
case SoundCommand::PLAY: {
|
||||
switch (cmd->j1command) {
|
||||
case Jak1SoundCommand::PLAY: {
|
||||
if (cmd->play.sound_id == 0) {
|
||||
break;
|
||||
}
|
||||
@ -217,7 +222,7 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
|
||||
sound->id = cmd->play.sound_id;
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::PAUSE_SOUND: {
|
||||
case Jak1SoundCommand::PAUSE_SOUND: {
|
||||
Sound* sound = LookupSound(cmd->sound_id.sound_id);
|
||||
if (sound != nullptr) {
|
||||
snd_PauseSound(sound->sound_handle);
|
||||
@ -225,7 +230,7 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
|
||||
PauseVAGStream();
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::STOP_SOUND: {
|
||||
case Jak1SoundCommand::STOP_SOUND: {
|
||||
Sound* sound = LookupSound(cmd->sound_id.sound_id);
|
||||
if (sound != nullptr) {
|
||||
snd_StopSound(sound->sound_handle);
|
||||
@ -233,7 +238,7 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
|
||||
StopVAGStream(nullptr, 0);
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::CONTINUE_SOUND: {
|
||||
case Jak1SoundCommand::CONTINUE_SOUND: {
|
||||
Sound* sound = LookupSound(cmd->sound_id.sound_id);
|
||||
if (sound != nullptr) {
|
||||
snd_ContinueSound(sound->sound_handle);
|
||||
@ -241,7 +246,7 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
|
||||
UnpauseVAGStream();
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::SET_PARAM: {
|
||||
case Jak1SoundCommand::SET_PARAM: {
|
||||
Sound* sound = LookupSound(cmd->sound_id.sound_id);
|
||||
u32 mask = cmd->param.parms.mask;
|
||||
if (sound != nullptr) {
|
||||
@ -282,7 +287,7 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
|
||||
SetVAGStreamVolume(cmd->param.parms.volume);
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::SET_MASTER_VOLUME: {
|
||||
case Jak1SoundCommand::SET_MASTER_VOLUME: {
|
||||
u32 group = cmd->master_volume.group.group;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (((group >> i) & 1) != 0) {
|
||||
@ -296,7 +301,7 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::PAUSE_GROUP: {
|
||||
case Jak1SoundCommand::PAUSE_GROUP: {
|
||||
snd_PauseAllSoundsInGroup(cmd->group.group);
|
||||
if ((cmd->group.group & 4) != 0) {
|
||||
PauseVAGStream();
|
||||
@ -305,14 +310,14 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
|
||||
gMusicPause = 1;
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::STOP_GROUP: {
|
||||
case Jak1SoundCommand::STOP_GROUP: {
|
||||
u8 group = cmd->group.group;
|
||||
KillSoundsInGroup(group);
|
||||
if ((group & 4) != 0) {
|
||||
StopVAGStream(nullptr, 0);
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::CONTINUE_GROUP: {
|
||||
case Jak1SoundCommand::CONTINUE_GROUP: {
|
||||
snd_ContinueAllSoundsInGroup(cmd->group.group);
|
||||
if (cmd->group.group & 4) {
|
||||
UnpauseVAGStream();
|
||||
@ -322,10 +327,10 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
|
||||
gMusicPause = 0;
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::SET_FALLOFF_CURVE: {
|
||||
case Jak1SoundCommand::SET_FALLOFF_CURVE: {
|
||||
SetCurve(cmd->fallof_curve.curve, cmd->fallof_curve.falloff, cmd->fallof_curve.ease);
|
||||
} break;
|
||||
case SoundCommand::SET_SOUND_FALLOFF: {
|
||||
case Jak1SoundCommand::SET_SOUND_FALLOFF: {
|
||||
SoundBank* bank;
|
||||
s32 idx = LookupSoundIndex(cmd->fallof.name, &bank);
|
||||
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;
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::SET_FLAVA: {
|
||||
case Jak1SoundCommand::SET_FLAVA: {
|
||||
gFlava = cmd->flava.flava;
|
||||
} break;
|
||||
case SoundCommand::SET_EAR_TRANS: {
|
||||
SetEarTrans(&cmd->ear_trans.ear_trans, &cmd->ear_trans.cam_trans,
|
||||
cmd->ear_trans.cam_angle);
|
||||
case Jak1SoundCommand::SET_EAR_TRANS: {
|
||||
SetEarTrans(&cmd->ear_trans.ear_trans, &cmd->ear_trans.ear_trans,
|
||||
&cmd->ear_trans.cam_trans, cmd->ear_trans.cam_angle);
|
||||
} break;
|
||||
case SoundCommand::SHUTDOWN: {
|
||||
case Jak1SoundCommand::SHUTDOWN: {
|
||||
gSoundEnable = 0;
|
||||
snd_StopSoundSystem();
|
||||
// TODO ShutdownFilingSystem();
|
||||
} break;
|
||||
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;
|
||||
}
|
||||
n_messages--;
|
||||
@ -356,11 +362,300 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
|
||||
if (g_game_version == GameVersion::Jak2) {
|
||||
printf("RPC_Loader skip %d\n", (int)((SoundRpcCommand*)data)->command);
|
||||
void* RPC_Player2(unsigned int /*fno*/, void* data, int size) {
|
||||
if (!gSoundEnable) {
|
||||
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;
|
||||
SoundRpcCommand* cmd = (SoundRpcCommand*)(data);
|
||||
if (gSoundEnable) {
|
||||
@ -369,8 +664,8 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
|
||||
ASSERT(false);
|
||||
}
|
||||
while (n_messages > 0) {
|
||||
switch (cmd->command) {
|
||||
case SoundCommand::LOAD_BANK: {
|
||||
switch (cmd->j1command) {
|
||||
case Jak1SoundCommand::LOAD_BANK: {
|
||||
// see if it's already loaded
|
||||
auto bank = LookupBank(cmd->load_bank.bank_name);
|
||||
if (!bank) {
|
||||
@ -382,7 +677,7 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::UNLOAD_BANK: {
|
||||
case Jak1SoundCommand::UNLOAD_BANK: {
|
||||
SoundBank* bank = LookupBank(cmd->load_bank.bank_name);
|
||||
if (bank != nullptr) {
|
||||
s32 id = bank->bank_handle;
|
||||
@ -391,20 +686,20 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
|
||||
snd_ResolveBankXREFS();
|
||||
}
|
||||
} break;
|
||||
case SoundCommand::GET_IRX_VERSION: {
|
||||
case Jak1SoundCommand::GET_IRX_VERSION: {
|
||||
cmd->irx_version.major = IRX_VERSION_MAJOR;
|
||||
cmd->irx_version.minor = IRX_VERSION_MINOR;
|
||||
gInfoEE = cmd->irx_version.ee_addr;
|
||||
return cmd;
|
||||
} break;
|
||||
case SoundCommand::RELOAD_INFO: {
|
||||
case Jak1SoundCommand::RELOAD_INFO: {
|
||||
ReloadBankInfo();
|
||||
} break;
|
||||
case SoundCommand::SET_LANGUAGE: {
|
||||
case Jak1SoundCommand::SET_LANGUAGE: {
|
||||
gLanguage = languages[cmd->set_language.langauge_id];
|
||||
printf("IOP language: %s\n", gLanguage); // added.
|
||||
} break;
|
||||
case SoundCommand::LOAD_MUSIC: {
|
||||
case Jak1SoundCommand::LOAD_MUSIC: {
|
||||
while (WaitSema(gSema))
|
||||
;
|
||||
if (gMusic) {
|
||||
@ -419,10 +714,10 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
|
||||
LoadMusic(cmd->load_bank.bank_name, &gMusic);
|
||||
SignalSema(gSema);
|
||||
} break;
|
||||
case SoundCommand::LIST_SOUNDS: {
|
||||
case Jak1SoundCommand::LIST_SOUNDS: {
|
||||
PrintActiveSounds();
|
||||
} break;
|
||||
case SoundCommand::UNLOAD_MUSIC: {
|
||||
case Jak1SoundCommand::UNLOAD_MUSIC: {
|
||||
while (WaitSema(gSema))
|
||||
;
|
||||
if (gMusic) {
|
||||
@ -437,7 +732,8 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
|
||||
SignalSema(gSema);
|
||||
} break;
|
||||
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--;
|
||||
cmd++;
|
||||
@ -446,6 +742,104 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
|
||||
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;
|
||||
|
||||
s32 VBlank_Handler(void*) {
|
||||
|
@ -20,7 +20,7 @@ struct MusicTweaks {
|
||||
} MusicTweak[MUSIC_TWEAK_COUNT];
|
||||
};
|
||||
|
||||
enum class SoundCommand : u16 {
|
||||
enum class Jak1SoundCommand : u16 {
|
||||
LOAD_BANK = 0,
|
||||
LOAD_MUSIC = 1,
|
||||
UNLOAD_BANK = 2,
|
||||
@ -46,6 +46,60 @@ enum class SoundCommand : u16 {
|
||||
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 {
|
||||
u32 major;
|
||||
u32 minor;
|
||||
@ -96,6 +150,13 @@ struct SoundRpcSetEarTrans {
|
||||
s32 cam_angle;
|
||||
};
|
||||
|
||||
struct SoundRpc2SetEarTrans {
|
||||
Vec3w ear_trans1;
|
||||
Vec3w ear_trans2;
|
||||
Vec3w cam_trans;
|
||||
s32 cam_angle;
|
||||
};
|
||||
|
||||
struct SoundRpcSetFPSCommand {
|
||||
u8 fps;
|
||||
};
|
||||
@ -123,9 +184,21 @@ struct SoundRpcMasterVolCommand {
|
||||
s32 volume;
|
||||
};
|
||||
|
||||
struct SoundRpcStereoMode {
|
||||
s32 stereo_mode;
|
||||
};
|
||||
|
||||
struct SoundRpcSetMidiReg {
|
||||
s32 reg;
|
||||
s32 value;
|
||||
};
|
||||
|
||||
struct SoundRpcCommand {
|
||||
u16 rsvd1;
|
||||
SoundCommand command;
|
||||
union {
|
||||
Jak1SoundCommand j1command;
|
||||
Jak2SoundCommand j2command;
|
||||
};
|
||||
union {
|
||||
SoundRpcGetIrxVersion irx_version;
|
||||
SoundRpcBankCommand load_bank;
|
||||
@ -134,6 +207,7 @@ struct SoundRpcCommand {
|
||||
SoundRpcSoundIdCommand sound_id;
|
||||
SoundRpcSetFPSCommand fps;
|
||||
SoundRpcSetEarTrans ear_trans;
|
||||
SoundRpc2SetEarTrans ear_trans_j2;
|
||||
SoundRpcSetReverb reverb;
|
||||
SoundRpcSetFallof fallof;
|
||||
SoundRpcSetFallofCurve fallof_curve;
|
||||
@ -141,6 +215,8 @@ struct SoundRpcCommand {
|
||||
SoundRpcSetFlavaCommand flava;
|
||||
SoundRpcMasterVolCommand master_volume;
|
||||
SoundRpcSetParamCommand param;
|
||||
SoundRpcStereoMode stereo_mode;
|
||||
SoundRpcSetMidiReg midi_reg;
|
||||
u8 max_size[0x4C]; // Temporary
|
||||
};
|
||||
};
|
||||
|
@ -7,15 +7,16 @@
|
||||
|
||||
#include "game/overlord/iso.h"
|
||||
#include "game/overlord/srpc.h"
|
||||
#include "game/runtime.h"
|
||||
#include "game/sound/sndshim.h"
|
||||
|
||||
using namespace iop;
|
||||
|
||||
Sound gSounds[64];
|
||||
Curve gCurve[8]; // TODO verify count
|
||||
Curve gCurve[12]; // TODO verify count
|
||||
VolumePair gPanTable[361];
|
||||
|
||||
Vec3w gEarTrans;
|
||||
Vec3w gEarTrans[2];
|
||||
Vec3w gCamTrans;
|
||||
s32 gCamAngle;
|
||||
|
||||
@ -75,18 +76,31 @@ void InitSound_Overlord() {
|
||||
s.id = 0;
|
||||
}
|
||||
|
||||
SetCurve(1, 0, 0);
|
||||
SetCurve(2, 0x1000, 0);
|
||||
SetCurve(3, 0, 0x1000);
|
||||
SetCurve(4, 0x800, 0);
|
||||
SetCurve(5, 0x800, 0x800);
|
||||
SetCurve(6, -0x1000, 0);
|
||||
SetCurve(6, -0x800, 0);
|
||||
if (g_game_version == GameVersion::Jak1) {
|
||||
SetCurve(1, 0, 0);
|
||||
SetCurve(2, 4096, 0);
|
||||
SetCurve(3, 0, 4096);
|
||||
SetCurve(4, 2048, 0);
|
||||
SetCurve(5, 2048, 2048);
|
||||
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_RegisterIOPMemAllocator(SndMemAlloc, SndMemFree);
|
||||
snd_LockVoiceAllocator(1);
|
||||
u32 voice = snd_ExternVoiceVoiceAlloc(2, 0x7f);
|
||||
u32 voice = snd_ExternVoiceAlloc(2, 0x7f);
|
||||
snd_UnlockVoiceAllocator();
|
||||
|
||||
// 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) {
|
||||
if (fo_curve == 0) {
|
||||
return volume;
|
||||
}
|
||||
s32 xdiff = 0;
|
||||
s32 ydiff = 0;
|
||||
s32 zdiff = 0;
|
||||
|
||||
s32 xdiff = gEarTrans.x - pos->x;
|
||||
s32 ydiff = gEarTrans.y - pos->y;
|
||||
s32 zdiff = gEarTrans.z - pos->z;
|
||||
if (g_game_version == GameVersion::Jak1) {
|
||||
if (fo_curve == 0) {
|
||||
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) {
|
||||
xdiff = pos->x - gEarTrans.x;
|
||||
xdiff = -xdiff;
|
||||
}
|
||||
if (ydiff < 0) {
|
||||
ydiff = pos->y - gEarTrans.y;
|
||||
ydiff = -ydiff;
|
||||
}
|
||||
if (zdiff < 0) {
|
||||
zdiff = pos->z - gEarTrans.z;
|
||||
zdiff = -zdiff;
|
||||
}
|
||||
|
||||
s32 min = fo_min << 8;
|
||||
@ -300,7 +348,12 @@ s32 CalculateFallofVolume(Vec3w* pos, s32 volume, s32 fo_curve, s32 fo_min, s32
|
||||
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) {
|
||||
@ -368,8 +421,10 @@ static void UpdateLocation(Sound* sound) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((sound->bank_entry->fallof_params >> 28) == 0) {
|
||||
return;
|
||||
if (g_game_version == GameVersion::Jak1) {
|
||||
if ((sound->bank_entry->fallof_params >> 28) == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
u32 delta = tick - sLastTick;
|
||||
sLastTick = tick;
|
||||
|
||||
gEarTrans = *ear_trans;
|
||||
gEarTrans[0] = *ear_trans1;
|
||||
gEarTrans[1] = *ear_trans2;
|
||||
gCamTrans = *cam_trans;
|
||||
gCamAngle = cam_angle;
|
||||
|
||||
@ -465,14 +521,24 @@ void PrintActiveSounds() {
|
||||
|
||||
for (auto& s : gSounds) {
|
||||
if (s.id != 0 && s.is_music == 0) {
|
||||
u32 len = strlen(s.bank_entry->name);
|
||||
if (len > 16) {
|
||||
len = 16;
|
||||
if (s.bank_entry != nullptr) {
|
||||
u32 len = strlen(s.bank_entry->name);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,9 +36,11 @@ struct SoundParams {
|
||||
s32 volume;
|
||||
Vec3w trans;
|
||||
u8 group;
|
||||
u8 reg[3];
|
||||
};
|
||||
|
||||
struct Sound {
|
||||
char name[16];
|
||||
s32 id;
|
||||
s32 sound_handle;
|
||||
s32 is_music;
|
||||
@ -57,7 +59,7 @@ struct Curve {
|
||||
|
||||
void InitSound_Overlord();
|
||||
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 PrintActiveSounds();
|
||||
void SetMusicVol();
|
||||
|
@ -17,7 +17,7 @@ ame_handler::ame_handler(MultiMIDIBlockHeader* block,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
locator& loc,
|
||||
u32 bank)
|
||||
SoundBank& bank)
|
||||
: m_sound(sound),
|
||||
m_bank(bank),
|
||||
m_header(block),
|
||||
@ -75,7 +75,6 @@ void ame_handler::stop_segment(u32 id) {
|
||||
return;
|
||||
|
||||
m->second->stop();
|
||||
m_midis.erase(id);
|
||||
}
|
||||
|
||||
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) {
|
||||
// TODO
|
||||
for (auto& m : m_midis) {
|
||||
m.second->set_pmod(mod);
|
||||
}
|
||||
}
|
||||
|
||||
#define AME_BEGIN(op) \
|
||||
|
@ -26,9 +26,9 @@ class ame_handler : public sound_handler {
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
locator& loc,
|
||||
u32 bank);
|
||||
SoundBank& bank);
|
||||
bool tick() override;
|
||||
u32 bank() override { return m_bank; };
|
||||
SoundBank& bank() override { return m_bank; };
|
||||
|
||||
void pause() override;
|
||||
void unpause() override;
|
||||
@ -61,7 +61,7 @@ class ame_handler : public sound_handler {
|
||||
std::pair<bool, u8*> run_ame(midi_handler&, u8* stream);
|
||||
|
||||
MIDISound& m_sound;
|
||||
u32 m_bank{0};
|
||||
SoundBank& m_bank;
|
||||
|
||||
MultiMIDIBlockHeader* m_header{nullptr};
|
||||
locator& m_locator;
|
||||
|
@ -5,10 +5,14 @@
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
|
||||
namespace snd {
|
||||
std::array<s8, 32> g_block_reg{};
|
||||
|
||||
void blocksound_handler::init() {
|
||||
m_next_grain = 0;
|
||||
m_countdown = m_sfx.grains[0].Delay;
|
||||
m_countdown = m_sfx.grains[0]->delay();
|
||||
|
||||
// if (m_sfx.d.Flags & 2) {
|
||||
// fmt::print("solo flag\n");
|
||||
@ -16,6 +20,12 @@ void blocksound_handler::init() {
|
||||
// return;
|
||||
// }
|
||||
|
||||
int idx = 0;
|
||||
for (auto& g : m_sfx.grains) {
|
||||
lg::info("grain {}: {}", idx, g->inspect());
|
||||
idx++;
|
||||
}
|
||||
|
||||
while (m_countdown <= 0 && !m_done) {
|
||||
do_grain();
|
||||
}
|
||||
@ -24,9 +34,21 @@ void blocksound_handler::init() {
|
||||
bool blocksound_handler::tick() {
|
||||
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()) {
|
||||
// fmt::print("{}: voices empty\n", (void*)this);
|
||||
return m_done;
|
||||
} else {
|
||||
return false;
|
||||
@ -86,14 +108,10 @@ void blocksound_handler::stop() {
|
||||
void blocksound_handler::set_vol_pan(s32 vol, s32 pan) {
|
||||
if (vol >= 0) {
|
||||
if (vol != VOLUME_DONT_CHANGE) {
|
||||
m_app_volume = (vol * m_sfx.d.Vol) >> 10;
|
||||
m_app_volume = vol;
|
||||
}
|
||||
} else {
|
||||
m_app_volume = -vol;
|
||||
}
|
||||
|
||||
if (m_app_volume >= 128) {
|
||||
m_app_volume = 127;
|
||||
m_app_volume = -1024 * vol / 127;
|
||||
}
|
||||
|
||||
if (pan == PAN_RESET) {
|
||||
@ -102,21 +120,24 @@ void blocksound_handler::set_vol_pan(s32 vol, s32 pan) {
|
||||
m_app_pan = pan;
|
||||
}
|
||||
|
||||
vol = m_app_volume; // + lfo vol
|
||||
// TODO LFO logic here
|
||||
s32 new_vol = ((m_app_volume * m_orig_volume) >> 10) + m_lfo_volume;
|
||||
new_vol = std::clamp(new_vol, 0, 127);
|
||||
|
||||
pan = m_app_pan; // + lfo pan
|
||||
while (pan >= 360) {
|
||||
pan -= 360;
|
||||
s32 new_pan = m_app_pan + m_lfo_pan;
|
||||
while (new_pan >= 360) {
|
||||
new_pan -= 360;
|
||||
}
|
||||
while (new_pan < 0) {
|
||||
new_pan += 360;
|
||||
}
|
||||
|
||||
while (pan < 0) {
|
||||
pan += 360;
|
||||
}
|
||||
if (new_pan != m_cur_pan || new_vol != m_cur_volume) {
|
||||
m_cur_volume = new_vol;
|
||||
m_cur_pan = new_pan;
|
||||
|
||||
if (pan != m_cur_pan || vol != m_cur_volume) {
|
||||
m_cur_volume = vol;
|
||||
m_cur_pan = pan;
|
||||
for (auto& c : m_children) {
|
||||
c->set_vol_pan(m_app_volume * m_orig_volume / 127, pan);
|
||||
}
|
||||
|
||||
for (auto& p : m_voices) {
|
||||
auto voice = p.lock();
|
||||
@ -135,8 +156,8 @@ void blocksound_handler::set_vol_pan(s32 vol, s32 pan) {
|
||||
}
|
||||
|
||||
void blocksound_handler::update_pitch() {
|
||||
m_cur_pm = m_app_pm;
|
||||
m_cur_pb = m_app_pb;
|
||||
m_cur_pm = m_app_pm + m_lfo_pm;
|
||||
m_cur_pb = std::clamp<s32>(m_app_pb + m_lfo_pb, INT16_MIN, INT16_MAX);
|
||||
|
||||
for (auto& p : m_voices) {
|
||||
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 pitch =
|
||||
PS1Note2Pitch(voice->tone.CenterNote, voice->tone.CenterFine, note.first, note.second);
|
||||
|
||||
voice->set_pitch(pitch);
|
||||
}
|
||||
}
|
||||
|
||||
void blocksound_handler::set_pmod(s32 mod) {
|
||||
// TODO update children
|
||||
m_app_pm = mod;
|
||||
update_pitch();
|
||||
}
|
||||
|
||||
void blocksound_handler::set_pbend(s32 bend) {
|
||||
// TODO update children
|
||||
m_app_pb = bend;
|
||||
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() {
|
||||
auto& grain = m_sfx.grains[m_next_grain];
|
||||
|
||||
auto handler = m_grain_handler.find((grain_type)grain.Type);
|
||||
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));
|
||||
}
|
||||
s32 ret = grain->execute(*this);
|
||||
|
||||
if (m_skip_grains) {
|
||||
m_grains_to_play--;
|
||||
@ -228,7 +204,7 @@ void blocksound_handler::do_grain() {
|
||||
return;
|
||||
}
|
||||
|
||||
m_countdown = m_sfx.grains[m_next_grain].Delay;
|
||||
m_countdown = m_sfx.grains[m_next_grain]->delay() + ret;
|
||||
}
|
||||
|
||||
} // namespace snd
|
||||
|
@ -1,44 +1,90 @@
|
||||
#pragma once
|
||||
#include <unordered_map>
|
||||
|
||||
#include "sfxblock.h"
|
||||
#include "sound_handler.h"
|
||||
#include "vagvoice.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/sound/989snd/lfo.h"
|
||||
#include "sfxblock2.h"
|
||||
|
||||
namespace snd {
|
||||
|
||||
extern std::array<s8, 32> g_block_reg;
|
||||
|
||||
class blocksound_handler : public sound_handler {
|
||||
public:
|
||||
blocksound_handler(SFX& sfx, voice_manager& vm, s32 vol, s32 pan, s32 pm, s32 pb, u32 bank_id)
|
||||
: m_sfx(sfx), m_vm(vm), m_bank(bank_id) {
|
||||
vol = (vol * m_sfx.d.Vol) >> 10;
|
||||
if (vol >= 128) {
|
||||
vol = 127;
|
||||
blocksound_handler(SoundBank& bank,
|
||||
SFX2& sfx,
|
||||
voice_manager& vm,
|
||||
s32 sfx_vol,
|
||||
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) {
|
||||
pan = m_sfx.d.Pan;
|
||||
if (params.vol.has_value()) {
|
||||
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_pm = pm;
|
||||
m_cur_pb = pb;
|
||||
m_cur_pb = pitch_bend;
|
||||
m_cur_pm = pitch_mod;
|
||||
|
||||
m_app_volume = vol;
|
||||
m_app_pan = pan;
|
||||
m_app_pm = 0; // why only this one?
|
||||
m_app_pb = pb;
|
||||
m_app_pb = pitch_bend;
|
||||
m_app_pm = pitch_mod;
|
||||
|
||||
m_orig_pan = m_sfx.d.Pan;
|
||||
m_orig_volume = m_sfx.d.Vol;
|
||||
m_lfo_volume = 0;
|
||||
m_lfo_pan = 0;
|
||||
m_lfo_pb = 0;
|
||||
m_lfo_pm = 0;
|
||||
|
||||
m_group = sfx.d.VolGroup;
|
||||
|
||||
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));
|
||||
if (params.registers.has_value()) {
|
||||
m_registers = params.registers.value();
|
||||
}
|
||||
}
|
||||
|
||||
~blocksound_handler() override {
|
||||
@ -51,7 +97,7 @@ class blocksound_handler : public sound_handler {
|
||||
}
|
||||
|
||||
bool tick() override;
|
||||
u32 bank() override { return m_bank; };
|
||||
SoundBank& bank() override { return m_bank; };
|
||||
|
||||
void pause() override;
|
||||
void unpause() override;
|
||||
@ -59,34 +105,15 @@ class blocksound_handler : public sound_handler {
|
||||
u8 group() override { return m_group; };
|
||||
void set_vol_pan(s32 vol, s32 pan) 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 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();
|
||||
|
||||
s32 null(SFXGrain& grain);
|
||||
s32 play_tone(SFXGrain& grain);
|
||||
s32 rand_play(SFXGrain& grain);
|
||||
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};
|
||||
|
||||
u8 m_group{0};
|
||||
@ -96,11 +123,13 @@ class blocksound_handler : public sound_handler {
|
||||
u32 m_grains_to_skip{0};
|
||||
bool m_skip_grains{false};
|
||||
|
||||
SFX& m_sfx;
|
||||
SFX2& m_sfx;
|
||||
voice_manager& m_vm;
|
||||
|
||||
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_pm{0};
|
||||
|
||||
@ -118,13 +147,15 @@ class blocksound_handler : public sound_handler {
|
||||
s32 m_lfo_volume{0};
|
||||
s32 m_lfo_pan{0};
|
||||
s32 m_lfo_pm{0};
|
||||
s32 m_lfo_pb{0};
|
||||
|
||||
u32 m_bank{0};
|
||||
SoundBank& m_bank;
|
||||
|
||||
u8 m_note{60};
|
||||
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
|
||||
|
||||
|
140
game/sound/989snd/lfo.cpp
Normal file
140
game/sound/989snd/lfo.cpp
Normal 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
43
game/sound/989snd/lfo.h
Normal 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_
|
172
game/sound/989snd/lfo_sine.c.inc
Normal file
172
game/sound/989snd/lfo_sine.c.inc
Normal 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}};
|
@ -6,9 +6,11 @@
|
||||
#include <optional>
|
||||
|
||||
#include "midi_handler.h"
|
||||
#include "sfxblock.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
|
||||
#include "sfxblock2.h"
|
||||
#include <third-party/fmt/core.h>
|
||||
|
||||
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)))
|
||||
|
||||
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) {
|
||||
size_t origin = in.tellg();
|
||||
FileAttributes<3> attr;
|
||||
@ -91,27 +28,38 @@ u32 loader::read_bank(std::fstream& in) {
|
||||
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) {
|
||||
// Fix for bugged tooling I assume?
|
||||
attr.where[chunk::bank].size += 4;
|
||||
}
|
||||
*/
|
||||
|
||||
// auto pos = in.tellg();
|
||||
auto bank_buf = std::make_unique<u8[]>(attr.where[chunk::bank].size);
|
||||
in.seekg(origin + attr.where[chunk::bank].offset, std::fstream::beg);
|
||||
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')) {
|
||||
bank_id = read_music_bank((SoundBankData*)bank_buf.get());
|
||||
} else if (bank->DataID == FOURCC('S', 'B', 'l', 'k')) {
|
||||
bank_id = read_sfx_bank((SFXBlockData*)bank_buf.get());
|
||||
if (bank_tag->DataID == FOURCC('S', 'B', 'v', '2')) {
|
||||
bank = std::make_unique<MusicBank>(*this, bank_id, bank_tag);
|
||||
} else if (bank_tag->DataID == FOURCC('S', 'B', 'l', 'k')) {
|
||||
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 {
|
||||
m_id_allocator.free_id(bank_id);
|
||||
throw std::runtime_error("Unknown bank ID, bad file?");
|
||||
}
|
||||
|
||||
m_soundbanks.emplace(bank_id, std::move(bank));
|
||||
|
||||
if (attr.num_chunks >= 2) {
|
||||
in.seekg(origin + attr.where[chunk::samples].offset, std::fstream::beg);
|
||||
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();
|
||||
}
|
||||
|
||||
MusicBank* loader::get_bank_by_name(u32 id) {
|
||||
MusicBank* loader::get_bank_by_id(u32 id) {
|
||||
for (auto& b : m_soundbanks) {
|
||||
if (b.second->type == BankType::Music) {
|
||||
auto* bank = static_cast<MusicBank*>(b.second.get());
|
||||
@ -165,6 +113,30 @@ MusicBank* loader::get_bank_by_name(u32 id) {
|
||||
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) {
|
||||
return m_midi.at(id);
|
||||
}
|
||||
|
@ -33,10 +33,13 @@ struct FileAttributes {
|
||||
class loader : public locator {
|
||||
public:
|
||||
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;
|
||||
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);
|
||||
|
||||
u32 read_bank(std::fstream& in);
|
||||
@ -46,8 +49,6 @@ class loader : public locator {
|
||||
|
||||
private:
|
||||
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;
|
||||
std::unordered_map<u32, std::unique_ptr<SoundBank>> m_soundbanks;
|
||||
|
@ -12,7 +12,7 @@ class locator {
|
||||
public:
|
||||
virtual ~locator() = default;
|
||||
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 MIDIBlock* get_midi(u32 id) = 0;
|
||||
};
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "common/log/log.h"
|
||||
|
||||
#include "game/sound/989snd/util.h"
|
||||
#include <third-party/fmt/core.h>
|
||||
|
||||
namespace snd {
|
||||
@ -26,7 +27,7 @@ midi_handler::midi_handler(MIDIBlockHeader* block,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
locator& loc,
|
||||
u32 bank)
|
||||
SoundBank& bank)
|
||||
: m_sound(sound),
|
||||
m_locator(loc),
|
||||
m_repeats(sound.Repeats),
|
||||
@ -57,7 +58,7 @@ midi_handler::midi_handler(MIDIBlockHeader* block,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
locator& loc,
|
||||
u32 bank,
|
||||
SoundBank& bank,
|
||||
std::optional<ame_handler*> parent)
|
||||
: m_parent(parent),
|
||||
m_sound(sound),
|
||||
@ -140,7 +141,21 @@ void midi_handler::set_vol_pan(s32 vol, s32 pan) {
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -172,8 +187,8 @@ void midi_handler::note_on() {
|
||||
// velocity);
|
||||
|
||||
// Key on all the applicable tones for the program
|
||||
auto bank = dynamic_cast<MusicBank*>(m_locator.get_bank_by_name(m_header->BankID));
|
||||
auto& program = bank->programs[m_programs[channel]];
|
||||
auto bank = dynamic_cast<MusicBank*>(m_locator.get_bank_by_id(m_header->BankID));
|
||||
auto& program = bank->m_programs[m_programs[channel]];
|
||||
|
||||
for (auto& t : program.tones) {
|
||||
if (note >= t.MapLow && note <= t.MapHigh) {
|
||||
@ -192,12 +207,11 @@ void midi_handler::note_on() {
|
||||
voice->start_note = note;
|
||||
voice->start_fine = 0;
|
||||
|
||||
// TODO
|
||||
// voice->current_pm = 0;
|
||||
// voice->current_pb = 0;
|
||||
voice->current_pm = m_pitch_bend[channel];
|
||||
voice->current_pb = m_cur_pm;
|
||||
|
||||
voice->group = m_sound.VolGroup;
|
||||
m_vm.start_tone(voice);
|
||||
m_vm.start_tone(voice, m_bank.bank_id);
|
||||
m_voices.emplace_front(voice);
|
||||
}
|
||||
}
|
||||
@ -260,10 +274,26 @@ void midi_handler::channel_pressure() {
|
||||
|
||||
void midi_handler::channel_pitch() {
|
||||
u8 channel = m_status & 0xF;
|
||||
u32 pitch = (m_seq_ptr[0] << 7) | m_seq_ptr[1];
|
||||
(void)pitch;
|
||||
(void)channel;
|
||||
s32 pitch = 0xFFFF * ((m_seq_ptr[0] & 0x7f) | ((m_seq_ptr[1] & 0x7f) << 7)) / 0x3FFF;
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -289,6 +319,7 @@ void midi_handler::meta_event() {
|
||||
|
||||
if (*m_seq_ptr == 0x51) {
|
||||
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;
|
||||
|
@ -45,7 +45,7 @@ class midi_handler : public sound_handler {
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
locator& loc,
|
||||
u32 bank);
|
||||
SoundBank& bank);
|
||||
|
||||
midi_handler(MIDIBlockHeader* block,
|
||||
voice_manager& vm,
|
||||
@ -53,7 +53,7 @@ class midi_handler : public sound_handler {
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
locator& loc,
|
||||
u32 bank,
|
||||
SoundBank& bank,
|
||||
std::optional<ame_handler*> parent);
|
||||
|
||||
~midi_handler() override {
|
||||
@ -69,7 +69,7 @@ class midi_handler : public sound_handler {
|
||||
bool tick() override;
|
||||
void mute_channel(u8 channel);
|
||||
void unmute_channel(u8 channel);
|
||||
u32 bank() override { return m_bank; };
|
||||
SoundBank& bank() override { return m_bank; };
|
||||
|
||||
void pause() override;
|
||||
void stop() override;
|
||||
@ -98,8 +98,9 @@ class midi_handler : public sound_handler {
|
||||
locator& m_locator;
|
||||
s32 m_vol{0x7f};
|
||||
s32 m_pan{0};
|
||||
s32 m_cur_pm{0};
|
||||
s8 m_repeats{0};
|
||||
u32 m_bank;
|
||||
SoundBank& m_bank;
|
||||
|
||||
bool m_paused{false};
|
||||
|
||||
@ -108,6 +109,7 @@ class midi_handler : public sound_handler {
|
||||
std::array<bool, 16> m_mute_state{};
|
||||
std::array<s8, 16> m_chanvol{};
|
||||
std::array<s8, 16> m_chanpan{};
|
||||
std::array<s16, 16> m_pitch_bend{};
|
||||
u8* m_sample_data{nullptr};
|
||||
|
||||
u8* m_seq_data_start{nullptr};
|
||||
|
@ -6,21 +6,48 @@
|
||||
#include "../common/synth.h"
|
||||
|
||||
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,
|
||||
u32 sound_id,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
s32 pm,
|
||||
s32 pb) {
|
||||
auto& sound = sounds[sound_id];
|
||||
auto& sound = m_sounds[sound_id];
|
||||
std::unique_ptr<sound_handler> handler;
|
||||
|
||||
if (sound.Type == 4) { // midi
|
||||
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
|
||||
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 {
|
||||
// error
|
||||
}
|
||||
@ -28,4 +55,12 @@ std::unique_ptr<sound_handler> MusicBank::make_handler(voice_manager& vm,
|
||||
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
|
||||
|
@ -60,7 +60,7 @@ struct MIDISound {
|
||||
struct Prog;
|
||||
class MusicBank : public SoundBank {
|
||||
public:
|
||||
MusicBank(locator& loc) : m_locator(loc) {}
|
||||
MusicBank(locator& loc, u32 id, BankTag* tag);
|
||||
std::unique_ptr<sound_handler> make_handler(voice_manager& vm,
|
||||
u32 sound_id,
|
||||
s32 vol,
|
||||
@ -68,8 +68,14 @@ class MusicBank : public SoundBank {
|
||||
s32 pm,
|
||||
s32 pb) override;
|
||||
|
||||
std::vector<Prog> programs;
|
||||
std::vector<MIDISound> sounds;
|
||||
std::unique_ptr<sound_handler> make_handler(voice_manager& vm,
|
||||
u32 sound_id,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
SndPlayParams& params) override;
|
||||
|
||||
std::vector<Prog> m_programs;
|
||||
std::vector<MIDISound> m_sounds;
|
||||
|
||||
private:
|
||||
locator& m_locator;
|
||||
|
@ -143,6 +143,38 @@ u32 player::play_sound(u32 bank_id, u32 sound_id, s32 vol, s32 pan, s32 pm, s32
|
||||
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) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
auto handler = m_handlers.find(sound_id);
|
||||
@ -155,7 +187,7 @@ void player::stop_sound(u32 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);
|
||||
if (m_handlers.find(sound_id) == m_handlers.end()) {
|
||||
// fmt::print("set_midi_reg: Handler {} does not exist\n", sound_id);
|
||||
@ -210,7 +242,7 @@ void player::unload_bank(u32 bank_handle) {
|
||||
return;
|
||||
|
||||
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);
|
||||
it = m_handlers.erase(it);
|
||||
} else {
|
||||
@ -285,4 +317,54 @@ void player::set_sound_pmod(s32 sound_handle, s32 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
|
||||
|
@ -36,7 +36,14 @@ class player {
|
||||
u32 load_bank(fs::path& path, size_t offset);
|
||||
|
||||
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);
|
||||
void set_master_volume(u32 group, s32 volume);
|
||||
void unload_bank(u32 bank_handle);
|
||||
@ -53,6 +60,12 @@ class player {
|
||||
void init_cubeb();
|
||||
void destroy_cubeb();
|
||||
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:
|
||||
std::recursive_mutex m_ticklock; // TODO does not need to recursive with some light restructuring
|
||||
|
@ -1,25 +1,48 @@
|
||||
#include "sfxblock.h"
|
||||
|
||||
#include "blocksound_handler.h"
|
||||
#include "sfxgrain.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
|
||||
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,
|
||||
u32 sound_id,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
s32 pm,
|
||||
s32 pb) {
|
||||
std::unique_ptr<blocksound_handler> handler;
|
||||
auto& SFX = sounds[sound_id];
|
||||
SndPlayParams& params) {
|
||||
auto& SFX = m_sounds[sound_id];
|
||||
|
||||
if (SFX.grains.empty()) {
|
||||
// fmt::print("skipping empty sfx\n");
|
||||
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();
|
||||
return handler;
|
||||
}
|
||||
|
||||
} // namespace snd
|
||||
|
@ -1,8 +1,11 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
#include "sfxgrain.h"
|
||||
#include "soundbank.h"
|
||||
|
||||
#include "sfxblock2.h"
|
||||
|
||||
namespace snd {
|
||||
|
||||
struct SFXBlockData : BankTag {
|
||||
@ -19,76 +22,14 @@ struct SFXBlockData : BankTag {
|
||||
/* 28 */ u32 VagDataSize;
|
||||
/* 2c */ u32 SRAMAllocSize;
|
||||
/* 30 */ u32 NextBlock;
|
||||
|
||||
/* these last ones are probably not in jak1? */
|
||||
/* 34 */ u32 BlockNames;
|
||||
/* 38 */ u32 SFXUD;
|
||||
};
|
||||
|
||||
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 {
|
||||
/* 0 */ s8 Vol;
|
||||
/* 1 */ s8 VolGroup;
|
||||
@ -106,23 +47,21 @@ enum SFXFlags {
|
||||
|
||||
struct SFX {
|
||||
SFXData d;
|
||||
std::vector<SFXGrain> grains;
|
||||
std::vector<std::unique_ptr<Grain>> grains;
|
||||
};
|
||||
|
||||
class SFXBlock : public SoundBank {
|
||||
public:
|
||||
SFXBlock(locator& loc) : m_locator(loc) {}
|
||||
SFXBlock(locator& loc, u32 handle, BankTag* tag);
|
||||
std::unique_ptr<sound_handler> make_handler(voice_manager& vm,
|
||||
u32 sound_id,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
s32 pm,
|
||||
s32 pb) override;
|
||||
|
||||
std::vector<SFX> sounds;
|
||||
SndPlayParams& params) override;
|
||||
|
||||
private:
|
||||
locator& m_locator;
|
||||
std::vector<SFX2> m_sounds;
|
||||
};
|
||||
|
||||
} // namespace snd
|
||||
|
103
game/sound/989snd/sfxblock2.cpp
Normal file
103
game/sound/989snd/sfxblock2.cpp
Normal 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
|
115
game/sound/989snd/sfxblock2.h
Normal file
115
game/sound/989snd/sfxblock2.h
Normal 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
|
614
game/sound/989snd/sfxgrain.cpp
Normal file
614
game/sound/989snd/sfxgrain.cpp
Normal 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
|
529
game/sound/989snd/sfxgrain.h
Normal file
529
game/sound/989snd/sfxgrain.h
Normal 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
|
@ -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 "common/log/log.h"
|
||||
@ -7,22 +16,65 @@ int main(int argc, char* argv[]) {
|
||||
unsigned bankid = 0;
|
||||
|
||||
fs::path file = argv[1];
|
||||
bankid = player.load_bank(file, 0);
|
||||
|
||||
if (argc > 2) {
|
||||
bankid = player.load_bank(file, 0);
|
||||
unsigned sound = player.play_sound(bankid, atoi(argv[2]), 0x400, 0, 0, 0);
|
||||
lg::info("sound {} started", sound);
|
||||
}
|
||||
|
||||
printf("commands:\n");
|
||||
printf(" play [id]\n");
|
||||
printf(" stop\n");
|
||||
|
||||
while (true) {
|
||||
#ifdef __linux
|
||||
timespec rqtp{}, rmtp{};
|
||||
rqtp.tv_nsec = 0;
|
||||
rqtp.tv_sec = 1;
|
||||
if (nanosleep(&rqtp, &rmtp) == -1) {
|
||||
break;
|
||||
printf("> ");
|
||||
std::string command;
|
||||
std::getline(std::cin, command);
|
||||
|
||||
std::stringstream ss(command);
|
||||
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;
|
||||
|
@ -4,16 +4,18 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace snd {
|
||||
static constexpr int PAN_RESET = -1;
|
||||
static constexpr int PAN_DONT_CHANGE = -2;
|
||||
static constexpr int VOLUME_DONT_CHANGE = 0x7fffffff;
|
||||
|
||||
namespace snd {
|
||||
class SoundBank;
|
||||
|
||||
class sound_handler {
|
||||
public:
|
||||
virtual ~sound_handler() = default;
|
||||
virtual bool tick() = 0;
|
||||
virtual u32 bank() = 0;
|
||||
virtual SoundBank& bank() = 0;
|
||||
virtual void pause() = 0;
|
||||
virtual void unpause() = 0;
|
||||
virtual u8 group() = 0;
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "locator.h"
|
||||
#include "sound_handler.h"
|
||||
@ -10,6 +11,15 @@
|
||||
#include "../common/synth.h"
|
||||
|
||||
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 {
|
||||
/* 0 */ u32 DataID;
|
||||
/* 4 */ u32 Version;
|
||||
@ -22,17 +32,40 @@ enum class BankType {
|
||||
SFX,
|
||||
};
|
||||
|
||||
struct SFXUserData;
|
||||
class SoundBank {
|
||||
public:
|
||||
SoundBank(u32 id, BankType type) : type(type), bank_id(id){};
|
||||
virtual ~SoundBank() = default;
|
||||
BankType type;
|
||||
|
||||
virtual std::unique_ptr<sound_handler> make_handler(voice_manager& vm,
|
||||
u32 sound_id,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
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_name;
|
||||
std::unique_ptr<u8[]> sampleBuf;
|
||||
|
@ -129,12 +129,14 @@ std::pair<s16, s16> pitchbend(Tone& tone,
|
||||
int current_pm,
|
||||
int start_note,
|
||||
int start_fine) {
|
||||
auto v9 = (start_note << 7) + start_fine + current_pm;
|
||||
u32 v7;
|
||||
s32 v9 = (start_note << 7) + start_fine + current_pm;
|
||||
|
||||
s32 v7;
|
||||
if (current_pb >= 0)
|
||||
v7 = tone.PBHigh * (current_pb << 7) / 0x7fff + v9;
|
||||
else
|
||||
v7 = tone.PBLow * (current_pb << 7) / 0x7fff + v9;
|
||||
v7 = tone.PBLow * (current_pb << 7) / 0x8000 + v9;
|
||||
|
||||
return {v7 / 128, v7 % 128};
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ voice_manager::voice_manager(synth& synth, locator& loc) : m_synth(synth), m_loc
|
||||
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 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_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->key_on();
|
||||
|
@ -32,11 +32,7 @@ struct Tone {
|
||||
/* c */ s16 ADSR2;
|
||||
/* e */ s16 Flags;
|
||||
/* 10 */ /*void**/ u32 VAGInSR;
|
||||
///* 14 */ u32 reserved1; confiscated
|
||||
|
||||
// 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;
|
||||
/* 14 */ u32 reserved1;
|
||||
};
|
||||
|
||||
class vag_voice : public voice {
|
||||
@ -55,7 +51,7 @@ class vag_voice : public voice {
|
||||
class voice_manager {
|
||||
public:
|
||||
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 unpause(std::shared_ptr<vag_voice> voice);
|
||||
void set_pan_table(vol_pair* table) { m_pan_table = table; };
|
||||
|
@ -7,8 +7,11 @@ set(SOUND_SOURCES
|
||||
989snd/blocksound_handler.cpp
|
||||
989snd/musicbank.cpp
|
||||
989snd/sfxblock.cpp
|
||||
989snd/sfxblock2.cpp
|
||||
989snd/sfxgrain.cpp
|
||||
989snd/loader.cpp
|
||||
989snd/vagvoice.cpp
|
||||
989snd/lfo.cpp
|
||||
989snd/util.cpp
|
||||
common/synth.cpp
|
||||
common/voice.cpp
|
||||
@ -35,4 +38,4 @@ if (NOT WIN32)
|
||||
-Wno-unused-parameter
|
||||
-Wno-shadow
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
@ -41,31 +41,32 @@ void snd_RegisterIOPMemAllocator(AllocFun, FreeFun) {
|
||||
// printf("snd_RegisterIOPMemAllocator\n");
|
||||
}
|
||||
|
||||
void snd_LockVoiceAllocator(s32) {
|
||||
int snd_LockVoiceAllocator(bool block) {
|
||||
// printf("snd_LockVoiceAllocator\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void snd_UnlockVoiceAllocator() {
|
||||
// printf("snd_UnlockVoiceAllocator\n");
|
||||
}
|
||||
|
||||
s32 snd_ExternVoiceVoiceAlloc(s32, s32) {
|
||||
s32 snd_ExternVoiceAlloc(s32 vol_group, s32 priority) {
|
||||
// printf("snd_ExternVoiceVoiceAlloc\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 snd_SRAMMalloc(u32) {
|
||||
u32 snd_SRAMMalloc(u32 size) {
|
||||
// spu memory currently hardcoded
|
||||
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) {
|
||||
if (player) {
|
||||
@ -89,9 +90,9 @@ s32 snd_SoundIsStillPlaying(s32 sound_handle) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void snd_StopSound(s32 handle) {
|
||||
void snd_StopSound(s32 sound_handle) {
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
void snd_ContinueAllSoundsInGroup(u8 group) {
|
||||
void snd_ContinueAllSoundsInGroup(u8 groups) {
|
||||
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) {
|
||||
player->pause_all_sounds_in_group(group);
|
||||
player->pause_all_sounds_in_group(groups);
|
||||
}
|
||||
}
|
||||
|
||||
void snd_SetMIDIRegister(s32 sound_handle, u8 reg, u8 value) {
|
||||
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) {
|
||||
return player->play_sound(bank, sound, vol, pan, pm, pb);
|
||||
return player->play_sound(bank, sound, vol, pan, pitch_mod, pitch_bend);
|
||||
} else {
|
||||
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) {
|
||||
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) {
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
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
|
||||
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");
|
||||
if (player) {
|
||||
fs::path path = filename;
|
||||
@ -205,3 +222,26 @@ void snd_keyOffVoiceRaw(u32 core, u32 voice_id) {
|
||||
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");
|
||||
}
|
||||
|
@ -7,44 +7,70 @@ constexpr int SND_CORE_0 = 1;
|
||||
constexpr int SND_CORE_1 = 2;
|
||||
constexpr int SD_REV_MODE_OFF = 0;
|
||||
|
||||
struct SFXUserData {
|
||||
u32 data[4];
|
||||
};
|
||||
|
||||
typedef void* (*AllocFun)();
|
||||
typedef void (*FreeFun)(void*);
|
||||
|
||||
void snd_StartSoundSystem();
|
||||
void snd_StopSoundSystem();
|
||||
s32 snd_GetTick();
|
||||
void snd_RegisterIOPMemAllocator(AllocFun, FreeFun);
|
||||
void snd_LockVoiceAllocator(s32);
|
||||
void snd_RegisterIOPMemAllocator(AllocFun alloc, FreeFun free);
|
||||
int snd_LockVoiceAllocator(bool block);
|
||||
void snd_UnlockVoiceAllocator();
|
||||
s32 snd_ExternVoiceVoiceAlloc(s32, s32);
|
||||
u32 snd_SRAMMalloc(u32);
|
||||
void snd_SetMixerMode(s32, s32);
|
||||
void snd_SetGroupVoiceRange(s32, s32, s32);
|
||||
void snd_SetReverbDepth(s32, s32, s32);
|
||||
void snd_SetReverbType(s32, s32);
|
||||
void snd_SetPanTable(s16*);
|
||||
void snd_SetPlayBackMode(s32);
|
||||
s32 snd_SoundIsStillPlaying(s32);
|
||||
void snd_StopSound(s32);
|
||||
void snd_SetSoundVolPan(s32, s32, s32);
|
||||
void snd_SetMasterVolume(s32, s32);
|
||||
void snd_UnloadBank(s32);
|
||||
s32 snd_ExternVoiceAlloc(s32 vol_group, s32 priority);
|
||||
u32 snd_SRAMMalloc(u32 size);
|
||||
void snd_SetMixerMode(s32 channel_mode, s32 reverb_mode);
|
||||
void snd_SetGroupVoiceRange(s32 group, s32 min, s32 max);
|
||||
void snd_SetReverbDepth(s32 core, s32 left, s32 right);
|
||||
void snd_SetReverbType(s32 core, s32 type);
|
||||
void snd_SetPanTable(s16* table);
|
||||
void snd_SetPlayBackMode(s32 mode);
|
||||
s32 snd_SoundIsStillPlaying(s32 sound_handle);
|
||||
void snd_StopSound(s32 sound_handle);
|
||||
void snd_SetSoundVolPan(s32 sound_handle, s32 vol, s32 pan);
|
||||
void snd_SetMasterVolume(s32 which, s32 volume);
|
||||
void snd_UnloadBank(s32 bank_handle);
|
||||
void snd_ResolveBankXREFS();
|
||||
void snd_ContinueAllSoundsInGroup(u8);
|
||||
void snd_PauseAllSoundsInGroup(u8);
|
||||
void snd_SetMIDIRegister(s32, u8, u8);
|
||||
s32 snd_PlaySoundVolPanPMPB(s32, s32, s32, s32, s32, s32);
|
||||
void snd_SetSoundPitchModifier(s32, s32);
|
||||
void snd_SetSoundPitchBend(s32, s32);
|
||||
void snd_PauseSound(s32);
|
||||
void snd_ContinueSound(s32);
|
||||
void snd_AutoPitch(s32, s32, s32, s32);
|
||||
void snd_AutoPitchBend(s32, s32, s32, s32);
|
||||
s32 snd_BankLoadEx(const char* filepath, s32 data_offset, s32 unk1, s32 unk2);
|
||||
void snd_ContinueAllSoundsInGroup(u8 groups);
|
||||
void snd_PauseAllSoundsInGroup(u8 groups);
|
||||
void snd_SetMIDIRegister(s32 handle, u8 reg, u8 value);
|
||||
void snd_SetGlobalExcite(u8 value);
|
||||
|
||||
s32 snd_PlaySoundVolPanPMPB(s32 bank_handle,
|
||||
s32 sound_id,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
s32 pitch_mod,
|
||||
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_GetFreeSPUDMA();
|
||||
void snd_FreeSPUDMA(s32 channel);
|
||||
void snd_keyOnVoiceRaw(u32, u32);
|
||||
void snd_keyOffVoiceRaw(u32, u32);
|
||||
void snd_keyOnVoiceRaw(u32 core, u32 voice);
|
||||
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_
|
||||
|
@ -644,7 +644,7 @@
|
||||
; )
|
||||
|
||||
;; 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
|
||||
; (str-play-kick)
|
||||
|
@ -935,9 +935,6 @@
|
||||
0
|
||||
)
|
||||
|
||||
;; temporarily removed, will crash on startup
|
||||
(format 0 "HACK: skipping check-irx-version and common soundbank load.~%")
|
||||
#|
|
||||
(check-irx-version)
|
||||
|
||||
(case (scf-get-territory)
|
||||
@ -958,7 +955,6 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
|#
|
||||
|
||||
|
||||
|
||||
|
@ -166,6 +166,24 @@
|
||||
: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
|
||||
;;;;;;;;;;;;;;;;;
|
||||
@ -664,6 +682,159 @@
|
||||
"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
|
||||
;;;;;;;;;;;;;;;;;;;;;
|
||||
|
1387
third-party/magic_enum.hpp
generated
vendored
Normal file
1387
third-party/magic_enum.hpp
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user