mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-27 00:10:31 +00:00
989snd: sound bank loading overhaul (#3185)
This commit is contained in:
parent
9b6f16f561
commit
168afa0e2e
@ -12,30 +12,33 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "third-party/span.hpp"
|
||||
|
||||
class BinaryReader {
|
||||
public:
|
||||
explicit BinaryReader(const std::vector<uint8_t>& _buffer) : m_buffer(_buffer) {}
|
||||
explicit BinaryReader(nonstd::span<const uint8_t> _span) : m_span(_span) {}
|
||||
|
||||
template <typename T>
|
||||
T read() {
|
||||
ASSERT(m_seek + sizeof(T) <= m_buffer.size());
|
||||
ASSERT(m_seek + sizeof(T) <= m_span.size());
|
||||
T obj;
|
||||
memcpy(&obj, m_buffer.data() + m_seek, sizeof(T));
|
||||
memcpy(&obj, m_span.data() + m_seek, sizeof(T));
|
||||
m_seek += sizeof(T);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void ffwd(int amount) {
|
||||
m_seek += amount;
|
||||
ASSERT(m_seek <= m_buffer.size());
|
||||
ASSERT(m_seek <= m_span.size());
|
||||
}
|
||||
|
||||
uint32_t bytes_left() const { return m_buffer.size() - m_seek; }
|
||||
uint8_t* here() { return m_buffer.data() + m_seek; }
|
||||
uint32_t bytes_left() const { return m_span.size() - m_seek; }
|
||||
const uint8_t* here() { return m_span.data() + m_seek; }
|
||||
BinaryReader at(u32 pos) { return BinaryReader(m_span.subspan(pos)); }
|
||||
uint32_t get_seek() const { return m_seek; }
|
||||
void set_seek(u32 seek) { m_seek = seek; }
|
||||
|
||||
private:
|
||||
std::vector<u8> m_buffer;
|
||||
nonstd::span<const u8> m_span;
|
||||
uint32_t m_seek = 0;
|
||||
};
|
||||
|
@ -21,7 +21,7 @@ void assert_string_empty_after(const char* str, int size) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_object_file_name(const std::string& original_name, u8* data, int size) {
|
||||
std::string get_object_file_name(const std::string& original_name, const u8* data, int size) {
|
||||
const std::string art_group_text_strings[] = {
|
||||
fmt::format("/src/next/data/art-group{}/", versions::jak1::ART_FILE_VERSION),
|
||||
fmt::format("/src/jak2/final/art-group{}/", versions::jak2::ART_FILE_VERSION),
|
||||
|
@ -6,4 +6,4 @@
|
||||
#include "common/versions/versions.h"
|
||||
|
||||
void assert_string_empty_after(const char* str, int size);
|
||||
std::string get_object_file_name(const std::string& original_name, u8* data, int size);
|
||||
std::string get_object_file_name(const std::string& original_name, const u8* data, int size);
|
||||
|
@ -19,10 +19,12 @@ SoundBank* gBanks[N_BANKS] = {&gCommonBank, &gGunBank, &gBoardBank,
|
||||
&gLevelBanks[0], &gLevelBanks[1], &gLevelBanks[2]};
|
||||
|
||||
void sbank_init_globals() {
|
||||
memset((void*)&gCommonBank, 0, sizeof(gCommonBank));
|
||||
memset((void*)&gGunBank, 0, sizeof(gGunBank));
|
||||
memset((void*)&gBoardBank, 0, sizeof(gBoardBank));
|
||||
memset((void*)&gLevelBanks, 0, sizeof(gLevelBanks));
|
||||
gCommonBank = {};
|
||||
gGunBank = {};
|
||||
gBoardBank = {};
|
||||
for (auto& b : gLevelBanks) {
|
||||
b = {};
|
||||
}
|
||||
}
|
||||
|
||||
void InitBanks() {
|
||||
@ -30,34 +32,41 @@ void InitBanks() {
|
||||
bank->bank_handle = 0;
|
||||
bank->sound_count = 0;
|
||||
|
||||
// paper over version differences
|
||||
// in_use doesn't exist in jak1, but setting it to true
|
||||
// for all banks results in the same behaviour
|
||||
if (g_game_version == GameVersion::Jak1) {
|
||||
bank->in_use = true;
|
||||
} else {
|
||||
bank->in_use = false;
|
||||
bank->unk4 = 0;
|
||||
}
|
||||
|
||||
strcpy(bank->name, "<unused>");
|
||||
bank->unk4 = 0;
|
||||
strcpy(bank->name.data(), "<unused>");
|
||||
}
|
||||
|
||||
if (g_game_version == GameVersion::Jak2) {
|
||||
strncpy(gBanks[0]->name, "common", 16);
|
||||
strncpy(gBanks[0]->name.data(), "common", 16);
|
||||
gBanks[0]->spu_loc = 0x20000;
|
||||
gBanks[0]->spu_size = 0xAFCC0;
|
||||
|
||||
strncpy(gBanks[1]->name, "gun", 16);
|
||||
strncpy(gBanks[1]->name.data(), "gun", 16);
|
||||
gBanks[1]->spu_loc = 0x131740;
|
||||
gBanks[1]->spu_size = 0;
|
||||
|
||||
strncpy(gBanks[2]->name, "board", 16);
|
||||
strncpy(gBanks[2]->name.data(), "board", 16);
|
||||
gBanks[2]->spu_loc = 0x131740;
|
||||
gBanks[2]->spu_size = 0;
|
||||
|
||||
strncpy(gBanks[3]->name, "level0", 16);
|
||||
strncpy(gBanks[3]->name.data(), "level0", 16);
|
||||
gBanks[3]->spu_loc = 0x131740;
|
||||
gBanks[3]->spu_size = 0x42800;
|
||||
|
||||
strncpy(gBanks[4]->name, "level1", 16);
|
||||
strncpy(gBanks[4]->name.data(), "level1", 16);
|
||||
gBanks[4]->spu_loc = 0x173f40;
|
||||
gBanks[4]->spu_size = 0x42800;
|
||||
|
||||
strncpy(gBanks[5]->name, "level2", 16);
|
||||
strncpy(gBanks[5]->name.data(), "level2", 16);
|
||||
gBanks[5]->spu_loc = 0x1B6740;
|
||||
gBanks[5]->spu_size = 0x42800;
|
||||
}
|
||||
@ -111,7 +120,7 @@ s32 LookupSoundIndex(const char* name, SoundBank** bank_out) {
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)bank->sound_count; i++) {
|
||||
if (memcmp(bank->sound[i].name, name, 16) == 0) {
|
||||
if (memcmp(bank->sound[i].name.data(), name, 16) == 0) {
|
||||
*bank_out = bank;
|
||||
return i;
|
||||
}
|
||||
@ -129,17 +138,10 @@ SoundBank* LookupBank(const char* name) {
|
||||
return nullptr; // not found.
|
||||
}
|
||||
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 (g_game_version == GameVersion::Jak1) {
|
||||
if ((memcmp(bank->name, name, 16) == 0)) {
|
||||
|
||||
if ((memcmp(bank->name.data(), name, 16) == 0) && bank->in_use) {
|
||||
return bank;
|
||||
}
|
||||
} else {
|
||||
if ((memcmp(bank->name, name, 16) == 0) && bank->in_use) {
|
||||
return bank;
|
||||
}
|
||||
}
|
||||
idx--;
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/sound/sndshim.h"
|
||||
|
||||
struct SoundRecord {
|
||||
char name[16];
|
||||
std::array<char, 16> name;
|
||||
u32 fallof_params;
|
||||
};
|
||||
|
||||
struct SoundBank {
|
||||
char name[16];
|
||||
u32 bank_handle;
|
||||
std::array<char, 16> name;
|
||||
snd::BankHandle bank_handle;
|
||||
u32 sound_count;
|
||||
|
||||
union {
|
||||
SoundRecord sound[1];
|
||||
// name list, only for jak1
|
||||
std::vector<SoundRecord> sound;
|
||||
|
||||
// Needs to fit the biggest bank (common.sbk)
|
||||
u8 buffer[10 * 2048];
|
||||
|
||||
// Jak 2 additions go here
|
||||
struct {
|
||||
// jak2 additions
|
||||
u32 spu_loc;
|
||||
u32 spu_size;
|
||||
u32 unk4;
|
||||
bool in_use;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
void sbank_init_globals();
|
||||
|
@ -33,10 +33,10 @@ void PrintBankInfo(SoundBank* bank) {
|
||||
// we dont need this and it spams the console too much
|
||||
return;
|
||||
|
||||
printf("Bank %s\n\n", bank->name);
|
||||
printf("Bank %s\n\n", bank->name.data());
|
||||
for (u32 i = 0; i < bank->sound_count; i++) {
|
||||
// Some characters use the full 16 characters (bonelurker-grunt) and dont have a null terminator
|
||||
std::string name = std::string(bank->sound[i].name, 16);
|
||||
std::string name = std::string(bank->sound[i].name.data(), 16);
|
||||
printf("%d : %16s : min %d max %d curve %d\n", i, name.c_str(),
|
||||
bank->sound[i].fallof_params & 0x3fff, (bank->sound[i].fallof_params >> 14) & 0x3fff,
|
||||
bank->sound[i].fallof_params >> 28);
|
||||
|
@ -1,13 +1,14 @@
|
||||
#include "srpc.h"
|
||||
|
||||
#include <cstring>
|
||||
#include "game/sound/sndshim.h"
|
||||
|
||||
// added
|
||||
u32 gMusicFadeHack = 0;
|
||||
MusicTweaks gMusicTweakInfo;
|
||||
s32 gMusicTweak = 0x80;
|
||||
int32_t gSoundEnable = 1;
|
||||
s32 gMusic = 0;
|
||||
snd::BankHandle gMusic = nullptr;
|
||||
s32 gMusicPause = 0;
|
||||
s32 gSoundInUse = 0;
|
||||
u8 gFPS = 60;
|
||||
|
@ -117,7 +117,7 @@ struct SoundRpcSetMirrror {
|
||||
extern s32 gMusicTweak;
|
||||
extern MusicTweaks gMusicTweakInfo;
|
||||
extern int32_t gSoundEnable;
|
||||
extern s32 gMusic;
|
||||
extern snd::BankHandle gMusic;
|
||||
extern s32 gMusicPause;
|
||||
extern s32 gSoundInUse;
|
||||
extern u8 gFPS;
|
||||
|
@ -395,12 +395,12 @@ void PrintActiveSounds() {
|
||||
for (auto& s : gSounds) {
|
||||
if (s.id != 0 && s.is_music == 0) {
|
||||
if (s.bank_entry != nullptr) {
|
||||
u32 len = strlen(s.bank_entry->name);
|
||||
u32 len = strlen(s.bank_entry->name.data());
|
||||
if (len > 16) {
|
||||
len = 16;
|
||||
}
|
||||
sprintf(string, " : Vol %d", GetVolume(&s));
|
||||
memcpy(string, s.bank_entry->name, len);
|
||||
memcpy(string, s.bank_entry->name.data(), len);
|
||||
printf("%s\n", string);
|
||||
} else { // added for printing jak2 sounds
|
||||
u32 len = strlen(s.name);
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/Assert.h"
|
||||
#include "common/util/BinaryReader.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#include "game/overlord/common/fake_iso.h"
|
||||
@ -18,7 +19,7 @@ LoadStackEntry* FS_OpenWad(FileRecord* fr, int32_t offset);
|
||||
uint32_t FS_LoadSoundBank(char* name, SoundBank* bank);
|
||||
void FS_PollDrive();
|
||||
uint32_t FS_SyncRead();
|
||||
uint32_t FS_LoadMusic(char* name, s32* bank_handle);
|
||||
uint32_t FS_LoadMusic(char* name, snd::BankHandle* bank_handle);
|
||||
void FS_Close(LoadStackEntry* fd);
|
||||
static LoadStackEntry sLoadStack[MAX_OPEN_FILES]; //! List of all files that are "open"
|
||||
static LoadStackEntry* sReadInfo; // LoadStackEntry for currently reading file
|
||||
@ -200,7 +201,7 @@ uint32_t FS_SyncRead() {
|
||||
*/
|
||||
void FS_PollDrive() {}
|
||||
|
||||
uint32_t FS_LoadMusic(char* name, s32* bank_handle) {
|
||||
uint32_t FS_LoadMusic(char* name, snd::BankHandle* bank_handle) {
|
||||
char namebuf[16];
|
||||
strcpy(namebuf, name);
|
||||
namebuf[8] = 0;
|
||||
@ -215,9 +216,27 @@ uint32_t FS_LoadMusic(char* name, s32* bank_handle) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// original overlord uses good old fread into a struct instance
|
||||
// lets use BinaryReader instead
|
||||
static void parseSoundBank(BinaryReader data, SoundBank& bank) {
|
||||
bank.name = data.read<std::array<char, 16>>();
|
||||
|
||||
data.read<u32>();
|
||||
bank.bank_handle = nullptr;
|
||||
|
||||
bank.sound_count = data.read<u32>();
|
||||
bank.sound.resize(bank.sound_count);
|
||||
|
||||
for (auto& snd : bank.sound) {
|
||||
snd.name = data.read<std::array<char, 16>>();
|
||||
snd.fallof_params = data.read<u32>();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t FS_LoadSoundBank(char* name, SoundBank* bank) {
|
||||
char namebuf[16];
|
||||
|
||||
// sector size of sound name list
|
||||
int offset = 10 * 2048;
|
||||
if (bank->sound_count == 101) {
|
||||
offset = 1 * 2048;
|
||||
@ -234,11 +253,14 @@ uint32_t FS_LoadSoundBank(char* name, SoundBank* bank) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto fp = file_util::open_file(get_file_path(file), "rb");
|
||||
fread(bank, offset, 1, fp);
|
||||
fclose(fp);
|
||||
// auto fp = file_util::open_file(get_file_path(file), "rb");
|
||||
// fread(bank, offset, 1, fp);
|
||||
// fclose(fp);
|
||||
|
||||
s32 handle = snd_BankLoadEx(get_file_path(file), offset, 0, 0);
|
||||
auto data = file_util::read_binary_file(fs::path(get_file_path(file)));
|
||||
parseSoundBank(BinaryReader(data), *bank);
|
||||
|
||||
snd::BankHandle handle = snd_BankLoadEx(get_file_path(file), offset, 0, 0);
|
||||
snd_ResolveBankXREFS();
|
||||
PrintBankInfo(bank);
|
||||
bank->bank_handle = handle;
|
||||
|
@ -31,7 +31,7 @@ void LoadSoundBank(const char* bank_name, SoundBank* bank) {
|
||||
SleepThread(); // wait for finish.
|
||||
}
|
||||
|
||||
void LoadMusic(const char* music_name, s32* bank) {
|
||||
void LoadMusic(const char* music_name, snd::BankHandle* bank) {
|
||||
ASSERT(strlen(music_name) < 16);
|
||||
MusicLoadCommand cmd;
|
||||
cmd.cmd_id = LOAD_MUSIC;
|
||||
|
@ -7,7 +7,7 @@ struct SoundBank;
|
||||
namespace jak1 {
|
||||
struct VagDirEntry;
|
||||
void LoadSoundBank(const char* bank_name, SoundBank* bank);
|
||||
void LoadMusic(const char* music_name, s32* bank);
|
||||
void LoadMusic(const char* music_name, snd::BankHandle* bank);
|
||||
|
||||
void QueueVAGStream(FileRecord* file, VagDirEntry* vag, u32 sound_id, u32 unk);
|
||||
void PlayVAGStream(FileRecord* file,
|
||||
|
@ -58,7 +58,7 @@ struct IsoFs {
|
||||
uint32_t (*begin_read)(LoadStackEntry*, void*, int32_t); // 1c
|
||||
uint32_t (*sync_read)(); // 20
|
||||
uint32_t (*load_sound_bank)(char*, SoundBank*); // 24
|
||||
uint32_t (*load_music)(char*, s32*);
|
||||
uint32_t (*load_music)(char*, snd::BankHandle*);
|
||||
void (*poll_drive)();
|
||||
};
|
||||
|
||||
@ -173,7 +173,7 @@ struct SoundBankLoadCommand : public IsoMessage {
|
||||
|
||||
struct MusicLoadCommand : public IsoMessage {
|
||||
char music_name[16];
|
||||
s32* music_handle;
|
||||
snd::BankHandle* music_handle;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -374,7 +374,7 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
|
||||
case Jak1SoundCommand::UNLOAD_BANK: {
|
||||
SoundBank* bank = LookupBank(cmd->load_bank.bank_name);
|
||||
if (bank != nullptr) {
|
||||
s32 id = bank->bank_handle;
|
||||
snd::BankHandle id = bank->bank_handle;
|
||||
bank->bank_handle = 0;
|
||||
snd_UnloadBank(id);
|
||||
snd_ResolveBankXREFS();
|
||||
@ -403,7 +403,7 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
|
||||
}
|
||||
snd_UnloadBank(gMusic);
|
||||
snd_ResolveBankXREFS();
|
||||
gMusic = 0;
|
||||
gMusic = nullptr;
|
||||
}
|
||||
LoadMusic(cmd->load_bank.bank_name, &gMusic);
|
||||
SignalSema(gSema);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "game/overlord/common/isocommon.h"
|
||||
#include "game/overlord/jak2/pages.h"
|
||||
#include "game/sce/iop.h"
|
||||
#include "game/sound/sndshim.h"
|
||||
|
||||
namespace jak2 {
|
||||
void iso_init_globals();
|
||||
@ -48,7 +49,7 @@ struct IsoFs {
|
||||
int (*page_begin_read)(LoadStackEntry*, Buffer*); // 1c
|
||||
uint32_t (*sync_read)(); // 20
|
||||
uint32_t (*load_sound_bank)(char*, SoundBank*); // 24
|
||||
uint32_t (*load_music)(char*, s32*);
|
||||
uint32_t (*load_music)(char*, snd::BankHandle*);
|
||||
// void (*poll_drive)();
|
||||
};
|
||||
|
||||
@ -147,7 +148,7 @@ struct CmdLoadSoundBank {
|
||||
struct CmdLoadMusic {
|
||||
CmdHeader header;
|
||||
char name[16];
|
||||
s32* handle;
|
||||
snd::BankHandle* handle;
|
||||
};
|
||||
|
||||
struct VagDirEntry {
|
||||
|
@ -218,7 +218,7 @@ void LoadSoundBank(char* param_1, SoundBank* param_2) {
|
||||
SleepThread();
|
||||
}
|
||||
|
||||
void LoadMusic(char* param_1, s32* param_2) {
|
||||
void LoadMusic(char* param_1, snd::BankHandle* param_2) {
|
||||
CmdLoadMusic auStack88;
|
||||
|
||||
auStack88.header.cmd_kind = 0x380;
|
||||
@ -239,7 +239,7 @@ void LoadMusic(char* param_1, s32* param_2) {
|
||||
gMusicTweak = 0x80;
|
||||
}
|
||||
|
||||
void UnLoadMusic(s32* param_1) {
|
||||
void UnLoadMusic(snd::BankHandle* param_1) {
|
||||
gMusicFadeDir = -1;
|
||||
if (gMusicFade != 0) {
|
||||
do {
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/sound/sndshim.h"
|
||||
|
||||
struct SoundBank;
|
||||
struct FileRecord;
|
||||
namespace jak2 {
|
||||
@ -9,8 +11,8 @@ struct VagStrListNode;
|
||||
void SetVAGStreamPitch(int param_1, int param_2);
|
||||
void SetDialogVolume(int param_1);
|
||||
void LoadSoundBank(char* param_1, SoundBank* param_2);
|
||||
void UnLoadMusic(s32* param_1);
|
||||
void LoadMusic(char* param_1, s32* param_2);
|
||||
void UnLoadMusic(snd::BankHandle* param_1);
|
||||
void LoadMusic(char* param_1, snd::BankHandle* param_2);
|
||||
void QueueVAGStream(VagStrListNode* param_1);
|
||||
int LoadISOFileToEE(FileRecord* param_1, uint32_t param_2, int param_3);
|
||||
int LoadISOFileToIOP(FileRecord* fr, uint8_t* addr, int len);
|
||||
|
@ -77,7 +77,7 @@ LoadStackEntry* FS_OpenWad(FileRecord* fr, int offset);
|
||||
void FS_Close(LoadStackEntry* lse);
|
||||
int FS_PageBeginRead(LoadStackEntry* lse, Buffer* buffer);
|
||||
uint32_t FS_LoadSoundBank(char* name, SoundBank* buffer);
|
||||
uint32_t FS_LoadMusic(char* name, s32* buffer);
|
||||
uint32_t FS_LoadMusic(char* name, snd::BankHandle* buffer);
|
||||
u32 FS_SyncRead();
|
||||
|
||||
void iso_cd_init_globals() {
|
||||
@ -661,7 +661,7 @@ uint32_t FS_LoadSoundBank(char* name, SoundBank* buffer) {
|
||||
FileRecord* file = nullptr;
|
||||
char namebuf[16];
|
||||
char isoname[16];
|
||||
u32 handle;
|
||||
snd::BankHandle handle;
|
||||
|
||||
strncpy(namebuf, name, 12);
|
||||
namebuf[8] = 0;
|
||||
@ -680,11 +680,11 @@ uint32_t FS_LoadSoundBank(char* name, SoundBank* buffer) {
|
||||
return CMD_STATUS_DONE;
|
||||
}
|
||||
|
||||
uint32_t FS_LoadMusic(char* name, s32* bank_handle) {
|
||||
uint32_t FS_LoadMusic(char* name, snd::BankHandle* bank_handle) {
|
||||
FileRecord* file = nullptr;
|
||||
char namebuf[16];
|
||||
char isoname[16];
|
||||
u32 handle;
|
||||
snd::BankHandle handle;
|
||||
|
||||
strncpy(namebuf, name, 12);
|
||||
namebuf[8] = 0;
|
||||
|
@ -383,7 +383,7 @@ void* RPC_Loader(unsigned int /*fno*/, void* data, int size) {
|
||||
break;
|
||||
}
|
||||
|
||||
strncpy(bank->name, cmd->load_bank.bank_name, 16);
|
||||
strncpy(bank->name.data(), cmd->load_bank.bank_name, 16);
|
||||
bank->in_use = true;
|
||||
bank->unk4 = 0;
|
||||
LoadSoundBank(cmd->load_bank.bank_name, bank);
|
||||
|
@ -12,19 +12,13 @@ namespace snd {
|
||||
u64 SoundFlavaHack = 0;
|
||||
u8 GlobalExcite = 0;
|
||||
|
||||
ame_handler::ame_handler(MultiMIDIBlockHeader* block,
|
||||
voice_manager& vm,
|
||||
MIDISound& sound,
|
||||
AmeHandler::AmeHandler(MultiMidi* block,
|
||||
VoiceManager& vm,
|
||||
MusicBank::MIDISound& sound,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
locator& loc,
|
||||
SoundBank& bank)
|
||||
: m_sound(sound),
|
||||
m_bank(bank),
|
||||
m_header(block),
|
||||
m_locator(loc),
|
||||
m_vm(vm),
|
||||
m_repeats(sound.Repeats) {
|
||||
: m_sound(sound), m_bank(bank), m_header(block), m_vm(vm), m_repeats(sound.Repeats) {
|
||||
if (vol == VOLUME_DONT_CHANGE) {
|
||||
vol = 1024;
|
||||
}
|
||||
@ -41,12 +35,12 @@ ame_handler::ame_handler(MultiMIDIBlockHeader* block,
|
||||
m_pan = pan;
|
||||
}
|
||||
|
||||
start_segment(0);
|
||||
StartSegment(0);
|
||||
};
|
||||
|
||||
bool ame_handler::tick() {
|
||||
bool AmeHandler::Tick() {
|
||||
for (auto it = m_midis.begin(); it != m_midis.end();) {
|
||||
bool done = it->second->tick();
|
||||
bool done = it->second->Tick();
|
||||
if (done) {
|
||||
it = m_midis.erase(it);
|
||||
} else {
|
||||
@ -57,48 +51,47 @@ bool ame_handler::tick() {
|
||||
return m_midis.empty();
|
||||
};
|
||||
|
||||
void ame_handler::start_segment(u32 id) {
|
||||
void AmeHandler::StartSegment(u32 id) {
|
||||
if (m_midis.find(id) == m_midis.end()) {
|
||||
auto midiblock = (MIDIBlockHeader*)(m_header->BlockPtr[id] + (uintptr_t)m_header);
|
||||
auto sound_handler = (MIDISoundHandler*)((uintptr_t)midiblock + sizeof(MIDIBlockHeader));
|
||||
auto& midi = m_header->midi[id];
|
||||
|
||||
// Skip adding if not midi type
|
||||
u32 type = (sound_handler->OwnerID >> 24) & 0xf;
|
||||
u32 type = (midi.SoundHandle >> 24) & 0xf;
|
||||
if (type == 1 || type == 3) {
|
||||
m_midis.emplace(id, std::make_unique<midi_handler>(midiblock, m_vm, m_sound, m_vol, m_pan,
|
||||
m_locator, m_bank, this));
|
||||
m_midis.emplace(id, std::make_unique<MidiHandler>(static_cast<Midi*>(&midi), m_vm, m_sound,
|
||||
m_vol, m_pan, m_bank, this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ame_handler::stop() {
|
||||
void AmeHandler::Stop() {
|
||||
for (auto it = m_midis.begin(); it != m_midis.end();) {
|
||||
it->second->stop();
|
||||
it->second->Stop();
|
||||
it = m_midis.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void ame_handler::stop_segment(u32 id) {
|
||||
void AmeHandler::StopSegment(u32 id) {
|
||||
auto m = m_midis.find(id);
|
||||
if (m == m_midis.end())
|
||||
return;
|
||||
|
||||
m->second->stop();
|
||||
m->second->Stop();
|
||||
}
|
||||
|
||||
void ame_handler::pause() {
|
||||
void AmeHandler::Pause() {
|
||||
for (auto& m : m_midis) {
|
||||
m.second->pause();
|
||||
m.second->Pause();
|
||||
}
|
||||
}
|
||||
|
||||
void ame_handler::unpause() {
|
||||
void AmeHandler::Unpause() {
|
||||
for (auto& m : m_midis) {
|
||||
m.second->unpause();
|
||||
m.second->Unpause();
|
||||
}
|
||||
}
|
||||
|
||||
void ame_handler::set_vol_pan(s32 vol, s32 pan) {
|
||||
void AmeHandler::SetVolPan(s32 vol, s32 pan) {
|
||||
if (vol >= 0) {
|
||||
if (vol != VOLUME_DONT_CHANGE) {
|
||||
m_vol = (m_sound.Vol * vol) >> 10;
|
||||
@ -118,13 +111,13 @@ void ame_handler::set_vol_pan(s32 vol, s32 pan) {
|
||||
}
|
||||
|
||||
for (auto& m : m_midis) {
|
||||
m.second->set_vol_pan(vol, pan);
|
||||
m.second->SetVolPan(vol, pan);
|
||||
}
|
||||
}
|
||||
|
||||
void ame_handler::set_pmod(s32 mod) {
|
||||
void AmeHandler::SetPMod(s32 mod) {
|
||||
for (auto& m : m_midis) {
|
||||
m.second->set_pmod(mod);
|
||||
m.second->SetPMod(mod);
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,7 +134,7 @@ void ame_handler::set_pmod(s32 mod) {
|
||||
; \
|
||||
stream += (x);
|
||||
|
||||
std::pair<bool, u8*> ame_handler::run_ame(midi_handler& midi, u8* stream) {
|
||||
std::pair<bool, u8*> AmeHandler::RunAME(MidiHandler& midi, u8* stream) {
|
||||
int skip = 0;
|
||||
bool done = false;
|
||||
bool cont = true;
|
||||
@ -186,7 +179,7 @@ std::pair<bool, u8*> ame_handler::run_ame(midi_handler& midi, u8* stream) {
|
||||
} break;
|
||||
case 0x3: {
|
||||
AME_BEGIN(op)
|
||||
stop_segment(stream[0]);
|
||||
StopSegment(stream[0]);
|
||||
AME_END(1)
|
||||
} break;
|
||||
case 0x4: {
|
||||
@ -225,7 +218,7 @@ std::pair<bool, u8*> ame_handler::run_ame(midi_handler& midi, u8* stream) {
|
||||
} break;
|
||||
case 0xc: {
|
||||
AME_BEGIN(op)
|
||||
auto [sub_cont, ptr] = run_ame(midi, m_macro[stream[0]]);
|
||||
auto [sub_cont, ptr] = RunAME(midi, m_macro[stream[0]]);
|
||||
if (!sub_cont) {
|
||||
cont = false;
|
||||
done = true;
|
||||
@ -236,12 +229,12 @@ std::pair<bool, u8*> ame_handler::run_ame(midi_handler& midi, u8* stream) {
|
||||
AME_BEGIN(op)
|
||||
cont = false;
|
||||
done = true;
|
||||
start_segment(m_register[stream[0]] - 1);
|
||||
StartSegment(m_register[stream[0]] - 1);
|
||||
AME_END(1)
|
||||
} break;
|
||||
case 0xe: {
|
||||
AME_BEGIN(op)
|
||||
start_segment(m_register[stream[0]] - 1);
|
||||
StartSegment(m_register[stream[0]] - 1);
|
||||
AME_END(1)
|
||||
} break;
|
||||
case 0xf: {
|
||||
@ -285,9 +278,9 @@ std::pair<bool, u8*> ame_handler::run_ame(midi_handler& midi, u8* stream) {
|
||||
// note : added hack here! :-)
|
||||
if (!SoundFlavaHack &&
|
||||
(comp < m_groups[group].excite_min[i] || comp > m_groups[group].excite_max[i])) {
|
||||
midi.mute_channel(m_groups[group].channel[i]);
|
||||
midi.MuteChannel(m_groups[group].channel[i]);
|
||||
} else {
|
||||
midi.unmute_channel(m_groups[group].channel[i]);
|
||||
midi.UnmuteChannel(m_groups[group].channel[i]);
|
||||
}
|
||||
}
|
||||
AME_END(1)
|
||||
@ -296,12 +289,12 @@ std::pair<bool, u8*> ame_handler::run_ame(midi_handler& midi, u8* stream) {
|
||||
AME_BEGIN(op)
|
||||
done = true;
|
||||
cont = false;
|
||||
start_segment(stream[0]);
|
||||
StartSegment(stream[0]);
|
||||
AME_END(1)
|
||||
} break;
|
||||
case 0x12: {
|
||||
AME_BEGIN(op)
|
||||
start_segment(stream[0]);
|
||||
StartSegment(stream[0]);
|
||||
AME_END(1)
|
||||
} break;
|
||||
case 0x13: {
|
||||
@ -334,7 +327,7 @@ std::pair<bool, u8*> ame_handler::run_ame(midi_handler& midi, u8* stream) {
|
||||
AME_END(2)
|
||||
} break;
|
||||
default: {
|
||||
throw ame_error(fmt::format("Unhandled AME event {:02x}", (u8)op));
|
||||
throw AMEError(fmt::format("Unhandled AME event {:02x}", (u8)op));
|
||||
} break;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "midi_handler.h"
|
||||
#include "sound_handler.h"
|
||||
#include "vagvoice.h"
|
||||
#include "musicbank.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
@ -17,34 +18,33 @@ extern u64 SoundFlavaHack;
|
||||
|
||||
extern u8 GlobalExcite;
|
||||
|
||||
class midi_handler;
|
||||
class ame_handler : public sound_handler {
|
||||
friend class midi_handler;
|
||||
class MidiHandler;
|
||||
class AmeHandler : public SoundHandler {
|
||||
friend class MidiHandler;
|
||||
|
||||
public:
|
||||
ame_handler(MultiMIDIBlockHeader* block,
|
||||
voice_manager& vm,
|
||||
MIDISound& sound,
|
||||
AmeHandler(MultiMidi* block,
|
||||
VoiceManager& vm,
|
||||
MusicBank::MIDISound& sound,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
locator& loc,
|
||||
SoundBank& bank);
|
||||
bool tick() override;
|
||||
SoundBank& bank() override { return m_bank; };
|
||||
bool Tick() override;
|
||||
SoundBank& Bank() override { return m_bank; };
|
||||
|
||||
void pause() override;
|
||||
void unpause() override;
|
||||
void stop() override;
|
||||
u8 group() override { return m_sound.VolGroup; };
|
||||
void set_vol_pan(s32 vol, s32 pan) override;
|
||||
void Pause() override;
|
||||
void Unpause() override;
|
||||
void Stop() override;
|
||||
u8 Group() override { return m_sound.VolGroup; };
|
||||
void SetVolPan(s32 vol, s32 pan) override;
|
||||
|
||||
void set_register(u8 reg, u8 value) override { m_register[reg] = value; }
|
||||
void set_pmod(s32 mod) override;
|
||||
void SetRegister(u8 reg, u8 value) override { m_register[reg] = value; }
|
||||
void SetPMod(s32 mod) override;
|
||||
|
||||
private:
|
||||
struct ame_error : public std::exception {
|
||||
ame_error(std::string text) : msg(std::move(text)) {}
|
||||
ame_error() : msg("Unknown AME error") {}
|
||||
struct AMEError : public std::exception {
|
||||
AMEError(std::string text) : msg(std::move(text)) {}
|
||||
AMEError() : msg("Unknown AME error") {}
|
||||
std::string msg;
|
||||
const char* what() const noexcept override { return msg.c_str(); }
|
||||
};
|
||||
@ -58,21 +58,20 @@ class ame_handler : public sound_handler {
|
||||
/* 24 */ s8 excite_max[16];
|
||||
};
|
||||
|
||||
void start_segment(u32 id);
|
||||
void stop_segment(u32 id);
|
||||
std::pair<bool, u8*> run_ame(midi_handler&, u8* stream);
|
||||
void StartSegment(u32 id);
|
||||
void StopSegment(u32 id);
|
||||
std::pair<bool, u8*> RunAME(MidiHandler&, u8* stream);
|
||||
|
||||
MIDISound& m_sound;
|
||||
MusicBank::MIDISound& m_sound;
|
||||
SoundBank& m_bank;
|
||||
|
||||
MultiMIDIBlockHeader* m_header{nullptr};
|
||||
locator& m_locator;
|
||||
voice_manager& m_vm;
|
||||
MultiMidi* m_header{nullptr};
|
||||
VoiceManager& m_vm;
|
||||
s32 m_vol{0};
|
||||
s32 m_pan{0};
|
||||
s8 m_repeats{0};
|
||||
|
||||
std::unordered_map<u32, std::unique_ptr<midi_handler>> m_midis;
|
||||
std::unordered_map<u32, std::unique_ptr<MidiHandler>> m_midis;
|
||||
|
||||
std::array<GroupDescription, 16> m_groups{};
|
||||
std::array<u8, 16> m_register{};
|
||||
|
@ -10,15 +10,111 @@
|
||||
namespace snd {
|
||||
std::array<s8, 32> g_block_reg{};
|
||||
|
||||
bool blocksound_handler::tick() {
|
||||
m_voices.remove_if([](std::weak_ptr<blocksound_voice>& p) { return p.expired(); });
|
||||
BlockSoundHandler::BlockSoundHandler(SoundBank& bank,
|
||||
SFXBlock::SFX& sfx,
|
||||
VoiceManager& vm,
|
||||
s32 sfx_vol,
|
||||
s32 sfx_pan,
|
||||
SndPlayParams& params)
|
||||
: m_group(sfx.VolGroup), m_sfx(sfx), m_vm(vm), m_bank(bank) {
|
||||
s32 vol, pan, pitch_mod, pitch_bend;
|
||||
if (sfx_vol == -1) {
|
||||
sfx_vol = sfx.Vol;
|
||||
}
|
||||
if (sfx_pan == -1) {
|
||||
sfx_pan = sfx.Pan;
|
||||
}
|
||||
|
||||
if (params.vol.has_value()) {
|
||||
vol = params.vol.value();
|
||||
} else {
|
||||
vol = 1024;
|
||||
}
|
||||
|
||||
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_pb = pitch_bend;
|
||||
m_cur_pm = pitch_mod;
|
||||
|
||||
m_app_volume = vol;
|
||||
m_app_pan = pan;
|
||||
m_app_pb = pitch_bend;
|
||||
m_app_pm = pitch_mod;
|
||||
|
||||
m_lfo_volume = 0;
|
||||
m_lfo_pan = 0;
|
||||
m_lfo_pb = 0;
|
||||
m_lfo_pm = 0;
|
||||
|
||||
if (params.registers.has_value()) {
|
||||
m_registers = params.registers.value();
|
||||
}
|
||||
|
||||
// Figure this stuff out properly someday
|
||||
// if (m_sfx.d.Flags & 2) {
|
||||
// fmt::print("solo flag\n");
|
||||
// m_done = true;
|
||||
// return;
|
||||
// }
|
||||
|
||||
m_next_grain = 0;
|
||||
m_countdown = m_sfx.Grains[0].Delay;
|
||||
while (m_countdown <= 0 && !m_done) {
|
||||
DoGrain();
|
||||
}
|
||||
}
|
||||
|
||||
BlockSoundHandler::~BlockSoundHandler() {
|
||||
for (auto& p : m_voices) {
|
||||
auto v = p.lock();
|
||||
if (v != nullptr) {
|
||||
v->Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BlockSoundHandler::Tick() {
|
||||
m_voices.remove_if([](std::weak_ptr<BlockSoundVoice>& p) { return p.expired(); });
|
||||
|
||||
for (auto& lfo : m_lfo) {
|
||||
lfo.tick();
|
||||
lfo.Tick();
|
||||
}
|
||||
|
||||
for (auto it = m_children.begin(); it != m_children.end();) {
|
||||
bool done = it->get()->tick();
|
||||
bool done = it->get()->Tick();
|
||||
if (done) {
|
||||
it = m_children.erase(it);
|
||||
} else {
|
||||
@ -39,17 +135,17 @@ bool blocksound_handler::tick() {
|
||||
|
||||
m_countdown--;
|
||||
while (m_countdown <= 0 && !m_done) {
|
||||
do_grain();
|
||||
DoGrain();
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
void blocksound_handler::pause() {
|
||||
void BlockSoundHandler::Pause() {
|
||||
m_paused = true;
|
||||
|
||||
for (auto& c : m_children) {
|
||||
c->pause();
|
||||
c->Pause();
|
||||
}
|
||||
|
||||
for (auto& p : m_voices) {
|
||||
@ -58,15 +154,15 @@ void blocksound_handler::pause() {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_vm.pause(voice);
|
||||
m_vm.Pause(voice);
|
||||
}
|
||||
}
|
||||
|
||||
void blocksound_handler::unpause() {
|
||||
void BlockSoundHandler::Unpause() {
|
||||
m_paused = false;
|
||||
|
||||
for (auto& c : m_children) {
|
||||
c->unpause();
|
||||
c->Unpause();
|
||||
}
|
||||
|
||||
for (auto& p : m_voices) {
|
||||
@ -75,15 +171,15 @@ void blocksound_handler::unpause() {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_vm.unpause(voice);
|
||||
m_vm.Unpause(voice);
|
||||
}
|
||||
}
|
||||
|
||||
void blocksound_handler::stop() {
|
||||
void BlockSoundHandler::Stop() {
|
||||
m_done = true;
|
||||
|
||||
for (auto& c : m_children) {
|
||||
c->stop();
|
||||
c->Stop();
|
||||
}
|
||||
|
||||
for (auto& p : m_voices) {
|
||||
@ -92,11 +188,11 @@ void blocksound_handler::stop() {
|
||||
continue;
|
||||
}
|
||||
|
||||
voice->key_off();
|
||||
voice->KeyOff();
|
||||
}
|
||||
}
|
||||
|
||||
void blocksound_handler::set_vol_pan(s32 vol, s32 pan) {
|
||||
void BlockSoundHandler::SetVolPan(s32 vol, s32 pan) {
|
||||
if (vol >= 0) {
|
||||
if (vol != VOLUME_DONT_CHANGE) {
|
||||
m_app_volume = vol;
|
||||
@ -106,7 +202,7 @@ void blocksound_handler::set_vol_pan(s32 vol, s32 pan) {
|
||||
}
|
||||
|
||||
if (pan == PAN_RESET) {
|
||||
m_app_pan = m_sfx.d.Pan;
|
||||
m_app_pan = m_sfx.Pan;
|
||||
} else if (pan != PAN_DONT_CHANGE) {
|
||||
m_app_pan = pan;
|
||||
}
|
||||
@ -127,7 +223,7 @@ void blocksound_handler::set_vol_pan(s32 vol, s32 pan) {
|
||||
m_cur_pan = new_pan;
|
||||
|
||||
for (auto& c : m_children) {
|
||||
c->set_vol_pan(m_app_volume * m_orig_volume / 127, pan);
|
||||
c->SetVolPan(m_app_volume * m_orig_volume / 127, pan);
|
||||
}
|
||||
|
||||
for (auto& p : m_voices) {
|
||||
@ -136,16 +232,16 @@ void blocksound_handler::set_vol_pan(s32 vol, s32 pan) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto volume = m_vm.make_volume(127, 0, m_cur_volume, m_cur_pan, voice->g_vol, voice->g_pan);
|
||||
auto left = m_vm.adjust_vol_to_group(volume.left, m_group);
|
||||
auto right = m_vm.adjust_vol_to_group(volume.right, m_group);
|
||||
auto volume = m_vm.MakeVolume(127, 0, m_cur_volume, m_cur_pan, voice->g_vol, voice->g_pan);
|
||||
auto left = m_vm.AdjustVolToGroup(volume.left, m_group);
|
||||
auto right = m_vm.AdjustVolToGroup(volume.right, m_group);
|
||||
|
||||
voice->set_volume(left >> 1, right >> 1);
|
||||
voice->SetVolume(left >> 1, right >> 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void blocksound_handler::update_pitch() {
|
||||
void BlockSoundHandler::UpdatePitch() {
|
||||
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);
|
||||
|
||||
@ -155,34 +251,34 @@ void blocksound_handler::update_pitch() {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto note = pitchbend(voice->tone, m_cur_pb, m_cur_pm, m_note, m_fine);
|
||||
auto note = PitchBend(voice->tone, m_cur_pb, m_cur_pm, m_note, m_fine);
|
||||
auto pitch =
|
||||
PS1Note2Pitch(voice->tone.CenterNote, voice->tone.CenterFine, note.first, note.second);
|
||||
|
||||
voice->set_pitch(pitch);
|
||||
voice->SetPitch(pitch);
|
||||
}
|
||||
}
|
||||
|
||||
void blocksound_handler::set_pmod(s32 mod) {
|
||||
void BlockSoundHandler::SetPMod(s32 mod) {
|
||||
for (auto& c : m_children) {
|
||||
c->set_pmod(mod);
|
||||
c->SetPMod(mod);
|
||||
}
|
||||
m_app_pm = mod;
|
||||
update_pitch();
|
||||
UpdatePitch();
|
||||
}
|
||||
|
||||
void blocksound_handler::set_pbend(s32 bend) {
|
||||
void BlockSoundHandler::SetPBend(s32 bend) {
|
||||
for (auto& c : m_children) {
|
||||
c->set_pbend(bend);
|
||||
c->SetPBend(bend);
|
||||
}
|
||||
m_app_pb = bend;
|
||||
update_pitch();
|
||||
UpdatePitch();
|
||||
}
|
||||
|
||||
void blocksound_handler::do_grain() {
|
||||
auto& grain = m_sfx.grains[m_next_grain];
|
||||
void BlockSoundHandler::DoGrain() {
|
||||
auto& grain = m_sfx.Grains[m_next_grain];
|
||||
|
||||
s32 ret = grain->execute(*this);
|
||||
s32 ret = grain(*this);
|
||||
|
||||
if (m_skip_grains) {
|
||||
m_grains_to_play--;
|
||||
@ -193,12 +289,12 @@ void blocksound_handler::do_grain() {
|
||||
}
|
||||
|
||||
m_next_grain++;
|
||||
if (m_next_grain >= m_sfx.grains.size()) {
|
||||
if (m_next_grain >= m_sfx.Grains.size()) {
|
||||
m_done = true;
|
||||
return;
|
||||
}
|
||||
|
||||
m_countdown = m_sfx.grains[m_next_grain]->delay() + ret;
|
||||
m_countdown = m_sfx.Grains[m_next_grain].Delay + ret;
|
||||
}
|
||||
|
||||
} // namespace snd
|
||||
|
@ -5,132 +5,44 @@
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/sound/989snd/lfo.h"
|
||||
#include "sfxblock2.h"
|
||||
#include "game/sound/989snd/sfxblock.h"
|
||||
|
||||
namespace snd {
|
||||
|
||||
extern std::array<s8, 32> g_block_reg;
|
||||
|
||||
class blocksound_voice : public vag_voice {
|
||||
class BlockSoundVoice : public VagVoice {
|
||||
public:
|
||||
blocksound_voice(Tone& t) : vag_voice(t) {}
|
||||
BlockSoundVoice(Tone& t) : VagVoice(t) {}
|
||||
s32 g_vol;
|
||||
s32 g_pan;
|
||||
};
|
||||
|
||||
class blocksound_handler : public sound_handler {
|
||||
class BlockSoundHandler : public SoundHandler {
|
||||
public:
|
||||
blocksound_handler(SoundBank& bank,
|
||||
SFX2& sfx,
|
||||
voice_manager& vm,
|
||||
BlockSoundHandler(SoundBank& bank,
|
||||
SFXBlock::SFX& sfx,
|
||||
VoiceManager& vm,
|
||||
s32 sfx_vol,
|
||||
s32 sfx_pan,
|
||||
SndPlayParams& params)
|
||||
: m_group(sfx.d.VolGroup), 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;
|
||||
}
|
||||
SndPlayParams& params);
|
||||
|
||||
if (params.vol.has_value()) {
|
||||
vol = params.vol.value();
|
||||
} else {
|
||||
vol = 1024;
|
||||
}
|
||||
~BlockSoundHandler() override;
|
||||
bool Tick() override;
|
||||
SoundBank& Bank() override { return m_bank; };
|
||||
|
||||
if (params.pan.has_value()) {
|
||||
pan = params.pan.value();
|
||||
} else {
|
||||
pan = -1;
|
||||
}
|
||||
void Pause() override;
|
||||
void Unpause() override;
|
||||
void Stop() override;
|
||||
u8 Group() override { return m_group; };
|
||||
void SetVolPan(s32 vol, s32 pan) override;
|
||||
void SetPMod(s32 mod) override;
|
||||
void SetRegister(u8 reg, u8 value) override { m_registers.at(reg) = value; };
|
||||
void SetPBend(s32 bend) override;
|
||||
|
||||
if (params.pitch_mod.has_value()) {
|
||||
pitch_mod = params.pitch_mod.value();
|
||||
} else {
|
||||
pitch_mod = 0;
|
||||
}
|
||||
void DoGrain();
|
||||
|
||||
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_pb = pitch_bend;
|
||||
m_cur_pm = pitch_mod;
|
||||
|
||||
m_app_volume = vol;
|
||||
m_app_pan = pan;
|
||||
m_app_pb = pitch_bend;
|
||||
m_app_pm = pitch_mod;
|
||||
|
||||
m_lfo_volume = 0;
|
||||
m_lfo_pan = 0;
|
||||
m_lfo_pb = 0;
|
||||
m_lfo_pm = 0;
|
||||
|
||||
if (params.registers.has_value()) {
|
||||
m_registers = params.registers.value();
|
||||
}
|
||||
|
||||
// Figure this stuff out properly someday
|
||||
// if (m_sfx.d.Flags & 2) {
|
||||
// fmt::print("solo flag\n");
|
||||
// m_done = true;
|
||||
// return;
|
||||
// }
|
||||
|
||||
m_next_grain = 0;
|
||||
m_countdown = m_sfx.grains[0]->delay();
|
||||
while (m_countdown <= 0 && !m_done) {
|
||||
do_grain();
|
||||
}
|
||||
}
|
||||
|
||||
~blocksound_handler() override {
|
||||
for (auto& p : m_voices) {
|
||||
auto v = p.lock();
|
||||
if (v != nullptr) {
|
||||
v->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool tick() override;
|
||||
SoundBank& bank() override { return m_bank; };
|
||||
|
||||
void pause() override;
|
||||
void unpause() override;
|
||||
void stop() override;
|
||||
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) override;
|
||||
|
||||
void do_grain();
|
||||
|
||||
void update_pitch();
|
||||
void UpdatePitch();
|
||||
|
||||
bool m_paused{false};
|
||||
|
||||
@ -141,12 +53,12 @@ class blocksound_handler : public sound_handler {
|
||||
u32 m_grains_to_skip{0};
|
||||
bool m_skip_grains{false};
|
||||
|
||||
SFX2& m_sfx;
|
||||
voice_manager& m_vm;
|
||||
SFXBlock::SFX& m_sfx;
|
||||
VoiceManager& m_vm;
|
||||
|
||||
std::list<std::weak_ptr<blocksound_voice>> m_voices;
|
||||
std::list<std::weak_ptr<BlockSoundVoice>> m_voices;
|
||||
|
||||
std::list<std::unique_ptr<sound_handler>> m_children;
|
||||
std::list<std::unique_ptr<SoundHandler>> m_children;
|
||||
|
||||
s32 m_orig_volume{0};
|
||||
s32 m_orig_pan{0};
|
||||
@ -172,8 +84,6 @@ class blocksound_handler : public sound_handler {
|
||||
std::array<s8, 4> m_registers{};
|
||||
std::array<LFOTracker, 4> m_lfo{{*this, *this, *this, *this}};
|
||||
|
||||
// TODO LFO
|
||||
|
||||
s32 m_countdown{0};
|
||||
u32 m_next_grain{0};
|
||||
};
|
||||
|
@ -9,24 +9,24 @@
|
||||
|
||||
namespace snd {
|
||||
|
||||
class id_allocator {
|
||||
class IdAllocator {
|
||||
public:
|
||||
u32 get_id() {
|
||||
u32 GetId() {
|
||||
u32 id = 0;
|
||||
if (m_free_ids.empty()) {
|
||||
id = next_id++;
|
||||
if (mFreeIds.empty()) {
|
||||
id = mNextId++;
|
||||
} else {
|
||||
id = m_free_ids.front();
|
||||
m_free_ids.pop();
|
||||
id = mFreeIds.front();
|
||||
mFreeIds.pop();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void free_id(u32 id) { m_free_ids.push(id); }
|
||||
void FreeId(u32 id) { mFreeIds.push(id); }
|
||||
|
||||
private:
|
||||
u32 next_id{1};
|
||||
std::queue<u32> m_free_ids;
|
||||
u32 mNextId{1};
|
||||
std::queue<u32> mFreeIds;
|
||||
};
|
||||
|
||||
} // namespace snd
|
||||
|
@ -7,79 +7,79 @@
|
||||
namespace snd {
|
||||
#include "lfo_sine.c.inc"
|
||||
|
||||
void LFOTracker::init() {
|
||||
if (m_type == lfo_type::RAND) {
|
||||
void LFOTracker::Init() {
|
||||
if (m_type == LFOType::RAND) {
|
||||
m_state_hold1 = -(rand() & 0x7fff) * (rand() & 1);
|
||||
m_state_hold2 = 1;
|
||||
}
|
||||
|
||||
calc_depth();
|
||||
tick();
|
||||
CalcDepth();
|
||||
Tick();
|
||||
}
|
||||
|
||||
void LFOTracker::calc_depth() {
|
||||
if (m_target == lfo_target::VOLUME) {
|
||||
m_range = (m_handler.m_sfx.d.Vol * m_depth) >> 10;
|
||||
void LFOTracker::CalcDepth() {
|
||||
if (m_target == LFOTarget::VOLUME) {
|
||||
m_range = (m_handler.m_sfx.Vol * m_depth) >> 10;
|
||||
}
|
||||
if (m_target == lfo_target::PAN) {
|
||||
if (m_target == LFOTarget::PAN) {
|
||||
m_range = (180 * m_depth) >> 10;
|
||||
}
|
||||
if (m_target == lfo_target::PMOD) {
|
||||
if (m_target == LFOTarget::PMOD) {
|
||||
m_range = (6096 * m_depth) >> 10;
|
||||
}
|
||||
if (m_target == lfo_target::PBEND) {
|
||||
if (m_target == LFOTarget::PBEND) {
|
||||
m_range = (0x7fff * m_depth) >> 10;
|
||||
}
|
||||
|
||||
m_last_lfo = 0;
|
||||
}
|
||||
|
||||
void LFOTracker::tick() {
|
||||
void LFOTracker::Tick() {
|
||||
m_tick++;
|
||||
|
||||
if (m_target == lfo_target::NONE || (m_tick & 1) == 0) {
|
||||
if (m_target == LFOTarget::NONE || (m_tick & 1) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_target) {
|
||||
case lfo_target::VOLUME: {
|
||||
s32 vol = (m_range * (get_lfo(2) - 0x7fff)) >> 16;
|
||||
case LFOTarget::VOLUME: {
|
||||
s32 vol = (m_range * (GetLFO(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);
|
||||
m_handler.SetVolPan(VOLUME_DONT_CHANGE, PAN_DONT_CHANGE);
|
||||
}
|
||||
} break;
|
||||
case lfo_target::PAN: {
|
||||
s32 pan = (m_range * get_lfo(2)) >> 15;
|
||||
case LFOTarget::PAN: {
|
||||
s32 pan = (m_range * GetLFO(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);
|
||||
m_handler.SetVolPan(VOLUME_DONT_CHANGE, PAN_DONT_CHANGE);
|
||||
}
|
||||
} break;
|
||||
case lfo_target::PMOD: {
|
||||
s32 pm = (get_lfo(2) * m_range) >> 15;
|
||||
case LFOTarget::PMOD: {
|
||||
s32 pm = (GetLFO(2) * m_range) >> 15;
|
||||
if (m_handler.m_lfo_pm != pm) {
|
||||
m_handler.m_lfo_pm = pm;
|
||||
m_handler.update_pitch();
|
||||
m_handler.UpdatePitch();
|
||||
}
|
||||
} break;
|
||||
case lfo_target::PBEND: {
|
||||
s32 pb = (get_lfo(2) * m_range) >> 15;
|
||||
case LFOTarget::PBEND: {
|
||||
s32 pb = (GetLFO(2) * m_range) >> 15;
|
||||
if (m_handler.m_lfo_pb != pb) {
|
||||
m_handler.m_lfo_pb = pb;
|
||||
m_handler.update_pitch();
|
||||
m_handler.UpdatePitch();
|
||||
}
|
||||
} break;
|
||||
case lfo_target::UNK1: {
|
||||
case LFOTarget::UNK1: {
|
||||
} break;
|
||||
case lfo_target::UNK2: {
|
||||
case LFOTarget::UNK2: {
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s32 LFOTracker::get_lfo(s32 step_mult) {
|
||||
s32 LFOTracker::GetLFO(s32 step_mult) {
|
||||
s32 step = m_next_step >> 16;
|
||||
m_next_step += step_mult * m_step_size;
|
||||
if (m_next_step > 0x7ffffff) {
|
||||
@ -89,20 +89,20 @@ s32 LFOTracker::get_lfo(s32 step_mult) {
|
||||
s32 ret = 0;
|
||||
|
||||
switch (m_type) {
|
||||
case lfo_type::OFF:
|
||||
case LFOType::OFF:
|
||||
ret = 0;
|
||||
break;
|
||||
case lfo_type::SINE:
|
||||
case LFOType::SINE:
|
||||
ret = gLFO_sine.at(step);
|
||||
break;
|
||||
case lfo_type::SQUARE:
|
||||
case LFOType::SQUARE:
|
||||
if (step >= m_state_hold1) {
|
||||
ret = -32767;
|
||||
} else {
|
||||
ret = 32767;
|
||||
}
|
||||
break;
|
||||
case lfo_type::TRIANGLE:
|
||||
case LFOType::TRIANGLE:
|
||||
if (step < 512) {
|
||||
ret = 0x7fff * step / 512;
|
||||
} else if (step >= 1536) {
|
||||
@ -111,14 +111,14 @@ s32 LFOTracker::get_lfo(s32 step_mult) {
|
||||
ret = 0x7fff - 65534 * (step - 512) / 1024;
|
||||
}
|
||||
break;
|
||||
case lfo_type::SAW:
|
||||
case LFOType::SAW:
|
||||
if (step >= 1024) {
|
||||
ret = 0x7fff * (step - 1024) / 1024 - 0x7fff;
|
||||
} else {
|
||||
ret = 0x7fff * step / 1023;
|
||||
}
|
||||
break;
|
||||
case lfo_type::RAND:
|
||||
case LFOType::RAND:
|
||||
if (step >= 1024 && m_state_hold2 == 1) {
|
||||
m_state_hold2 = 0;
|
||||
m_state_hold1 = 2 * ((rand() & 0x7fff) - 0x3fff);
|
||||
|
@ -5,16 +5,16 @@
|
||||
|
||||
namespace snd {
|
||||
|
||||
enum class lfo_type { OFF, SINE, SQUARE, TRIANGLE, SAW, RAND };
|
||||
enum class lfo_target { NONE, VOLUME, PAN, PMOD, PBEND, UNK1, UNK2 };
|
||||
enum class LFOType { OFF, SINE, SQUARE, TRIANGLE, SAW, RAND };
|
||||
enum class LFOTarget { NONE, VOLUME, PAN, PMOD, PBEND, UNK1, UNK2 };
|
||||
|
||||
class blocksound_handler;
|
||||
class BlockSoundHandler;
|
||||
|
||||
class LFOTracker {
|
||||
public:
|
||||
LFOTracker(blocksound_handler& handler) : m_handler(handler) {}
|
||||
lfo_type m_type{lfo_type::OFF};
|
||||
lfo_target m_target{0};
|
||||
LFOTracker(BlockSoundHandler& handler) : m_handler(handler) {}
|
||||
LFOType m_type{LFOType::OFF};
|
||||
LFOTarget m_target{0};
|
||||
u8 m_target_extra{0};
|
||||
u8 m_setup_flags{0};
|
||||
u8 m_running_flags{0};
|
||||
@ -30,12 +30,12 @@ class LFOTracker {
|
||||
|
||||
u32 m_tick{0};
|
||||
|
||||
void init();
|
||||
void calc_depth();
|
||||
void tick();
|
||||
s32 get_lfo(s32 step_mult);
|
||||
void Init();
|
||||
void CalcDepth();
|
||||
void Tick();
|
||||
s32 GetLFO(s32 step_mult);
|
||||
|
||||
blocksound_handler& m_handler;
|
||||
BlockSoundHandler& m_handler;
|
||||
};
|
||||
|
||||
} // namespace snd
|
||||
|
@ -5,121 +5,460 @@
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
|
||||
#include "midi_handler.h"
|
||||
#include "sfxblock.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/BinaryReader.h"
|
||||
|
||||
#include "sfxblock2.h"
|
||||
#include "game/sound/989snd/musicbank.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
namespace snd {
|
||||
|
||||
enum chunk : u32 { bank, samples, midi };
|
||||
|
||||
#define FOURCC(a, b, c, d) ((u32)(((d) << 24) | ((c) << 16) | ((b) << 8) | (a)))
|
||||
|
||||
u32 loader::read_bank(std::fstream& in) {
|
||||
size_t origin = in.tellg();
|
||||
FileAttributes<3> attr;
|
||||
in.read((char*)(&attr), sizeof(attr));
|
||||
|
||||
if (attr.type != 1 && attr.type != 3) {
|
||||
lg::error("Error: File type {} not supported.", attr.type);
|
||||
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) {
|
||||
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_tag = (BankTag*)bank_buf.get();
|
||||
|
||||
u32 bank_id = m_id_allocator.get_id();
|
||||
std::unique_ptr<SoundBank> bank;
|
||||
|
||||
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);
|
||||
in.read((char*)samples.get(), attr.where[chunk::samples].size);
|
||||
load_samples(bank_id, std::move(samples));
|
||||
}
|
||||
|
||||
if (attr.num_chunks >= 3) {
|
||||
in.seekg(origin + attr.where[chunk::midi].offset, std::fstream::beg);
|
||||
load_midi(in);
|
||||
}
|
||||
|
||||
return bank_id;
|
||||
inline static constexpr u32 fourcc(std::string_view p) {
|
||||
return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0];
|
||||
}
|
||||
|
||||
void loader::load_midi(std::fstream& in) {
|
||||
FileAttributes<1> attr;
|
||||
u32 cur = in.tellg();
|
||||
void FileAttributes::Read(BinaryReader& data) {
|
||||
type = data.read<u32>();
|
||||
num_chunks = data.read<u32>();
|
||||
|
||||
in.read((char*)&attr, sizeof(attr));
|
||||
in.seekg(cur + attr.where[0].offset, std::fstream::beg);
|
||||
|
||||
auto midi = std::make_unique<u8[]>(attr.where[0].size);
|
||||
in.read((char*)midi.get(), attr.where[0].size);
|
||||
|
||||
auto h = (MIDIBlock*)midi.get();
|
||||
lg::info("Loaded midi {:.4}", (char*)&h->ID);
|
||||
|
||||
m_midi.emplace(h->ID, (MIDIBlock*)midi.get());
|
||||
m_midi_chunks.emplace_back(std::move(midi));
|
||||
where.resize(num_chunks);
|
||||
for (int i = 0; i < num_chunks; i++) {
|
||||
where[i].offset = data.read<u32>();
|
||||
where[i].size = data.read<u32>();
|
||||
}
|
||||
}
|
||||
|
||||
SoundBank* loader::get_bank_by_handle(u32 id) {
|
||||
if (m_soundbanks.find(id) == m_soundbanks.end()) {
|
||||
static Tone ReadTone(BinaryReader& data, u8* samples) {
|
||||
Tone tone{};
|
||||
|
||||
tone.Priority = data.read<s8>();
|
||||
tone.Vol = data.read<s8>();
|
||||
tone.CenterNote = data.read<s8>();
|
||||
tone.CenterFine = data.read<s8>();
|
||||
tone.Pan = data.read<s16>();
|
||||
tone.MapLow = data.read<s8>();
|
||||
tone.MapHigh = data.read<s8>();
|
||||
tone.PBLow = data.read<s8>();
|
||||
tone.PBHigh = data.read<s8>();
|
||||
tone.ADSR1 = data.read<u16>();
|
||||
tone.ADSR2 = data.read<u16>();
|
||||
tone.Flags = data.read<u16>();
|
||||
u32 SampleOffset = data.read<u32>();
|
||||
tone.Sample = &samples[SampleOffset];
|
||||
|
||||
data.read<u32>(); // reserved1
|
||||
|
||||
return tone;
|
||||
};
|
||||
|
||||
static Midi ReadMidi(BinaryReader& data) {
|
||||
Midi mid;
|
||||
u8* base = const_cast<u8*>(data.here());
|
||||
|
||||
mid.DataID = data.read<u32>();
|
||||
mid.Version = data.read<s16>();
|
||||
mid.Flags = data.read<s8>();
|
||||
data.read<s8>();
|
||||
mid.ID = data.read<u32>();
|
||||
data.read<u32>();
|
||||
mid.BankID = data.read<u32>();
|
||||
data.read<u32>();
|
||||
u32 DataStart = data.read<u32>();
|
||||
mid.DataStart = base + DataStart;
|
||||
data.read<u32>();
|
||||
mid.Tempo = data.read<u32>();
|
||||
mid.PPQ = data.read<s32>();
|
||||
|
||||
return mid;
|
||||
}
|
||||
|
||||
static MultiMidi ReadMultiMidi(BinaryReader& data) {
|
||||
MultiMidi mmid;
|
||||
|
||||
mmid.DataID = data.read<u32>();
|
||||
mmid.Version = data.read<s16>();
|
||||
mmid.Flags = data.read<s8>();
|
||||
s8 NumSegments = data.read<s8>();
|
||||
mmid.midi.resize(NumSegments);
|
||||
mmid.ID = data.read<u32>();
|
||||
data.read<u32>();
|
||||
|
||||
for (auto& midi : mmid.midi) {
|
||||
u32 offset = data.read<u32>();
|
||||
auto mr = data.at(offset);
|
||||
midi.Midi::operator=(ReadMidi(mr));
|
||||
midi.SoundHandle = mr.read<u32>();
|
||||
}
|
||||
|
||||
return mmid;
|
||||
}
|
||||
|
||||
static Grain ReadGrainV1(BinaryReader& data, u8* samples) {
|
||||
Grain grain{};
|
||||
|
||||
u32 pos = data.get_seek();
|
||||
|
||||
grain.Type = static_cast<GrainType>(data.read<u32>());
|
||||
grain.Delay = data.read<s32>();
|
||||
|
||||
switch (grain.Type) {
|
||||
case GrainType::TONE:
|
||||
case GrainType::TONE2: {
|
||||
grain.data = ReadTone(data, samples);
|
||||
} break;
|
||||
case GrainType::LFO_SETTINGS: {
|
||||
grain.data = data.read<LFOParams>();
|
||||
} break;
|
||||
case GrainType::BRANCH:
|
||||
case GrainType::STARTCHILDSOUND:
|
||||
case GrainType::STOPCHILDSOUND:
|
||||
grain.data = data.read<PlaySoundParams>();
|
||||
break;
|
||||
case GrainType::PLUGIN_MESSAGE:
|
||||
grain.data = data.read<PluginParams>();
|
||||
break;
|
||||
case GrainType::RAND_DELAY:
|
||||
grain.data = data.read<RandDelayParams>();
|
||||
break;
|
||||
default:
|
||||
grain.data = data.read<ControlParams>();
|
||||
break;
|
||||
}
|
||||
|
||||
data.set_seek(pos);
|
||||
data.ffwd(0x28);
|
||||
|
||||
return grain;
|
||||
};
|
||||
|
||||
static Grain ReadGrainV2(BinaryReader& data, BinaryReader grainData, u8* samples) {
|
||||
union OpcodeData {
|
||||
struct {
|
||||
s8 arg[3];
|
||||
u8 type;
|
||||
};
|
||||
|
||||
u32 Opcode;
|
||||
} op;
|
||||
|
||||
Grain grain{};
|
||||
|
||||
op = data.read<OpcodeData>();
|
||||
grain.Type = static_cast<GrainType>(op.type);
|
||||
grain.Delay = data.read<s32>();
|
||||
|
||||
u32 value = op.Opcode & 0xFFFFFF;
|
||||
|
||||
switch (grain.Type) {
|
||||
case GrainType::TONE:
|
||||
case GrainType::TONE2: {
|
||||
grainData.set_seek(value);
|
||||
grain.data = ReadTone(grainData, samples);
|
||||
} break;
|
||||
case GrainType::LFO_SETTINGS: {
|
||||
grainData.set_seek(value);
|
||||
grain.data = grainData.read<LFOParams>();
|
||||
} break;
|
||||
case GrainType::BRANCH:
|
||||
case GrainType::STARTCHILDSOUND:
|
||||
case GrainType::STOPCHILDSOUND:
|
||||
grainData.set_seek(value);
|
||||
grain.data = grainData.read<PlaySoundParams>();
|
||||
break;
|
||||
case GrainType::PLUGIN_MESSAGE:
|
||||
grainData.set_seek(value);
|
||||
grain.data = grainData.read<PluginParams>();
|
||||
break;
|
||||
case GrainType::RAND_DELAY:
|
||||
RandDelayParams rp;
|
||||
rp.Amount = (s32)value + 1;
|
||||
grain.data = rp;
|
||||
break;
|
||||
default:
|
||||
ControlParams p;
|
||||
p.param[0] = op.arg[0];
|
||||
p.param[1] = op.arg[1];
|
||||
p.param[2] = op.arg[2];
|
||||
grain.data = p;
|
||||
break;
|
||||
}
|
||||
|
||||
return grain;
|
||||
};
|
||||
|
||||
SFXBlock* SFXBlock::ReadBlock(nonstd::span<u8> bank_data, nonstd::span<u8> samples) {
|
||||
BinaryReader data(bank_data);
|
||||
// auto block = std::make_unique<SFXBlock>();
|
||||
auto block = new SFXBlock();
|
||||
block->DataID = data.read<u32>();
|
||||
if (block->DataID != fourcc("SBlk")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m_soundbanks[id].get();
|
||||
block->SampleData = std::make_unique<u8[]>(samples.size());
|
||||
std::copy(samples.begin(), samples.end(), block->SampleData.get());
|
||||
|
||||
block->Version = data.read<u32>();
|
||||
block->Flags.flags = data.read<u32>();
|
||||
block->BankID = data.read<u32>();
|
||||
block->BankNum = data.read<s8>();
|
||||
|
||||
// discard padding
|
||||
data.read<s8>();
|
||||
data.read<s16>();
|
||||
data.read<s16>();
|
||||
|
||||
s16 NumSounds = data.read<s16>();
|
||||
block->Sounds.resize(NumSounds);
|
||||
s16 NumGrains = data.read<s16>();
|
||||
s16 NumVAGs = data.read<s16>();
|
||||
|
||||
u32 FirstSound = data.read<u32>();
|
||||
u32 FirstGrain = data.read<u32>();
|
||||
|
||||
u32 VagsInSR = data.read<u32>();
|
||||
u32 VagDataSize = data.read<u32>();
|
||||
u32 SRAMAllocSize = data.read<u32>();
|
||||
u32 NextBlock = data.read<u32>();
|
||||
u32 GrainData = 0;
|
||||
if (block->Version >= 2) {
|
||||
GrainData = data.read<u32>();
|
||||
}
|
||||
u32 BlockNames = data.read<u32>();
|
||||
u32 SFXUD = data.read<u32>();
|
||||
|
||||
data.set_seek(FirstSound);
|
||||
for (auto& sfx : block->Sounds) {
|
||||
sfx.Vol = data.read<s8>();
|
||||
sfx.VolGroup = data.read<s8>();
|
||||
sfx.Pan = data.read<s16>();
|
||||
|
||||
s8 NumGrains = data.read<s8>();
|
||||
sfx.Grains.resize(NumGrains);
|
||||
|
||||
sfx.InstanceLimit = data.read<s8>();
|
||||
sfx.Flags.flags = data.read<u16>();
|
||||
|
||||
u32 FirstSFXGrain = data.read<u32>();
|
||||
|
||||
auto grains = data.at(FirstGrain + FirstSFXGrain);
|
||||
for (auto& grain : sfx.Grains) {
|
||||
if (block->Version < 2) {
|
||||
grain = ReadGrainV1(grains, block->SampleData.get());
|
||||
} else {
|
||||
grain = ReadGrainV2(grains, data.at(GrainData), block->SampleData.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (block->Flags.hasNames()) {
|
||||
struct SFXBlockNames { // 0x98
|
||||
/* 0x00 */ u32 BlockName[2];
|
||||
/* 0x08 */ u32 SFXNameTableOffset;
|
||||
/* 0x0c */ u32 VAGNameTableOffset;
|
||||
/* 0x10 */ u32 VAGImportsTableOffset;
|
||||
/* 0x14 */ u32 VAGExportsTableOffset;
|
||||
/* 0x18 */ s16 SFXHashOffsets[32];
|
||||
/* 0x58 */ s16 VAGHashOffsets[32];
|
||||
};
|
||||
|
||||
struct SFXName { // 0x14
|
||||
/* 0x00 */ u32 Name[4];
|
||||
/* 0x10 */ s16 Index;
|
||||
/* 0x12 */ s16 reserved;
|
||||
};
|
||||
|
||||
data.set_seek(BlockNames);
|
||||
auto names = data.read<SFXBlockNames>();
|
||||
|
||||
char buf[16];
|
||||
strncpy(buf, (char*)names.BlockName, 8);
|
||||
block->Name = buf;
|
||||
|
||||
data.set_seek(BlockNames + names.SFXNameTableOffset);
|
||||
|
||||
auto name_table = (SFXName*)(data.here());
|
||||
for (auto SFXHashOffset : names.SFXHashOffsets) {
|
||||
auto name = &name_table[SFXHashOffset];
|
||||
while (name->Name[0] != 0) {
|
||||
strncpy(buf, (char*)name->Name, 16);
|
||||
|
||||
std::string str(buf);
|
||||
if (block->Names.find(str) == block->Names.end()) {
|
||||
block->Names[str] = name->Index;
|
||||
}
|
||||
|
||||
name++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (block->Flags.hasUserdata()) {
|
||||
data.set_seek(SFXUD);
|
||||
for (auto& sfx : block->Sounds) {
|
||||
sfx.UserData.data[0] = data.read<u32>();
|
||||
sfx.UserData.data[1] = data.read<u32>();
|
||||
sfx.UserData.data[2] = data.read<u32>();
|
||||
sfx.UserData.data[3] = data.read<u32>();
|
||||
}
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
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());
|
||||
if (bank->bank_name == id) {
|
||||
MusicBank* MusicBank::ReadBank(nonstd::span<u8> bank_data,
|
||||
nonstd::span<u8> samples,
|
||||
nonstd::span<u8> midi_data) {
|
||||
BinaryReader data(bank_data);
|
||||
// auto bank = std::make_unique<MusicBank>();
|
||||
auto bank = new MusicBank();
|
||||
|
||||
bank->DataID = data.read<u32>();
|
||||
if (bank->DataID != fourcc("SBv2")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bank->SampleData = std::make_unique<u8[]>(samples.size_bytes());
|
||||
std::copy(samples.begin(), samples.end(), bank->SampleData.get());
|
||||
|
||||
bank->SeqData = std::make_unique<u8[]>(midi_data.size_bytes());
|
||||
std::copy(midi_data.begin(), midi_data.end(), bank->SeqData.get());
|
||||
|
||||
bank->Version = data.read<u32>();
|
||||
bank->Flags.flags = data.read<u32>();
|
||||
bank->BankID = data.read<u32>();
|
||||
bank->BankNum = data.read<s8>();
|
||||
|
||||
// discard padding
|
||||
data.read<s8>();
|
||||
data.read<s16>();
|
||||
|
||||
s16 NumSounds = data.read<s16>();
|
||||
bank->Sounds.resize(NumSounds);
|
||||
s16 NumProgs = data.read<s16>();
|
||||
bank->Progs.resize(NumProgs);
|
||||
s16 NumTones = data.read<s16>();
|
||||
s16 NumVAGs = data.read<s16>();
|
||||
|
||||
u32 FirstSound = data.read<u32>();
|
||||
u32 FirstProg = data.read<u32>();
|
||||
u32 FirstTone = data.read<u32>();
|
||||
|
||||
// vagsinsr
|
||||
data.read<u32>();
|
||||
// vagdatasize
|
||||
data.read<u32>();
|
||||
|
||||
data.set_seek(FirstSound);
|
||||
for (auto& sound : bank->Sounds) {
|
||||
sound.Type = data.read<s32>();
|
||||
data.read<u32>(); // bankptr
|
||||
data.read<u32>(); // ogbankptr
|
||||
sound.MIDIID = data.read<s32>();
|
||||
sound.Vol = data.read<s16>();
|
||||
sound.Repeats = data.read<s8>();
|
||||
sound.VolGroup = data.read<s8>();
|
||||
sound.Pan = data.read<s16>();
|
||||
sound.Index = data.read<s8>();
|
||||
sound.Flags = data.read<u8>();
|
||||
data.read<u32>(); // midiblockptr
|
||||
|
||||
sound.Bank = bank;
|
||||
sound.OrigBank = bank;
|
||||
}
|
||||
|
||||
data.set_seek(FirstProg);
|
||||
for (auto& prog : bank->Progs) {
|
||||
s8 NumTones = data.read<s8>();
|
||||
prog.Vol = data.read<s8>();
|
||||
prog.Pan = data.read<s16>();
|
||||
u32 FirstTone = data.read<u32>();
|
||||
prog.Tones.resize(NumTones);
|
||||
|
||||
auto tones = data.at(FirstTone);
|
||||
for (auto& tone : prog.Tones) {
|
||||
tone = ReadTone(tones, bank->SampleData.get());
|
||||
}
|
||||
}
|
||||
|
||||
auto seq_buf = nonstd::span<u8>(bank->SeqData.get(), midi_data.size_bytes());
|
||||
BinaryReader seq_data(seq_buf);
|
||||
FileAttributes fa;
|
||||
fa.Read(seq_data);
|
||||
|
||||
BinaryReader midi(seq_buf.subspan(fa.where[0].offset));
|
||||
u32 id = midi.read<u32>();
|
||||
midi.set_seek(0);
|
||||
if (id == fourcc("MID ")) {
|
||||
bank->MidiData = ReadMidi(midi);
|
||||
} else if (id == fourcc("MMID")) {
|
||||
bank->MidiData = ReadMultiMidi(midi);
|
||||
} else {
|
||||
lg::error("Invalid midi\n");
|
||||
}
|
||||
|
||||
return bank;
|
||||
}
|
||||
|
||||
BankHandle Loader::BankLoad(nonstd::span<u8> bank) {
|
||||
BinaryReader reader(bank);
|
||||
FileAttributes fa;
|
||||
fa.Read(reader);
|
||||
|
||||
if (fa.type != 1 && fa.type != 3) {
|
||||
fmt::print("bad file type\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
reader.set_seek(fa.where[0].offset);
|
||||
u32 fourcc = reader.read<u32>();
|
||||
nonstd::span<u8> bank_data(nonstd::span<u8>(bank).subspan(fa.where[0].offset, fa.where[0].size));
|
||||
nonstd::span<u8> sample_data(
|
||||
nonstd::span<u8>(bank).subspan(fa.where[1].offset, fa.where[1].size));
|
||||
|
||||
if (fourcc == snd::fourcc("SBv2")) {
|
||||
if (fa.num_chunks != 3) {
|
||||
fmt::print("SBv2 without midi data not supported\n");
|
||||
return 0;
|
||||
}
|
||||
nonstd::span<u8> midi_data(
|
||||
nonstd::span<u8>(bank).subspan(fa.where[2].offset, fa.where[2].size));
|
||||
|
||||
auto bank = MusicBank::ReadBank(bank_data, sample_data, midi_data);
|
||||
mBanks.emplace_back(bank);
|
||||
|
||||
return bank;
|
||||
} else if (fourcc == snd::fourcc("SBlk")) {
|
||||
auto block = SFXBlock::ReadBlock(bank_data, sample_data);
|
||||
mBanks.emplace_back(block);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SoundBank* loader::get_bank_by_name(const char* name) {
|
||||
for (auto& b : m_soundbanks) {
|
||||
auto bankname = b.second->get_name();
|
||||
SoundBank* Loader::GetBankByHandle(BankHandle handle) {
|
||||
auto bank = std::find_if(mBanks.begin(), mBanks.end(),
|
||||
[handle](auto& bank) { return bank.get() == handle; });
|
||||
if (bank == mBanks.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return bank->get();
|
||||
}
|
||||
|
||||
SoundBank* Loader::GetBankByName(const char* name) {
|
||||
for (auto& b : mBanks) {
|
||||
auto bankname = b->GetName();
|
||||
if (bankname.has_value()) {
|
||||
if (bankname->compare(name) == 0) {
|
||||
return b.second.get();
|
||||
return b.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -127,45 +466,23 @@ SoundBank* loader::get_bank_by_name(const char* name) {
|
||||
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);
|
||||
SoundBank* Loader::GetBankWithSound(const char* name) {
|
||||
for (auto& b : mBanks) {
|
||||
auto sound = b->GetSoundByName(name);
|
||||
if (sound.has_value()) {
|
||||
return b.second.get();
|
||||
return b.get();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MIDIBlock* loader::get_midi(u32 id) {
|
||||
return m_midi.at(id);
|
||||
}
|
||||
|
||||
u8* loader::get_bank_samples(u32 id) {
|
||||
return m_soundbanks.at(id).get()->sampleBuf.get();
|
||||
}
|
||||
|
||||
void loader::load_samples(u32 bank_id, std::unique_ptr<u8[]> samples) {
|
||||
auto& bank = m_soundbanks.at(bank_id);
|
||||
bank->sampleBuf = std::move(samples);
|
||||
}
|
||||
|
||||
void loader::unload_bank(u32 id) {
|
||||
fmt::print("Deleting bank {}\n", id);
|
||||
for (auto it = m_midi_chunks.begin(); it != m_midi_chunks.end();) {
|
||||
bool del = false;
|
||||
// FIXME delete midi
|
||||
|
||||
if (del) {
|
||||
it = m_midi_chunks.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
void Loader::UnloadBank(BankHandle handle) {
|
||||
auto bank = std::find_if(mBanks.begin(), mBanks.end(),
|
||||
[handle](auto& bank) { return bank.get() == handle; });
|
||||
if (bank != mBanks.end()) {
|
||||
mBanks.erase(bank);
|
||||
}
|
||||
}
|
||||
|
||||
m_soundbanks.erase(id);
|
||||
m_id_allocator.free_id(id);
|
||||
}
|
||||
|
||||
} // namespace snd
|
||||
|
@ -5,58 +5,41 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "handle_allocator.h"
|
||||
#include "musicbank.h"
|
||||
#include "sfxblock.h"
|
||||
#include "sound_handler.h"
|
||||
#include "soundbank.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/util/BinaryReader.h"
|
||||
|
||||
#include "../common/synth.h"
|
||||
#include "third-party/span.hpp"
|
||||
|
||||
namespace snd {
|
||||
|
||||
#define FOURCC(a, b, c, d) ((u32)(((d) << 24) | ((c) << 16) | ((b) << 8) | (a)))
|
||||
struct LocAndSize {
|
||||
/* 0 */ u32 offset;
|
||||
/* 4 */ u32 size;
|
||||
};
|
||||
using BankHandle = SoundBank*;
|
||||
|
||||
template <size_t chunks>
|
||||
struct FileAttributes {
|
||||
/* 0 */ u32 type;
|
||||
/* 4 */ u32 num_chunks;
|
||||
/* 8 */ LocAndSize where[chunks];
|
||||
};
|
||||
|
||||
class loader : public locator {
|
||||
class FileAttributes {
|
||||
public:
|
||||
SoundBank* get_bank_by_handle(u32 id) override;
|
||||
MusicBank* get_bank_by_id(u32 id) override;
|
||||
MIDIBlock* get_midi(u32 id) override;
|
||||
u8* get_bank_samples(u32 id) override;
|
||||
struct LocAndSize {
|
||||
u32 offset;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
SoundBank* get_bank_by_name(const char* name);
|
||||
SoundBank* get_bank_with_sound(const char* name);
|
||||
u32 type;
|
||||
u32 num_chunks;
|
||||
std::vector<LocAndSize> where;
|
||||
void Read(BinaryReader& data);
|
||||
};
|
||||
|
||||
void unload_bank(u32 id);
|
||||
class Loader {
|
||||
public:
|
||||
SoundBank* GetBankByHandle(BankHandle id);
|
||||
SoundBank* GetBankByName(const char* name);
|
||||
SoundBank* GetBankWithSound(const char* name);
|
||||
|
||||
u32 read_bank(std::fstream& in);
|
||||
void load_midi(std::fstream& in);
|
||||
void UnloadBank(BankHandle id);
|
||||
|
||||
bool read_midi();
|
||||
BankHandle BankLoad(nonstd::span<u8> bank);
|
||||
|
||||
private:
|
||||
void load_samples(u32 bank, std::unique_ptr<u8[]> samples);
|
||||
|
||||
id_allocator m_id_allocator;
|
||||
std::unordered_map<u32, std::unique_ptr<SoundBank>> m_soundbanks;
|
||||
|
||||
std::vector<std::unique_ptr<u8[]>> m_midi_chunks;
|
||||
|
||||
std::unordered_map<u32, MIDIBlock*> m_midi;
|
||||
|
||||
u32 m_next_id{0};
|
||||
std::vector<std::unique_ptr<SoundBank>> mBanks;
|
||||
};
|
||||
} // namespace snd
|
||||
|
@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace snd {
|
||||
|
||||
class MusicBank;
|
||||
class SoundBank;
|
||||
struct MIDIBlock;
|
||||
|
||||
class locator {
|
||||
public:
|
||||
virtual ~locator() = default;
|
||||
virtual SoundBank* get_bank_by_handle(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;
|
||||
};
|
||||
} // namespace snd
|
@ -22,19 +22,13 @@ namespace snd {
|
||||
**
|
||||
*/
|
||||
|
||||
midi_handler::midi_handler(MIDIBlockHeader* block,
|
||||
voice_manager& vm,
|
||||
MIDISound& sound,
|
||||
MidiHandler::MidiHandler(Midi* block,
|
||||
VoiceManager& vm,
|
||||
MusicBank::MIDISound& sound,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
locator& loc,
|
||||
SoundBank& bank)
|
||||
: m_sound(sound),
|
||||
m_locator(loc),
|
||||
m_repeats(sound.Repeats),
|
||||
m_bank(bank),
|
||||
m_header(block),
|
||||
m_vm(vm) {
|
||||
: m_sound(sound), m_repeats(sound.Repeats), m_bank(bank), m_header(block), m_vm(vm) {
|
||||
if (vol == VOLUME_DONT_CHANGE) {
|
||||
vol = 1024;
|
||||
}
|
||||
@ -50,31 +44,29 @@ midi_handler::midi_handler(MIDIBlockHeader* block,
|
||||
m_pan = pan;
|
||||
}
|
||||
|
||||
init_midi();
|
||||
InitMidi();
|
||||
}
|
||||
|
||||
midi_handler::midi_handler(MIDIBlockHeader* block,
|
||||
voice_manager& vm,
|
||||
MIDISound& sound,
|
||||
MidiHandler::MidiHandler(Midi* block,
|
||||
VoiceManager& vm,
|
||||
MusicBank::MIDISound& sound,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
locator& loc,
|
||||
SoundBank& bank,
|
||||
std::optional<ame_handler*> parent)
|
||||
std::optional<AmeHandler*> parent)
|
||||
: m_parent(parent),
|
||||
m_sound(sound),
|
||||
m_locator(loc),
|
||||
m_vol(vol),
|
||||
m_pan(pan),
|
||||
m_repeats(sound.Repeats),
|
||||
m_bank(bank),
|
||||
m_header(block),
|
||||
m_vm(vm) {
|
||||
init_midi();
|
||||
InitMidi();
|
||||
}
|
||||
|
||||
void midi_handler::init_midi() {
|
||||
m_seq_data_start = (u8*)((uintptr_t)m_header + (uintptr_t)m_header->DataStart);
|
||||
void MidiHandler::InitMidi() {
|
||||
m_seq_data_start = m_header->DataStart;
|
||||
m_seq_ptr = m_seq_data_start;
|
||||
m_tempo = m_header->Tempo;
|
||||
m_ppq = m_header->PPQ;
|
||||
@ -82,7 +74,7 @@ void midi_handler::init_midi() {
|
||||
m_chanpan.fill(0);
|
||||
}
|
||||
|
||||
std::pair<size_t, u32> midi_handler::read_vlq(u8* value) {
|
||||
std::pair<size_t, u32> MidiHandler::ReadVLQ(u8* value) {
|
||||
size_t len = 1;
|
||||
u32 out = *value & 0x7f;
|
||||
// fmt::print("starting with {:x}\n", *value);
|
||||
@ -98,7 +90,7 @@ std::pair<size_t, u32> midi_handler::read_vlq(u8* value) {
|
||||
return {len, out};
|
||||
}
|
||||
|
||||
void midi_handler::pause() {
|
||||
void MidiHandler::Pause() {
|
||||
m_paused = true;
|
||||
|
||||
for (auto& p : m_voices) {
|
||||
@ -107,11 +99,11 @@ void midi_handler::pause() {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_vm.pause(voice);
|
||||
m_vm.Pause(voice);
|
||||
}
|
||||
}
|
||||
|
||||
void midi_handler::unpause() {
|
||||
void MidiHandler::Unpause() {
|
||||
m_paused = false;
|
||||
|
||||
for (auto& p : m_voices) {
|
||||
@ -120,11 +112,11 @@ void midi_handler::unpause() {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_vm.unpause(voice);
|
||||
m_vm.Unpause(voice);
|
||||
}
|
||||
}
|
||||
|
||||
void midi_handler::stop() {
|
||||
void MidiHandler::Stop() {
|
||||
m_track_complete = true;
|
||||
|
||||
for (auto& p : m_voices) {
|
||||
@ -133,11 +125,11 @@ void midi_handler::stop() {
|
||||
continue;
|
||||
}
|
||||
|
||||
voice->key_off();
|
||||
voice->KeyOff();
|
||||
}
|
||||
}
|
||||
|
||||
void midi_handler::set_vol_pan(s32 vol, s32 pan) {
|
||||
void MidiHandler::SetVolPan(s32 vol, s32 pan) {
|
||||
if (vol != VOLUME_DONT_CHANGE) {
|
||||
if (vol >= 0) {
|
||||
m_vol = (m_sound.Vol * vol) >> 10;
|
||||
@ -170,16 +162,16 @@ void midi_handler::set_vol_pan(s32 vol, s32 pan) {
|
||||
}
|
||||
|
||||
voice->basevol =
|
||||
m_vm.make_volume_b(m_vol, voice->velocity * m_chanvol[voice->channel] / 127, pan,
|
||||
m_vm.MakeVolumeB(m_vol, voice->velocity * m_chanvol[voice->channel] / 127, pan,
|
||||
voice->prog.Vol, voice->prog.Pan, voice->tone.Vol, voice->tone.Pan);
|
||||
|
||||
auto left = m_vm.adjust_vol_to_group(voice->basevol.left, voice->group);
|
||||
auto right = m_vm.adjust_vol_to_group(voice->basevol.right, voice->group);
|
||||
voice->set_volume(left >> 1, right >> 1);
|
||||
auto left = m_vm.AdjustVolToGroup(voice->basevol.left, voice->group);
|
||||
auto right = m_vm.AdjustVolToGroup(voice->basevol.right, voice->group);
|
||||
voice->SetVolume(left >> 1, right >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
void midi_handler::set_pmod(s32 mod) {
|
||||
void MidiHandler::SetPMod(s32 mod) {
|
||||
m_cur_pm = mod;
|
||||
|
||||
for (auto& v : m_voices) {
|
||||
@ -189,31 +181,31 @@ void midi_handler::set_pmod(s32 mod) {
|
||||
}
|
||||
|
||||
voice->current_pm = m_cur_pm;
|
||||
auto note = pitchbend(voice->tone, voice->current_pb, voice->current_pm, voice->start_note,
|
||||
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);
|
||||
voice->SetPitch(pitch);
|
||||
}
|
||||
}
|
||||
|
||||
void midi_handler::mute_channel(u8 channel) {
|
||||
void MidiHandler::MuteChannel(u8 channel) {
|
||||
// fmt::print("{:x} ame muting channel {}\n", (u64)this, channel);
|
||||
m_mute_state[channel] = true;
|
||||
}
|
||||
|
||||
void midi_handler::unmute_channel(u8 channel) {
|
||||
void MidiHandler::UnmuteChannel(u8 channel) {
|
||||
// fmt::print("{:x} ame unmuting channel {}\n", (u64)this, channel);
|
||||
m_mute_state[channel] = false;
|
||||
}
|
||||
|
||||
void midi_handler::note_on() {
|
||||
void MidiHandler::NoteOn() {
|
||||
u8 channel = m_status & 0xf;
|
||||
u8 note = m_seq_ptr[0];
|
||||
u8 velocity = m_seq_ptr[1];
|
||||
|
||||
if (velocity == 0) {
|
||||
note_off();
|
||||
NoteOff();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -226,19 +218,21 @@ 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_id(m_header->BankID));
|
||||
auto& program = bank->m_programs[m_programs[channel]];
|
||||
// FIXME bank from midi
|
||||
// auto bank = dynamic_cast<MusicBank*>(m_locator.get_bank_by_id(m_header->BankID));
|
||||
auto bank = static_cast<MusicBank*>(&m_bank);
|
||||
auto& program = bank->Progs[m_programs[channel]];
|
||||
|
||||
for (auto& t : program.tones) {
|
||||
for (auto& t : program.Tones) {
|
||||
if (note >= t.MapLow && note <= t.MapHigh) {
|
||||
s16 pan = m_chanpan[channel] + m_pan;
|
||||
if (pan >= 360) {
|
||||
pan -= 360;
|
||||
}
|
||||
|
||||
auto voice = std::make_shared<midi_voice>(t, program.d);
|
||||
voice->basevol = m_vm.make_volume_b(m_vol, (velocity * m_chanvol[channel]) / 0x7f, pan,
|
||||
program.d.Vol, program.d.Pan, t.Vol, t.Pan);
|
||||
auto voice = std::make_shared<midi_voice>(t, program);
|
||||
voice->basevol = m_vm.MakeVolumeB(m_vol, (velocity * m_chanvol[channel]) / 0x7f, pan,
|
||||
program.Vol, program.Pan, t.Vol, t.Pan);
|
||||
|
||||
voice->note = note;
|
||||
voice->channel = channel;
|
||||
@ -251,7 +245,7 @@ void midi_handler::note_on() {
|
||||
voice->current_pb = m_cur_pm;
|
||||
|
||||
voice->group = m_sound.VolGroup;
|
||||
m_vm.start_tone(voice, m_bank.bank_id);
|
||||
m_vm.StartTone(voice);
|
||||
m_voices.emplace_front(voice);
|
||||
}
|
||||
}
|
||||
@ -259,7 +253,7 @@ void midi_handler::note_on() {
|
||||
m_seq_ptr += 2;
|
||||
}
|
||||
|
||||
void midi_handler::note_off() {
|
||||
void MidiHandler::NoteOff() {
|
||||
u8 channel = m_status & 0xf;
|
||||
u8 note = m_seq_ptr[0];
|
||||
// Yep, no velocity for note-offs
|
||||
@ -275,14 +269,14 @@ void midi_handler::note_off() {
|
||||
}
|
||||
|
||||
if (voice->channel == channel && voice->note == note) {
|
||||
voice->key_off();
|
||||
voice->KeyOff();
|
||||
}
|
||||
}
|
||||
|
||||
m_seq_ptr += 2;
|
||||
}
|
||||
|
||||
void midi_handler::program_change() {
|
||||
void MidiHandler::ProgramChange() {
|
||||
u8 channel = m_status & 0xf;
|
||||
u8 program = m_seq_ptr[0];
|
||||
|
||||
@ -293,7 +287,7 @@ void midi_handler::program_change() {
|
||||
m_seq_ptr += 1;
|
||||
}
|
||||
|
||||
void midi_handler::channel_pressure() {
|
||||
void MidiHandler::ChannelPressure() {
|
||||
u8 channel = m_status & 0xf;
|
||||
u8 note = m_seq_ptr[0];
|
||||
// fmt::print("{}: channel pressure {:02x} {:02x}\n", m_time, m_status, m_seq_ptr[0]);
|
||||
@ -305,14 +299,14 @@ void midi_handler::channel_pressure() {
|
||||
}
|
||||
|
||||
if (voice->channel == channel && voice->note == note) {
|
||||
voice->key_off();
|
||||
voice->KeyOff();
|
||||
}
|
||||
}
|
||||
|
||||
m_seq_ptr += 1;
|
||||
}
|
||||
|
||||
void midi_handler::channel_pitch() {
|
||||
void MidiHandler::ChannelPitch() {
|
||||
u8 channel = m_status & 0xF;
|
||||
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);
|
||||
@ -326,18 +320,18 @@ void midi_handler::channel_pitch() {
|
||||
|
||||
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,
|
||||
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);
|
||||
voice->SetPitch(pitch);
|
||||
}
|
||||
}
|
||||
|
||||
m_seq_ptr += 2;
|
||||
}
|
||||
|
||||
void midi_handler::meta_event() {
|
||||
void MidiHandler::MetaEvent() {
|
||||
// fmt::print("{}: meta event {:02x}\n", m_time, *m_seq_ptr);
|
||||
size_t len = m_seq_ptr[1];
|
||||
|
||||
@ -373,7 +367,7 @@ static s16 midiTo360Pan(u8 pan) {
|
||||
}
|
||||
}
|
||||
|
||||
void midi_handler::controller_change() {
|
||||
void MidiHandler::ControllerChange() {
|
||||
u8 channel = m_status & 0xf;
|
||||
u8 controller = m_seq_ptr[0];
|
||||
u8 value = m_seq_ptr[1];
|
||||
@ -388,21 +382,21 @@ void midi_handler::controller_change() {
|
||||
} break;
|
||||
// case 0x40: {} break; // TODO damper
|
||||
default:
|
||||
throw midi_error(fmt::format("invalid midi controller change {}", controller));
|
||||
throw MidiError(fmt::format("invalid midi controller change {}", controller));
|
||||
break;
|
||||
}
|
||||
|
||||
m_seq_ptr += 2;
|
||||
}
|
||||
|
||||
void midi_handler::system_event() {
|
||||
void MidiHandler::SystemEvent() {
|
||||
// fmt::print("{}: system event {:02x}\n", m_time, *m_seq_ptr);
|
||||
|
||||
switch (*m_seq_ptr) {
|
||||
case 0x75:
|
||||
m_seq_ptr++;
|
||||
if (m_parent.has_value()) {
|
||||
auto [cont, ptr] = m_parent.value()->run_ame(*this, m_seq_ptr);
|
||||
auto [cont, ptr] = m_parent.value()->RunAME(*this, m_seq_ptr);
|
||||
m_seq_ptr = ptr;
|
||||
|
||||
if (!cont) {
|
||||
@ -410,23 +404,23 @@ void midi_handler::system_event() {
|
||||
m_track_complete = true;
|
||||
}
|
||||
} else {
|
||||
throw midi_error("MIDI tried to run AME without an AME handler");
|
||||
throw MidiError("MIDI tried to run AME without an AME handler");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw midi_error(fmt::format("Unknown system message {:02x}", *m_seq_ptr));
|
||||
throw MidiError(fmt::format("Unknown system message {:02x}", *m_seq_ptr));
|
||||
}
|
||||
}
|
||||
|
||||
bool midi_handler::tick() {
|
||||
bool MidiHandler::Tick() {
|
||||
if (m_paused) {
|
||||
return m_track_complete;
|
||||
}
|
||||
|
||||
try {
|
||||
m_voices.remove_if([](auto& v) { return v.expired(); });
|
||||
step();
|
||||
} catch (midi_error& e) {
|
||||
Step();
|
||||
} catch (MidiError& e) {
|
||||
m_track_complete = true;
|
||||
lg::error("MIDI Error: {}", e.what());
|
||||
fmt::print("Sequence following: ");
|
||||
@ -439,8 +433,8 @@ bool midi_handler::tick() {
|
||||
return m_track_complete;
|
||||
}
|
||||
|
||||
void midi_handler::new_delta() {
|
||||
auto [len, delta] = read_vlq(m_seq_ptr);
|
||||
void MidiHandler::NewDelta() {
|
||||
auto [len, delta] = ReadVLQ(m_seq_ptr);
|
||||
|
||||
m_seq_ptr += len;
|
||||
m_time += delta;
|
||||
@ -457,9 +451,9 @@ void midi_handler::new_delta() {
|
||||
}
|
||||
}
|
||||
|
||||
void midi_handler::step() {
|
||||
void MidiHandler::Step() {
|
||||
if (m_get_delta) {
|
||||
new_delta();
|
||||
NewDelta();
|
||||
m_get_delta = false;
|
||||
} else {
|
||||
m_tick_countdown--;
|
||||
@ -474,40 +468,40 @@ void midi_handler::step() {
|
||||
|
||||
switch (m_status >> 4) {
|
||||
case 0x8:
|
||||
note_off();
|
||||
NoteOff();
|
||||
break;
|
||||
case 0x9:
|
||||
note_on();
|
||||
NoteOn();
|
||||
break;
|
||||
case 0xB:
|
||||
controller_change();
|
||||
ControllerChange();
|
||||
break;
|
||||
case 0xD:
|
||||
channel_pressure();
|
||||
ChannelPressure();
|
||||
break;
|
||||
case 0xC:
|
||||
program_change();
|
||||
ProgramChange();
|
||||
break;
|
||||
case 0xE:
|
||||
channel_pitch();
|
||||
ChannelPitch();
|
||||
break;
|
||||
case 0xF:
|
||||
// normal meta-event
|
||||
if (m_status == 0xFF) {
|
||||
meta_event();
|
||||
MetaEvent();
|
||||
break;
|
||||
}
|
||||
if (m_status == 0xF0) {
|
||||
system_event();
|
||||
SystemEvent();
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
default:
|
||||
throw midi_error(fmt::format("invalid status {}", m_status));
|
||||
throw MidiError(fmt::format("invalid status {}", m_status));
|
||||
return;
|
||||
}
|
||||
|
||||
new_delta();
|
||||
NewDelta();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "ame_handler.h"
|
||||
#include "loader.h"
|
||||
#include "musicbank.h"
|
||||
#include "sound_handler.h"
|
||||
#include "vagvoice.h"
|
||||
|
||||
@ -17,87 +17,72 @@
|
||||
|
||||
namespace snd {
|
||||
|
||||
struct ProgData {
|
||||
/* 0 */ s8 NumTones;
|
||||
/* 1 */ s8 Vol;
|
||||
/* 2 */ s16 Pan;
|
||||
/* 4 */ /*Tone**/ u32 FirstTone;
|
||||
};
|
||||
|
||||
struct Prog {
|
||||
ProgData d;
|
||||
std::vector<Tone> tones;
|
||||
};
|
||||
|
||||
class midi_voice : public vag_voice {
|
||||
class midi_voice : public VagVoice {
|
||||
public:
|
||||
midi_voice(Tone& t, ProgData& prog) : vag_voice(t), prog(prog) {}
|
||||
midi_voice(Tone& t, MusicBank::Prog& _prog) : VagVoice(t), prog(_prog) {}
|
||||
u8 note{0};
|
||||
u8 channel{0};
|
||||
u8 velocity{0};
|
||||
ProgData& prog;
|
||||
MusicBank::Prog& prog;
|
||||
};
|
||||
|
||||
class ame_handler;
|
||||
class midi_handler : public sound_handler {
|
||||
class AmeHandler;
|
||||
class MidiHandler : public SoundHandler {
|
||||
public:
|
||||
midi_handler(MIDIBlockHeader* block,
|
||||
voice_manager& vm,
|
||||
MIDISound& sound,
|
||||
MidiHandler(Midi* block,
|
||||
VoiceManager& vm,
|
||||
MusicBank::MIDISound& sound,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
locator& loc,
|
||||
SoundBank& bank);
|
||||
|
||||
midi_handler(MIDIBlockHeader* block,
|
||||
voice_manager& vm,
|
||||
MIDISound& sound,
|
||||
MidiHandler(Midi* block,
|
||||
VoiceManager& vm,
|
||||
MusicBank::MIDISound& sound,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
locator& loc,
|
||||
SoundBank& bank,
|
||||
std::optional<ame_handler*> parent);
|
||||
std::optional<AmeHandler*> parent);
|
||||
|
||||
~midi_handler() override {
|
||||
~MidiHandler() override {
|
||||
for (auto& p : m_voices) {
|
||||
auto v = p.lock();
|
||||
if (v != nullptr) {
|
||||
v->stop();
|
||||
v->Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
void init_midi();
|
||||
void start();
|
||||
bool tick() override;
|
||||
void mute_channel(u8 channel);
|
||||
void unmute_channel(u8 channel);
|
||||
SoundBank& bank() override { return m_bank; };
|
||||
void InitMidi();
|
||||
void Start();
|
||||
bool Tick() override;
|
||||
void MuteChannel(u8 channel);
|
||||
void UnmuteChannel(u8 channel);
|
||||
SoundBank& Bank() override { return m_bank; };
|
||||
|
||||
void pause() override;
|
||||
void stop() override;
|
||||
void unpause() override;
|
||||
u8 group() override { return m_sound.VolGroup; }
|
||||
void set_vol_pan(s32 vol, s32 pan) override;
|
||||
void Pause() override;
|
||||
void Stop() override;
|
||||
void Unpause() override;
|
||||
u8 Group() override { return m_sound.VolGroup; }
|
||||
void SetVolPan(s32 vol, s32 pan) override;
|
||||
|
||||
bool complete() { return m_track_complete; };
|
||||
void set_pmod(s32 mod) override;
|
||||
bool Complete() { return m_track_complete; };
|
||||
void SetPMod(s32 mod) override;
|
||||
|
||||
private:
|
||||
static constexpr int tickrate = 240;
|
||||
static constexpr int mics_per_tick = 1000000 / tickrate;
|
||||
struct midi_error : public std::exception {
|
||||
midi_error(std::string text) : msg(std::move(text)) {}
|
||||
midi_error() : msg("Unknown MIDI error") {}
|
||||
struct MidiError : public std::exception {
|
||||
MidiError(std::string text) : msg(std::move(text)) {}
|
||||
MidiError() : msg("Unknown MIDI error") {}
|
||||
std::string msg;
|
||||
const char* what() const noexcept override { return msg.c_str(); }
|
||||
};
|
||||
|
||||
std::optional<ame_handler*> m_parent;
|
||||
std::optional<AmeHandler*> m_parent;
|
||||
|
||||
std::list<std::weak_ptr<midi_voice>> m_voices;
|
||||
|
||||
MIDISound& m_sound;
|
||||
locator& m_locator;
|
||||
MusicBank::MIDISound& m_sound;
|
||||
s32 m_vol{0x7f};
|
||||
s32 m_pan{0};
|
||||
s32 m_cur_pm{0};
|
||||
@ -106,7 +91,7 @@ class midi_handler : public sound_handler {
|
||||
|
||||
bool m_paused{false};
|
||||
|
||||
MIDIBlockHeader* m_header{nullptr};
|
||||
Midi* m_header{nullptr};
|
||||
|
||||
std::array<bool, 16> m_mute_state{};
|
||||
std::array<s8, 16> m_chanvol{};
|
||||
@ -130,20 +115,20 @@ class midi_handler : public sound_handler {
|
||||
|
||||
std::array<u8, 16> m_programs{};
|
||||
|
||||
voice_manager& m_vm;
|
||||
VoiceManager& m_vm;
|
||||
|
||||
void step();
|
||||
void new_delta();
|
||||
void Step();
|
||||
void NewDelta();
|
||||
|
||||
void note_on();
|
||||
void note_off();
|
||||
void controller_change();
|
||||
void channel_pressure();
|
||||
void program_change();
|
||||
void meta_event();
|
||||
void system_event();
|
||||
void channel_pitch();
|
||||
void NoteOn();
|
||||
void NoteOff();
|
||||
void ControllerChange();
|
||||
void ChannelPressure();
|
||||
void ProgramChange();
|
||||
void MetaEvent();
|
||||
void SystemEvent();
|
||||
void ChannelPitch();
|
||||
|
||||
static std::pair<size_t, u32> read_vlq(u8* value);
|
||||
static std::pair<size_t, u32> ReadVLQ(u8* value);
|
||||
};
|
||||
} // namespace snd
|
||||
|
@ -3,50 +3,34 @@
|
||||
#include "ame_handler.h"
|
||||
#include "midi_handler.h"
|
||||
|
||||
#include "../common/synth.h"
|
||||
#include "common/log/log.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::optional<std::unique_ptr<sound_handler>> MusicBank::make_handler(voice_manager& vm,
|
||||
std::optional<std::unique_ptr<SoundHandler>> MusicBank::MakeHandler(VoiceManager& vm,
|
||||
u32 sound_id,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
s32 pm,
|
||||
s32 pb) {
|
||||
auto& sound = m_sounds[sound_id];
|
||||
auto& sound = Sounds[sound_id];
|
||||
|
||||
if (sound.Type == 4) { // midi
|
||||
auto midi = static_cast<MIDIBlockHeader*>(m_locator.get_midi(sound.MIDIID));
|
||||
return 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));
|
||||
return std::make_unique<ame_handler>(midi, vm, sound, vol, pan, m_locator, *this);
|
||||
// FIXME: global midi list
|
||||
// search only local bank for now
|
||||
// (always the case in jak games anyway)
|
||||
|
||||
if (sound.Type == 4) {
|
||||
auto& midi = std::get<Midi>(MidiData);
|
||||
if (sound.MIDIID == midi.ID) {
|
||||
return std::make_unique<MidiHandler>(&midi, vm, sound, vol, pan, *this);
|
||||
}
|
||||
return std::nullopt;
|
||||
} else if (sound.Type == 5) {
|
||||
auto& midi = std::get<MultiMidi>(MidiData);
|
||||
if (sound.MIDIID == midi.ID) {
|
||||
return std::make_unique<AmeHandler>(&midi, vm, sound, vol, pan, *this);
|
||||
}
|
||||
return std::nullopt;
|
||||
} else {
|
||||
lg::error("Invalid music sound type");
|
||||
return std::nullopt;
|
||||
@ -54,7 +38,7 @@ std::optional<std::unique_ptr<sound_handler>> MusicBank::make_handler(voice_mana
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::unique_ptr<sound_handler>> MusicBank::make_handler(voice_manager& vm,
|
||||
std::optional<std::unique_ptr<SoundHandler>> MusicBank::MakeHandler(VoiceManager& vm,
|
||||
u32 sound_id,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
|
@ -1,88 +1,83 @@
|
||||
#pragma once
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "soundbank.h"
|
||||
|
||||
#include "third-party/span.hpp"
|
||||
|
||||
namespace snd {
|
||||
struct SoundBankData : BankTag {
|
||||
/* 10 */ s8 BankNum;
|
||||
/* 11 */ s8 pad1;
|
||||
/* 12 */ s16 pad2;
|
||||
/* 14 */ s16 NumSounds;
|
||||
/* 16 */ s16 NumProgs;
|
||||
/* 18 */ s16 NumTones;
|
||||
/* 1a */ s16 NumVAGs;
|
||||
/* 1c */ /*Sound**/ u32 FirstSound;
|
||||
/* 20 */ /*Prog**/ u32 FirstProg;
|
||||
/* 24 */ /*Tone**/ u32 FirstTone;
|
||||
/* 28 */ /*void**/ u32 VagsInSR;
|
||||
/* 2c */ u32 VagDataSize;
|
||||
/* 30 */ /*SoundBank**/ u32 NextBank;
|
||||
|
||||
struct Midi {
|
||||
u32 DataID;
|
||||
s16 Version;
|
||||
s8 Flags;
|
||||
u32 ID;
|
||||
u32 BankID;
|
||||
u8* DataStart;
|
||||
u32 Tempo;
|
||||
s32 PPQ;
|
||||
};
|
||||
|
||||
struct MIDIBlock {
|
||||
/* 0 */ u32 DataID;
|
||||
/* 4 */ s16 Version;
|
||||
/* 6 */ s8 Flags;
|
||||
/* 7 */ s8 NumMIDIBlocks;
|
||||
/* 8 */ u32 ID;
|
||||
struct MidiSegment : Midi {
|
||||
// For MultiMidi data where the sound handler struct is baked into the
|
||||
// file, we need to know the handler type to determine if we should
|
||||
// play the segment. For unknown reasons the files contain segments
|
||||
// that have invalid/unknown handler types that should not be played as midi.
|
||||
u32 SoundHandle;
|
||||
};
|
||||
|
||||
struct MIDIBlockHeader : MIDIBlock {
|
||||
/* c */ /*void**/ u32 NextMIDIBlock;
|
||||
/* 10 */ u32 BankID;
|
||||
/* 14 */ /*SoundBank**/ u32 BankPtr;
|
||||
/* 18 */ /*s8**/ u32 DataStart;
|
||||
/* 1c */ /*s8**/ u32 MultiMIDIParent;
|
||||
/* 20 */ u32 Tempo;
|
||||
/* 24 */ u32 PPQ;
|
||||
struct MultiMidi {
|
||||
u32 DataID;
|
||||
u32 Version;
|
||||
s8 Flags;
|
||||
u32 ID;
|
||||
|
||||
std::vector<MidiSegment> midi;
|
||||
};
|
||||
|
||||
struct MultiMIDIBlockHeader : MIDIBlock {
|
||||
/* c */ /*void**/ u32 NextMIDIBlock;
|
||||
/* 10 */ /*s8**/ u32 BlockPtr[0];
|
||||
};
|
||||
|
||||
struct MIDISound {
|
||||
/* 0 */ s32 Type;
|
||||
/* 4 */ /*SoundBank**/ u32 Bank;
|
||||
/* 8 */ /*void**/ u32 OrigBank;
|
||||
/* c */ u32 MIDIID;
|
||||
/* 10 */ s16 Vol;
|
||||
/* 12 */ s8 Repeats;
|
||||
/* 13 */ s8 VolGroup;
|
||||
/* 14 */ s16 Pan;
|
||||
/* 16 */ s8 Index;
|
||||
/* 17 */ s8 Flags;
|
||||
/* 18 */ /*void**/ u32 MIDIBlock;
|
||||
};
|
||||
|
||||
// Annoyingly enough we need some data out of this struct
|
||||
struct MIDISoundHandler {
|
||||
u32 OwnerID;
|
||||
};
|
||||
|
||||
struct Prog;
|
||||
class MusicBank : public SoundBank {
|
||||
public:
|
||||
MusicBank(locator& loc, u32 id, BankTag* tag);
|
||||
std::optional<std::unique_ptr<sound_handler>> make_handler(voice_manager& vm,
|
||||
struct MIDISound { // 0x1c
|
||||
s32 Type;
|
||||
SoundBank* Bank;
|
||||
void* OrigBank;
|
||||
u32 MIDIID;
|
||||
s16 Vol;
|
||||
s8 Repeats;
|
||||
s8 VolGroup;
|
||||
s16 Pan;
|
||||
s8 Index;
|
||||
u8 Flags;
|
||||
};
|
||||
|
||||
struct Prog {
|
||||
s8 Vol;
|
||||
s16 Pan;
|
||||
std::vector<Tone> Tones;
|
||||
};
|
||||
|
||||
std::vector<MIDISound> Sounds;
|
||||
std::vector<Prog> Progs;
|
||||
std::unique_ptr<u8[]> SampleData;
|
||||
std::unique_ptr<u8[]> SeqData;
|
||||
std::variant<Midi, MultiMidi> MidiData;
|
||||
|
||||
static MusicBank* ReadBank(nonstd::span<u8> bank_data,
|
||||
nonstd::span<u8> samples,
|
||||
nonstd::span<u8> midi_data);
|
||||
|
||||
std::optional<std::unique_ptr<SoundHandler>> MakeHandler(VoiceManager& vm,
|
||||
u32 sound_id,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
s32 pm,
|
||||
s32 pb) override;
|
||||
|
||||
std::optional<std::unique_ptr<sound_handler>> make_handler(voice_manager& vm,
|
||||
std::optional<std::unique_ptr<SoundHandler>> MakeHandler(VoiceManager& 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;
|
||||
};
|
||||
} // namespace snd
|
||||
|
@ -4,9 +4,12 @@
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "sfxblock.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <combaseapi.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
@ -16,15 +19,15 @@ namespace snd {
|
||||
|
||||
u8 g_global_excite = 0;
|
||||
|
||||
player::player() : m_vmanager(m_synth, m_loader) {
|
||||
init_cubeb();
|
||||
Player::Player() : mVmanager(mSynth) {
|
||||
InitCubeb();
|
||||
}
|
||||
|
||||
player::~player() {
|
||||
destroy_cubeb();
|
||||
Player::~Player() {
|
||||
DestroyCubeb();
|
||||
}
|
||||
|
||||
void player::init_cubeb() {
|
||||
void Player::InitCubeb() {
|
||||
#ifdef _WIN32
|
||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
m_coinitialized = SUCCEEDED(hr);
|
||||
@ -35,7 +38,7 @@ void player::init_cubeb() {
|
||||
}
|
||||
#endif
|
||||
|
||||
cubeb_init(&m_ctx, "OpenGOAL", nullptr);
|
||||
cubeb_init(&mCtx, "OpenGOAL", nullptr);
|
||||
|
||||
cubeb_stream_params outparam = {};
|
||||
outparam.channels = 2;
|
||||
@ -46,30 +49,30 @@ void player::init_cubeb() {
|
||||
|
||||
s32 err = 0;
|
||||
u32 latency = 0;
|
||||
err = cubeb_get_min_latency(m_ctx, &outparam, &latency);
|
||||
err = cubeb_get_min_latency(mCtx, &outparam, &latency);
|
||||
if (err != CUBEB_OK) {
|
||||
lg::error("Cubeb init failed");
|
||||
return;
|
||||
}
|
||||
|
||||
err = cubeb_stream_init(m_ctx, &m_stream, "OpenGOAL", nullptr, nullptr, nullptr, &outparam,
|
||||
err = cubeb_stream_init(mCtx, &mStream, "OpenGOAL", nullptr, nullptr, nullptr, &outparam,
|
||||
latency, &sound_callback, &state_callback, this);
|
||||
if (err != CUBEB_OK) {
|
||||
lg::error("Cubeb init failed");
|
||||
return;
|
||||
}
|
||||
|
||||
err = cubeb_stream_start(m_stream);
|
||||
err = cubeb_stream_start(mStream);
|
||||
if (err != CUBEB_OK) {
|
||||
lg::error("Cubeb init failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void player::destroy_cubeb() {
|
||||
cubeb_stream_stop(m_stream);
|
||||
cubeb_stream_destroy(m_stream);
|
||||
cubeb_destroy(m_ctx);
|
||||
void Player::DestroyCubeb() {
|
||||
cubeb_stream_stop(mStream);
|
||||
cubeb_stream_destroy(mStream);
|
||||
cubeb_destroy(mCtx);
|
||||
#ifdef _WIN32
|
||||
if (m_coinitialized) {
|
||||
CoUninitialize();
|
||||
@ -78,34 +81,35 @@ void player::destroy_cubeb() {
|
||||
#endif
|
||||
}
|
||||
|
||||
long player::sound_callback([[maybe_unused]] cubeb_stream* stream,
|
||||
long Player::sound_callback([[maybe_unused]] cubeb_stream* stream,
|
||||
void* user,
|
||||
[[maybe_unused]] const void* input,
|
||||
void* output_buffer,
|
||||
long nframes) {
|
||||
((player*)user)->tick((s16_output*)output_buffer, nframes);
|
||||
((Player*)user)->Tick((s16Output*)output_buffer, nframes);
|
||||
return nframes;
|
||||
}
|
||||
|
||||
void player::state_callback([[maybe_unused]] cubeb_stream* stream,
|
||||
void Player::state_callback([[maybe_unused]] cubeb_stream* stream,
|
||||
[[maybe_unused]] void* user,
|
||||
[[maybe_unused]] cubeb_state state) {}
|
||||
|
||||
void player::tick(s16_output* stream, int samples) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
m_tick++;
|
||||
void Player::Tick(s16Output* stream, int samples) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
static int htick = 200;
|
||||
static int stick = 48000;
|
||||
for (int i = 0; i < samples; i++) {
|
||||
// The handlers expect to tick at 240hz
|
||||
// 48000/240 = 200
|
||||
if (htick == 200) {
|
||||
for (auto it = m_handlers.begin(); it != m_handlers.end();) {
|
||||
bool done = it->second->tick();
|
||||
mTick++;
|
||||
|
||||
for (auto it = mHandlers.begin(); it != mHandlers.end();) {
|
||||
bool done = it->second->Tick();
|
||||
if (done) {
|
||||
// fmt::print("erasing handler\n");
|
||||
m_handle_allocator.free_id(it->first);
|
||||
it = m_handlers.erase(it);
|
||||
mHandleAllocator.FreeId(it->first);
|
||||
it = mHandlers.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
@ -121,45 +125,45 @@ void player::tick(s16_output* stream, int samples) {
|
||||
|
||||
stick++;
|
||||
htick++;
|
||||
*stream++ = m_synth.tick();
|
||||
*stream++ = mSynth.Tick();
|
||||
}
|
||||
}
|
||||
|
||||
u32 player::play_sound(u32 bank_id, u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
auto bank = m_loader.get_bank_by_handle(bank_id);
|
||||
u32 Player::PlaySound(BankHandle bank_id, u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
auto bank = mLoader.GetBankByHandle(bank_id);
|
||||
if (bank == nullptr) {
|
||||
lg::error("play_sound: Bank {} does not exist", bank_id);
|
||||
lg::error("play_sound: Bank {} does not exist", static_cast<void*>(bank_id));
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto handler = bank->make_handler(m_vmanager, sound_id, vol, pan, pm, pb);
|
||||
auto handler = bank->MakeHandler(mVmanager, sound_id, vol, pan, pm, pb);
|
||||
if (!handler.has_value()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 handle = m_handle_allocator.get_id();
|
||||
m_handlers.emplace(handle, std::move(handler.value()));
|
||||
u32 handle = mHandleAllocator.GetId();
|
||||
mHandlers.emplace(handle, std::move(handler.value()));
|
||||
// fmt::print("play_sound {}:{} - {}\n", bank_id, sound_id, handle);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
u32 player::play_sound_by_name(u32 bank_id,
|
||||
u32 Player::PlaySoundByName(BankHandle bank_id,
|
||||
char* bank_name,
|
||||
char* sound_name,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
s32 pm,
|
||||
s32 pb) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
std::scoped_lock lock(mTickLock);
|
||||
SoundBank* bank = nullptr;
|
||||
if (bank_id == 0 && bank_name != nullptr) {
|
||||
bank = m_loader.get_bank_by_name(bank_name);
|
||||
bank = mLoader.GetBankByName(bank_name);
|
||||
} else if (bank_id != 0) {
|
||||
bank = m_loader.get_bank_by_handle(bank_id);
|
||||
bank = mLoader.GetBankByHandle(bank_id);
|
||||
} else {
|
||||
bank = m_loader.get_bank_with_sound(sound_name);
|
||||
bank = mLoader.GetBankWithSound(sound_name);
|
||||
}
|
||||
|
||||
if (bank == nullptr) {
|
||||
@ -167,9 +171,9 @@ u32 player::play_sound_by_name(u32 bank_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto sound = bank->get_sound_by_name(sound_name);
|
||||
auto sound = bank->GetSoundByName(sound_name);
|
||||
if (sound.has_value()) {
|
||||
return play_sound(bank->bank_id, sound.value(), vol, pan, pm, pb);
|
||||
return PlaySound(bank, sound.value(), vol, pan, pm, pb);
|
||||
}
|
||||
|
||||
// lg::error("play_sound_by_name: failed to find sound {}", sound_name);
|
||||
@ -177,41 +181,41 @@ u32 player::play_sound_by_name(u32 bank_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void player::stop_sound(u32 sound_id) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
auto handler = m_handlers.find(sound_id);
|
||||
if (handler == m_handlers.end())
|
||||
void Player::StopSound(u32 sound_id) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
auto handler = mHandlers.find(sound_id);
|
||||
if (handler == mHandlers.end())
|
||||
return;
|
||||
|
||||
handler->second->stop();
|
||||
handler->second->Stop();
|
||||
|
||||
// m_handle_allocator.free_id(sound_id);
|
||||
// m_handlers.erase(sound_id);
|
||||
}
|
||||
|
||||
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()) {
|
||||
void Player::SetSoundReg(u32 sound_id, u8 reg, u8 value) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
if (mHandlers.find(sound_id) == mHandlers.end()) {
|
||||
// fmt::print("set_midi_reg: Handler {} does not exist\n", sound_id);
|
||||
return;
|
||||
}
|
||||
|
||||
auto* handler = m_handlers.at(sound_id).get();
|
||||
handler->set_register(reg, value);
|
||||
auto* handler = mHandlers.at(sound_id).get();
|
||||
handler->SetRegister(reg, value);
|
||||
}
|
||||
|
||||
bool player::sound_still_active(u32 sound_id) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
auto handler = m_handlers.find(sound_id);
|
||||
if (handler == m_handlers.end())
|
||||
bool Player::SoundStillActive(u32 sound_id) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
auto handler = mHandlers.find(sound_id);
|
||||
if (handler == mHandlers.end())
|
||||
return false;
|
||||
|
||||
// fmt::print("sound_still_active {}\n", sound_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
void player::set_master_volume(u32 group, s32 volume) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
void Player::SetMasterVolume(u32 group, s32 volume) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
if (volume > 0x400)
|
||||
volume = 0x400;
|
||||
|
||||
@ -221,125 +225,124 @@ void player::set_master_volume(u32 group, s32 volume) {
|
||||
if (group == 15)
|
||||
return;
|
||||
|
||||
m_vmanager.set_master_vol(group, volume);
|
||||
mVmanager.SetMasterVol(group, volume);
|
||||
|
||||
// Master volume
|
||||
if (group == 16) {
|
||||
m_synth.set_master_vol(0x3ffff * volume / 0x400);
|
||||
mSynth.SetMasterVol(0x3ffff * volume / 0x400);
|
||||
}
|
||||
}
|
||||
|
||||
u32 player::load_bank(fs::path& filepath, size_t offset) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
std::fstream in(filepath, std::fstream::binary | std::fstream::in);
|
||||
in.seekg(offset, std::fstream::beg);
|
||||
|
||||
return m_loader.read_bank(in);
|
||||
BankHandle Player::LoadBank(nonstd::span<u8> bank) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
return mLoader.BankLoad(bank);
|
||||
}
|
||||
|
||||
void player::unload_bank(u32 bank_handle) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
auto* bank = m_loader.get_bank_by_handle(bank_handle);
|
||||
void Player::UnloadBank(BankHandle bank_handle) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
auto* bank = mLoader.GetBankByHandle(bank_handle);
|
||||
if (bank == nullptr)
|
||||
return;
|
||||
|
||||
for (auto it = m_handlers.begin(); it != m_handlers.end();) {
|
||||
if (it->second->bank().bank_id == bank_handle) {
|
||||
m_handle_allocator.free_id(it->first);
|
||||
it = m_handlers.erase(it);
|
||||
for (auto it = mHandlers.begin(); it != mHandlers.end();) {
|
||||
if (&it->second->Bank() == bank_handle) {
|
||||
mHandleAllocator.FreeId(it->first);
|
||||
it = mHandlers.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
m_loader.unload_bank(bank_handle);
|
||||
mLoader.UnloadBank(bank_handle);
|
||||
}
|
||||
|
||||
void player::set_pan_table(vol_pair* pantable) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
m_vmanager.set_pan_table(pantable);
|
||||
void Player::SetPanTable(VolPair* pantable) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
mVmanager.SetPanTable(pantable);
|
||||
}
|
||||
|
||||
void player::set_playback_mode(s32 mode) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
m_vmanager.set_playback_mode(mode);
|
||||
void Player::SetPlaybackMode(s32 mode) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
mVmanager.SetPlaybackMode(mode);
|
||||
}
|
||||
|
||||
void player::pause_sound(s32 sound_id) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
auto handler = m_handlers.find(sound_id);
|
||||
if (handler == m_handlers.end())
|
||||
void Player::PauseSound(s32 sound_id) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
auto handler = mHandlers.find(sound_id);
|
||||
if (handler == mHandlers.end())
|
||||
return;
|
||||
|
||||
handler->second->pause();
|
||||
handler->second->Pause();
|
||||
}
|
||||
|
||||
void player::continue_sound(s32 sound_id) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
auto handler = m_handlers.find(sound_id);
|
||||
if (handler == m_handlers.end())
|
||||
void Player::ContinueSound(s32 sound_id) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
auto handler = mHandlers.find(sound_id);
|
||||
if (handler == mHandlers.end())
|
||||
return;
|
||||
|
||||
handler->second->unpause();
|
||||
handler->second->Unpause();
|
||||
}
|
||||
|
||||
void player::pause_all_sounds_in_group(u8 group) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
void Player::PauseAllSoundsInGroup(u8 group) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
|
||||
for (auto& h : m_handlers) {
|
||||
if ((1 << h.second->group()) & group) {
|
||||
h.second->pause();
|
||||
for (auto& h : mHandlers) {
|
||||
if ((1 << h.second->Group()) & group) {
|
||||
h.second->Pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void player::continue_all_sounds_in_group(u8 group) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
void Player::ContinueAllSoundsInGroup(u8 group) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
|
||||
for (auto& h : m_handlers) {
|
||||
if ((1 << h.second->group()) & group) {
|
||||
h.second->unpause();
|
||||
for (auto& h : mHandlers) {
|
||||
if ((1 << h.second->Group()) & group) {
|
||||
h.second->Unpause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void player::set_sound_vol_pan(s32 sound_id, s32 vol, s32 pan) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
auto handler = m_handlers.find(sound_id);
|
||||
if (handler == m_handlers.end())
|
||||
void Player::SetSoundVolPan(s32 sound_id, s32 vol, s32 pan) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
auto handler = mHandlers.find(sound_id);
|
||||
if (handler == mHandlers.end())
|
||||
return;
|
||||
|
||||
handler->second->set_vol_pan(vol, pan);
|
||||
handler->second->SetVolPan(vol, pan);
|
||||
}
|
||||
|
||||
void player::set_sound_pmod(s32 sound_handle, s32 mod) {
|
||||
auto handler = m_handlers.find(sound_handle);
|
||||
if (handler == m_handlers.end())
|
||||
void Player::SetSoundPmod(s32 sound_handle, s32 mod) {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
auto handler = mHandlers.find(sound_handle);
|
||||
if (handler == mHandlers.end())
|
||||
return;
|
||||
|
||||
handler->second->set_pmod(mod);
|
||||
handler->second->SetPMod(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);
|
||||
void Player::StopAllSounds() {
|
||||
std::scoped_lock lock(mTickLock);
|
||||
for (auto it = mHandlers.begin(); it != mHandlers.end();) {
|
||||
mHandleAllocator.FreeId(it->first);
|
||||
it = mHandlers.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
s32 player::get_sound_user_data(s32 block_handle,
|
||||
s32 Player::GetSoundUserData(BankHandle block_handle,
|
||||
char* block_name,
|
||||
s32 sound_id,
|
||||
char* sound_name,
|
||||
SFXUserData* dst) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
std::scoped_lock lock(mTickLock);
|
||||
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);
|
||||
if (block_handle == nullptr && block_name != nullptr) {
|
||||
bank = mLoader.GetBankByName(block_name);
|
||||
} else if (block_handle != nullptr) {
|
||||
bank = mLoader.GetBankByHandle(block_handle);
|
||||
} else {
|
||||
bank = m_loader.get_bank_with_sound(sound_name);
|
||||
bank = mLoader.GetBankWithSound(sound_name);
|
||||
}
|
||||
|
||||
if (bank == nullptr) {
|
||||
@ -347,7 +350,7 @@ s32 player::get_sound_user_data(s32 block_handle,
|
||||
}
|
||||
|
||||
if (sound_id == -1) {
|
||||
auto sound = bank->get_sound_by_name(sound_name);
|
||||
auto sound = bank->GetSoundByName(sound_name);
|
||||
if (sound.has_value()) {
|
||||
sound_id = sound.value();
|
||||
} else {
|
||||
@ -355,7 +358,7 @@ s32 player::get_sound_user_data(s32 block_handle,
|
||||
}
|
||||
}
|
||||
|
||||
auto ud = bank->get_sound_user_data(sound_id);
|
||||
auto ud = bank->GetSoundUserData(sound_id);
|
||||
if (ud.has_value()) {
|
||||
dst->data[0] = ud.value()->data[0];
|
||||
dst->data[1] = ud.value()->data[1];
|
||||
|
@ -10,82 +10,81 @@
|
||||
#include "ame_handler.h"
|
||||
#include "handle_allocator.h"
|
||||
#include "loader.h"
|
||||
#include "midi_handler.h"
|
||||
#include "sound_handler.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#include "../common/synth.h"
|
||||
#include "game/sound/989snd/vagvoice.h"
|
||||
|
||||
#include "third-party/cubeb/cubeb/include/cubeb/cubeb.h"
|
||||
#include "third-party/span.hpp"
|
||||
|
||||
namespace snd {
|
||||
|
||||
class player {
|
||||
class Player {
|
||||
public:
|
||||
player();
|
||||
~player();
|
||||
player(const player&) = delete;
|
||||
player operator=(const player&) = delete;
|
||||
Player();
|
||||
~Player();
|
||||
Player(const Player&) = delete;
|
||||
Player operator=(const Player&) = delete;
|
||||
|
||||
// player(player&& other) noexcept = default;
|
||||
// player& operator=(player&& other) noexcept = default;
|
||||
|
||||
u32 load_bank(fs::path& path, size_t offset);
|
||||
BankHandle LoadBank(nonstd::span<u8> bank);
|
||||
|
||||
u32 play_sound(u32 bank, u32 sound, s32 vol, s32 pan, s32 pm, s32 pb);
|
||||
u32 play_sound_by_name(u32 bank,
|
||||
u32 PlaySound(BankHandle bank, u32 sound, s32 vol, s32 pan, s32 pm, s32 pb);
|
||||
u32 PlaySoundByName(BankHandle 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);
|
||||
void set_global_excite(u8 value) { GlobalExcite = value; };
|
||||
bool sound_still_active(u32 sound_id);
|
||||
void set_master_volume(u32 group, s32 volume);
|
||||
void unload_bank(u32 bank_handle);
|
||||
void stop_sound(u32 sound_handle);
|
||||
void set_pan_table(vol_pair* pantable);
|
||||
void set_playback_mode(s32 mode);
|
||||
void pause_sound(s32 sound_handle);
|
||||
void continue_sound(s32 sound_handle);
|
||||
void pause_all_sounds_in_group(u8 group);
|
||||
void continue_all_sounds_in_group(u8 group);
|
||||
void set_sound_vol_pan(s32 sound_handle, s32 vol, s32 pan);
|
||||
void submit_voice(std::shared_ptr<voice>& voice) { m_synth.add_voice(voice); };
|
||||
void set_sound_pmod(s32 sound_handle, s32 mod);
|
||||
void init_cubeb();
|
||||
void destroy_cubeb();
|
||||
s32 get_tick() { return m_tick; };
|
||||
void stop_all_sounds();
|
||||
s32 get_sound_user_data(s32 block_handle,
|
||||
void SetSoundReg(u32 sound_id, u8 reg, u8 value);
|
||||
void SetGlobalExcite(u8 value) { GlobalExcite = value; };
|
||||
bool SoundStillActive(u32 sound_id);
|
||||
void SetMasterVolume(u32 group, s32 volume);
|
||||
void UnloadBank(BankHandle bank_handle);
|
||||
void StopSound(u32 sound_handle);
|
||||
void SetPanTable(VolPair* pantable);
|
||||
void SetPlaybackMode(s32 mode);
|
||||
void PauseSound(s32 sound_handle);
|
||||
void ContinueSound(s32 sound_handle);
|
||||
void PauseAllSoundsInGroup(u8 group);
|
||||
void ContinueAllSoundsInGroup(u8 group);
|
||||
void SetSoundVolPan(s32 sound_handle, s32 vol, s32 pan);
|
||||
void SubmitVoice(std::shared_ptr<Voice>& voice) { mSynth.AddVoice(voice); };
|
||||
void SetSoundPmod(s32 sound_handle, s32 mod);
|
||||
void InitCubeb();
|
||||
void DestroyCubeb();
|
||||
s32 GetTick() { return mTick; };
|
||||
void StopAllSounds();
|
||||
s32 GetSoundUserData(BankHandle 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
|
||||
id_allocator m_handle_allocator;
|
||||
std::unordered_map<u32, std::unique_ptr<sound_handler>> m_handlers;
|
||||
std::recursive_mutex mTickLock; // TODO does not need to recursive with some light restructuring
|
||||
IdAllocator mHandleAllocator;
|
||||
std::unordered_map<u32, std::unique_ptr<SoundHandler>> mHandlers;
|
||||
|
||||
void tick(s16_output* stream, int samples);
|
||||
void Tick(s16Output* stream, int samples);
|
||||
|
||||
#ifdef _WIN32
|
||||
bool m_coinitialized = false;
|
||||
#endif
|
||||
|
||||
loader m_loader;
|
||||
synth m_synth;
|
||||
voice_manager m_vmanager;
|
||||
s32 m_tick{0};
|
||||
Loader mLoader;
|
||||
Synth mSynth;
|
||||
VoiceManager mVmanager;
|
||||
s32 mTick{0};
|
||||
|
||||
cubeb* m_ctx{nullptr};
|
||||
cubeb_stream* m_stream{nullptr};
|
||||
cubeb* mCtx{nullptr};
|
||||
cubeb_stream* mStream{nullptr};
|
||||
|
||||
static long sound_callback(cubeb_stream* stream,
|
||||
void* user,
|
||||
|
@ -7,40 +7,28 @@
|
||||
|
||||
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::optional<std::unique_ptr<sound_handler>> SFXBlock::make_handler(voice_manager& vm,
|
||||
std::optional<std::unique_ptr<SoundHandler>> SFXBlock::MakeHandler(VoiceManager& vm,
|
||||
u32 sound_id,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
SndPlayParams& params) {
|
||||
auto& SFX = m_sounds[sound_id];
|
||||
auto& SFX = Sounds[sound_id];
|
||||
|
||||
if (SFX.grains.empty()) {
|
||||
if (SFX.Grains.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto handler =
|
||||
std::make_unique<blocksound_handler>(*this, m_sounds[sound_id], vm, vol, pan, params);
|
||||
auto handler = std::make_unique<BlockSoundHandler>(*this, SFX, vm, vol, pan, params);
|
||||
return handler;
|
||||
}
|
||||
|
||||
std::optional<u32> SFXBlock::GetSoundByName(const char* name) {
|
||||
auto sound = Names.find(name);
|
||||
if (sound != Names.end()) {
|
||||
return sound->second;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace snd
|
||||
|
@ -4,64 +4,61 @@
|
||||
#include "sfxgrain.h"
|
||||
#include "soundbank.h"
|
||||
|
||||
#include "sfxblock2.h"
|
||||
#include "third-party/span.hpp"
|
||||
|
||||
namespace snd {
|
||||
|
||||
struct SFXBlockData : BankTag {
|
||||
/* 10 */ s8 BlockNum;
|
||||
/* 11 */ s8 pad1;
|
||||
/* 12 */ s16 pad2;
|
||||
/* 14 */ s16 pad3;
|
||||
/* 16 */ s16 NumSounds;
|
||||
/* 18 */ s16 NumGrains;
|
||||
/* 1a */ s16 NumVAGs;
|
||||
/* 1c */ u32 FirstSound;
|
||||
/* 20 */ u32 FirstGrain;
|
||||
/* 24 */ u32 VagsInSR;
|
||||
/* 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 SFXData {
|
||||
/* 0 */ s8 Vol;
|
||||
/* 1 */ s8 VolGroup;
|
||||
/* 2 */ s16 Pan;
|
||||
/* 4 */ s8 NumGrains;
|
||||
/* 5 */ s8 InstanceLimit;
|
||||
/* 6 */ u16 Flags;
|
||||
/* 8 */ u32 FirstGrain;
|
||||
};
|
||||
|
||||
enum SFXFlags {
|
||||
Looper = 1,
|
||||
SoloSound = 2, // Stop previous instances
|
||||
};
|
||||
|
||||
struct SFX {
|
||||
SFXData d;
|
||||
std::vector<std::unique_ptr<Grain>> grains;
|
||||
struct SFXUserData { // 0x10
|
||||
/* 0x0 */ u32 data[4];
|
||||
};
|
||||
|
||||
class SFXBlock : public SoundBank {
|
||||
public:
|
||||
SFXBlock(locator& loc, u32 handle, BankTag* tag);
|
||||
std::optional<std::unique_ptr<sound_handler>> make_handler(voice_manager& vm,
|
||||
struct SFXFlags {
|
||||
u16 flags;
|
||||
|
||||
static constexpr u32 SFX_LOOP = 1;
|
||||
static constexpr u32 SFX_SOLO = 2;
|
||||
static constexpr u32 SFX_INSTLIMIT = 8;
|
||||
static constexpr u32 SFX_INSTLIMIT_VOL = 0x10;
|
||||
static constexpr u32 SFX_INSTLIMIT_TICK = 0x20;
|
||||
|
||||
bool has_loop() { return flags & SFX_LOOP; }
|
||||
bool solo() { return flags & SFX_SOLO; }
|
||||
bool has_instlimit() { return flags & SFX_INSTLIMIT; }
|
||||
bool instlimit_vol() { return flags & SFX_INSTLIMIT_VOL; }
|
||||
bool instlimit_tick() { return flags & SFX_INSTLIMIT_TICK; }
|
||||
};
|
||||
|
||||
struct SFX {
|
||||
s8 Vol;
|
||||
s8 VolGroup;
|
||||
s16 Pan;
|
||||
s8 InstanceLimit;
|
||||
SFXFlags Flags;
|
||||
|
||||
std::vector<Grain> Grains;
|
||||
SFXUserData UserData;
|
||||
};
|
||||
|
||||
std::vector<SFX> Sounds;
|
||||
std::string Name;
|
||||
std::map<std::string, u32> Names;
|
||||
std::unique_ptr<u8[]> SampleData;
|
||||
|
||||
static SFXBlock* ReadBlock(nonstd::span<u8> bank_data, nonstd::span<u8> samples);
|
||||
|
||||
std::optional<std::unique_ptr<SoundHandler>> MakeHandler(VoiceManager& vm,
|
||||
u32 sound_id,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
SndPlayParams& params) override;
|
||||
|
||||
private:
|
||||
[[maybe_unused]] locator& m_locator;
|
||||
std::vector<SFX2> m_sounds;
|
||||
std::optional<std::string_view> GetName() override { return Name; };
|
||||
std::optional<u32> GetSoundByName(const char* name) override;
|
||||
std::optional<const SFXUserData*> GetSoundUserData(u32 sound_id) override {
|
||||
return &Sounds.at(sound_id).UserData;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace snd
|
||||
|
@ -1,102 +0,0 @@
|
||||
#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);
|
||||
if (m_names.find(str) == m_names.end()) {
|
||||
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::optional<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 std::nullopt;
|
||||
}
|
||||
|
||||
auto& SFX = m_sounds[sound_id];
|
||||
|
||||
if (SFX.grains.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto handler =
|
||||
std::make_unique<blocksound_handler>(*this, m_sounds[sound_id], vm, vol, pan, params);
|
||||
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
|
@ -1,115 +0,0 @@
|
||||
#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::optional<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:
|
||||
[[maybe_unused]] locator& m_locator;
|
||||
std::string m_name;
|
||||
std::unordered_map<std::string, u32> m_names;
|
||||
std::vector<SFX2> m_sounds;
|
||||
};
|
||||
|
||||
} // namespace snd
|
@ -7,12 +7,13 @@
|
||||
|
||||
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 Grain::snd_SFX_GRAIN_TYPE_NULL(BlockSoundHandler& handler) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 SFXGrain_Tone::execute(blocksound_handler& handler) {
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_TONE(BlockSoundHandler& handler) {
|
||||
auto& tone = std::get<Tone>(data);
|
||||
|
||||
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);
|
||||
@ -23,14 +24,14 @@ s32 SFXGrain_Tone::execute(blocksound_handler& handler) {
|
||||
while (handler.m_cur_pan < 0)
|
||||
handler.m_cur_pan += 360;
|
||||
|
||||
if ((m_tone.Flags & 8) != 0) {
|
||||
if ((tone.Flags & 8) != 0) {
|
||||
// Noise unsupported
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto voice = std::make_shared<blocksound_voice>(m_tone);
|
||||
auto voice = std::make_shared<BlockSoundVoice>(tone);
|
||||
|
||||
s32 vol = m_tone.Vol;
|
||||
s32 vol = tone.Vol;
|
||||
|
||||
if (vol < 0) {
|
||||
if (vol >= -4) {
|
||||
@ -44,7 +45,7 @@ s32 SFXGrain_Tone::execute(blocksound_handler& handler) {
|
||||
|
||||
vol = std::max(vol, 0);
|
||||
|
||||
s32 pan = m_tone.Pan;
|
||||
s32 pan = tone.Pan;
|
||||
if (pan < 0) {
|
||||
if (pan >= -4) {
|
||||
pan = 360 * handler.m_registers.at(-pan - 1) / 127;
|
||||
@ -69,61 +70,60 @@ s32 SFXGrain_Tone::execute(blocksound_handler& handler) {
|
||||
voice->g_pan = pan;
|
||||
|
||||
voice->basevol =
|
||||
handler.m_vm.make_volume(127, 0, handler.m_cur_volume, handler.m_cur_pan, vol, pan);
|
||||
handler.m_vm.MakeVolume(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_vm.StartTone(voice);
|
||||
handler.m_voices.emplace_front(voice);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SFXGrain_LfoSettings::SFXGrain_LfoSettings(SFXGrain& grain) : Grain(grain) {
|
||||
m_lfop = grain.GrainParams.lfo;
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_XREF_ID(BlockSoundHandler& handler) {
|
||||
return 0;
|
||||
}
|
||||
SFXGrain_LfoSettings::SFXGrain_LfoSettings(SFXGrain2& grain, u8* data) : Grain(grain) {
|
||||
m_lfop = *(LFOParams*)(data + (grain.OpcodeData.Opcode & 0xFFFFFF));
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_XREF_NUM(BlockSoundHandler& handler) {
|
||||
return 0;
|
||||
}
|
||||
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;
|
||||
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_LFO_SETTINGS(BlockSoundHandler& handler) {
|
||||
auto lfop = std::get<LFOParams>(data);
|
||||
auto& lfo = handler.m_lfo.at(lfop.which_lfo);
|
||||
|
||||
lfo.m_target = static_cast<LFOTarget>(lfop.target);
|
||||
if (lfo.m_target != LFOTarget::NONE) {
|
||||
lfo.m_type = static_cast<LFOType>(lfop.shape);
|
||||
lfo.m_target_extra = lfop.target_extra;
|
||||
lfo.m_setup_flags = lfop.flags;
|
||||
lfo.m_depth = lfop.depth;
|
||||
lfo.m_orig_depth = lfop.depth;
|
||||
lfo.m_step_size = lfop.step_size;
|
||||
lfo.m_orig_step_size = 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;
|
||||
if (lfo.m_type == LFOType::SQUARE) {
|
||||
lfo.m_state_hold1 = 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;
|
||||
lfo.m_next_step = 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();
|
||||
lfo.Init();
|
||||
} else {
|
||||
lfo.m_type = lfo_type::OFF;
|
||||
lfo.m_type = LFOType::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;
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_STARTCHILDSOUND(BlockSoundHandler& handler) {
|
||||
auto psp = std::get<PlaySoundParams>(data);
|
||||
|
||||
s32 vol = psp.vol;
|
||||
if (vol < 0) {
|
||||
if (vol >= -4) {
|
||||
vol = handler.m_registers.at(-vol - 1);
|
||||
@ -136,7 +136,7 @@ s32 SFXGrain_StartChildSound::execute(blocksound_handler& handler) {
|
||||
|
||||
vol = std::clamp(std::abs(vol), 0, 127);
|
||||
|
||||
s32 pan = m_psp.pan;
|
||||
s32 pan = psp.pan;
|
||||
if (pan < 0) {
|
||||
if (pan >= -4) {
|
||||
pan = 360 * std::min(std::abs(handler.m_registers.at(-pan - 1)), 127) / 127;
|
||||
@ -154,11 +154,11 @@ s32 SFXGrain_StartChildSound::execute(blocksound_handler& handler) {
|
||||
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;
|
||||
auto& block = static_cast<SoundBank&>(handler.Bank());
|
||||
s32 index = psp.sound_id;
|
||||
|
||||
if (index >= 0) {
|
||||
auto child_handler = block.make_handler(handler.m_vm, index, vol, pan, params);
|
||||
auto child_handler = block.MakeHandler(handler.m_vm, index, vol, pan, params);
|
||||
if (child_handler.has_value()) {
|
||||
handler.m_children.emplace_front(std::move(child_handler.value()));
|
||||
}
|
||||
@ -171,17 +171,15 @@ s32 SFXGrain_StartChildSound::execute(blocksound_handler& handler) {
|
||||
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) {
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_STOPCHILDSOUND(BlockSoundHandler& handler) {
|
||||
auto psp = std::get<PlaySoundParams>(data);
|
||||
auto& block = static_cast<SFXBlock&>(handler.m_bank);
|
||||
|
||||
if (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) {
|
||||
auto* sound = static_cast<BlockSoundHandler*>(it->get());
|
||||
// TODO VERIFY that this works
|
||||
if (&sound->m_sfx == &block.Sounds[psp.sound_id]) {
|
||||
it = handler.m_children.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
@ -191,30 +189,36 @@ s32 SFXGrain_StopChildSound::execute(blocksound_handler& handler) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
lg::error("indirect createchildsound");
|
||||
lg::error("indirect stopchildsound");
|
||||
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) {
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_PLUGIN_MESSAGE(BlockSoundHandler& 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) {
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_BRANCH(BlockSoundHandler& 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) {
|
||||
s32 Grain::snd_SFX_UNKNOWN_GRAIN_TYPE(BlockSoundHandler& handler) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_CONTROL_NULL(BlockSoundHandler& handler) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_LOOP_START(BlockSoundHandler& handler) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_LOOP_END(BlockSoundHandler& 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) {
|
||||
if (handler.m_sfx.Grains[i].Type == GrainType::LOOP_START) {
|
||||
handler.m_next_grain = i - 1;
|
||||
found = true;
|
||||
}
|
||||
@ -227,12 +231,10 @@ s32 SFXGrain_LoopEnd::execute(blocksound_handler& handler) {
|
||||
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) {
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_LOOP_CONTINUE(BlockSoundHandler& handler) {
|
||||
bool found = false;
|
||||
for (int i = handler.m_next_grain + 1; i < (int)handler.m_sfx.grains.size() && !found; i++) {
|
||||
if (handler.m_sfx.grains[i]->type() == grain_type::LOOP_END) {
|
||||
for (int i = handler.m_next_grain + 1; i < (int)handler.m_sfx.Grains.size() && !found; i++) {
|
||||
if (handler.m_sfx.Grains[i].Type == GrainType::LOOP_END) {
|
||||
handler.m_next_grain = i;
|
||||
found = true;
|
||||
}
|
||||
@ -245,27 +247,18 @@ s32 SFXGrain_LoopContinue::execute(blocksound_handler& handler) {
|
||||
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) {
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_STOP(BlockSoundHandler& 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];
|
||||
}
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_RAND_PLAY(BlockSoundHandler& handler) {
|
||||
auto cp = std::get<ControlParams>(data);
|
||||
auto options = cp.param[0];
|
||||
auto count = cp.param[1];
|
||||
auto previous = cp.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++;
|
||||
@ -282,183 +275,152 @@ s32 SFXGrain_RandPlay::execute(blocksound_handler& handler) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SFXGrain_RandDelay::SFXGrain_RandDelay(SFXGrain& grain) : Grain(grain) {
|
||||
m_max = grain.GrainParams.delay.Amount;
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_RAND_DELAY(BlockSoundHandler& handler) {
|
||||
auto max = std::get<RandDelayParams>(data);
|
||||
return rand() % max.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;
|
||||
}
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_RAND_PB(BlockSoundHandler& handler) {
|
||||
auto cp = std::get<ControlParams>(data);
|
||||
auto pb = cp.param[0];
|
||||
|
||||
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);
|
||||
handler.SetPBend(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);
|
||||
}
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_ADD_PB(BlockSoundHandler& handler) {
|
||||
auto cp = std::get<ControlParams>(data);
|
||||
auto pb = cp.param[0];
|
||||
|
||||
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;
|
||||
s32 new_pb = handler.m_cur_pb + 0x7fff * pb / 127;
|
||||
new_pb = std::clamp<s32>(new_pb, INT16_MIN, INT16_MAX);
|
||||
|
||||
handler.set_pbend(new_pb);
|
||||
handler.SetPBend(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;
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_PB(BlockSoundHandler& handler) {
|
||||
auto cp = std::get<ControlParams>(data);
|
||||
auto pb = cp.param[0];
|
||||
|
||||
if (pb >= 0) {
|
||||
handler.SetPBend(0x7fff * pb / 127);
|
||||
} else {
|
||||
handler.m_registers.at(m_reg) = m_value;
|
||||
handler.SetPBend(-0x8000 * pb / -128);
|
||||
}
|
||||
|
||||
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;
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_SET_REGISTER(BlockSoundHandler& handler) {
|
||||
auto cp = std::get<ControlParams>(data);
|
||||
auto reg = cp.param[0];
|
||||
auto value = cp.param[1];
|
||||
|
||||
if (reg < 0) {
|
||||
g_block_reg.at(-reg - 1) = value;
|
||||
} else {
|
||||
handler.m_registers.at(m_reg) = rnd;
|
||||
handler.m_registers.at(reg) = value;
|
||||
}
|
||||
|
||||
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);
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_SET_REGISTER_RAND(BlockSoundHandler& handler) {
|
||||
auto cp = std::get<ControlParams>(data);
|
||||
auto reg = cp.param[0];
|
||||
auto lower_bound = cp.param[1];
|
||||
auto upper_bound = cp.param[2];
|
||||
|
||||
s32 range = upper_bound - lower_bound + 1;
|
||||
s32 rnd = (rand() % range) + lower_bound;
|
||||
if (reg < 0) {
|
||||
g_block_reg.at(-reg - 1) = rnd;
|
||||
} 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);
|
||||
handler.m_registers.at(reg) = rnd;
|
||||
}
|
||||
|
||||
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];
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_INC_REGISTER(BlockSoundHandler& handler) {
|
||||
auto cp = std::get<ControlParams>(data);
|
||||
auto reg = cp.param[0];
|
||||
|
||||
if (reg < 0) {
|
||||
s32 new_val = g_block_reg.at(-reg - 1) + 1;
|
||||
g_block_reg.at(-reg - 1) = std::clamp<s32>(new_val, INT8_MIN, INT8_MAX);
|
||||
|
||||
} else {
|
||||
s32 new_val = handler.m_registers.at(reg) + 1;
|
||||
handler.m_registers.at(reg) = std::clamp<s32>(new_val, INT8_MIN, INT8_MAX);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
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 Grain::snd_SFX_GRAIN_TYPE_DEC_REGISTER(BlockSoundHandler& handler) {
|
||||
auto cp = std::get<ControlParams>(data);
|
||||
auto reg = cp.param[0];
|
||||
|
||||
if (reg < 0) {
|
||||
s32 new_val = g_block_reg.at(-reg - 1) - 1;
|
||||
g_block_reg.at(-reg - 1) = std::clamp<s32>(new_val, INT8_MIN, INT8_MAX);
|
||||
|
||||
} else {
|
||||
s32 new_val = handler.m_registers.at(reg) - 1;
|
||||
handler.m_registers.at(reg) = std::clamp<s32>(new_val, INT8_MIN, INT8_MAX);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
s32 SFXGrain_TestRegister::execute(blocksound_handler& handler) {
|
||||
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_TEST_REGISTER(BlockSoundHandler& handler) {
|
||||
auto cp = std::get<ControlParams>(data);
|
||||
auto reg = cp.param[0];
|
||||
auto action = cp.param[1];
|
||||
auto cmp = cp.param[2];
|
||||
|
||||
s32 value;
|
||||
if (m_reg < 0) {
|
||||
value = g_block_reg[m_reg - 1];
|
||||
if (reg < 0) {
|
||||
value = g_block_reg[-reg - 1];
|
||||
} else {
|
||||
value = handler.m_registers.at(m_reg);
|
||||
value = handler.m_registers.at(reg);
|
||||
}
|
||||
|
||||
if (m_action == 0) {
|
||||
if (value >= m_cmp) {
|
||||
if (action == 0) {
|
||||
if (value >= cmp) {
|
||||
handler.m_next_grain++;
|
||||
}
|
||||
} else if (m_action == 1) {
|
||||
if (value != m_cmp) {
|
||||
} else if (action == 1) {
|
||||
if (value != cmp) {
|
||||
handler.m_next_grain++;
|
||||
}
|
||||
} else if (m_action >= 2) {
|
||||
if (m_cmp >= value)
|
||||
} else if (action >= 2) {
|
||||
if (cmp >= value)
|
||||
handler.m_next_grain++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SFXGrain_GotoMarker::SFXGrain_GotoMarker(SFXGrain& grain) : Grain(grain) {
|
||||
m_mark = grain.GrainParams.control.param[0];
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_MARKER(BlockSoundHandler& handler) {
|
||||
return 0;
|
||||
}
|
||||
SFXGrain_GotoMarker::SFXGrain_GotoMarker(SFXGrain2& grain, u8* data) : Grain(grain) {
|
||||
m_mark = grain.OpcodeData.arg[0];
|
||||
}
|
||||
s32 SFXGrain_GotoMarker::execute(blocksound_handler& handler) {
|
||||
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_GOTO_MARKER(BlockSoundHandler& handler) {
|
||||
auto cp = std::get<ControlParams>(data);
|
||||
auto target_mark = cp.param[0];
|
||||
bool found = false;
|
||||
for (int i = 0; i < (int)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) {
|
||||
|
||||
for (int i = 0; i < (int)handler.m_sfx.Grains.size() && !found; i++) {
|
||||
if (handler.m_sfx.Grains.at(i).Type == GrainType::MARKER) {
|
||||
auto mcp = std::get<ControlParams>(handler.m_sfx.Grains.at(i).data);
|
||||
auto mark = mcp.param[0];
|
||||
|
||||
if (mark == target_mark) {
|
||||
handler.m_next_grain = i - 1;
|
||||
found = true;
|
||||
}
|
||||
@ -472,22 +434,21 @@ s32 SFXGrain_GotoMarker::execute(blocksound_handler& handler) {
|
||||
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;
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_GOTO_RANDOM_MARKER(BlockSoundHandler& handler) {
|
||||
auto cp = std::get<ControlParams>(data);
|
||||
auto lower_bound = cp.param[0];
|
||||
auto upper_bound = cp.param[1];
|
||||
|
||||
for (int i = 0; i < (int)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) {
|
||||
bool found = false;
|
||||
s32 range = upper_bound - lower_bound + 1;
|
||||
s32 target_mark = (rand() % range) + lower_bound;
|
||||
|
||||
for (int i = 0; i < (int)handler.m_sfx.Grains.size() && !found; i++) {
|
||||
if (handler.m_sfx.Grains.at(i).Type == GrainType::MARKER) {
|
||||
auto mcp = std::get<ControlParams>(handler.m_sfx.Grains.at(i).data);
|
||||
auto mark = mcp.param[0];
|
||||
|
||||
if (mark == target_mark) {
|
||||
handler.m_next_grain = i - 1;
|
||||
found = true;
|
||||
}
|
||||
@ -501,9 +462,7 @@ s32 SFXGrain_GotoRandomMarker::execute(blocksound_handler& handler) {
|
||||
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) {
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_WAIT_FOR_ALL_VOICES(BlockSoundHandler& handler) {
|
||||
if (!handler.m_voices.empty()) {
|
||||
handler.m_next_grain--;
|
||||
return 1;
|
||||
@ -512,108 +471,88 @@ s32 SFXGrain_WaitForAllVoices::execute(blocksound_handler& handler) {
|
||||
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];
|
||||
}
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_PLAY_CYCLE(BlockSoundHandler& handler) {
|
||||
auto& cp = std::get<ControlParams>(data);
|
||||
auto group_size = cp.param[0];
|
||||
auto group_count = cp.param[1];
|
||||
auto index = cp.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;
|
||||
auto a = index++;
|
||||
if (index == group_size) {
|
||||
index = 0;
|
||||
}
|
||||
cp.param[2] = index;
|
||||
|
||||
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_next_grain += group_count * a;
|
||||
handler.m_grains_to_play = group_count + 1;
|
||||
handler.m_grains_to_skip = (group_size - 1 - a) * 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);
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_ADD_REGISTER(BlockSoundHandler& handler) {
|
||||
auto cp = std::get<ControlParams>(data);
|
||||
auto val = cp.param[0];
|
||||
auto reg = cp.param[1];
|
||||
|
||||
if (reg < 0) {
|
||||
s32 new_val = g_block_reg.at(-reg - 1) + val;
|
||||
g_block_reg.at(-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);
|
||||
s32 new_val = handler.m_registers.at(reg) + val;
|
||||
handler.m_registers.at(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) {
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_KEY_OFF_VOICES(BlockSoundHandler& handler) {
|
||||
for (auto& p : handler.m_voices) {
|
||||
auto v = p.lock();
|
||||
if (v == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
v->key_off();
|
||||
v->KeyOff();
|
||||
}
|
||||
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) {
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_KILL_VOICES(BlockSoundHandler& 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);
|
||||
v->KeyOff();
|
||||
v->SetVolumeL(0);
|
||||
v->SetVolumeR(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;
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_ON_STOP_MARKER(BlockSoundHandler& 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) {
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_COPY_REGISTER(BlockSoundHandler& handler) {
|
||||
auto cp = std::get<ControlParams>(data);
|
||||
auto src = cp.param[0];
|
||||
auto dst = cp.param[1];
|
||||
|
||||
s8 value = 0;
|
||||
if (m_src < 0) {
|
||||
value = g_block_reg.at(-m_src - 1);
|
||||
if (src < 0) {
|
||||
value = g_block_reg.at(-src - 1);
|
||||
} else {
|
||||
value = handler.m_registers.at(m_src);
|
||||
value = handler.m_registers.at(src);
|
||||
}
|
||||
|
||||
if (m_dst < 0) {
|
||||
g_block_reg.at(-m_dst - 1) = value;
|
||||
if (dst < 0) {
|
||||
g_block_reg.at(-dst - 1) = value;
|
||||
} else {
|
||||
handler.m_registers.at(m_dst) = value;
|
||||
handler.m_registers.at(dst) = value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -51,39 +51,7 @@ struct PluginParams {
|
||||
/* 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 {
|
||||
enum class GrainType : u32 {
|
||||
NULL_GRAIN = 0,
|
||||
TONE = 1,
|
||||
TONE2 = 9,
|
||||
@ -121,409 +89,100 @@ enum class grain_type : u32 {
|
||||
COPY_REGISTER = 44,
|
||||
};
|
||||
|
||||
class blocksound_handler;
|
||||
class BlockSoundHandler;
|
||||
|
||||
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; }
|
||||
struct Grain {
|
||||
GrainType Type;
|
||||
s32 Delay;
|
||||
std::variant<Tone, RandDelayParams, ControlParams, LFOParams, PlaySoundParams, PluginParams> data;
|
||||
s32 operator()(BlockSoundHandler& handler) { return (this->*func[(u32)Type])(handler); }
|
||||
|
||||
private:
|
||||
std::array<int, 4> m_args;
|
||||
grain_type m_type{0};
|
||||
s32 m_delay{0};
|
||||
s32 snd_SFX_GRAIN_TYPE_NULL(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_TONE(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_XREF_ID(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_XREF_NUM(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_LFO_SETTINGS(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_STARTCHILDSOUND(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_STOPCHILDSOUND(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_PLUGIN_MESSAGE(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_BRANCH(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_UNKNOWN_GRAIN_TYPE(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_CONTROL_NULL(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_LOOP_START(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_LOOP_END(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_LOOP_CONTINUE(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_STOP(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_RAND_PLAY(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_RAND_DELAY(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_RAND_PB(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_ADD_PB(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_PB(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_SET_REGISTER(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_SET_REGISTER_RAND(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_INC_REGISTER(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_DEC_REGISTER(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_TEST_REGISTER(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_MARKER(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_GOTO_MARKER(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_GOTO_RANDOM_MARKER(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_WAIT_FOR_ALL_VOICES(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_PLAY_CYCLE(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_ADD_REGISTER(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_KEY_OFF_VOICES(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_KILL_VOICES(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_ON_STOP_MARKER(BlockSoundHandler& handler);
|
||||
s32 snd_SFX_GRAIN_TYPE_COPY_REGISTER(BlockSoundHandler& handler);
|
||||
|
||||
using GrainFunc = s32 (Grain::*)(BlockSoundHandler& handler);
|
||||
|
||||
static constexpr std::array<GrainFunc, 45> func = {
|
||||
&Grain::snd_SFX_GRAIN_TYPE_NULL,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_TONE,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_XREF_ID,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_XREF_NUM,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_LFO_SETTINGS,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_STARTCHILDSOUND,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_STOPCHILDSOUND,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_PLUGIN_MESSAGE,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_BRANCH,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_TONE,
|
||||
&Grain::snd_SFX_UNKNOWN_GRAIN_TYPE,
|
||||
&Grain::snd_SFX_UNKNOWN_GRAIN_TYPE,
|
||||
&Grain::snd_SFX_UNKNOWN_GRAIN_TYPE,
|
||||
&Grain::snd_SFX_UNKNOWN_GRAIN_TYPE,
|
||||
&Grain::snd_SFX_UNKNOWN_GRAIN_TYPE,
|
||||
&Grain::snd_SFX_UNKNOWN_GRAIN_TYPE,
|
||||
&Grain::snd_SFX_UNKNOWN_GRAIN_TYPE,
|
||||
&Grain::snd_SFX_UNKNOWN_GRAIN_TYPE,
|
||||
&Grain::snd_SFX_UNKNOWN_GRAIN_TYPE,
|
||||
&Grain::snd_SFX_UNKNOWN_GRAIN_TYPE,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_CONTROL_NULL,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_LOOP_START,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_LOOP_END,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_LOOP_CONTINUE,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_STOP,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_RAND_PLAY,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_RAND_DELAY,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_RAND_PB,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_PB,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_ADD_PB,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_SET_REGISTER,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_SET_REGISTER_RAND,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_INC_REGISTER,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_DEC_REGISTER,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_TEST_REGISTER,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_MARKER,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_GOTO_MARKER,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_GOTO_RANDOM_MARKER,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_WAIT_FOR_ALL_VOICES,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_PLAY_CYCLE,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_ADD_REGISTER,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_KEY_OFF_VOICES,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_KILL_VOICES,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_ON_STOP_MARKER,
|
||||
&Grain::snd_SFX_GRAIN_TYPE_COPY_REGISTER,
|
||||
};
|
||||
};
|
||||
|
||||
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 {}", fmt::underlying(id)));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace snd
|
||||
|
@ -2,6 +2,8 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define sleep(n) Sleep(n * 1000)
|
||||
@ -12,14 +14,14 @@
|
||||
#include "common/log/log.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
snd::player player;
|
||||
unsigned bankid = 0;
|
||||
snd::Player player;
|
||||
|
||||
fs::path file = argv[1];
|
||||
bankid = player.load_bank(file, 0);
|
||||
auto file_buf = file_util::read_binary_file(file);
|
||||
auto bankid = player.LoadBank(file_buf);
|
||||
|
||||
if (argc > 2) {
|
||||
unsigned sound = player.play_sound(bankid, atoi(argv[2]), 0x400, 0, 0, 0);
|
||||
unsigned sound = player.PlaySound(bankid, atoi(argv[2]), 0x400, 0, 0, 0);
|
||||
lg::info("sound {} started", sound);
|
||||
}
|
||||
|
||||
@ -44,20 +46,20 @@ int main(int argc, char* argv[]) {
|
||||
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);
|
||||
auto id = player.PlaySound(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);
|
||||
auto id = player.PlaySound(bankid, idx, 0x400, 0, 0, 0);
|
||||
while (true) {
|
||||
if (player.sound_still_active(id)) {
|
||||
if (player.SoundStillActive(id)) {
|
||||
sleep(1);
|
||||
} else {
|
||||
idx++;
|
||||
id = player.play_sound(bankid, idx, 0x400, 0, 0, 0);
|
||||
id = player.PlaySound(bankid, idx, 0x400, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,14 +68,14 @@ int main(int argc, char* argv[]) {
|
||||
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()),
|
||||
player.SetSoundReg(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();
|
||||
player.StopAllSounds();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,18 +11,18 @@ static constexpr int VOLUME_DONT_CHANGE = 0x7fffffff;
|
||||
|
||||
class SoundBank;
|
||||
|
||||
class sound_handler {
|
||||
class SoundHandler {
|
||||
public:
|
||||
virtual ~sound_handler() = default;
|
||||
virtual bool tick() = 0;
|
||||
virtual SoundBank& bank() = 0;
|
||||
virtual void pause() = 0;
|
||||
virtual void unpause() = 0;
|
||||
virtual u8 group() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void set_vol_pan(s32 vol, s32 pan) = 0;
|
||||
virtual void set_pmod(s32 mod) = 0;
|
||||
virtual void set_pbend(s32 /*mod*/){};
|
||||
virtual void set_register(u8 /*reg*/, u8 /*value*/) {}
|
||||
virtual ~SoundHandler() = default;
|
||||
virtual bool Tick() = 0;
|
||||
virtual SoundBank& Bank() = 0;
|
||||
virtual void Pause() = 0;
|
||||
virtual void Unpause() = 0;
|
||||
virtual u8 Group() = 0;
|
||||
virtual void Stop() = 0;
|
||||
virtual void SetVolPan(s32 vol, s32 pan) = 0;
|
||||
virtual void SetPMod(s32 mod) = 0;
|
||||
virtual void SetPBend(s32 /*mod*/){};
|
||||
virtual void SetRegister(u8 /*reg*/, u8 /*value*/) {}
|
||||
};
|
||||
} // namespace snd
|
||||
|
@ -2,13 +2,13 @@
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "locator.h"
|
||||
#include "sound_handler.h"
|
||||
#include "vagvoice.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "../common/synth.h"
|
||||
#include "game/sound/989snd/loader.h"
|
||||
|
||||
namespace snd {
|
||||
|
||||
@ -35,10 +35,25 @@ enum class BankType {
|
||||
struct SFXUserData;
|
||||
class SoundBank {
|
||||
public:
|
||||
SoundBank(u32 id, BankType type) : type(type), bank_id(id){};
|
||||
struct BlockFlags {
|
||||
u32 flags;
|
||||
|
||||
static constexpr u32 BLOCK_HAS_NAMES = 0x100;
|
||||
static constexpr u32 BLOCK_HAS_USERDATA = 0x200;
|
||||
|
||||
bool hasNames() { return flags & BLOCK_HAS_NAMES; }
|
||||
bool hasUserdata() { return flags & BLOCK_HAS_USERDATA; }
|
||||
};
|
||||
|
||||
virtual ~SoundBank() = default;
|
||||
|
||||
virtual std::optional<std::unique_ptr<sound_handler>> make_handler(voice_manager& vm,
|
||||
u32 DataID;
|
||||
u32 Version;
|
||||
BlockFlags Flags;
|
||||
u32 BankID;
|
||||
s8 BankNum;
|
||||
|
||||
virtual std::optional<std::unique_ptr<SoundHandler>> MakeHandler(VoiceManager& vm,
|
||||
u32 sound_id,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
@ -50,25 +65,20 @@ class SoundBank {
|
||||
params.pitch_mod = pm;
|
||||
params.pitch_bend = pb;
|
||||
|
||||
return make_handler(vm, sound_id, -1, -1, params);
|
||||
return MakeHandler(vm, sound_id, -1, -1, params);
|
||||
};
|
||||
|
||||
virtual std::optional<std::unique_ptr<sound_handler>> make_handler(voice_manager& vm,
|
||||
virtual std::optional<std::unique_ptr<SoundHandler>> MakeHandler(VoiceManager& 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*/) {
|
||||
virtual std::optional<std::string_view> GetName() { return std::nullopt; };
|
||||
virtual std::optional<u32> GetSoundByName(const char* /*name*/) { return std::nullopt; };
|
||||
virtual std::optional<const SFXUserData*> GetSoundUserData(u32 /*sound_id*/) {
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
BankType type;
|
||||
u32 bank_id;
|
||||
u32 bank_name;
|
||||
std::unique_ptr<u8[]> sampleBuf;
|
||||
};
|
||||
|
||||
} // namespace snd
|
||||
|
@ -69,7 +69,7 @@ u16 sceSdNote2Pitch(u16 center_note, u16 center_fine, u16 note, short fine) {
|
||||
return (u16)ret;
|
||||
}
|
||||
|
||||
const vol_pair normalPanTable[181] = {
|
||||
const VolPair normalPanTable[181] = {
|
||||
{0x3fff, 0x0000}, {0x3ffe, 0x008e}, {0x3ffc, 0x011d}, {0x3ff9, 0x01ac}, {0x3ff5, 0x023b},
|
||||
{0x3fef, 0x02ca}, {0x3fe8, 0x0359}, {0x3fe0, 0x03e8}, {0x3fd7, 0x0476}, {0x3fcc, 0x0505},
|
||||
{0x3fc0, 0x0593}, {0x3fb3, 0x0622}, {0x3fa5, 0x06b0}, {0x3f95, 0x073e}, {0x3f84, 0x07cc},
|
||||
@ -124,7 +124,7 @@ u16 PS1Note2Pitch(s8 center_note, s8 center_fine, short note, short fine) {
|
||||
return pitch;
|
||||
}
|
||||
|
||||
std::pair<s16, s16> pitchbend(Tone& tone,
|
||||
std::pair<s16, s16> PitchBend(Tone& tone,
|
||||
int current_pb,
|
||||
int current_pm,
|
||||
int start_note,
|
||||
|
@ -9,11 +9,11 @@
|
||||
namespace snd {
|
||||
|
||||
extern const u16 NotePitchTable[];
|
||||
extern const vol_pair normalPanTable[181];
|
||||
extern const VolPair normalPanTable[181];
|
||||
|
||||
u16 PS1Note2Pitch(s8 center_note, s8 center_fine, short note, short fine);
|
||||
u16 sceSdNote2Pitch(u16 center_note, u16 center_fine, u16 note, short fine);
|
||||
std::pair<s16, s16> pitchbend(Tone& tone,
|
||||
std::pair<s16, s16> PitchBend(Tone& tone,
|
||||
int current_pb,
|
||||
int current_pm,
|
||||
int start_note,
|
||||
|
@ -9,42 +9,41 @@
|
||||
#include "../common/voice.h"
|
||||
|
||||
namespace snd {
|
||||
voice_manager::voice_manager(synth& synth, locator& loc) : m_synth(synth), m_locator(loc) {
|
||||
m_pan_table = normalPanTable;
|
||||
m_master_vol.fill(0x400);
|
||||
m_group_duck.fill(0x10000);
|
||||
VoiceManager::VoiceManager(Synth& synth) : mSynth(synth) {
|
||||
mPanTable = normalPanTable;
|
||||
mMasterVol.fill(0x400);
|
||||
mGroupDuck.fill(0x10000);
|
||||
}
|
||||
|
||||
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);
|
||||
void VoiceManager::StartTone(std::shared_ptr<VagVoice> voice) {
|
||||
s16 left = AdjustVolToGroup(voice->basevol.left, voice->group);
|
||||
s16 right = AdjustVolToGroup(voice->basevol.right, voice->group);
|
||||
|
||||
voice->set_volume(left >> 1, right >> 1);
|
||||
voice->SetVolume(left >> 1, right >> 1);
|
||||
|
||||
if ((voice->tone.Flags & 0x10) != 0x0) {
|
||||
throw std::runtime_error("reverb only voice not handler");
|
||||
}
|
||||
|
||||
std::pair<s16, s16> note = pitchbend(voice->tone, voice->current_pb, voice->current_pm,
|
||||
std::pair<s16, s16> 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);
|
||||
voice->set_asdr1(voice->tone.ADSR1);
|
||||
voice->set_asdr2(voice->tone.ADSR2);
|
||||
voice->SetPitch(pitch);
|
||||
voice->SetAsdr1(voice->tone.ADSR1);
|
||||
voice->SetAsdr2(voice->tone.ADSR2);
|
||||
|
||||
u8* sbuf = m_locator.get_bank_samples(bank);
|
||||
voice->set_sample((u16*)(sbuf + voice->tone.VAGInSR));
|
||||
voice->SetSample((u16*)(voice->tone.Sample));
|
||||
|
||||
voice->key_on();
|
||||
voice->KeyOn();
|
||||
|
||||
clean_voices();
|
||||
m_voices.emplace_front(voice);
|
||||
m_synth.add_voice(voice);
|
||||
CleanVoices();
|
||||
mVoices.emplace_front(voice);
|
||||
mSynth.AddVoice(voice);
|
||||
}
|
||||
vol_pair voice_manager::make_volume(int vol1, int pan1, int vol2, int pan2, int vol3, int pan3) {
|
||||
VolPair VoiceManager::MakeVolume(int vol1, int pan1, int vol2, int pan2, int vol3, int pan3) {
|
||||
// Scale up as close as we can to max positive 16bit volume
|
||||
// I'd have just used shifting but I guess this does get closer
|
||||
|
||||
@ -57,7 +56,7 @@ vol_pair voice_manager::make_volume(int vol1, int pan1, int vol2, int pan2, int
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
if (m_stereo_or_mono == 1) {
|
||||
if (mStereoOrMono == 1) {
|
||||
return {(s16)vol, (s16)vol};
|
||||
}
|
||||
|
||||
@ -85,11 +84,11 @@ vol_pair voice_manager::make_volume(int vol1, int pan1, int vol2, int pan2, int
|
||||
// it. (For surround audio positioning?)
|
||||
|
||||
if (total_pan < 180) {
|
||||
lvol = (m_pan_table[total_pan].left * vol) / 0x3fff;
|
||||
rvol = (m_pan_table[total_pan].right * vol) / 0x3fff;
|
||||
lvol = (mPanTable[total_pan].left * vol) / 0x3fff;
|
||||
rvol = (mPanTable[total_pan].right * vol) / 0x3fff;
|
||||
} else {
|
||||
rvol = (m_pan_table[total_pan - 180].left * vol) / 0x3fff;
|
||||
lvol = (m_pan_table[total_pan - 180].right * vol) / 0x3fff;
|
||||
rvol = (mPanTable[total_pan - 180].left * vol) / 0x3fff;
|
||||
lvol = (mPanTable[total_pan - 180].right * vol) / 0x3fff;
|
||||
}
|
||||
|
||||
// TODO rest of this function
|
||||
@ -98,7 +97,7 @@ vol_pair voice_manager::make_volume(int vol1, int pan1, int vol2, int pan2, int
|
||||
return {lvol, rvol};
|
||||
}
|
||||
|
||||
vol_pair voice_manager::make_volume_b(int sound_vol,
|
||||
VolPair VoiceManager::MakeVolumeB(int sound_vol,
|
||||
int velocity_volume,
|
||||
int pan,
|
||||
int prog_vol,
|
||||
@ -118,7 +117,7 @@ vol_pair voice_manager::make_volume_b(int sound_vol,
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
if (m_stereo_or_mono == 1) {
|
||||
if (mStereoOrMono == 1) {
|
||||
return {(s16)vol, (s16)vol};
|
||||
}
|
||||
|
||||
@ -146,11 +145,11 @@ vol_pair voice_manager::make_volume_b(int sound_vol,
|
||||
// it. (For surround audio positioning?)
|
||||
|
||||
if (total_pan < 180) {
|
||||
lvol = (m_pan_table[total_pan].left * vol) / 0x3fff;
|
||||
rvol = (m_pan_table[total_pan].right * vol) / 0x3fff;
|
||||
lvol = (mPanTable[total_pan].left * vol) / 0x3fff;
|
||||
rvol = (mPanTable[total_pan].right * vol) / 0x3fff;
|
||||
} else {
|
||||
rvol = (m_pan_table[total_pan - 180].left * vol) / 0x3fff;
|
||||
lvol = (m_pan_table[total_pan - 180].right * vol) / 0x3fff;
|
||||
rvol = (mPanTable[total_pan - 180].left * vol) / 0x3fff;
|
||||
lvol = (mPanTable[total_pan - 180].right * vol) / 0x3fff;
|
||||
}
|
||||
|
||||
// TODO rest of this function
|
||||
@ -159,7 +158,7 @@ vol_pair voice_manager::make_volume_b(int sound_vol,
|
||||
return {lvol, rvol};
|
||||
}
|
||||
|
||||
s16 voice_manager::adjust_vol_to_group(s16 involume, int group) {
|
||||
s16 VoiceManager::AdjustVolToGroup(s16 involume, int group) {
|
||||
s32 volume = involume;
|
||||
// NOTE grou >= 7 in version 2
|
||||
if (group >= 15)
|
||||
@ -169,7 +168,7 @@ s16 voice_manager::adjust_vol_to_group(s16 involume, int group) {
|
||||
volume = 0x7ffe;
|
||||
|
||||
// NOTE no duckers in version 2
|
||||
s32 modifier = (m_master_vol[group] * m_group_duck[group]) / 0x10000;
|
||||
s32 modifier = (mMasterVol[group] * mGroupDuck[group]) / 0x10000;
|
||||
volume = (volume * modifier) / 0x400;
|
||||
int sign = 1;
|
||||
if (volume < 0) {
|
||||
@ -181,43 +180,43 @@ s16 voice_manager::adjust_vol_to_group(s16 involume, int group) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
void voice_manager::set_master_vol(u8 group, s32 volume) {
|
||||
m_master_vol[group] = volume;
|
||||
void VoiceManager::SetMasterVol(u8 group, s32 volume) {
|
||||
mMasterVol[group] = volume;
|
||||
|
||||
for (auto& p : m_voices) {
|
||||
for (auto& p : mVoices) {
|
||||
auto voice = p.lock();
|
||||
if (voice == nullptr || voice->paused) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (voice->group == group) {
|
||||
s16 left = adjust_vol_to_group(voice->basevol.left, voice->group);
|
||||
s16 right = adjust_vol_to_group(voice->basevol.right, voice->group);
|
||||
s16 left = AdjustVolToGroup(voice->basevol.left, voice->group);
|
||||
s16 right = AdjustVolToGroup(voice->basevol.right, voice->group);
|
||||
|
||||
voice->set_volume(left >> 1, right >> 1);
|
||||
voice->SetVolume(left >> 1, right >> 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void voice_manager::pause(std::shared_ptr<vag_voice> voice) {
|
||||
voice->set_volume(0, 0);
|
||||
voice->set_pitch(0);
|
||||
void VoiceManager::Pause(std::shared_ptr<VagVoice> voice) {
|
||||
voice->SetVolume(0, 0);
|
||||
voice->SetPitch(0);
|
||||
voice->paused = true;
|
||||
}
|
||||
|
||||
void voice_manager::unpause(std::shared_ptr<vag_voice> voice) {
|
||||
s16 left = adjust_vol_to_group(voice->basevol.left, voice->group);
|
||||
s16 right = adjust_vol_to_group(voice->basevol.right, voice->group);
|
||||
void VoiceManager::Unpause(std::shared_ptr<VagVoice> voice) {
|
||||
s16 left = AdjustVolToGroup(voice->basevol.left, voice->group);
|
||||
s16 right = AdjustVolToGroup(voice->basevol.right, voice->group);
|
||||
|
||||
voice->set_volume(left >> 1, right >> 1);
|
||||
voice->SetVolume(left >> 1, right >> 1);
|
||||
|
||||
std::pair<s16, s16> note = pitchbend(voice->tone, voice->current_pb, voice->current_pm,
|
||||
std::pair<s16, s16> 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);
|
||||
voice->SetPitch(pitch);
|
||||
voice->paused = false;
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,6 @@
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include "locator.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "game/sound/common/synth.h"
|
||||
@ -19,28 +17,27 @@ enum class toneflag : u16 {
|
||||
};
|
||||
|
||||
struct Tone {
|
||||
/* 0 */ s8 Priority;
|
||||
/* 1 */ s8 Vol;
|
||||
/* 2 */ s8 CenterNote;
|
||||
/* 3 */ s8 CenterFine;
|
||||
/* 4 */ s16 Pan;
|
||||
/* 6 */ s8 MapLow;
|
||||
/* 7 */ s8 MapHigh;
|
||||
/* 8 */ s8 PBLow;
|
||||
/* 9 */ s8 PBHigh;
|
||||
/* a */ s16 ADSR1;
|
||||
/* c */ s16 ADSR2;
|
||||
/* e */ s16 Flags;
|
||||
/* 10 */ /*void**/ u32 VAGInSR;
|
||||
/* 14 */ u32 reserved1;
|
||||
s8 Priority;
|
||||
s8 Vol;
|
||||
s8 CenterNote;
|
||||
s8 CenterFine;
|
||||
s16 Pan;
|
||||
s8 MapLow;
|
||||
s8 MapHigh;
|
||||
s8 PBLow;
|
||||
s8 PBHigh;
|
||||
u16 ADSR1;
|
||||
u16 ADSR2;
|
||||
u16 Flags;
|
||||
u8* Sample;
|
||||
};
|
||||
|
||||
class vag_voice : public voice {
|
||||
class VagVoice : public Voice {
|
||||
public:
|
||||
vag_voice(Tone& t) : tone(t) {}
|
||||
VagVoice(Tone& t) : tone(t) {}
|
||||
Tone& tone;
|
||||
u8 group{0};
|
||||
vol_pair basevol{};
|
||||
VolPair basevol{};
|
||||
s32 current_pm{0};
|
||||
s32 current_pb{0};
|
||||
s32 start_note{0};
|
||||
@ -48,18 +45,18 @@ class vag_voice : public voice {
|
||||
bool paused{false};
|
||||
};
|
||||
|
||||
class voice_manager {
|
||||
class VoiceManager {
|
||||
public:
|
||||
voice_manager(synth& synth, locator& loc);
|
||||
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; };
|
||||
VoiceManager(Synth& synth);
|
||||
void StartTone(std::shared_ptr<VagVoice> voice);
|
||||
void Pause(std::shared_ptr<VagVoice> voice);
|
||||
void Unpause(std::shared_ptr<VagVoice> voice);
|
||||
void SetPanTable(VolPair* table) { mPanTable = table; };
|
||||
|
||||
vol_pair make_volume(int vol1, int pan1, int vol2, int pan2, int vol3, int pan3);
|
||||
VolPair MakeVolume(int vol1, int pan1, int vol2, int pan2, int vol3, int pan3);
|
||||
|
||||
// This is super silly, but it's what 989snd does
|
||||
vol_pair make_volume_b(int sound_vol,
|
||||
VolPair MakeVolumeB(int sound_vol,
|
||||
int velocity_volume,
|
||||
int pan,
|
||||
int prog_vol,
|
||||
@ -67,25 +64,24 @@ class voice_manager {
|
||||
int tone_vol,
|
||||
int tone_pan);
|
||||
|
||||
void set_master_vol(u8 group, s32 volume);
|
||||
void set_playback_mode(s32 mode) { m_stereo_or_mono = mode; }
|
||||
s16 adjust_vol_to_group(s16 involume, int group);
|
||||
void SetMasterVol(u8 group, s32 volume);
|
||||
void SetPlaybackMode(s32 mode) { mStereoOrMono = mode; }
|
||||
s16 AdjustVolToGroup(s16 involume, int group);
|
||||
|
||||
private:
|
||||
synth& m_synth;
|
||||
locator& m_locator;
|
||||
Synth& mSynth;
|
||||
|
||||
std::list<std::weak_ptr<vag_voice>> m_voices;
|
||||
void clean_voices() {
|
||||
m_voices.remove_if([](auto& v) { return v.expired(); });
|
||||
std::list<std::weak_ptr<VagVoice>> mVoices;
|
||||
void CleanVoices() {
|
||||
mVoices.remove_if([](auto& v) { return v.expired(); });
|
||||
}
|
||||
|
||||
s32 m_stereo_or_mono{0};
|
||||
s32 mStereoOrMono{0};
|
||||
|
||||
std::array<s32, 32> m_master_vol;
|
||||
std::array<s32, 32> m_group_duck;
|
||||
std::array<s32, 32> mMasterVol;
|
||||
std::array<s32, 32> mGroupDuck;
|
||||
|
||||
const vol_pair* m_pan_table{nullptr};
|
||||
const VolPair* mPanTable{nullptr};
|
||||
};
|
||||
|
||||
} // namespace snd
|
||||
|
@ -7,7 +7,6 @@ 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
|
||||
|
@ -5,15 +5,15 @@
|
||||
|
||||
namespace snd {
|
||||
|
||||
struct vol_pair {
|
||||
struct VolPair {
|
||||
s16 left;
|
||||
s16 right;
|
||||
};
|
||||
|
||||
struct s16_output {
|
||||
struct s16Output {
|
||||
s16 left{0}, right{0};
|
||||
|
||||
s16_output& operator+=(const s16_output& rhs) {
|
||||
s16Output& operator+=(const s16Output& rhs) {
|
||||
left = static_cast<s16>(std::clamp<s32>(left + rhs.left, INT16_MIN, INT16_MAX));
|
||||
right = static_cast<s16>(std::clamp<s32>(right + rhs.right, INT16_MIN, INT16_MAX));
|
||||
return *this;
|
||||
|
@ -10,28 +10,28 @@ static s16 ApplyVolume(s16 sample, s32 volume) {
|
||||
return (sample * volume) >> 15;
|
||||
}
|
||||
|
||||
s16_output synth::tick() {
|
||||
s16_output out{};
|
||||
s16Output Synth::Tick() {
|
||||
s16Output out{};
|
||||
|
||||
m_voices.remove_if([](std::shared_ptr<voice>& v) { return v->dead(); });
|
||||
for (auto& v : m_voices) {
|
||||
out += v->run();
|
||||
mVoices.remove_if([](std::shared_ptr<Voice>& v) { return v->Dead(); });
|
||||
for (auto& v : mVoices) {
|
||||
out += v->Run();
|
||||
}
|
||||
|
||||
out.left = ApplyVolume(out.left, m_Volume.left.Get());
|
||||
out.right = ApplyVolume(out.right, m_Volume.right.Get());
|
||||
out.left = ApplyVolume(out.left, mVolume.left.Get());
|
||||
out.right = ApplyVolume(out.right, mVolume.right.Get());
|
||||
|
||||
m_Volume.Run();
|
||||
mVolume.Run();
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void synth::add_voice(std::shared_ptr<voice> voice) {
|
||||
m_voices.emplace_front(voice);
|
||||
void Synth::AddVoice(std::shared_ptr<Voice> voice) {
|
||||
mVoices.emplace_front(voice);
|
||||
}
|
||||
|
||||
void synth::set_master_vol(u32 volume) {
|
||||
m_Volume.left.Set(volume);
|
||||
m_Volume.right.Set(volume);
|
||||
void Synth::SetMasterVol(u32 volume) {
|
||||
mVolume.left.Set(volume);
|
||||
mVolume.right.Set(volume);
|
||||
}
|
||||
} // namespace snd
|
||||
|
@ -18,20 +18,20 @@ struct SpuVolume {
|
||||
/* 2 */ s16 right;
|
||||
};
|
||||
|
||||
class synth {
|
||||
class Synth {
|
||||
public:
|
||||
synth() {
|
||||
m_Volume.left.Set(0x3FFF);
|
||||
m_Volume.right.Set(0x3FFF);
|
||||
Synth() {
|
||||
mVolume.left.Set(0x3FFF);
|
||||
mVolume.right.Set(0x3FFF);
|
||||
}
|
||||
|
||||
s16_output tick();
|
||||
void add_voice(std::shared_ptr<voice> voice);
|
||||
void set_master_vol(u32 volume);
|
||||
s16Output Tick();
|
||||
void AddVoice(std::shared_ptr<Voice> voice);
|
||||
void SetMasterVol(u32 volume);
|
||||
|
||||
private:
|
||||
std::forward_list<std::shared_ptr<voice>> m_voices;
|
||||
std::forward_list<std::shared_ptr<Voice>> mVoices;
|
||||
|
||||
VolumePair m_Volume{};
|
||||
VolumePair mVolume{};
|
||||
};
|
||||
} // namespace snd
|
||||
|
@ -18,122 +18,122 @@ static constexpr std::array<std::array<s16, 2>, 5> adpcm_coefs = {{
|
||||
{122, -60},
|
||||
}};
|
||||
|
||||
void voice::DecodeSamples() {
|
||||
void Voice::DecodeSamples() {
|
||||
// This doesn't exactly match the real behaviour,
|
||||
// it seems to initially decode a bigger chunk
|
||||
// and then decode more data after a bit has drained
|
||||
if (m_DecodeBuf.Size() >= 16) {
|
||||
if (mDecodeBuf.Size() >= 16) {
|
||||
// sufficient data buffered
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip decoding for stopped voices.
|
||||
if (m_ADSR.GetPhase() == ADSR::Phase::Stopped) {
|
||||
if (mADSR.GetPhase() == ADSR::Phase::Stopped) {
|
||||
for (int i = 0; i < 4; i++)
|
||||
m_DecodeBuf.Push(0);
|
||||
mDecodeBuf.Push(0);
|
||||
} else {
|
||||
u32 data = m_sample[m_NAX];
|
||||
u32 data = mSample[mNAX];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
s32 sample = (s16)((data & 0xF) << 12);
|
||||
sample >>= m_CurHeader.Shift.get();
|
||||
sample >>= mCurHeader.Shift.get();
|
||||
|
||||
// TODO do the right thing for invalid shift/filter values
|
||||
sample += (adpcm_coefs[m_CurHeader.Filter.get()][0] * m_DecodeHist1) >> 6;
|
||||
sample += (adpcm_coefs[m_CurHeader.Filter.get()][1] * m_DecodeHist2) >> 6;
|
||||
sample += (adpcm_coefs[mCurHeader.Filter.get()][0] * mDecodeHist1) >> 6;
|
||||
sample += (adpcm_coefs[mCurHeader.Filter.get()][1] * mDecodeHist2) >> 6;
|
||||
|
||||
// We do get overflow here otherwise, should we?
|
||||
sample = std::clamp<s32>(sample, INT16_MIN, INT16_MAX);
|
||||
|
||||
m_DecodeHist2 = m_DecodeHist1;
|
||||
m_DecodeHist1 = static_cast<s16>(sample);
|
||||
mDecodeHist2 = mDecodeHist1;
|
||||
mDecodeHist1 = static_cast<s16>(sample);
|
||||
|
||||
m_DecodeBuf.Push(static_cast<s16>(sample));
|
||||
mDecodeBuf.Push(static_cast<s16>(sample));
|
||||
data >>= 4;
|
||||
}
|
||||
}
|
||||
|
||||
m_NAX++;
|
||||
mNAX++;
|
||||
|
||||
if ((m_NAX & 0x7) == 0) {
|
||||
if (m_CurHeader.LoopEnd.get()) {
|
||||
m_NAX = m_LSA;
|
||||
m_ENDX = true;
|
||||
if ((mNAX & 0x7) == 0) {
|
||||
if (mCurHeader.LoopEnd.get()) {
|
||||
mNAX = mLSA;
|
||||
mENDX = true;
|
||||
|
||||
if (!m_CurHeader.LoopRepeat.get()) {
|
||||
if (!mCurHeader.LoopRepeat.get()) {
|
||||
// Need to inhibit stopping here in noise is on
|
||||
// seems to result in the right thing but would like to verify
|
||||
if (!m_Noise)
|
||||
m_ADSR.Stop();
|
||||
if (!mNoise)
|
||||
mADSR.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
UpdateBlockHeader();
|
||||
|
||||
m_NAX++;
|
||||
mNAX++;
|
||||
}
|
||||
}
|
||||
|
||||
void voice::UpdateBlockHeader() {
|
||||
m_CurHeader.bits = m_sample[m_NAX & ~0x7];
|
||||
if (m_CurHeader.LoopStart.get() && !m_CustomLoop)
|
||||
m_LSA = m_NAX & ~0x7;
|
||||
void Voice::UpdateBlockHeader() {
|
||||
mCurHeader.bits = mSample[mNAX & ~0x7];
|
||||
if (mCurHeader.LoopStart.get() && !mCustomLoop)
|
||||
mLSA = mNAX & ~0x7;
|
||||
}
|
||||
|
||||
static s16 ApplyVolume(s16 sample, s32 volume) {
|
||||
return (sample * volume) >> 15;
|
||||
}
|
||||
|
||||
void voice::key_on() {
|
||||
m_NAX = m_SSA;
|
||||
m_NAX++;
|
||||
void Voice::KeyOn() {
|
||||
mNAX = mSSA;
|
||||
mNAX++;
|
||||
|
||||
UpdateBlockHeader();
|
||||
|
||||
m_ENDX = false;
|
||||
m_ADSR.Attack();
|
||||
m_Counter = 0;
|
||||
m_DecodeHist1 = 0;
|
||||
m_DecodeHist2 = 0;
|
||||
m_DecodeBuf.Reset();
|
||||
m_CustomLoop = false;
|
||||
mENDX = false;
|
||||
mADSR.Attack();
|
||||
mCounter = 0;
|
||||
mDecodeHist1 = 0;
|
||||
mDecodeHist2 = 0;
|
||||
mDecodeBuf.Reset();
|
||||
mCustomLoop = false;
|
||||
// Console.WriteLn("SPU[%d]:VOICE[%d] Key On, SSA %08x", m_SPU.m_Id, m_Id, m_SSA);
|
||||
}
|
||||
|
||||
void voice::key_off() {
|
||||
m_ADSR.Release();
|
||||
void Voice::KeyOff() {
|
||||
mADSR.Release();
|
||||
// fmt::print("Key Off\n");
|
||||
}
|
||||
|
||||
s16_output voice::run() {
|
||||
s16Output Voice::Run() {
|
||||
DecodeSamples();
|
||||
|
||||
u32 index = (m_Counter & 0x0FF0) >> 4;
|
||||
u32 index = (mCounter & 0x0FF0) >> 4;
|
||||
|
||||
s16 sample = 0;
|
||||
sample = static_cast<s16>(sample + ((m_DecodeBuf.Peek(0) * interp_table[index][0]) >> 15));
|
||||
sample = static_cast<s16>(sample + ((m_DecodeBuf.Peek(1) * interp_table[index][1]) >> 15));
|
||||
sample = static_cast<s16>(sample + ((m_DecodeBuf.Peek(2) * interp_table[index][2]) >> 15));
|
||||
sample = static_cast<s16>(sample + ((m_DecodeBuf.Peek(3) * interp_table[index][3]) >> 15));
|
||||
sample = static_cast<s16>(sample + ((mDecodeBuf.Peek(0) * interp_table[index][0]) >> 15));
|
||||
sample = static_cast<s16>(sample + ((mDecodeBuf.Peek(1) * interp_table[index][1]) >> 15));
|
||||
sample = static_cast<s16>(sample + ((mDecodeBuf.Peek(2) * interp_table[index][2]) >> 15));
|
||||
sample = static_cast<s16>(sample + ((mDecodeBuf.Peek(3) * interp_table[index][3]) >> 15));
|
||||
|
||||
s32 step = m_Pitch;
|
||||
s32 step = mPitch;
|
||||
step = std::min(step, 0x3FFF);
|
||||
m_Counter += step;
|
||||
mCounter += step;
|
||||
|
||||
auto steps = m_Counter >> 12;
|
||||
m_Counter &= 0xFFF;
|
||||
auto steps = mCounter >> 12;
|
||||
mCounter &= 0xFFF;
|
||||
|
||||
while (steps > 0) {
|
||||
steps--;
|
||||
m_DecodeBuf.Pop();
|
||||
mDecodeBuf.Pop();
|
||||
}
|
||||
|
||||
sample = ApplyVolume(sample, m_ADSR.Level());
|
||||
s16 left = ApplyVolume(sample, m_Volume.left.GetCurrent());
|
||||
s16 right = ApplyVolume(sample, m_Volume.right.GetCurrent());
|
||||
sample = ApplyVolume(sample, mADSR.Level());
|
||||
s16 left = ApplyVolume(sample, mVolume.left.GetCurrent());
|
||||
s16 right = ApplyVolume(sample, mVolume.right.GetCurrent());
|
||||
|
||||
m_ADSR.Run();
|
||||
m_Volume.Run();
|
||||
mADSR.Run();
|
||||
mVolume.Run();
|
||||
|
||||
return s16_output{left, right};
|
||||
return s16Output{left, right};
|
||||
}
|
||||
} // namespace snd
|
||||
|
@ -12,68 +12,68 @@
|
||||
|
||||
namespace snd {
|
||||
|
||||
class voice {
|
||||
class Voice {
|
||||
public:
|
||||
enum class AllocationType {
|
||||
managed,
|
||||
permanent,
|
||||
Managed,
|
||||
Permanent,
|
||||
};
|
||||
|
||||
voice(AllocationType alloc = AllocationType::managed) : m_Alloc(alloc) {}
|
||||
s16_output run();
|
||||
Voice(AllocationType alloc = AllocationType::Managed) : mAlloc(alloc) {}
|
||||
s16Output Run();
|
||||
|
||||
void key_on();
|
||||
void KeyOn();
|
||||
|
||||
void key_off();
|
||||
void KeyOff();
|
||||
|
||||
bool dead() {
|
||||
if (m_Alloc == AllocationType::permanent) {
|
||||
bool Dead() {
|
||||
if (mAlloc == AllocationType::Permanent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_ADSR.GetPhase() == ADSR::Phase::Stopped;
|
||||
return mADSR.GetPhase() == ADSR::Phase::Stopped;
|
||||
}
|
||||
|
||||
void set_pitch(u16 reg) {
|
||||
void SetPitch(u16 reg) {
|
||||
// fmt::print("VOICE[{}] PITCH WRITE {:x}\n", m_channel, reg);
|
||||
m_Pitch = reg;
|
||||
mPitch = reg;
|
||||
}
|
||||
void set_asdr1(u16 reg) {
|
||||
void SetAsdr1(u16 reg) {
|
||||
// fmt::print("VOICE[{}] ADSR1 WRITE {:x}\n", m_channel, reg);
|
||||
m_ADSR.m_Reg.lo.set(reg);
|
||||
mADSR.m_Reg.lo.set(reg);
|
||||
}
|
||||
void set_asdr2(u16 reg) {
|
||||
void SetAsdr2(u16 reg) {
|
||||
// fmt::print("VOICE[{}] ADSR2 WRITE {:x}\n", m_channel, reg);
|
||||
m_ADSR.m_Reg.hi.set(reg);
|
||||
mADSR.m_Reg.hi.set(reg);
|
||||
}
|
||||
void set_volume(u16 left, u16 right) {
|
||||
void SetVolume(u16 left, u16 right) {
|
||||
// fmt::print("VOICE[{}] VOLL WRITE {:x}\n", m_channel, left);
|
||||
// fmt::print("VOICE[{}] VOLR WRITE {:x}\n", m_channel, right);
|
||||
m_Volume.left.Set(left);
|
||||
m_Volume.right.Set(right);
|
||||
mVolume.left.Set(left);
|
||||
mVolume.right.Set(right);
|
||||
}
|
||||
|
||||
void set_volume_l(u16 vol) { m_Volume.left.Set(vol); }
|
||||
void SetVolumeL(u16 vol) { mVolume.left.Set(vol); }
|
||||
|
||||
void set_volume_r(u16 vol) { m_Volume.right.Set(vol); }
|
||||
void SetVolumeR(u16 vol) { mVolume.right.Set(vol); }
|
||||
|
||||
s16 get_envx() { return m_ADSR.Level(); }
|
||||
s16 GetEnvx() { return mADSR.Level(); }
|
||||
|
||||
void set_sample(u16* sample) {
|
||||
m_sample = sample;
|
||||
m_SSA = 0;
|
||||
void SetSample(u16* sample) {
|
||||
mSample = sample;
|
||||
mSSA = 0;
|
||||
}
|
||||
|
||||
u32 get_nax() { return m_NAX; }
|
||||
u32 GetNax() { return mNAX; }
|
||||
|
||||
void set_ssa(u32 addr) { m_SSA = addr; }
|
||||
void SetSsa(u32 addr) { mSSA = addr; }
|
||||
|
||||
void set_lsa(u32 addr) {
|
||||
m_LSA = addr;
|
||||
m_CustomLoop = true;
|
||||
void SetLsa(u32 addr) {
|
||||
mLSA = addr;
|
||||
mCustomLoop = true;
|
||||
}
|
||||
|
||||
void stop() { m_ADSR.Stop(); }
|
||||
void Stop() { mADSR.Stop(); }
|
||||
|
||||
private:
|
||||
union ADPCMHeader {
|
||||
@ -85,33 +85,29 @@ class voice {
|
||||
bitfield<u16, u8, 0, 4> Shift;
|
||||
};
|
||||
|
||||
AllocationType m_Alloc;
|
||||
bool m_Noise{false};
|
||||
[[maybe_unused]] bool m_PitchMod{false};
|
||||
[[maybe_unused]] bool m_KeyOn{false};
|
||||
[[maybe_unused]] bool m_KeyOff{false};
|
||||
bool m_ENDX{false};
|
||||
AllocationType mAlloc;
|
||||
bool mNoise{false};
|
||||
bool mENDX{false};
|
||||
|
||||
void DecodeSamples();
|
||||
void UpdateBlockHeader();
|
||||
|
||||
fifo<s16, 0x20> m_DecodeBuf{};
|
||||
s16 m_DecodeHist1{0};
|
||||
s16 m_DecodeHist2{0};
|
||||
u32 m_Counter{0};
|
||||
fifo<s16, 0x20> mDecodeBuf{};
|
||||
s16 mDecodeHist1{0};
|
||||
s16 mDecodeHist2{0};
|
||||
u32 mCounter{0};
|
||||
|
||||
u16 m_Pitch{0};
|
||||
[[maybe_unused]] s16 m_Out{0};
|
||||
u16 mPitch{0};
|
||||
|
||||
u16* m_sample{nullptr};
|
||||
u32 m_SSA{0};
|
||||
u32 m_NAX{0};
|
||||
u32 m_LSA{0};
|
||||
bool m_CustomLoop{false};
|
||||
u16* mSample{nullptr};
|
||||
u32 mSSA{0};
|
||||
u32 mNAX{0};
|
||||
u32 mLSA{0};
|
||||
bool mCustomLoop{false};
|
||||
|
||||
ADPCMHeader m_CurHeader{};
|
||||
ADPCMHeader mCurHeader{};
|
||||
|
||||
ADSR m_ADSR{};
|
||||
VolumePair m_Volume{};
|
||||
ADSR mADSR{};
|
||||
VolumePair mVolume{};
|
||||
};
|
||||
} // namespace snd
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
std::shared_ptr<snd::voice> voices[4];
|
||||
std::shared_ptr<snd::Voice> voices[4];
|
||||
u8 spu_memory[0x15160 * 10];
|
||||
|
||||
static sceSdTransIntrHandler trans_handler[2] = {nullptr, nullptr};
|
||||
@ -20,7 +20,7 @@ u32 sceSdGetSwitch(u32 entry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
snd::voice* voice_from_entry(u32 entry) {
|
||||
snd::Voice* voice_from_entry(u32 entry) {
|
||||
u32 it = entry & 3;
|
||||
return voices[it].get();
|
||||
}
|
||||
@ -35,7 +35,7 @@ u32 sceSdGetAddr(u32 entry) {
|
||||
// u32 reg = entry & ~0x3f;
|
||||
|
||||
// Only ever used for getting NAX
|
||||
return voice->get_nax() << 1;
|
||||
return voice->GetNax() << 1;
|
||||
}
|
||||
|
||||
void sceSdSetSwitch(u32 entry, u32 /*value*/) {
|
||||
@ -43,21 +43,21 @@ void sceSdSetSwitch(u32 entry, u32 /*value*/) {
|
||||
u32 reg = entry & ~0x3f;
|
||||
switch (reg) {
|
||||
case 0x1500:
|
||||
voice_from_entry(entry)->key_on();
|
||||
voice_from_entry(entry + 1)->key_on();
|
||||
voice_from_entry(entry)->KeyOn();
|
||||
voice_from_entry(entry + 1)->KeyOn();
|
||||
break;
|
||||
case 0x1600:
|
||||
voice_from_entry(entry)->key_off();
|
||||
voice_from_entry(entry)->KeyOff();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void sceSdkey_on_jak2_voice(int id) {
|
||||
voice_from_entry(id)->key_on();
|
||||
voice_from_entry(id)->KeyOn();
|
||||
}
|
||||
|
||||
void sceSdkey_off_jak2_voice(int id) {
|
||||
voice_from_entry(id)->key_off();
|
||||
voice_from_entry(id)->KeyOff();
|
||||
}
|
||||
|
||||
void sceSdSetAddr(u32 entry, u32 value) {
|
||||
@ -71,10 +71,10 @@ void sceSdSetAddr(u32 entry, u32 value) {
|
||||
|
||||
switch (reg) {
|
||||
case SD_VA_SSA: {
|
||||
voice->set_ssa(value >> 1);
|
||||
voice->SetSsa(value >> 1);
|
||||
} break;
|
||||
case SD_VA_LSAX: {
|
||||
voice->set_lsa(value >> 1);
|
||||
voice->SetLsa(value >> 1);
|
||||
} break;
|
||||
default:
|
||||
printf("unknown 0x%x\n", reg);
|
||||
@ -94,19 +94,19 @@ void sceSdSetParam(u32 entry, u32 value) {
|
||||
|
||||
switch (reg) {
|
||||
case SD_VP_VOLL: {
|
||||
voice->set_volume_l(value);
|
||||
voice->SetVolumeL(value);
|
||||
} break;
|
||||
case SD_VP_VOLR: {
|
||||
voice->set_volume_r(value);
|
||||
voice->SetVolumeR(value);
|
||||
} break;
|
||||
case SD_VP_PITCH: {
|
||||
voice->set_pitch(value);
|
||||
voice->SetPitch(value);
|
||||
} break;
|
||||
case SD_VP_ADSR1: {
|
||||
voice->set_asdr1(value);
|
||||
voice->SetAsdr1(value);
|
||||
} break;
|
||||
case SD_VP_ADSR2: {
|
||||
voice->set_asdr2(value);
|
||||
voice->SetAsdr2(value);
|
||||
} break;
|
||||
default: {
|
||||
} break;
|
||||
|
@ -16,7 +16,7 @@
|
||||
#define SD_VP_ADSR2 (0x04 << 8)
|
||||
#define SD_VA_NAX ((0x22 << 8) + (0x01 << 6))
|
||||
|
||||
extern std::shared_ptr<snd::voice> voices[4];
|
||||
extern std::shared_ptr<snd::Voice> voices[4];
|
||||
extern u8 spu_memory[0x15160 * 10];
|
||||
|
||||
using sceSdTransIntrHandler = int (*)(int, void*);
|
||||
|
@ -4,17 +4,20 @@
|
||||
|
||||
#include "sdshim.h"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#include "989snd/player.h"
|
||||
|
||||
std::unique_ptr<snd::player> player;
|
||||
std::unique_ptr<snd::Player> player;
|
||||
|
||||
void snd_StartSoundSystem() {
|
||||
player = std::make_unique<snd::player>();
|
||||
player = std::make_unique<snd::Player>();
|
||||
|
||||
for (auto& voice : voices) {
|
||||
voice = std::make_shared<snd::voice>(snd::voice::AllocationType::permanent);
|
||||
voice->set_sample((u16*)spu_memory);
|
||||
player->submit_voice(voice);
|
||||
voice = std::make_shared<snd::Voice>(snd::Voice::AllocationType::Permanent);
|
||||
voice->SetSample((u16*)spu_memory);
|
||||
player->SubmitVoice(voice);
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,7 +36,7 @@ void snd_FreeSPUDMA([[maybe_unused]] s32 channel) {}
|
||||
|
||||
s32 snd_GetTick() {
|
||||
if (player) {
|
||||
return player->get_tick();
|
||||
return player->GetTick();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
@ -76,19 +79,19 @@ void snd_SetReverbType(s32 core, s32 type) {}
|
||||
|
||||
void snd_SetPanTable(s16* table) {
|
||||
if (player) {
|
||||
player->set_pan_table((snd::vol_pair*)table);
|
||||
player->SetPanTable((snd::VolPair*)table);
|
||||
}
|
||||
}
|
||||
|
||||
void snd_SetPlayBackMode(s32 mode) {
|
||||
if (player) {
|
||||
player->set_playback_mode(mode);
|
||||
player->SetPlaybackMode(mode);
|
||||
}
|
||||
}
|
||||
|
||||
s32 snd_SoundIsStillPlaying(s32 sound_handle) {
|
||||
if (player) {
|
||||
if (player->sound_still_active(sound_handle)) {
|
||||
if (player->SoundStillActive(sound_handle)) {
|
||||
return sound_handle;
|
||||
}
|
||||
}
|
||||
@ -98,25 +101,25 @@ s32 snd_SoundIsStillPlaying(s32 sound_handle) {
|
||||
|
||||
void snd_StopSound(s32 sound_handle) {
|
||||
if (player) {
|
||||
player->stop_sound(sound_handle);
|
||||
player->StopSound(sound_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void snd_SetSoundVolPan(s32 sound_handle, s32 vol, s32 pan) {
|
||||
if (player) {
|
||||
player->set_sound_vol_pan(sound_handle, vol, pan);
|
||||
player->SetSoundVolPan(sound_handle, vol, pan);
|
||||
}
|
||||
}
|
||||
|
||||
void snd_SetMasterVolume(s32 which, s32 volume) {
|
||||
if (player) {
|
||||
player->set_master_volume(which, volume);
|
||||
player->SetMasterVolume(which, volume);
|
||||
}
|
||||
}
|
||||
|
||||
void snd_UnloadBank(s32 bank_handle) {
|
||||
void snd_UnloadBank(snd::BankHandle bank_handle) {
|
||||
if (player) {
|
||||
player->unload_bank(bank_handle);
|
||||
player->UnloadBank(bank_handle);
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,31 +129,36 @@ void snd_ResolveBankXREFS() {
|
||||
|
||||
void snd_ContinueAllSoundsInGroup(u8 groups) {
|
||||
if (player) {
|
||||
player->continue_all_sounds_in_group(groups);
|
||||
player->ContinueAllSoundsInGroup(groups);
|
||||
}
|
||||
}
|
||||
|
||||
void snd_PauseAllSoundsInGroup(u8 groups) {
|
||||
if (player) {
|
||||
player->pause_all_sounds_in_group(groups);
|
||||
player->PauseAllSoundsInGroup(groups);
|
||||
}
|
||||
}
|
||||
|
||||
void snd_SetMIDIRegister(s32 sound_handle, u8 reg, u8 value) {
|
||||
if (player) {
|
||||
player->set_sound_reg(sound_handle, reg, value);
|
||||
player->SetSoundReg(sound_handle, reg, value);
|
||||
}
|
||||
}
|
||||
|
||||
s32 snd_PlaySoundVolPanPMPB(s32 bank, s32 sound, s32 vol, s32 pan, s32 pitch_mod, s32 pitch_bend) {
|
||||
s32 snd_PlaySoundVolPanPMPB(snd::BankHandle bank,
|
||||
s32 sound,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
s32 pitch_mod,
|
||||
s32 pitch_bend) {
|
||||
if (player) {
|
||||
return player->play_sound(bank, sound, vol, pan, pitch_mod, pitch_bend);
|
||||
return player->PlaySound(bank, sound, vol, pan, pitch_mod, pitch_bend);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
s32 snd_PlaySoundByNameVolPanPMPB(s32 bank_handle,
|
||||
s32 snd_PlaySoundByNameVolPanPMPB(snd::BankHandle bank_handle,
|
||||
char* bank_name,
|
||||
char* sound_name,
|
||||
s32 vol,
|
||||
@ -158,7 +166,7 @@ s32 snd_PlaySoundByNameVolPanPMPB(s32 bank_handle,
|
||||
s32 pitch_mod,
|
||||
s32 pitch_bend) {
|
||||
if (player) {
|
||||
return player->play_sound_by_name(bank_handle, bank_name, sound_name, vol, pan, pitch_mod,
|
||||
return player->PlaySoundByName(bank_handle, bank_name, sound_name, vol, pan, pitch_mod,
|
||||
pitch_bend);
|
||||
} else {
|
||||
return 0;
|
||||
@ -167,7 +175,7 @@ s32 snd_PlaySoundByNameVolPanPMPB(s32 bank_handle,
|
||||
|
||||
void snd_SetSoundPitchModifier(s32 sound_handle, s32 pitch_mod) {
|
||||
if (player) {
|
||||
player->set_sound_pmod(sound_handle, pitch_mod);
|
||||
player->SetSoundPmod(sound_handle, pitch_mod);
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,13 +187,13 @@ void snd_SetSoundPitchBend(s32 sound_handle, s32 bend) {
|
||||
|
||||
void snd_PauseSound(s32 sound_handle) {
|
||||
if (player) {
|
||||
player->pause_sound(sound_handle);
|
||||
player->PauseSound(sound_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void snd_ContinueSound(s32 sound_handle) {
|
||||
if (player) {
|
||||
player->continue_sound(sound_handle);
|
||||
player->ContinueSound(sound_handle);
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,11 +206,15 @@ void snd_AutoPitchBend(s32 sound_handle, s32 pitch, s32 delta_time, s32 delta_fr
|
||||
lg::warn("Unimplemented snd_AutoPitchBend\n");
|
||||
}
|
||||
|
||||
s32 snd_BankLoadEx(const char* filename, s32 offset, u32 spu_mem_loc, u32 spu_mem_size) {
|
||||
snd::BankHandle 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;
|
||||
return player->load_bank(path, offset);
|
||||
// TODO put the load on the thread pool?
|
||||
auto file_buf = file_util::read_binary_file(std::string(filename));
|
||||
return player->LoadBank(nonstd::span(file_buf).subspan(offset));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
@ -219,23 +231,23 @@ s32 snd_GetVoiceStatus(s32 voice) {
|
||||
|
||||
void snd_keyOnVoiceRaw(u32 core, u32 voice_id) {
|
||||
if (voices[0]) {
|
||||
voices[0]->key_on();
|
||||
voices[0]->KeyOn();
|
||||
}
|
||||
}
|
||||
|
||||
void snd_keyOffVoiceRaw(u32 core, u32 voice_id) {
|
||||
if (voices[0]) {
|
||||
voices[0]->key_off();
|
||||
voices[0]->KeyOff();
|
||||
}
|
||||
}
|
||||
|
||||
s32 snd_GetSoundUserData(s32 block_handle,
|
||||
s32 snd_GetSoundUserData(snd::BankHandle 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,
|
||||
return player->GetSoundUserData(block_handle, block_name, sound_id, sound_name,
|
||||
(snd::SFXUserData*)dst);
|
||||
}
|
||||
return 0;
|
||||
@ -243,12 +255,12 @@ s32 snd_GetSoundUserData(s32 block_handle,
|
||||
|
||||
void snd_SetSoundReg(s32 sound_handle, s32 which, u8 val) {
|
||||
if (player) {
|
||||
player->set_sound_reg(sound_handle, which, val);
|
||||
player->SetSoundReg(sound_handle, which, val);
|
||||
}
|
||||
}
|
||||
|
||||
void snd_SetGlobalExcite(u8 value) {
|
||||
if (player) {
|
||||
player->set_global_excite(value);
|
||||
player->SetGlobalExcite(value);
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,11 @@ struct SFXUserData {
|
||||
typedef void* (*AllocFun)();
|
||||
typedef void (*FreeFun)(void*);
|
||||
|
||||
namespace snd {
|
||||
class SoundBank;
|
||||
using BankHandle = SoundBank*;
|
||||
}; // namespace snd
|
||||
|
||||
void snd_StartSoundSystem();
|
||||
void snd_StopSoundSystem();
|
||||
s32 snd_GetTick();
|
||||
@ -32,21 +37,21 @@ 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_UnloadBank(snd::BankHandle bank_handle);
|
||||
void snd_ResolveBankXREFS();
|
||||
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 snd_PlaySoundVolPanPMPB(snd::BankHandle bank_handle,
|
||||
s32 sound_id,
|
||||
s32 vol,
|
||||
s32 pan,
|
||||
s32 pitch_mod,
|
||||
s32 pitch_bend);
|
||||
|
||||
s32 snd_PlaySoundByNameVolPanPMPB(s32 bank_handle,
|
||||
s32 snd_PlaySoundByNameVolPanPMPB(snd::BankHandle bank_handle,
|
||||
char* bank_name,
|
||||
char* sound_name,
|
||||
s32 vol,
|
||||
@ -60,13 +65,16 @@ 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);
|
||||
snd::BankHandle 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 core, u32 voice);
|
||||
void snd_keyOffVoiceRaw(u32 core, u32 voice);
|
||||
s32 snd_GetSoundUserData(s32 block_handle,
|
||||
s32 snd_GetSoundUserData(snd::BankHandle block_handle,
|
||||
char* block_name,
|
||||
s32 sound_id,
|
||||
char* sound_name,
|
||||
|
1884
third-party/span.hpp
generated
vendored
Normal file
1884
third-party/span.hpp
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user