989snd: sound bank loading overhaul (#3185)

This commit is contained in:
Ziemas 2023-12-08 02:22:54 +01:00 committed by GitHub
parent 9b6f16f561
commit 168afa0e2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 3793 additions and 2251 deletions

View File

@ -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;
};

View File

@ -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),

View File

@ -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);

View File

@ -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;
bank->in_use = false;
bank->unk4 = 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;
}
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,16 +138,9 @@ 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)) {
return bank;
}
} else {
if ((memcmp(bank->name, name, 16) == 0) && bank->in_use) {
return bank;
}
if ((memcmp(bank->name.data(), name, 16) == 0) && bank->in_use) {
return bank;
}
idx--;
}

View File

@ -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 {
u32 spu_loc;
u32 spu_size;
u32 unk4;
bool in_use;
};
};
// jak2 additions
u32 spu_loc;
u32 spu_size;
u32 unk4;
bool in_use;
};
void sbank_init_globals();

View File

@ -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);

View File

@ -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;

View File

@ -117,10 +117,10 @@ 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;
extern const char* gLanguage;
extern u32 gFrameNum;
void srpc_init_globals();
void srpc_init_globals();

View File

@ -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);

View File

@ -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;

View File

@ -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;
@ -203,4 +203,4 @@ s32 LoadISOFileChunkToEE(FileRecord* file, uint32_t dest_addr, uint32_t length,
return cmd.length_to_copy;
}
} // namespace jak1
} // namespace jak1

View File

@ -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,

View 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;
};
/*!

View File

@ -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);
@ -508,4 +508,4 @@ s32 VBlank_Handler(void*) {
return 1;
}
} // namespace jak1
} // namespace jak1

View File

@ -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 {

View File

@ -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 {
@ -251,4 +251,4 @@ void UnLoadMusic(s32* param_1) {
*param_1 = 0;
}
} // namespace jak2
} // namespace jak2

View File

@ -2,6 +2,8 @@
#include "common/common_types.h"
#include "game/sound/sndshim.h"
struct SoundBank;
struct FileRecord;
namespace jak2 {
@ -9,10 +11,10 @@ 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);
int LoadISOFileChunkToEE(FileRecord* param_1, uint32_t param_2, int param_3, int param_4);
} // namespace jak2
} // namespace jak2

View File

@ -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;

View File

@ -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);
@ -611,4 +611,4 @@ void SetVagName(int param_1, char* param_2, int param_3) {
// CpuResumeIntr(local_18[0]);
}
} // namespace jak2
} // namespace jak2

View File

@ -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;
}

View File

@ -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{};

View File

@ -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

View File

@ -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,
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;
}
BlockSoundHandler(SoundBank& bank,
SFXBlock::SFX& sfx,
VoiceManager& vm,
s32 sfx_vol,
s32 sfx_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};
};

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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());
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) {
return bank;
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* 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

View File

@ -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

View File

@ -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

View File

@ -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();
}
}

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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];

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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();
}
}

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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;
}

View File

@ -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,44 +45,43 @@ 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,
int velocity_volume,
int pan,
int prog_vol,
int prog_pan,
int tone_vol,
int tone_pan);
VolPair MakeVolumeB(int sound_vol,
int velocity_volume,
int pan,
int prog_vol,
int prog_pan,
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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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*);

View File

@ -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);
}
}

View File

@ -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

File diff suppressed because it is too large Load Diff