From 168afa0e2e16b64c23839f63a51a4ac7fbbb8ddb Mon Sep 17 00:00:00 2001 From: Ziemas Date: Fri, 8 Dec 2023 02:22:54 +0100 Subject: [PATCH] 989snd: sound bank loading overhaul (#3185) --- common/util/BinaryReader.h | 17 +- common/util/dgo_util.cpp | 2 +- common/util/dgo_util.h | 2 +- game/overlord/common/sbank.cpp | 50 +- game/overlord/common/sbank.h | 32 +- game/overlord/common/soundcommon.cpp | 4 +- game/overlord/common/srpc.cpp | 3 +- game/overlord/common/srpc.h | 4 +- game/overlord/common/ssound.cpp | 4 +- game/overlord/jak1/fake_iso.cpp | 34 +- game/overlord/jak1/iso_api.cpp | 4 +- game/overlord/jak1/iso_api.h | 2 +- game/overlord/jak1/isocommon.h | 4 +- game/overlord/jak1/srpc.cpp | 6 +- game/overlord/jak2/iso.h | 5 +- game/overlord/jak2/iso_api.cpp | 6 +- game/overlord/jak2/iso_api.h | 8 +- game/overlord/jak2/iso_cd.cpp | 8 +- game/overlord/jak2/srpc.cpp | 4 +- game/sound/989snd/ame_handler.cpp | 75 +- game/sound/989snd/ame_handler.h | 53 +- game/sound/989snd/blocksound_handler.cpp | 166 +- game/sound/989snd/blocksound_handler.h | 144 +- game/sound/989snd/handle_allocator.h | 18 +- game/sound/989snd/lfo.cpp | 66 +- game/sound/989snd/lfo.h | 22 +- game/sound/989snd/loader.cpp | 553 +++++-- game/sound/989snd/loader.h | 59 +- game/sound/989snd/locator.h | 19 - game/sound/989snd/midi_handler.cpp | 150 +- game/sound/989snd/midi_handler.h | 107 +- game/sound/989snd/musicbank.cpp | 56 +- game/sound/989snd/musicbank.h | 119 +- game/sound/989snd/player.cpp | 239 +-- game/sound/989snd/player.h | 79 +- game/sound/989snd/sfxblock.cpp | 40 +- game/sound/989snd/sfxblock.h | 89 +- game/sound/989snd/sfxblock2.cpp | 102 -- game/sound/989snd/sfxblock2.h | 115 -- game/sound/989snd/sfxgrain.cpp | 513 +++--- game/sound/989snd/sfxgrain.h | 525 ++---- game/sound/989snd/sndplay.cpp | 22 +- game/sound/989snd/sound_handler.h | 24 +- game/sound/989snd/soundbank.h | 36 +- game/sound/989snd/util.cpp | 4 +- game/sound/989snd/util.h | 4 +- game/sound/989snd/vagvoice.cpp | 93 +- game/sound/989snd/vagvoice.h | 86 +- game/sound/CMakeLists.txt | 1 - game/sound/common/sound_types.h | 6 +- game/sound/common/synth.cpp | 26 +- game/sound/common/synth.h | 18 +- game/sound/common/voice.cpp | 106 +- game/sound/common/voice.h | 98 +- game/sound/sdshim.cpp | 30 +- game/sound/sdshim.h | 2 +- game/sound/sndshim.cpp | 78 +- game/sound/sndshim.h | 18 +- third-party/span.hpp | 1884 ++++++++++++++++++++++ 59 files changed, 3793 insertions(+), 2251 deletions(-) delete mode 100644 game/sound/989snd/locator.h delete mode 100644 game/sound/989snd/sfxblock2.cpp delete mode 100644 game/sound/989snd/sfxblock2.h create mode 100644 third-party/span.hpp diff --git a/common/util/BinaryReader.h b/common/util/BinaryReader.h index 5eee37d8f..4b1b0baea 100644 --- a/common/util/BinaryReader.h +++ b/common/util/BinaryReader.h @@ -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& _buffer) : m_buffer(_buffer) {} + explicit BinaryReader(nonstd::span _span) : m_span(_span) {} template 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 m_buffer; + nonstd::span m_span; uint32_t m_seek = 0; }; diff --git a/common/util/dgo_util.cpp b/common/util/dgo_util.cpp index 4b69a5160..74ad8947e 100644 --- a/common/util/dgo_util.cpp +++ b/common/util/dgo_util.cpp @@ -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), diff --git a/common/util/dgo_util.h b/common/util/dgo_util.h index 8f1b06d73..b4ef45f3c 100644 --- a/common/util/dgo_util.h +++ b/common/util/dgo_util.h @@ -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); diff --git a/game/overlord/common/sbank.cpp b/game/overlord/common/sbank.cpp index b64354c11..6c791b3ed 100644 --- a/game/overlord/common/sbank.cpp +++ b/game/overlord/common/sbank.cpp @@ -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, ""); + bank->unk4 = 0; + strcpy(bank->name.data(), ""); } 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--; } diff --git a/game/overlord/common/sbank.h b/game/overlord/common/sbank.h index 9d40c3c57..028b8a0af 100644 --- a/game/overlord/common/sbank.h +++ b/game/overlord/common/sbank.h @@ -1,31 +1,31 @@ #pragma once +#include +#include +#include + #include "common/common_types.h" +#include "game/sound/sndshim.h" + struct SoundRecord { - char name[16]; + std::array name; u32 fallof_params; }; struct SoundBank { - char name[16]; - u32 bank_handle; + std::array name; + snd::BankHandle bank_handle; u32 sound_count; - union { - SoundRecord sound[1]; + // name list, only for jak1 + std::vector 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(); diff --git a/game/overlord/common/soundcommon.cpp b/game/overlord/common/soundcommon.cpp index b6c6a2faa..c083171b9 100644 --- a/game/overlord/common/soundcommon.cpp +++ b/game/overlord/common/soundcommon.cpp @@ -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); diff --git a/game/overlord/common/srpc.cpp b/game/overlord/common/srpc.cpp index b14e3d334..5345a78b8 100644 --- a/game/overlord/common/srpc.cpp +++ b/game/overlord/common/srpc.cpp @@ -1,13 +1,14 @@ #include "srpc.h" #include +#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; diff --git a/game/overlord/common/srpc.h b/game/overlord/common/srpc.h index 1a1543e68..8dc48b1fe 100644 --- a/game/overlord/common/srpc.h +++ b/game/overlord/common/srpc.h @@ -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(); \ No newline at end of file +void srpc_init_globals(); diff --git a/game/overlord/common/ssound.cpp b/game/overlord/common/ssound.cpp index 756bcbc97..33a2b8c0f 100644 --- a/game/overlord/common/ssound.cpp +++ b/game/overlord/common/ssound.cpp @@ -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); diff --git a/game/overlord/jak1/fake_iso.cpp b/game/overlord/jak1/fake_iso.cpp index 44ff8f69b..77f4865a8 100644 --- a/game/overlord/jak1/fake_iso.cpp +++ b/game/overlord/jak1/fake_iso.cpp @@ -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>(); + + data.read(); + bank.bank_handle = nullptr; + + bank.sound_count = data.read(); + bank.sound.resize(bank.sound_count); + + for (auto& snd : bank.sound) { + snd.name = data.read>(); + snd.fallof_params = data.read(); + } +} + 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; diff --git a/game/overlord/jak1/iso_api.cpp b/game/overlord/jak1/iso_api.cpp index c9e826547..e74b35cb1 100644 --- a/game/overlord/jak1/iso_api.cpp +++ b/game/overlord/jak1/iso_api.cpp @@ -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 \ No newline at end of file +} // namespace jak1 diff --git a/game/overlord/jak1/iso_api.h b/game/overlord/jak1/iso_api.h index a2b5ecbb4..cbef12f08 100644 --- a/game/overlord/jak1/iso_api.h +++ b/game/overlord/jak1/iso_api.h @@ -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, diff --git a/game/overlord/jak1/isocommon.h b/game/overlord/jak1/isocommon.h index a38701a1e..e71b34c9b 100644 --- a/game/overlord/jak1/isocommon.h +++ b/game/overlord/jak1/isocommon.h @@ -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; }; /*! diff --git a/game/overlord/jak1/srpc.cpp b/game/overlord/jak1/srpc.cpp index b54c5db9a..0c88bab31 100644 --- a/game/overlord/jak1/srpc.cpp +++ b/game/overlord/jak1/srpc.cpp @@ -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 \ No newline at end of file +} // namespace jak1 diff --git a/game/overlord/jak2/iso.h b/game/overlord/jak2/iso.h index 2cd5c46ae..5424393d6 100644 --- a/game/overlord/jak2/iso.h +++ b/game/overlord/jak2/iso.h @@ -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 { diff --git a/game/overlord/jak2/iso_api.cpp b/game/overlord/jak2/iso_api.cpp index c9b963a24..0ad59ad95 100644 --- a/game/overlord/jak2/iso_api.cpp +++ b/game/overlord/jak2/iso_api.cpp @@ -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 \ No newline at end of file +} // namespace jak2 diff --git a/game/overlord/jak2/iso_api.h b/game/overlord/jak2/iso_api.h index 362db5ff5..410e8a8d1 100644 --- a/game/overlord/jak2/iso_api.h +++ b/game/overlord/jak2/iso_api.h @@ -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 \ No newline at end of file +} // namespace jak2 diff --git a/game/overlord/jak2/iso_cd.cpp b/game/overlord/jak2/iso_cd.cpp index a0082cb54..4a90e09a5 100644 --- a/game/overlord/jak2/iso_cd.cpp +++ b/game/overlord/jak2/iso_cd.cpp @@ -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; diff --git a/game/overlord/jak2/srpc.cpp b/game/overlord/jak2/srpc.cpp index db4cf093a..8b9a05cec 100644 --- a/game/overlord/jak2/srpc.cpp +++ b/game/overlord/jak2/srpc.cpp @@ -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 \ No newline at end of file +} // namespace jak2 diff --git a/game/sound/989snd/ame_handler.cpp b/game/sound/989snd/ame_handler.cpp index 01f4d445e..c3cd6bdd7 100644 --- a/game/sound/989snd/ame_handler.cpp +++ b/game/sound/989snd/ame_handler.cpp @@ -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(midiblock, m_vm, m_sound, m_vol, m_pan, - m_locator, m_bank, this)); + m_midis.emplace(id, std::make_unique(static_cast(&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 ame_handler::run_ame(midi_handler& midi, u8* stream) { +std::pair AmeHandler::RunAME(MidiHandler& midi, u8* stream) { int skip = 0; bool done = false; bool cont = true; @@ -186,7 +179,7 @@ std::pair 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 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 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 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 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 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; } diff --git a/game/sound/989snd/ame_handler.h b/game/sound/989snd/ame_handler.h index 56948028a..0f95ffc43 100644 --- a/game/sound/989snd/ame_handler.h +++ b/game/sound/989snd/ame_handler.h @@ -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 run_ame(midi_handler&, u8* stream); + void StartSegment(u32 id); + void StopSegment(u32 id); + std::pair 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> m_midis; + std::unordered_map> m_midis; std::array m_groups{}; std::array m_register{}; diff --git a/game/sound/989snd/blocksound_handler.cpp b/game/sound/989snd/blocksound_handler.cpp index 23570eee3..e6fca61b7 100644 --- a/game/sound/989snd/blocksound_handler.cpp +++ b/game/sound/989snd/blocksound_handler.cpp @@ -10,15 +10,111 @@ namespace snd { std::array g_block_reg{}; -bool blocksound_handler::tick() { - m_voices.remove_if([](std::weak_ptr& 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& 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(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 diff --git a/game/sound/989snd/blocksound_handler.h b/game/sound/989snd/blocksound_handler.h index e6c675838..52920b365 100644 --- a/game/sound/989snd/blocksound_handler.h +++ b/game/sound/989snd/blocksound_handler.h @@ -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 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> m_voices; + std::list> m_voices; - std::list> m_children; + std::list> m_children; s32 m_orig_volume{0}; s32 m_orig_pan{0}; @@ -172,8 +84,6 @@ class blocksound_handler : public sound_handler { std::array m_registers{}; std::array m_lfo{{*this, *this, *this, *this}}; - // TODO LFO - s32 m_countdown{0}; u32 m_next_grain{0}; }; diff --git a/game/sound/989snd/handle_allocator.h b/game/sound/989snd/handle_allocator.h index 82707bf0a..5cb4d6168 100644 --- a/game/sound/989snd/handle_allocator.h +++ b/game/sound/989snd/handle_allocator.h @@ -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 m_free_ids; + u32 mNextId{1}; + std::queue mFreeIds; }; } // namespace snd diff --git a/game/sound/989snd/lfo.cpp b/game/sound/989snd/lfo.cpp index c4d12ff2f..0b6bdfb86 100644 --- a/game/sound/989snd/lfo.cpp +++ b/game/sound/989snd/lfo.cpp @@ -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); diff --git a/game/sound/989snd/lfo.h b/game/sound/989snd/lfo.h index dbfd62f36..bd06e1748 100644 --- a/game/sound/989snd/lfo.h +++ b/game/sound/989snd/lfo.h @@ -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 diff --git a/game/sound/989snd/loader.cpp b/game/sound/989snd/loader.cpp index 11b791964..fc60528e8 100644 --- a/game/sound/989snd/loader.cpp +++ b/game/sound/989snd/loader.cpp @@ -5,121 +5,460 @@ #include #include -#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(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 bank; - - if (bank_tag->DataID == FOURCC('S', 'B', 'v', '2')) { - bank = std::make_unique(*this, bank_id, bank_tag); - } else if (bank_tag->DataID == FOURCC('S', 'B', 'l', 'k')) { - if (bank_tag->Version < 2) { - bank = std::make_unique(*this, bank_id, bank_tag); - } else { - bank = std::make_unique(*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(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(); + num_chunks = data.read(); - in.read((char*)&attr, sizeof(attr)); - in.seekg(cur + attr.where[0].offset, std::fstream::beg); - - auto midi = std::make_unique(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(); + where[i].size = data.read(); + } } -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(); + tone.Vol = data.read(); + tone.CenterNote = data.read(); + tone.CenterFine = data.read(); + tone.Pan = data.read(); + tone.MapLow = data.read(); + tone.MapHigh = data.read(); + tone.PBLow = data.read(); + tone.PBHigh = data.read(); + tone.ADSR1 = data.read(); + tone.ADSR2 = data.read(); + tone.Flags = data.read(); + u32 SampleOffset = data.read(); + tone.Sample = &samples[SampleOffset]; + + data.read(); // reserved1 + + return tone; +}; + +static Midi ReadMidi(BinaryReader& data) { + Midi mid; + u8* base = const_cast(data.here()); + + mid.DataID = data.read(); + mid.Version = data.read(); + mid.Flags = data.read(); + data.read(); + mid.ID = data.read(); + data.read(); + mid.BankID = data.read(); + data.read(); + u32 DataStart = data.read(); + mid.DataStart = base + DataStart; + data.read(); + mid.Tempo = data.read(); + mid.PPQ = data.read(); + + return mid; +} + +static MultiMidi ReadMultiMidi(BinaryReader& data) { + MultiMidi mmid; + + mmid.DataID = data.read(); + mmid.Version = data.read(); + mmid.Flags = data.read(); + s8 NumSegments = data.read(); + mmid.midi.resize(NumSegments); + mmid.ID = data.read(); + data.read(); + + for (auto& midi : mmid.midi) { + u32 offset = data.read(); + auto mr = data.at(offset); + midi.Midi::operator=(ReadMidi(mr)); + midi.SoundHandle = mr.read(); + } + + return mmid; +} + +static Grain ReadGrainV1(BinaryReader& data, u8* samples) { + Grain grain{}; + + u32 pos = data.get_seek(); + + grain.Type = static_cast(data.read()); + grain.Delay = data.read(); + + switch (grain.Type) { + case GrainType::TONE: + case GrainType::TONE2: { + grain.data = ReadTone(data, samples); + } break; + case GrainType::LFO_SETTINGS: { + grain.data = data.read(); + } break; + case GrainType::BRANCH: + case GrainType::STARTCHILDSOUND: + case GrainType::STOPCHILDSOUND: + grain.data = data.read(); + break; + case GrainType::PLUGIN_MESSAGE: + grain.data = data.read(); + break; + case GrainType::RAND_DELAY: + grain.data = data.read(); + break; + default: + grain.data = data.read(); + 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(); + grain.Type = static_cast(op.type); + grain.Delay = data.read(); + + 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(); + } break; + case GrainType::BRANCH: + case GrainType::STARTCHILDSOUND: + case GrainType::STOPCHILDSOUND: + grainData.set_seek(value); + grain.data = grainData.read(); + break; + case GrainType::PLUGIN_MESSAGE: + grainData.set_seek(value); + grain.data = grainData.read(); + 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 bank_data, nonstd::span samples) { + BinaryReader data(bank_data); + // auto block = std::make_unique(); + auto block = new SFXBlock(); + block->DataID = data.read(); + if (block->DataID != fourcc("SBlk")) { return nullptr; } - return m_soundbanks[id].get(); -} + block->SampleData = std::make_unique(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(b.second.get()); - if (bank->bank_name == id) { - return bank; + block->Version = data.read(); + block->Flags.flags = data.read(); + block->BankID = data.read(); + block->BankNum = data.read(); + + // discard padding + data.read(); + data.read(); + data.read(); + + s16 NumSounds = data.read(); + block->Sounds.resize(NumSounds); + s16 NumGrains = data.read(); + s16 NumVAGs = data.read(); + + u32 FirstSound = data.read(); + u32 FirstGrain = data.read(); + + u32 VagsInSR = data.read(); + u32 VagDataSize = data.read(); + u32 SRAMAllocSize = data.read(); + u32 NextBlock = data.read(); + u32 GrainData = 0; + if (block->Version >= 2) { + GrainData = data.read(); + } + u32 BlockNames = data.read(); + u32 SFXUD = data.read(); + + data.set_seek(FirstSound); + for (auto& sfx : block->Sounds) { + sfx.Vol = data.read(); + sfx.VolGroup = data.read(); + sfx.Pan = data.read(); + + s8 NumGrains = data.read(); + sfx.Grains.resize(NumGrains); + + sfx.InstanceLimit = data.read(); + sfx.Flags.flags = data.read(); + + u32 FirstSFXGrain = data.read(); + + 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(); + + 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(); + sfx.UserData.data[1] = data.read(); + sfx.UserData.data[2] = data.read(); + sfx.UserData.data[3] = data.read(); + } + } + + return block; +} + +MusicBank* MusicBank::ReadBank(nonstd::span bank_data, + nonstd::span samples, + nonstd::span midi_data) { + BinaryReader data(bank_data); + // auto bank = std::make_unique(); + auto bank = new MusicBank(); + + bank->DataID = data.read(); + if (bank->DataID != fourcc("SBv2")) { + return nullptr; + } + + bank->SampleData = std::make_unique(samples.size_bytes()); + std::copy(samples.begin(), samples.end(), bank->SampleData.get()); + + bank->SeqData = std::make_unique(midi_data.size_bytes()); + std::copy(midi_data.begin(), midi_data.end(), bank->SeqData.get()); + + bank->Version = data.read(); + bank->Flags.flags = data.read(); + bank->BankID = data.read(); + bank->BankNum = data.read(); + + // discard padding + data.read(); + data.read(); + + s16 NumSounds = data.read(); + bank->Sounds.resize(NumSounds); + s16 NumProgs = data.read(); + bank->Progs.resize(NumProgs); + s16 NumTones = data.read(); + s16 NumVAGs = data.read(); + + u32 FirstSound = data.read(); + u32 FirstProg = data.read(); + u32 FirstTone = data.read(); + + // vagsinsr + data.read(); + // vagdatasize + data.read(); + + data.set_seek(FirstSound); + for (auto& sound : bank->Sounds) { + sound.Type = data.read(); + data.read(); // bankptr + data.read(); // ogbankptr + sound.MIDIID = data.read(); + sound.Vol = data.read(); + sound.Repeats = data.read(); + sound.VolGroup = data.read(); + sound.Pan = data.read(); + sound.Index = data.read(); + sound.Flags = data.read(); + data.read(); // midiblockptr + + sound.Bank = bank; + sound.OrigBank = bank; + } + + data.set_seek(FirstProg); + for (auto& prog : bank->Progs) { + s8 NumTones = data.read(); + prog.Vol = data.read(); + prog.Pan = data.read(); + u32 FirstTone = data.read(); + 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(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(); + 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 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(); + nonstd::span bank_data(nonstd::span(bank).subspan(fa.where[0].offset, fa.where[0].size)); + nonstd::span sample_data( + nonstd::span(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 midi_data( + nonstd::span(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 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 diff --git a/game/sound/989snd/loader.h b/game/sound/989snd/loader.h index af345605c..099357ec2 100644 --- a/game/sound/989snd/loader.h +++ b/game/sound/989snd/loader.h @@ -5,58 +5,41 @@ #include #include -#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 -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 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 bank); private: - void load_samples(u32 bank, std::unique_ptr samples); - - id_allocator m_id_allocator; - std::unordered_map> m_soundbanks; - - std::vector> m_midi_chunks; - - std::unordered_map m_midi; - - u32 m_next_id{0}; + std::vector> mBanks; }; } // namespace snd diff --git a/game/sound/989snd/locator.h b/game/sound/989snd/locator.h deleted file mode 100644 index 103d8f1ce..000000000 --- a/game/sound/989snd/locator.h +++ /dev/null @@ -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 diff --git a/game/sound/989snd/midi_handler.cpp b/game/sound/989snd/midi_handler.cpp index dd19fb1e4..aba131ee9 100644 --- a/game/sound/989snd/midi_handler.cpp +++ b/game/sound/989snd/midi_handler.cpp @@ -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 parent) + std::optional 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 midi_handler::read_vlq(u8* value) { +std::pair 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 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(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(m_locator.get_bank_by_id(m_header->BankID)); + auto bank = static_cast(&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(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(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(); } } diff --git a/game/sound/989snd/midi_handler.h b/game/sound/989snd/midi_handler.h index f2de5796e..78e13e8b4 100644 --- a/game/sound/989snd/midi_handler.h +++ b/game/sound/989snd/midi_handler.h @@ -9,7 +9,7 @@ #include #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 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 parent); + std::optional 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 m_parent; + std::optional m_parent; std::list> 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 m_mute_state{}; std::array m_chanvol{}; @@ -130,20 +115,20 @@ class midi_handler : public sound_handler { std::array 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 read_vlq(u8* value); + static std::pair ReadVLQ(u8* value); }; } // namespace snd diff --git a/game/sound/989snd/musicbank.cpp b/game/sound/989snd/musicbank.cpp index 130363bfc..de5f46127 100644 --- a/game/sound/989snd/musicbank.cpp +++ b/game/sound/989snd/musicbank.cpp @@ -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> MusicBank::make_handler(voice_manager& vm, +std::optional> 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(m_locator.get_midi(sound.MIDIID)); - return std::make_unique(midi, vm, sound, vol, pan, m_locator, *this); - } else if (sound.Type == 5) { // ame - auto midi = static_cast(m_locator.get_midi(sound.MIDIID)); - return std::make_unique(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(MidiData); + if (sound.MIDIID == midi.ID) { + return std::make_unique(&midi, vm, sound, vol, pan, *this); + } + return std::nullopt; + } else if (sound.Type == 5) { + auto& midi = std::get(MidiData); + if (sound.MIDIID == midi.ID) { + return std::make_unique(&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> MusicBank::make_handler(voice_mana } } -std::optional> MusicBank::make_handler(voice_manager& vm, +std::optional> MusicBank::MakeHandler(VoiceManager& vm, u32 sound_id, s32 vol, s32 pan, diff --git a/game/sound/989snd/musicbank.h b/game/sound/989snd/musicbank.h index e00ce8bfc..4d8250ff3 100644 --- a/game/sound/989snd/musicbank.h +++ b/game/sound/989snd/musicbank.h @@ -1,88 +1,83 @@ #pragma once +#include #include #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 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> 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 Tones; + }; + + std::vector Sounds; + std::vector Progs; + std::unique_ptr SampleData; + std::unique_ptr SeqData; + std::variant MidiData; + + static MusicBank* ReadBank(nonstd::span bank_data, + nonstd::span samples, + nonstd::span midi_data); + + std::optional> MakeHandler(VoiceManager& vm, u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb) override; - std::optional> make_handler(voice_manager& vm, + std::optional> MakeHandler(VoiceManager& vm, u32 sound_id, s32 vol, s32 pan, SndPlayParams& params) override; - - std::vector m_programs; - std::vector m_sounds; - - private: - locator& m_locator; }; } // namespace snd diff --git a/game/sound/989snd/player.cpp b/game/sound/989snd/player.cpp index 242a501fe..bc4f6e188 100644 --- a/game/sound/989snd/player.cpp +++ b/game/sound/989snd/player.cpp @@ -4,9 +4,12 @@ #include +#include "sfxblock.h" + #include "third-party/fmt/core.h" #ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN #include #include #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(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 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]; diff --git a/game/sound/989snd/player.h b/game/sound/989snd/player.h index 267257525..07393c7d2 100644 --- a/game/sound/989snd/player.h +++ b/game/sound/989snd/player.h @@ -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 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) { 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) { 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> m_handlers; + std::recursive_mutex mTickLock; // TODO does not need to recursive with some light restructuring + IdAllocator mHandleAllocator; + std::unordered_map> 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, diff --git a/game/sound/989snd/sfxblock.cpp b/game/sound/989snd/sfxblock.cpp index 82ea658e3..765a2f09b 100644 --- a/game/sound/989snd/sfxblock.cpp +++ b/game/sound/989snd/sfxblock.cpp @@ -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> SFXBlock::make_handler(voice_manager& vm, +std::optional> 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(*this, m_sounds[sound_id], vm, vol, pan, params); + auto handler = std::make_unique(*this, SFX, vm, vol, pan, params); return handler; } + +std::optional SFXBlock::GetSoundByName(const char* name) { + auto sound = Names.find(name); + if (sound != Names.end()) { + return sound->second; + } + + return std::nullopt; +} + } // namespace snd diff --git a/game/sound/989snd/sfxblock.h b/game/sound/989snd/sfxblock.h index 9c893a0aa..b1bc2dd6b 100644 --- a/game/sound/989snd/sfxblock.h +++ b/game/sound/989snd/sfxblock.h @@ -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> grains; +struct SFXUserData { // 0x10 + /* 0x0 */ u32 data[4]; }; class SFXBlock : public SoundBank { public: - SFXBlock(locator& loc, u32 handle, BankTag* tag); - std::optional> 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 Grains; + SFXUserData UserData; + }; + + std::vector Sounds; + std::string Name; + std::map Names; + std::unique_ptr SampleData; + + static SFXBlock* ReadBlock(nonstd::span bank_data, nonstd::span samples); + + std::optional> MakeHandler(VoiceManager& vm, u32 sound_id, s32 vol, s32 pan, SndPlayParams& params) override; - private: - [[maybe_unused]] locator& m_locator; - std::vector m_sounds; + std::optional GetName() override { return Name; }; + std::optional GetSoundByName(const char* name) override; + std::optional GetSoundUserData(u32 sound_id) override { + return &Sounds.at(sound_id).UserData; + }; }; } // namespace snd diff --git a/game/sound/989snd/sfxblock2.cpp b/game/sound/989snd/sfxblock2.cpp deleted file mode 100644 index 7619b7624..000000000 --- a/game/sound/989snd/sfxblock2.cpp +++ /dev/null @@ -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> 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(*this, m_sounds[sound_id], vm, vol, pan, params); - return handler; -} - -std::optional 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 diff --git a/game/sound/989snd/sfxblock2.h b/game/sound/989snd/sfxblock2.h deleted file mode 100644 index b7dc9a188..000000000 --- a/game/sound/989snd/sfxblock2.h +++ /dev/null @@ -1,115 +0,0 @@ -#pragma once -#include - -#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> grains; - SFXUserData user_data; - int index; -}; - -class SFXBlock2 : public SoundBank { - public: - SFXBlock2(locator& loc, u32 handle, BankTag* tag); - std::optional> make_handler(voice_manager& vm, - u32 sound_id, - s32 vol, - s32 pan, - SndPlayParams& params) override; - - std::optional get_name() override { return m_name; }; - std::optional get_sound_by_name(const char* name) override; - - std::optional 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 m_names; - std::vector m_sounds; -}; - -} // namespace snd diff --git a/game/sound/989snd/sfxgrain.cpp b/game/sound/989snd/sfxgrain.cpp index 2de100c02..835e677ff 100644 --- a/game/sound/989snd/sfxgrain.cpp +++ b/game/sound/989snd/sfxgrain.cpp @@ -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(data); + handler.m_cur_volume = ((handler.m_app_volume * handler.m_orig_volume) >> 10) + handler.m_lfo_volume; handler.m_cur_volume = std::clamp(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(m_tone); + auto voice = std::make_shared(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(m_lfop.target); - if (lfo.m_target != lfo_target::NONE) { - lfo.m_type = static_cast(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(data); + auto& lfo = handler.m_lfo.at(lfop.which_lfo); + + lfo.m_target = static_cast(lfop.target); + if (lfo.m_target != LFOTarget::NONE) { + lfo.m_type = static_cast(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(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(handler.bank()); - s32 index = m_psp.sound_id; + auto& block = static_cast(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(data); + auto& block = static_cast(handler.m_bank); + + if (psp.sound_id >= 0) { for (auto it = handler.m_children.begin(); it != handler.m_children.end();) { - auto* sound = static_cast(it->get()); - if (sound->m_sfx.index == m_psp.sound_id) { + auto* sound = static_cast(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(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(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(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(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(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(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(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(new_val, INT8_MIN, INT8_MAX); +s32 Grain::snd_SFX_GRAIN_TYPE_SET_REGISTER_RAND(BlockSoundHandler& handler) { + auto cp = std::get(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(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(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(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(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(new_val, INT8_MIN, INT8_MAX); + + } else { + s32 new_val = handler.m_registers.at(reg) + 1; + handler.m_registers.at(reg) = std::clamp(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(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(new_val, INT8_MIN, INT8_MAX); + + } else { + s32 new_val = handler.m_registers.at(reg) - 1; + handler.m_registers.at(reg) = std::clamp(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(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(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(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(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(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(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(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(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(new_val, INT8_MIN, INT8_MAX); +s32 Grain::snd_SFX_GRAIN_TYPE_ADD_REGISTER(BlockSoundHandler& handler) { + auto cp = std::get(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(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(new_val, INT8_MIN, INT8_MAX); + s32 new_val = handler.m_registers.at(reg) + val; + handler.m_registers.at(reg) = std::clamp(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(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; diff --git a/game/sound/989snd/sfxgrain.h b/game/sound/989snd/sfxgrain.h index 545b27d71..b7e844e12 100644 --- a/game/sound/989snd/sfxgrain.h +++ b/game/sound/989snd/sfxgrain.h @@ -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& args() { return m_args; } +struct Grain { + GrainType Type; + s32 Delay; + std::variant data; + s32 operator()(BlockSoundHandler& handler) { return (this->*func[(u32)Type])(handler); } private: - std::array 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 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 -std::unique_ptr new_grain(grain_type id, Args&&... args) { - switch (id) { - case grain_type::NULL_GRAIN: - return std::make_unique(std::forward(args)...); - case grain_type::TONE: - case grain_type::TONE2: - return std::make_unique(std::forward(args)...); - case grain_type::XREF_ID: - return std::make_unique(std::forward(args)...); - case grain_type::XREF_NUM: - return std::make_unique(std::forward(args)...); - case grain_type::LFO_SETTINGS: - return std::make_unique(std::forward(args)...); - case grain_type::STARTCHILDSOUND: - return std::make_unique(std::forward(args)...); - case grain_type::STOPCHILDSOUND: - return std::make_unique(std::forward(args)...); - case grain_type::PLUGIN_MESSAGE: - return std::make_unique(std::forward(args)...); - case grain_type::BRANCH: - return std::make_unique(std::forward(args)...); - case grain_type::CONTROL_NULL: - return std::make_unique(std::forward(args)...); - case grain_type::LOOP_START: - return std::make_unique(std::forward(args)...); - case grain_type::LOOP_END: - return std::make_unique(std::forward(args)...); - case grain_type::LOOP_CONTINUE: - return std::make_unique(std::forward(args)...); - case grain_type::STOP: - return std::make_unique(std::forward(args)...); - case grain_type::RAND_PLAY: - return std::make_unique(std::forward(args)...); - case grain_type::RAND_DELAY: - return std::make_unique(std::forward(args)...); - case grain_type::RAND_PB: - return std::make_unique(std::forward(args)...); - case grain_type::PB: - return std::make_unique(std::forward(args)...); - case grain_type::ADD_PB: - return std::make_unique(std::forward(args)...); - case grain_type::SET_REGISTER: - return std::make_unique(std::forward(args)...); - case grain_type::SET_REGISTER_RAND: - return std::make_unique(std::forward(args)...); - case grain_type::INC_REGISTER: - return std::make_unique(std::forward(args)...); - case grain_type::DEC_REGISTER: - return std::make_unique(std::forward(args)...); - case grain_type::TEST_REGISTER: - return std::make_unique(std::forward(args)...); - case grain_type::MARKER: - return std::make_unique(std::forward(args)...); - case grain_type::GOTO_MARKER: - return std::make_unique(std::forward(args)...); - case grain_type::GOTO_RANDOM_MARKER: - return std::make_unique(std::forward(args)...); - case grain_type::WAIT_FOR_ALL_VOICES: - return std::make_unique(std::forward(args)...); - case grain_type::PLAY_CYCLE: - return std::make_unique(std::forward(args)...); - case grain_type::ADD_REGISTER: - return std::make_unique(std::forward(args)...); - case grain_type::KEY_OFF_VOICES: - return std::make_unique(std::forward(args)...); - case grain_type::KILL_VOICES: - return std::make_unique(std::forward(args)...); - case grain_type::ON_STOP_MARKER: - return std::make_unique(std::forward(args)...); - case grain_type::COPY_REGISTER: - return std::make_unique(std::forward(args)...); - default: - throw std::runtime_error(fmt::format("Unknown grain type {}", fmt::underlying(id))); - } - return nullptr; -} - } // namespace snd diff --git a/game/sound/989snd/sndplay.cpp b/game/sound/989snd/sndplay.cpp index ebca0d1a2..6d3b38a3f 100644 --- a/game/sound/989snd/sndplay.cpp +++ b/game/sound/989snd/sndplay.cpp @@ -2,6 +2,8 @@ #include #include +#include "common/util/FileUtil.h" + #ifdef _WIN32 #include #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(); } } diff --git a/game/sound/989snd/sound_handler.h b/game/sound/989snd/sound_handler.h index adbc67796..9fd3b6f1d 100644 --- a/game/sound/989snd/sound_handler.h +++ b/game/sound/989snd/sound_handler.h @@ -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 diff --git a/game/sound/989snd/soundbank.h b/game/sound/989snd/soundbank.h index 4a921aae4..e2eca4cf0 100644 --- a/game/sound/989snd/soundbank.h +++ b/game/sound/989snd/soundbank.h @@ -2,13 +2,13 @@ #include #include -#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> make_handler(voice_manager& vm, + u32 DataID; + u32 Version; + BlockFlags Flags; + u32 BankID; + s8 BankNum; + + virtual std::optional> 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> make_handler(voice_manager& vm, + virtual std::optional> MakeHandler(VoiceManager& vm, u32 sound_id, s32 vol, s32 pan, SndPlayParams& params) = 0; - virtual std::optional get_name() { return std::nullopt; }; - virtual std::optional get_sound_by_name(const char* /*name*/) { return std::nullopt; }; - virtual std::optional get_sound_user_data(u32 /*sound_id*/) { + virtual std::optional GetName() { return std::nullopt; }; + virtual std::optional GetSoundByName(const char* /*name*/) { return std::nullopt; }; + virtual std::optional GetSoundUserData(u32 /*sound_id*/) { return std::nullopt; }; - - BankType type; - u32 bank_id; - u32 bank_name; - std::unique_ptr sampleBuf; }; } // namespace snd diff --git a/game/sound/989snd/util.cpp b/game/sound/989snd/util.cpp index f721a1847..53bf23fff 100644 --- a/game/sound/989snd/util.cpp +++ b/game/sound/989snd/util.cpp @@ -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 pitchbend(Tone& tone, +std::pair PitchBend(Tone& tone, int current_pb, int current_pm, int start_note, diff --git a/game/sound/989snd/util.h b/game/sound/989snd/util.h index 809871c3d..3dd5f097c 100644 --- a/game/sound/989snd/util.h +++ b/game/sound/989snd/util.h @@ -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 pitchbend(Tone& tone, +std::pair PitchBend(Tone& tone, int current_pb, int current_pm, int start_note, diff --git a/game/sound/989snd/vagvoice.cpp b/game/sound/989snd/vagvoice.cpp index fdba66080..d6ca10e2b 100644 --- a/game/sound/989snd/vagvoice.cpp +++ b/game/sound/989snd/vagvoice.cpp @@ -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 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 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 note = pitchbend(voice->tone, voice->current_pb, voice->current_pm, + std::pair 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 voice) { - voice->set_volume(0, 0); - voice->set_pitch(0); +void VoiceManager::Pause(std::shared_ptr voice) { + voice->SetVolume(0, 0); + voice->SetPitch(0); voice->paused = true; } -void voice_manager::unpause(std::shared_ptr 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 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 note = pitchbend(voice->tone, voice->current_pb, voice->current_pm, + std::pair 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; } diff --git a/game/sound/989snd/vagvoice.h b/game/sound/989snd/vagvoice.h index 932c00593..728ee90b1 100644 --- a/game/sound/989snd/vagvoice.h +++ b/game/sound/989snd/vagvoice.h @@ -4,8 +4,6 @@ #include #include -#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 voice, u32 bank); - void pause(std::shared_ptr voice); - void unpause(std::shared_ptr voice); - void set_pan_table(vol_pair* table) { m_pan_table = table; }; + VoiceManager(Synth& synth); + void StartTone(std::shared_ptr voice); + void Pause(std::shared_ptr voice); + void Unpause(std::shared_ptr 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> m_voices; - void clean_voices() { - m_voices.remove_if([](auto& v) { return v.expired(); }); + std::list> mVoices; + void CleanVoices() { + mVoices.remove_if([](auto& v) { return v.expired(); }); } - s32 m_stereo_or_mono{0}; + s32 mStereoOrMono{0}; - std::array m_master_vol; - std::array m_group_duck; + std::array mMasterVol; + std::array mGroupDuck; - const vol_pair* m_pan_table{nullptr}; + const VolPair* mPanTable{nullptr}; }; } // namespace snd diff --git a/game/sound/CMakeLists.txt b/game/sound/CMakeLists.txt index c1422302c..4804d844d 100644 --- a/game/sound/CMakeLists.txt +++ b/game/sound/CMakeLists.txt @@ -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 diff --git a/game/sound/common/sound_types.h b/game/sound/common/sound_types.h index 5803ca345..f9878b345 100644 --- a/game/sound/common/sound_types.h +++ b/game/sound/common/sound_types.h @@ -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(std::clamp(left + rhs.left, INT16_MIN, INT16_MAX)); right = static_cast(std::clamp(right + rhs.right, INT16_MIN, INT16_MAX)); return *this; diff --git a/game/sound/common/synth.cpp b/game/sound/common/synth.cpp index 50de71864..15ec5bede 100644 --- a/game/sound/common/synth.cpp +++ b/game/sound/common/synth.cpp @@ -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& v) { return v->dead(); }); - for (auto& v : m_voices) { - out += v->run(); + mVoices.remove_if([](std::shared_ptr& 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) { - m_voices.emplace_front(voice); +void Synth::AddVoice(std::shared_ptr 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 diff --git a/game/sound/common/synth.h b/game/sound/common/synth.h index e3457a19c..100ad8386 100644 --- a/game/sound/common/synth.h +++ b/game/sound/common/synth.h @@ -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); - void set_master_vol(u32 volume); + s16Output Tick(); + void AddVoice(std::shared_ptr voice); + void SetMasterVol(u32 volume); private: - std::forward_list> m_voices; + std::forward_list> mVoices; - VolumePair m_Volume{}; + VolumePair mVolume{}; }; } // namespace snd diff --git a/game/sound/common/voice.cpp b/game/sound/common/voice.cpp index 6d5707204..c3fbf9117 100644 --- a/game/sound/common/voice.cpp +++ b/game/sound/common/voice.cpp @@ -18,122 +18,122 @@ static constexpr std::array, 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(sample, INT16_MIN, INT16_MAX); - m_DecodeHist2 = m_DecodeHist1; - m_DecodeHist1 = static_cast(sample); + mDecodeHist2 = mDecodeHist1; + mDecodeHist1 = static_cast(sample); - m_DecodeBuf.Push(static_cast(sample)); + mDecodeBuf.Push(static_cast(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(sample + ((m_DecodeBuf.Peek(0) * interp_table[index][0]) >> 15)); - sample = static_cast(sample + ((m_DecodeBuf.Peek(1) * interp_table[index][1]) >> 15)); - sample = static_cast(sample + ((m_DecodeBuf.Peek(2) * interp_table[index][2]) >> 15)); - sample = static_cast(sample + ((m_DecodeBuf.Peek(3) * interp_table[index][3]) >> 15)); + sample = static_cast(sample + ((mDecodeBuf.Peek(0) * interp_table[index][0]) >> 15)); + sample = static_cast(sample + ((mDecodeBuf.Peek(1) * interp_table[index][1]) >> 15)); + sample = static_cast(sample + ((mDecodeBuf.Peek(2) * interp_table[index][2]) >> 15)); + sample = static_cast(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 diff --git a/game/sound/common/voice.h b/game/sound/common/voice.h index 29cf4c8d6..3a32ed3dc 100644 --- a/game/sound/common/voice.h +++ b/game/sound/common/voice.h @@ -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 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 m_DecodeBuf{}; - s16 m_DecodeHist1{0}; - s16 m_DecodeHist2{0}; - u32 m_Counter{0}; + fifo 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 diff --git a/game/sound/sdshim.cpp b/game/sound/sdshim.cpp index ac50296a6..180286426 100644 --- a/game/sound/sdshim.cpp +++ b/game/sound/sdshim.cpp @@ -9,7 +9,7 @@ #include "third-party/fmt/core.h" -std::shared_ptr voices[4]; +std::shared_ptr 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; diff --git a/game/sound/sdshim.h b/game/sound/sdshim.h index 21e6a761c..eb3f1bc9b 100644 --- a/game/sound/sdshim.h +++ b/game/sound/sdshim.h @@ -16,7 +16,7 @@ #define SD_VP_ADSR2 (0x04 << 8) #define SD_VA_NAX ((0x22 << 8) + (0x01 << 6)) -extern std::shared_ptr voices[4]; +extern std::shared_ptr voices[4]; extern u8 spu_memory[0x15160 * 10]; using sceSdTransIntrHandler = int (*)(int, void*); diff --git a/game/sound/sndshim.cpp b/game/sound/sndshim.cpp index e06026c9e..f9d3ef2af 100644 --- a/game/sound/sndshim.cpp +++ b/game/sound/sndshim.cpp @@ -4,17 +4,20 @@ #include "sdshim.h" +#include "common/log/log.h" +#include "common/util/FileUtil.h" + #include "989snd/player.h" -std::unique_ptr player; +std::unique_ptr player; void snd_StartSoundSystem() { - player = std::make_unique(); + player = std::make_unique(); for (auto& voice : voices) { - voice = std::make_shared(snd::voice::AllocationType::permanent); - voice->set_sample((u16*)spu_memory); - player->submit_voice(voice); + voice = std::make_shared(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); } } diff --git a/game/sound/sndshim.h b/game/sound/sndshim.h index 1b8063028..998029803 100644 --- a/game/sound/sndshim.h +++ b/game/sound/sndshim.h @@ -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, diff --git a/third-party/span.hpp b/third-party/span.hpp new file mode 100644 index 000000000..5e611cedc --- /dev/null +++ b/third-party/span.hpp @@ -0,0 +1,1884 @@ +// +// span for C++98 and later. +// Based on http://wg21.link/p0122r7 +// For more information see https://github.com/martinmoene/span-lite +// +// Copyright 2018-2021 Martin Moene +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef NONSTD_SPAN_HPP_INCLUDED +#define NONSTD_SPAN_HPP_INCLUDED + +#define span_lite_MAJOR 0 +#define span_lite_MINOR 10 +#define span_lite_PATCH 3 + +#define span_lite_VERSION span_STRINGIFY(span_lite_MAJOR) "." span_STRINGIFY(span_lite_MINOR) "." span_STRINGIFY(span_lite_PATCH) + +#define span_STRINGIFY( x ) span_STRINGIFY_( x ) +#define span_STRINGIFY_( x ) #x + +// span configuration: + +#define span_SPAN_DEFAULT 0 +#define span_SPAN_NONSTD 1 +#define span_SPAN_STD 2 + +// tweak header support: + +#ifdef __has_include +# if __has_include() +# include +# endif +#define span_HAVE_TWEAK_HEADER 1 +#else +#define span_HAVE_TWEAK_HEADER 0 +//# pragma message("span.hpp: Note: Tweak header not supported.") +#endif + +// span selection and configuration: + +#define span_HAVE( feature ) ( span_HAVE_##feature ) + +#ifndef span_CONFIG_SELECT_SPAN +# define span_CONFIG_SELECT_SPAN ( span_HAVE_STD_SPAN ? span_SPAN_STD : span_SPAN_NONSTD ) +#endif + +#ifndef span_CONFIG_EXTENT_TYPE +# define span_CONFIG_EXTENT_TYPE std::size_t +#endif + +#ifndef span_CONFIG_SIZE_TYPE +# define span_CONFIG_SIZE_TYPE std::size_t +#endif + +#ifdef span_CONFIG_INDEX_TYPE +# error `span_CONFIG_INDEX_TYPE` is deprecated since v0.7.0; it is replaced by `span_CONFIG_SIZE_TYPE`. +#endif + +// span configuration (features): + +#ifndef span_FEATURE_WITH_CONTAINER +#ifdef span_FEATURE_WITH_CONTAINER_TO_STD +# define span_FEATURE_WITH_CONTAINER span_IN_STD( span_FEATURE_WITH_CONTAINER_TO_STD ) +#else +# define span_FEATURE_WITH_CONTAINER 0 +# define span_FEATURE_WITH_CONTAINER_TO_STD 0 +#endif +#endif + +#ifndef span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE +# define span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE 0 +#endif + +#ifndef span_FEATURE_MEMBER_AT +# define span_FEATURE_MEMBER_AT 0 +#endif + +#ifndef span_FEATURE_MEMBER_BACK_FRONT +# define span_FEATURE_MEMBER_BACK_FRONT 1 +#endif + +#ifndef span_FEATURE_MEMBER_CALL_OPERATOR +# define span_FEATURE_MEMBER_CALL_OPERATOR 0 +#endif + +#ifndef span_FEATURE_MEMBER_SWAP +# define span_FEATURE_MEMBER_SWAP 0 +#endif + +#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB 0 +#elif span_FEATURE_NON_MEMBER_FIRST_LAST_SUB +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN 1 +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER 1 +#endif + +#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN 0 +#endif + +#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER 0 +#endif + +#ifndef span_FEATURE_COMPARISON +# define span_FEATURE_COMPARISON 0 // Note: C++20 does not provide comparison +#endif + +#ifndef span_FEATURE_SAME +# define span_FEATURE_SAME 0 +#endif + +#if span_FEATURE_SAME && !span_FEATURE_COMPARISON +# error `span_FEATURE_SAME` requires `span_FEATURE_COMPARISON` +#endif + +#ifndef span_FEATURE_MAKE_SPAN +#ifdef span_FEATURE_MAKE_SPAN_TO_STD +# define span_FEATURE_MAKE_SPAN span_IN_STD( span_FEATURE_MAKE_SPAN_TO_STD ) +#else +# define span_FEATURE_MAKE_SPAN 0 +# define span_FEATURE_MAKE_SPAN_TO_STD 0 +#endif +#endif + +#ifndef span_FEATURE_BYTE_SPAN +# define span_FEATURE_BYTE_SPAN 0 +#endif + +// Control presence of exception handling (try and auto discover): + +#ifndef span_CONFIG_NO_EXCEPTIONS +# if defined(_MSC_VER) +# include // for _HAS_EXCEPTIONS +# endif +# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS) +# define span_CONFIG_NO_EXCEPTIONS 0 +# else +# define span_CONFIG_NO_EXCEPTIONS 1 +# undef span_CONFIG_CONTRACT_VIOLATION_THROWS +# undef span_CONFIG_CONTRACT_VIOLATION_TERMINATES +# define span_CONFIG_CONTRACT_VIOLATION_THROWS 0 +# define span_CONFIG_CONTRACT_VIOLATION_TERMINATES 1 +# endif +#endif + +// Control pre- and postcondition violation behaviour: + +#if defined( span_CONFIG_CONTRACT_LEVEL_ON ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x11 +#elif defined( span_CONFIG_CONTRACT_LEVEL_OFF ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x00 +#elif defined( span_CONFIG_CONTRACT_LEVEL_EXPECTS_ONLY ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x01 +#elif defined( span_CONFIG_CONTRACT_LEVEL_ENSURES_ONLY ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x10 +#else +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x11 +#endif + +#if defined( span_CONFIG_CONTRACT_VIOLATION_THROWS ) +# define span_CONFIG_CONTRACT_VIOLATION_THROWS_V span_CONFIG_CONTRACT_VIOLATION_THROWS +#else +# define span_CONFIG_CONTRACT_VIOLATION_THROWS_V 0 +#endif + +#if defined( span_CONFIG_CONTRACT_VIOLATION_THROWS ) && span_CONFIG_CONTRACT_VIOLATION_THROWS && \ + defined( span_CONFIG_CONTRACT_VIOLATION_TERMINATES ) && span_CONFIG_CONTRACT_VIOLATION_TERMINATES +# error Please define none or one of span_CONFIG_CONTRACT_VIOLATION_THROWS and span_CONFIG_CONTRACT_VIOLATION_TERMINATES to 1, but not both. +#endif + +// C++ language version detection (C++20 is speculative): +// Note: VC14.0/1900 (VS2015) lacks too much from C++14. + +#ifndef span_CPLUSPLUS +# if defined(_MSVC_LANG ) && !defined(__clang__) +# define span_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) +# else +# define span_CPLUSPLUS __cplusplus +# endif +#endif + +#define span_CPP98_OR_GREATER ( span_CPLUSPLUS >= 199711L ) +#define span_CPP11_OR_GREATER ( span_CPLUSPLUS >= 201103L ) +#define span_CPP14_OR_GREATER ( span_CPLUSPLUS >= 201402L ) +#define span_CPP17_OR_GREATER ( span_CPLUSPLUS >= 201703L ) +#define span_CPP20_OR_GREATER ( span_CPLUSPLUS >= 202000L ) + +// C++ language version (represent 98 as 3): + +#define span_CPLUSPLUS_V ( span_CPLUSPLUS / 100 - (span_CPLUSPLUS > 200000 ? 2000 : 1994) ) + +#define span_IN_STD( v ) ( ((v) == 98 ? 3 : (v)) >= span_CPLUSPLUS_V ) + +#define span_CONFIG( feature ) ( span_CONFIG_##feature ) +#define span_FEATURE( feature ) ( span_FEATURE_##feature ) +#define span_FEATURE_TO_STD( feature ) ( span_IN_STD( span_FEATURE( feature##_TO_STD ) ) ) + +// Use C++20 std::span if available and requested: + +#if span_CPP20_OR_GREATER && defined(__has_include ) +# if __has_include( ) +# define span_HAVE_STD_SPAN 1 +# else +# define span_HAVE_STD_SPAN 0 +# endif +#else +# define span_HAVE_STD_SPAN 0 +#endif + +#define span_USES_STD_SPAN ( (span_CONFIG_SELECT_SPAN == span_SPAN_STD) || ((span_CONFIG_SELECT_SPAN == span_SPAN_DEFAULT) && span_HAVE_STD_SPAN) ) + +// +// Use C++20 std::span: +// + +#if span_USES_STD_SPAN + +#include + +namespace nonstd { + +using std::span; + +// Note: C++20 does not provide comparison +// using std::operator==; +// using std::operator!=; +// using std::operator<; +// using std::operator<=; +// using std::operator>; +// using std::operator>=; +} // namespace nonstd + +#else // span_USES_STD_SPAN + +#include + +// Compiler versions: +// +// MSVC++ 6.0 _MSC_VER == 1200 span_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0) +// MSVC++ 7.0 _MSC_VER == 1300 span_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002) +// MSVC++ 7.1 _MSC_VER == 1310 span_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003) +// MSVC++ 8.0 _MSC_VER == 1400 span_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005) +// MSVC++ 9.0 _MSC_VER == 1500 span_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008) +// MSVC++ 10.0 _MSC_VER == 1600 span_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010) +// MSVC++ 11.0 _MSC_VER == 1700 span_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012) +// MSVC++ 12.0 _MSC_VER == 1800 span_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013) +// MSVC++ 14.0 _MSC_VER == 1900 span_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015) +// MSVC++ 14.1 _MSC_VER >= 1910 span_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017) +// MSVC++ 14.2 _MSC_VER >= 1920 span_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019) + +#if defined(_MSC_VER ) && !defined(__clang__) +# define span_COMPILER_MSVC_VER (_MSC_VER ) +# define span_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) +#else +# define span_COMPILER_MSVC_VER 0 +# define span_COMPILER_MSVC_VERSION 0 +#endif + +#define span_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) + +#if defined(__clang__) +# define span_COMPILER_CLANG_VERSION span_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) +#else +# define span_COMPILER_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define span_COMPILER_GNUC_VERSION span_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#else +# define span_COMPILER_GNUC_VERSION 0 +#endif + +// half-open range [lo..hi): +#define span_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) + +// Compiler warning suppression: + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wundef" +# pragma clang diagnostic ignored "-Wmismatched-tags" +# define span_RESTORE_WARNINGS() _Pragma( "clang diagnostic pop" ) + +#elif defined __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wundef" +# define span_RESTORE_WARNINGS() _Pragma( "GCC diagnostic pop" ) + +#elif span_COMPILER_MSVC_VER >= 1900 +# define span_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes)) +# define span_RESTORE_WARNINGS() __pragma(warning(pop )) + +// Suppress the following MSVC GSL warnings: +// - C26439, gsl::f.6 : special function 'function' can be declared 'noexcept' +// - C26440, gsl::f.6 : function 'function' can be declared 'noexcept' +// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; +// use brace initialization, gsl::narrow_cast or gsl::narrow +// - C26473: gsl::t.1 : don't cast between pointer types where the source type and the target type are the same +// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead +// - C26490: gsl::t.1 : don't use reinterpret_cast + +span_DISABLE_MSVC_WARNINGS( 26439 26440 26472 26473 26481 26490 ) + +#else +# define span_RESTORE_WARNINGS() /*empty*/ +#endif + +// Presence of language and library features: + +#ifdef _HAS_CPP0X +# define span_HAS_CPP0X _HAS_CPP0X +#else +# define span_HAS_CPP0X 0 +#endif + +#define span_CPP11_80 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1400) +#define span_CPP11_90 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1500) +#define span_CPP11_100 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1600) +#define span_CPP11_110 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1700) +#define span_CPP11_120 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1800) +#define span_CPP11_140 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1900) + +#define span_CPP14_000 (span_CPP14_OR_GREATER) +#define span_CPP14_120 (span_CPP14_OR_GREATER || span_COMPILER_MSVC_VER >= 1800) +#define span_CPP14_140 (span_CPP14_OR_GREATER || span_COMPILER_MSVC_VER >= 1900) + +#define span_CPP17_000 (span_CPP17_OR_GREATER) + +// Presence of C++11 language features: + +#define span_HAVE_ALIAS_TEMPLATE span_CPP11_140 +#define span_HAVE_AUTO span_CPP11_100 +#define span_HAVE_CONSTEXPR_11 span_CPP11_140 +#define span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG span_CPP11_120 +#define span_HAVE_EXPLICIT_CONVERSION span_CPP11_140 +#define span_HAVE_INITIALIZER_LIST span_CPP11_120 +#define span_HAVE_IS_DEFAULT span_CPP11_140 +#define span_HAVE_IS_DELETE span_CPP11_140 +#define span_HAVE_NOEXCEPT span_CPP11_140 +#define span_HAVE_NULLPTR span_CPP11_100 +#define span_HAVE_STATIC_ASSERT span_CPP11_100 + +// Presence of C++14 language features: + +#define span_HAVE_CONSTEXPR_14 span_CPP14_000 + +// Presence of C++17 language features: + +#define span_HAVE_DEPRECATED span_CPP17_000 +#define span_HAVE_NODISCARD span_CPP17_000 +#define span_HAVE_NORETURN span_CPP17_000 + +// MSVC: template parameter deduction guides since Visual Studio 2017 v15.7 + +#if defined(__cpp_deduction_guides) +# define span_HAVE_DEDUCTION_GUIDES 1 +#else +# define span_HAVE_DEDUCTION_GUIDES (span_CPP17_OR_GREATER && ! span_BETWEEN( span_COMPILER_MSVC_VER, 1, 1913 )) +#endif + +// Presence of C++ library features: + +#define span_HAVE_ADDRESSOF span_CPP17_000 +#define span_HAVE_ARRAY span_CPP11_110 +#define span_HAVE_BYTE span_CPP17_000 +#define span_HAVE_CONDITIONAL span_CPP11_120 +#define span_HAVE_CONTAINER_DATA_METHOD (span_CPP11_140 || ( span_COMPILER_MSVC_VER >= 1500 && span_HAS_CPP0X )) +#define span_HAVE_DATA span_CPP17_000 +#define span_HAVE_LONGLONG span_CPP11_80 +#define span_HAVE_REMOVE_CONST span_CPP11_110 +#define span_HAVE_SNPRINTF span_CPP11_140 +#define span_HAVE_STRUCT_BINDING span_CPP11_120 +#define span_HAVE_TYPE_TRAITS span_CPP11_90 + +// Presence of byte-lite: + +#ifdef NONSTD_BYTE_LITE_HPP +# define span_HAVE_NONSTD_BYTE 1 +#else +# define span_HAVE_NONSTD_BYTE 0 +#endif + +// C++ feature usage: + +#if span_HAVE_ADDRESSOF +# define span_ADDRESSOF(x) std::addressof(x) +#else +# define span_ADDRESSOF(x) (&x) +#endif + +#if span_HAVE_CONSTEXPR_11 +# define span_constexpr constexpr +#else +# define span_constexpr /*span_constexpr*/ +#endif + +#if span_HAVE_CONSTEXPR_14 +# define span_constexpr14 constexpr +#else +# define span_constexpr14 /*span_constexpr*/ +#endif + +#if span_HAVE_EXPLICIT_CONVERSION +# define span_explicit explicit +#else +# define span_explicit /*explicit*/ +#endif + +#if span_HAVE_IS_DELETE +# define span_is_delete = delete +#else +# define span_is_delete +#endif + +#if span_HAVE_IS_DELETE +# define span_is_delete_access public +#else +# define span_is_delete_access private +#endif + +#if span_HAVE_NOEXCEPT && ! span_CONFIG_CONTRACT_VIOLATION_THROWS_V +# define span_noexcept noexcept +#else +# define span_noexcept /*noexcept*/ +#endif + +#if span_HAVE_NULLPTR +# define span_nullptr nullptr +#else +# define span_nullptr NULL +#endif + +#if span_HAVE_DEPRECATED +# define span_deprecated(msg) [[deprecated(msg)]] +#else +# define span_deprecated(msg) /*[[deprecated]]*/ +#endif + +#if span_HAVE_NODISCARD +# define span_nodiscard [[nodiscard]] +#else +# define span_nodiscard /*[[nodiscard]]*/ +#endif + +#if span_HAVE_NORETURN +# define span_noreturn [[noreturn]] +#else +# define span_noreturn /*[[noreturn]]*/ +#endif + +// Other features: + +#define span_HAVE_CONSTRAINED_SPAN_CONTAINER_CTOR span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG +#define span_HAVE_ITERATOR_CTOR span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG + +// Additional includes: + +#if span_HAVE( ADDRESSOF ) +# include +#endif + +#if span_HAVE( ARRAY ) +# include +#endif + +#if span_HAVE( BYTE ) +# include +#endif + +#if span_HAVE( DATA ) +# include // for std::data(), std::size() +#endif + +#if span_HAVE( TYPE_TRAITS ) +# include +#endif + +#if ! span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) +# include +#endif + +#if span_FEATURE( MEMBER_AT ) > 1 +# include +#endif + +#if ! span_CONFIG( NO_EXCEPTIONS ) +# include +#endif + +// Contract violation + +#define span_ELIDE_CONTRACT_EXPECTS ( 0 == ( span_CONFIG_CONTRACT_LEVEL_MASK & 0x01 ) ) +#define span_ELIDE_CONTRACT_ENSURES ( 0 == ( span_CONFIG_CONTRACT_LEVEL_MASK & 0x10 ) ) + +#if span_ELIDE_CONTRACT_EXPECTS +# define span_constexpr_exp span_constexpr +# define span_EXPECTS( cond ) /* Expect elided */ +#else +# define span_constexpr_exp span_constexpr14 +# define span_EXPECTS( cond ) span_CONTRACT_CHECK( "Precondition", cond ) +#endif + +#if span_ELIDE_CONTRACT_ENSURES +# define span_constexpr_ens span_constexpr +# define span_ENSURES( cond ) /* Ensures elided */ +#else +# define span_constexpr_ens span_constexpr14 +# define span_ENSURES( cond ) span_CONTRACT_CHECK( "Postcondition", cond ) +#endif + +#define span_CONTRACT_CHECK( type, cond ) \ + cond ? static_cast< void >( 0 ) \ + : nonstd::span_lite::detail::report_contract_violation( span_LOCATION( __FILE__, __LINE__ ) ": " type " violation." ) + +#ifdef __GNUG__ +# define span_LOCATION( file, line ) file ":" span_STRINGIFY( line ) +#else +# define span_LOCATION( file, line ) file "(" span_STRINGIFY( line ) ")" +#endif + +// Method enabling + +#if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + +#define span_REQUIRES_0(VA) \ + template< bool B = (VA), typename std::enable_if::type = 0 > + +# if span_BETWEEN( span_COMPILER_MSVC_VERSION, 1, 140 ) +// VS 2013 and earlier seem to have trouble with SFINAE for default non-type arguments +# define span_REQUIRES_T(VA) \ + , typename = typename std::enable_if< ( VA ), nonstd::span_lite::detail::enabler >::type +# else +# define span_REQUIRES_T(VA) \ + , typename std::enable_if< (VA), int >::type = 0 +# endif + +#define span_REQUIRES_R(R, VA) \ + typename std::enable_if< (VA), R>::type + +#define span_REQUIRES_A(VA) \ + , typename std::enable_if< (VA), void*>::type = nullptr + +#else + +# define span_REQUIRES_0(VA) /*empty*/ +# define span_REQUIRES_T(VA) /*empty*/ +# define span_REQUIRES_R(R, VA) R +# define span_REQUIRES_A(VA) /*empty*/ + +#endif + +namespace nonstd { +namespace span_lite { + +// [views.constants], constants + +typedef span_CONFIG_EXTENT_TYPE extent_t; +typedef span_CONFIG_SIZE_TYPE size_t; + +span_constexpr const extent_t dynamic_extent = static_cast( -1 ); + +template< class T, extent_t Extent = dynamic_extent > +class span; + +// Tag to select span constructor taking a container (prevent ms-gsl warning C26426): + +struct with_container_t { span_constexpr with_container_t() span_noexcept {} }; +const span_constexpr with_container_t with_container; + +// C++11 emulation: + +namespace std11 { + +#if span_HAVE( REMOVE_CONST ) + +using std::remove_cv; +using std::remove_const; +using std::remove_volatile; + +#else + +template< class T > struct remove_const { typedef T type; }; +template< class T > struct remove_const< T const > { typedef T type; }; + +template< class T > struct remove_volatile { typedef T type; }; +template< class T > struct remove_volatile< T volatile > { typedef T type; }; + +template< class T > +struct remove_cv +{ + typedef typename std11::remove_volatile< typename std11::remove_const< T >::type >::type type; +}; + +#endif // span_HAVE( REMOVE_CONST ) + +#if span_HAVE( TYPE_TRAITS ) + +using std::is_same; +using std::is_signed; +using std::integral_constant; +using std::true_type; +using std::false_type; +using std::remove_reference; + +#else + +template< class T, T v > struct integral_constant { enum { value = v }; }; +typedef integral_constant< bool, true > true_type; +typedef integral_constant< bool, false > false_type; + +template< class T, class U > struct is_same : false_type{}; +template< class T > struct is_same : true_type{}; + +template< typename T > struct is_signed : false_type {}; +template<> struct is_signed : true_type {}; +template<> struct is_signed : true_type {}; +template<> struct is_signed : true_type {}; + +#endif + +} // namespace std11 + +// C++17 emulation: + +namespace std17 { + +template< bool v > struct bool_constant : std11::integral_constant{}; + +#if span_CPP11_120 + +template< class...> +using void_t = void; + +#endif + +#if span_HAVE( DATA ) + +using std::data; +using std::size; + +#elif span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + +template< typename T, std::size_t N > +inline span_constexpr auto size( const T(&)[N] ) span_noexcept -> size_t +{ + return N; +} + +template< typename C > +inline span_constexpr auto size( C const & cont ) -> decltype( cont.size() ) +{ + return cont.size(); +} + +template< typename T, std::size_t N > +inline span_constexpr auto data( T(&arr)[N] ) span_noexcept -> T* +{ + return &arr[0]; +} + +template< typename C > +inline span_constexpr auto data( C & cont ) -> decltype( cont.data() ) +{ + return cont.data(); +} + +template< typename C > +inline span_constexpr auto data( C const & cont ) -> decltype( cont.data() ) +{ + return cont.data(); +} + +template< typename E > +inline span_constexpr auto data( std::initializer_list il ) span_noexcept -> E const * +{ + return il.begin(); +} + +#endif // span_HAVE( DATA ) + +#if span_HAVE( BYTE ) +using std::byte; +#elif span_HAVE( NONSTD_BYTE ) +using nonstd::byte; +#endif + +} // namespace std17 + +// C++20 emulation: + +namespace std20 { + +#if span_HAVE( DEDUCTION_GUIDES ) +template< class T > +using iter_reference_t = decltype( *std::declval() ); +#endif + +} // namespace std20 + +// Implementation details: + +namespace detail { + +/*enum*/ struct enabler{}; + +template< typename T > +span_constexpr bool is_positive( T x ) +{ + return std11::is_signed::value ? x >= 0 : true; +} + +#if span_HAVE( TYPE_TRAITS ) + +template< class Q > +struct is_span_oracle : std::false_type{}; + +template< class T, span_CONFIG_EXTENT_TYPE Extent > +struct is_span_oracle< span > : std::true_type{}; + +template< class Q > +struct is_span : is_span_oracle< typename std::remove_cv::type >{}; + +template< class Q > +struct is_std_array_oracle : std::false_type{}; + +#if span_HAVE( ARRAY ) + +template< class T, std::size_t Extent > +struct is_std_array_oracle< std::array > : std::true_type{}; + +#endif + +template< class Q > +struct is_std_array : is_std_array_oracle< typename std::remove_cv::type >{}; + +template< class Q > +struct is_array : std::false_type {}; + +template< class T > +struct is_array : std::true_type {}; + +template< class T, std::size_t N > +struct is_array : std::true_type {}; + +#if span_CPP11_140 && ! span_BETWEEN( span_COMPILER_GNUC_VERSION, 1, 500 ) + +template< class, class = void > +struct has_size_and_data : std::false_type{}; + +template< class C > +struct has_size_and_data +< + C, std17::void_t< + decltype( std17::size(std::declval()) ), + decltype( std17::data(std::declval()) ) > +> : std::true_type{}; + +template< class, class, class = void > +struct is_compatible_element : std::false_type {}; + +template< class C, class E > +struct is_compatible_element +< + C, E, std17::void_t< + decltype( std17::data(std::declval()) ) > +> : std::is_convertible< typename std::remove_pointer() ) )>::type(*)[], E(*)[] >{}; + +template< class C > +struct is_container : std17::bool_constant +< + ! is_span< C >::value + && ! is_array< C >::value + && ! is_std_array< C >::value + && has_size_and_data< C >::value +>{}; + +template< class C, class E > +struct is_compatible_container : std17::bool_constant +< + is_container::value + && is_compatible_element::value +>{}; + +#else // span_CPP11_140 + +template< + class C, class E + span_REQUIRES_T(( + ! is_span< C >::value + && ! is_array< C >::value + && ! is_std_array< C >::value + && ( std::is_convertible< typename std::remove_pointer() ) )>::type(*)[], E(*)[] >::value) + // && has_size_and_data< C >::value + )) + , class = decltype( std17::size(std::declval()) ) + , class = decltype( std17::data(std::declval()) ) +> +struct is_compatible_container : std::true_type{}; + +#endif // span_CPP11_140 + +#endif // span_HAVE( TYPE_TRAITS ) + +#if ! span_CONFIG( NO_EXCEPTIONS ) +#if span_FEATURE( MEMBER_AT ) > 1 + +// format index and size: + +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wlong-long" +#elif defined __GNUC__ +# pragma GCC diagnostic ignored "-Wformat=ll" +# pragma GCC diagnostic ignored "-Wlong-long" +#endif + +span_noreturn inline void throw_out_of_range( size_t idx, size_t size ) +{ + const char fmt[] = "span::at(): index '%lli' is out of range [0..%lli)"; + char buffer[ 2 * 20 + sizeof fmt ]; + sprintf( buffer, fmt, static_cast(idx), static_cast(size) ); + + throw std::out_of_range( buffer ); +} + +#else // MEMBER_AT + +span_noreturn inline void throw_out_of_range( size_t /*idx*/, size_t /*size*/ ) +{ + throw std::out_of_range( "span::at(): index outside span" ); +} +#endif // MEMBER_AT +#endif // NO_EXCEPTIONS + +#if span_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + +struct contract_violation : std::logic_error +{ + explicit contract_violation( char const * const message ) + : std::logic_error( message ) + {} +}; + +inline void report_contract_violation( char const * msg ) +{ + throw contract_violation( msg ); +} + +#else // span_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + +span_noreturn inline void report_contract_violation( char const * /*msg*/ ) span_noexcept +{ + std::terminate(); +} + +#endif // span_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + +} // namespace detail + +// Prevent signed-unsigned mismatch: + +#define span_sizeof(T) static_cast( sizeof(T) ) + +template< class T > +inline span_constexpr size_t to_size( T size ) +{ + return static_cast( size ); +} + +// +// [views.span] - A view over a contiguous, single-dimension sequence of objects +// +template< class T, extent_t Extent /*= dynamic_extent*/ > +class span +{ +public: + // constants and types + + typedef T element_type; + typedef typename std11::remove_cv< T >::type value_type; + + typedef T & reference; + typedef T * pointer; + typedef T const * const_pointer; + typedef T const & const_reference; + + typedef size_t size_type; + typedef extent_t extent_type; + + typedef pointer iterator; + typedef const_pointer const_iterator; + + typedef std::ptrdiff_t difference_type; + + typedef std::reverse_iterator< iterator > reverse_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + +// static constexpr extent_type extent = Extent; + enum { extent = Extent }; + + // 26.7.3.2 Constructors, copy, and assignment [span.cons] + + span_REQUIRES_0( + ( Extent == 0 ) || + ( Extent == dynamic_extent ) + ) + span_constexpr span() span_noexcept + : data_( span_nullptr ) + , size_( 0 ) + { + // span_EXPECTS( data() == span_nullptr ); + // span_EXPECTS( size() == 0 ); + } + +#if span_HAVE( ITERATOR_CTOR ) + // Didn't yet succeed in combining the next two constructors: + + span_constexpr_exp span( std::nullptr_t, size_type count ) + : data_( span_nullptr ) + , size_( count ) + { + span_EXPECTS( data_ == span_nullptr && count == 0 ); + } + + template< typename It + span_REQUIRES_T(( + std::is_convertible()), element_type &>::value + )) + > + span_constexpr_exp span( It first, size_type count ) + : data_( to_address( first ) ) + , size_( count ) + { + span_EXPECTS( + ( data_ == span_nullptr && count == 0 ) || + ( data_ != span_nullptr && detail::is_positive( count ) ) + ); + } +#else + span_constexpr_exp span( pointer ptr, size_type count ) + : data_( ptr ) + , size_( count ) + { + span_EXPECTS( + ( ptr == span_nullptr && count == 0 ) || + ( ptr != span_nullptr && detail::is_positive( count ) ) + ); + } +#endif + +#if span_HAVE( ITERATOR_CTOR ) + template< typename It, typename End + span_REQUIRES_T(( + std::is_convertible()), element_type *>::value + && ! std::is_convertible::value + )) + > + span_constexpr_exp span( It first, End last ) + : data_( to_address( first ) ) + , size_( to_size( last - first ) ) + { + span_EXPECTS( + last - first >= 0 + ); + } +#else + span_constexpr_exp span( pointer first, pointer last ) + : data_( first ) + , size_( to_size( last - first ) ) + { + span_EXPECTS( + last - first >= 0 + ); + } +#endif + + template< std::size_t N + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == static_cast(N)) + && std::is_convertible< value_type(*)[], element_type(*)[] >::value + )) + > + span_constexpr span( element_type ( &arr )[ N ] ) span_noexcept + : data_( span_ADDRESSOF( arr[0] ) ) + , size_( N ) + {} + +#if span_HAVE( ARRAY ) + + template< std::size_t N + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == static_cast(N)) + && std::is_convertible< value_type(*)[], element_type(*)[] >::value + )) + > +# if span_FEATURE( CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE ) + span_constexpr span( std::array< element_type, N > & arr ) span_noexcept +# else + span_constexpr span( std::array< value_type, N > & arr ) span_noexcept +# endif + : data_( arr.data() ) + , size_( to_size( arr.size() ) ) + {} + + template< std::size_t N +# if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == static_cast(N)) + && std::is_convertible< value_type(*)[], element_type(*)[] >::value + )) +# endif + > + span_constexpr span( std::array< value_type, N> const & arr ) span_noexcept + : data_( arr.data() ) + , size_( to_size( arr.size() ) ) + {} + +#endif // span_HAVE( ARRAY ) + +#if span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + template< class Container + span_REQUIRES_T(( + detail::is_compatible_container< Container, element_type >::value + )) + > + span_constexpr span( Container & cont ) + : data_( std17::data( cont ) ) + , size_( to_size( std17::size( cont ) ) ) + {} + + template< class Container + span_REQUIRES_T(( + std::is_const< element_type >::value + && detail::is_compatible_container< Container, element_type >::value + )) + > + span_constexpr span( Container const & cont ) + : data_( std17::data( cont ) ) + , size_( to_size( std17::size( cont ) ) ) + {} + +#endif // span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + +#if span_FEATURE( WITH_CONTAINER ) + + template< class Container > + span_constexpr span( with_container_t, Container & cont ) + : data_( cont.size() == 0 ? span_nullptr : span_ADDRESSOF( cont[0] ) ) + , size_( to_size( cont.size() ) ) + {} + + template< class Container > + span_constexpr span( with_container_t, Container const & cont ) + : data_( cont.size() == 0 ? span_nullptr : const_cast( span_ADDRESSOF( cont[0] ) ) ) + , size_( to_size( cont.size() ) ) + {} +#endif + +#if span_HAVE( IS_DEFAULT ) + span_constexpr span( span const & other ) span_noexcept = default; + + ~span() span_noexcept = default; + + span_constexpr14 span & operator=( span const & other ) span_noexcept = default; +#else + span_constexpr span( span const & other ) span_noexcept + : data_( other.data_ ) + , size_( other.size_ ) + {} + + ~span() span_noexcept + {} + + span_constexpr14 span & operator=( span const & other ) span_noexcept + { + data_ = other.data_; + size_ = other.size_; + + return *this; + } +#endif + + template< class OtherElementType, extent_type OtherExtent + span_REQUIRES_T(( + (Extent == dynamic_extent || OtherExtent == dynamic_extent || Extent == OtherExtent) + && std::is_convertible::value + )) + > + span_constexpr_exp span( span const & other ) span_noexcept + : data_( other.data() ) + , size_( other.size() ) + { + span_EXPECTS( OtherExtent == dynamic_extent || other.size() == to_size(OtherExtent) ); + } + + // 26.7.3.3 Subviews [span.sub] + + template< extent_type Count > + span_constexpr_exp span< element_type, Count > + first() const + { + span_EXPECTS( detail::is_positive( Count ) && Count <= size() ); + + return span< element_type, Count >( data(), Count ); + } + + template< extent_type Count > + span_constexpr_exp span< element_type, Count > + last() const + { + span_EXPECTS( detail::is_positive( Count ) && Count <= size() ); + + return span< element_type, Count >( data() + (size() - Count), Count ); + } + +#if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + template< size_type Offset, extent_type Count = dynamic_extent > +#else + template< size_type Offset, extent_type Count /*= dynamic_extent*/ > +#endif + span_constexpr_exp span< element_type, Count > + subspan() const + { + span_EXPECTS( + ( detail::is_positive( Offset ) && Offset <= size() ) && + ( Count == dynamic_extent || (detail::is_positive( Count ) && Count + Offset <= size()) ) + ); + + return span< element_type, Count >( + data() + Offset, Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : size() - Offset) ); + } + + span_constexpr_exp span< element_type, dynamic_extent > + first( size_type count ) const + { + span_EXPECTS( detail::is_positive( count ) && count <= size() ); + + return span< element_type, dynamic_extent >( data(), count ); + } + + span_constexpr_exp span< element_type, dynamic_extent > + last( size_type count ) const + { + span_EXPECTS( detail::is_positive( count ) && count <= size() ); + + return span< element_type, dynamic_extent >( data() + ( size() - count ), count ); + } + + span_constexpr_exp span< element_type, dynamic_extent > + subspan( size_type offset, size_type count = static_cast(dynamic_extent) ) const + { + span_EXPECTS( + ( ( detail::is_positive( offset ) && offset <= size() ) ) && + ( count == static_cast(dynamic_extent) || ( detail::is_positive( count ) && offset + count <= size() ) ) + ); + + return span< element_type, dynamic_extent >( + data() + offset, count == static_cast(dynamic_extent) ? size() - offset : count ); + } + + // 26.7.3.4 Observers [span.obs] + + span_constexpr size_type size() const span_noexcept + { + return size_; + } + + span_constexpr std::ptrdiff_t ssize() const span_noexcept + { + return static_cast( size_ ); + } + + span_constexpr size_type size_bytes() const span_noexcept + { + return size() * to_size( sizeof( element_type ) ); + } + + span_nodiscard span_constexpr bool empty() const span_noexcept + { + return size() == 0; + } + + // 26.7.3.5 Element access [span.elem] + + span_constexpr_exp reference operator[]( size_type idx ) const + { + span_EXPECTS( detail::is_positive( idx ) && idx < size() ); + + return *( data() + idx ); + } + +#if span_FEATURE( MEMBER_CALL_OPERATOR ) + span_deprecated("replace operator() with operator[]") + + span_constexpr_exp reference operator()( size_type idx ) const + { + span_EXPECTS( detail::is_positive( idx ) && idx < size() ); + + return *( data() + idx ); + } +#endif + +#if span_FEATURE( MEMBER_AT ) + span_constexpr14 reference at( size_type idx ) const + { +#if span_CONFIG( NO_EXCEPTIONS ) + return this->operator[]( idx ); +#else + if ( !detail::is_positive( idx ) || size() <= idx ) + { + detail::throw_out_of_range( idx, size() ); + } + return *( data() + idx ); +#endif + } +#endif + + span_constexpr pointer data() const span_noexcept + { + return data_; + } + +#if span_FEATURE( MEMBER_BACK_FRONT ) + + span_constexpr_exp reference front() const span_noexcept + { + span_EXPECTS( ! empty() ); + + return *data(); + } + + span_constexpr_exp reference back() const span_noexcept + { + span_EXPECTS( ! empty() ); + + return *( data() + size() - 1 ); + } + +#endif + + // xx.x.x.x Modifiers [span.modifiers] + +#if span_FEATURE( MEMBER_SWAP ) + + span_constexpr14 void swap( span & other ) span_noexcept + { + using std::swap; + swap( data_, other.data_ ); + swap( size_, other.size_ ); + } +#endif + + // 26.7.3.6 Iterator support [span.iterators] + + span_constexpr iterator begin() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() }; +#else + return iterator( data() ); +#endif + } + + span_constexpr iterator end() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() + size() }; +#else + return iterator( data() + size() ); +#endif + } + + span_constexpr const_iterator cbegin() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() }; +#else + return const_iterator( data() ); +#endif + } + + span_constexpr const_iterator cend() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() + size() }; +#else + return const_iterator( data() + size() ); +#endif + } + + span_constexpr reverse_iterator rbegin() const span_noexcept + { + return reverse_iterator( end() ); + } + + span_constexpr reverse_iterator rend() const span_noexcept + { + return reverse_iterator( begin() ); + } + + span_constexpr const_reverse_iterator crbegin() const span_noexcept + { + return const_reverse_iterator ( cend() ); + } + + span_constexpr const_reverse_iterator crend() const span_noexcept + { + return const_reverse_iterator( cbegin() ); + } + +private: + + // Note: C++20 has std::pointer_traits::to_address( it ); + +#if span_HAVE( ITERATOR_CTOR ) + static inline span_constexpr pointer to_address( std::nullptr_t ) span_noexcept + { + return nullptr; + } + + template< typename U > + static inline span_constexpr U * to_address( U * p ) span_noexcept + { + return p; + } + + template< typename Ptr + span_REQUIRES_T(( ! std::is_pointer::value )) + > + static inline span_constexpr pointer to_address( Ptr const & it ) span_noexcept + { + return to_address( it.operator->() ); + } +#endif // span_HAVE( ITERATOR_CTOR ) + +private: + pointer data_; + size_type size_; +}; + +// class template argument deduction guides: + +#if span_HAVE( DEDUCTION_GUIDES ) + +template< class T, size_t N > +span( T (&)[N] ) -> span(N)>; + +template< class T, size_t N > +span( std::array & ) -> span(N)>; + +template< class T, size_t N > +span( std::array const & ) -> span(N)>; + +#if span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + +template< class Container > +span( Container& ) -> span; + +template< class Container > +span( Container const & ) -> span; + +#endif + +// iterator: constraints: It satisfies contiguous_­iterator. + +template< class It, class EndOrSize > +span( It, EndOrSize ) -> span< typename std11::remove_reference< typename std20::iter_reference_t >::type >; + +#endif // span_HAVE( DEDUCTION_GUIDES ) + +// 26.7.3.7 Comparison operators [span.comparison] + +#if span_FEATURE( COMPARISON ) +#if span_FEATURE( SAME ) + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool same( span const & l, span const & r ) span_noexcept +{ + return std11::is_same::value + && l.size() == r.size() + && static_cast( l.data() ) == r.data(); +} + +#endif + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator==( span const & l, span const & r ) +{ + return +#if span_FEATURE( SAME ) + same( l, r ) || +#endif + ( l.size() == r.size() && std::equal( l.begin(), l.end(), r.begin() ) ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator<( span const & l, span const & r ) +{ + return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator!=( span const & l, span const & r ) +{ + return !( l == r ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator<=( span const & l, span const & r ) +{ + return !( r < l ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator>( span const & l, span const & r ) +{ + return ( r < l ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator>=( span const & l, span const & r ) +{ + return !( l < r ); +} + +#endif // span_FEATURE( COMPARISON ) + +// 26.7.2.6 views of object representation [span.objectrep] + +#if span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) + +// Avoid MSVC 14.1 (1910), VS 2017: warning C4307: '*': integral constant overflow: + +template< typename T, extent_t Extent > +struct BytesExtent +{ +#if span_CPP11_OR_GREATER + enum ET : extent_t { value = span_sizeof(T) * Extent }; +#else + enum ET { value = span_sizeof(T) * Extent }; +#endif +}; + +template< typename T > +struct BytesExtent< T, dynamic_extent > +{ +#if span_CPP11_OR_GREATER + enum ET : extent_t { value = dynamic_extent }; +#else + enum ET { value = dynamic_extent }; +#endif +}; + +template< class T, extent_t Extent > +inline span_constexpr span< const std17::byte, BytesExtent::value > +as_bytes( span spn ) span_noexcept +{ +#if 0 + return { reinterpret_cast< std17::byte const * >( spn.data() ), spn.size_bytes() }; +#else + return span< const std17::byte, BytesExtent::value >( + reinterpret_cast< std17::byte const * >( spn.data() ), spn.size_bytes() ); // NOLINT +#endif +} + +template< class T, extent_t Extent > +inline span_constexpr span< std17::byte, BytesExtent::value > +as_writable_bytes( span spn ) span_noexcept +{ +#if 0 + return { reinterpret_cast< std17::byte * >( spn.data() ), spn.size_bytes() }; +#else + return span< std17::byte, BytesExtent::value >( + reinterpret_cast< std17::byte * >( spn.data() ), spn.size_bytes() ); // NOLINT +#endif +} + +#endif // span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) + +// 27.8 Container and view access [iterator.container] + +template< class T, extent_t Extent /*= dynamic_extent*/ > +span_constexpr std::size_t size( span const & spn ) +{ + return static_cast( spn.size() ); +} + +template< class T, extent_t Extent /*= dynamic_extent*/ > +span_constexpr std::ptrdiff_t ssize( span const & spn ) +{ + return static_cast( spn.size() ); +} + +} // namespace span_lite +} // namespace nonstd + +// make available in nonstd: + +namespace nonstd { + +using span_lite::dynamic_extent; + +using span_lite::span; + +using span_lite::with_container; + +#if span_FEATURE( COMPARISON ) +#if span_FEATURE( SAME ) +using span_lite::same; +#endif + +using span_lite::operator==; +using span_lite::operator!=; +using span_lite::operator<; +using span_lite::operator<=; +using span_lite::operator>; +using span_lite::operator>=; +#endif + +#if span_HAVE( BYTE ) +using span_lite::as_bytes; +using span_lite::as_writable_bytes; +#endif + +using span_lite::size; +using span_lite::ssize; + +} // namespace nonstd + +#endif // span_USES_STD_SPAN + +// make_span() [span-lite extension]: + +#if span_FEATURE( MAKE_SPAN ) || span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) || span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) + +#if span_USES_STD_SPAN +# define span_constexpr constexpr +# define span_noexcept noexcept +# define span_nullptr nullptr +# ifndef span_CONFIG_EXTENT_TYPE +# define span_CONFIG_EXTENT_TYPE std::size_t +# endif +using extent_t = span_CONFIG_EXTENT_TYPE; +#endif // span_USES_STD_SPAN + +namespace nonstd { +namespace span_lite { + +template< class T > +inline span_constexpr span +make_span( T * ptr, size_t count ) span_noexcept +{ + return span( ptr, count ); +} + +template< class T > +inline span_constexpr span +make_span( T * first, T * last ) span_noexcept +{ + return span( first, last ); +} + +template< class T, std::size_t N > +inline span_constexpr span(N)> +make_span( T ( &arr )[ N ] ) span_noexcept +{ + return span(N)>( &arr[ 0 ], N ); +} + +#if span_USES_STD_SPAN || span_HAVE( ARRAY ) + +template< class T, std::size_t N > +inline span_constexpr span(N)> +make_span( std::array< T, N > & arr ) span_noexcept +{ + return span(N)>( arr ); +} + +template< class T, std::size_t N > +inline span_constexpr span< const T, static_cast(N) > +make_span( std::array< T, N > const & arr ) span_noexcept +{ + return span(N)>( arr ); +} + +#endif // span_HAVE( ARRAY ) + +#if span_USES_STD_SPAN + +template< class Container, class EP = decltype( std::data(std::declval())) > +inline span_constexpr auto +make_span( Container & cont ) span_noexcept -> span< typename std::remove_pointer::type > +{ + return span< typename std::remove_pointer::type >( cont ); +} + +template< class Container, class EP = decltype( std::data(std::declval())) > +inline span_constexpr auto +make_span( Container const & cont ) span_noexcept -> span< const typename std::remove_pointer::type > +{ + return span< const typename std::remove_pointer::type >( cont ); +} + +#elif span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) && span_HAVE( AUTO ) + +template< class Container, class EP = decltype( std17::data(std::declval())) > +inline span_constexpr auto +make_span( Container & cont ) span_noexcept -> span< typename std::remove_pointer::type > +{ + return span< typename std::remove_pointer::type >( cont ); +} + +template< class Container, class EP = decltype( std17::data(std::declval())) > +inline span_constexpr auto +make_span( Container const & cont ) span_noexcept -> span< const typename std::remove_pointer::type > +{ + return span< const typename std::remove_pointer::type >( cont ); +} + +#else + +template< class T > +inline span_constexpr span +make_span( span spn ) span_noexcept +{ + return spn; +} + +template< class T, class Allocator > +inline span_constexpr span +make_span( std::vector & cont ) span_noexcept +{ + return span( with_container, cont ); +} + +template< class T, class Allocator > +inline span_constexpr span +make_span( std::vector const & cont ) span_noexcept +{ + return span( with_container, cont ); +} + +#endif // span_USES_STD_SPAN || ( ... ) + +#if ! span_USES_STD_SPAN && span_FEATURE( WITH_CONTAINER ) + +template< class Container > +inline span_constexpr span +make_span( with_container_t, Container & cont ) span_noexcept +{ + return span< typename Container::value_type >( with_container, cont ); +} + +template< class Container > +inline span_constexpr span +make_span( with_container_t, Container const & cont ) span_noexcept +{ + return span< const typename Container::value_type >( with_container, cont ); +} + +#endif // ! span_USES_STD_SPAN && span_FEATURE( WITH_CONTAINER ) + +// extensions: non-member views: +// this feature implies the presence of make_span() + +#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) + +template< extent_t Count, class T, extent_t Extent > +span_constexpr span +first( span spn ) +{ + return spn.template first(); +} + +template< class T, extent_t Extent > +span_constexpr span +first( span spn, size_t count ) +{ + return spn.first( count ); +} + +template< extent_t Count, class T, extent_t Extent > +span_constexpr span +last( span spn ) +{ + return spn.template last(); +} + +template< class T, extent_t Extent > +span_constexpr span +last( span spn, size_t count ) +{ + return spn.last( count ); +} + +template< size_t Offset, extent_t Count, class T, extent_t Extent > +span_constexpr span +subspan( span spn ) +{ + return spn.template subspan(); +} + +template< class T, extent_t Extent > +span_constexpr span +subspan( span spn, size_t offset, extent_t count = dynamic_extent ) +{ + return spn.subspan( offset, count ); +} + +#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) + +#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) && span_CPP11_120 + +template< extent_t Count, class T > +span_constexpr auto +first( T & t ) -> decltype( make_span(t).template first() ) +{ + return make_span( t ).template first(); +} + +template< class T > +span_constexpr auto +first( T & t, size_t count ) -> decltype( make_span(t).first(count) ) +{ + return make_span( t ).first( count ); +} + +template< extent_t Count, class T > +span_constexpr auto +last( T & t ) -> decltype( make_span(t).template last() ) +{ + return make_span(t).template last(); +} + +template< class T > +span_constexpr auto +last( T & t, extent_t count ) -> decltype( make_span(t).last(count) ) +{ + return make_span( t ).last( count ); +} + +template< size_t Offset, extent_t Count = dynamic_extent, class T > +span_constexpr auto +subspan( T & t ) -> decltype( make_span(t).template subspan() ) +{ + return make_span( t ).template subspan(); +} + +template< class T > +span_constexpr auto +subspan( T & t, size_t offset, extent_t count = dynamic_extent ) -> decltype( make_span(t).subspan(offset, count) ) +{ + return make_span( t ).subspan( offset, count ); +} + +#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) + +} // namespace span_lite +} // namespace nonstd + +// make available in nonstd: + +namespace nonstd { +using span_lite::make_span; + +#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) || ( span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) && span_CPP11_120 ) + +using span_lite::first; +using span_lite::last; +using span_lite::subspan; + +#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_[SPAN|CONTAINER] ) + +} // namespace nonstd + +#endif // #if span_FEATURE_TO_STD( MAKE_SPAN ) + +#if span_CPP11_OR_GREATER && span_FEATURE( BYTE_SPAN ) && ( span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) ) + +namespace nonstd { +namespace span_lite { + +template< class T > +inline span_constexpr auto +byte_span( T & t ) span_noexcept -> span< std17::byte, span_sizeof(T) > +{ + return span< std17::byte, span_sizeof(t) >( reinterpret_cast< std17::byte * >( &t ), span_sizeof(T) ); +} + +template< class T > +inline span_constexpr auto +byte_span( T const & t ) span_noexcept -> span< const std17::byte, span_sizeof(T) > +{ + return span< const std17::byte, span_sizeof(t) >( reinterpret_cast< std17::byte const * >( &t ), span_sizeof(T) ); +} + +} // namespace span_lite +} // namespace nonstd + +// make available in nonstd: + +namespace nonstd { +using span_lite::byte_span; +} // namespace nonstd + +#endif // span_FEATURE( BYTE_SPAN ) + +#if span_HAVE( STRUCT_BINDING ) + +#if span_CPP14_OR_GREATER +# include +#elif span_CPP11_OR_GREATER +# include +namespace std { + template< std::size_t I, typename T > + using tuple_element_t = typename tuple_element::type; +} +#else +namespace std { + template< typename T > + class tuple_size; /*undefined*/ + + template< std::size_t I, typename T > + class tuple_element; /* undefined */ +} +#endif // span_CPP14_OR_GREATER + +namespace std { + +// 26.7.X Tuple interface + +// std::tuple_size<>: + +template< typename ElementType, nonstd::span_lite::extent_t Extent > +class tuple_size< nonstd::span > : public integral_constant(Extent)> {}; + +// std::tuple_size<>: Leave undefined for dynamic extent: + +template< typename ElementType > +class tuple_size< nonstd::span >; + +// std::tuple_element<>: + +template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent > +class tuple_element< I, nonstd::span > +{ +public: +#if span_HAVE( STATIC_ASSERT ) + static_assert( Extent != nonstd::dynamic_extent && I < Extent, "tuple_element: dynamic extent or index out of range" ); +#endif + using type = ElementType; +}; + +// std::get<>(), 2 variants: + +template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent > +span_constexpr ElementType & get( nonstd::span & spn ) span_noexcept +{ +#if span_HAVE( STATIC_ASSERT ) + static_assert( Extent != nonstd::dynamic_extent && I < Extent, "get<>(span): dynamic extent or index out of range" ); +#endif + return spn[I]; +} + +template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent > +span_constexpr ElementType const & get( nonstd::span const & spn ) span_noexcept +{ +#if span_HAVE( STATIC_ASSERT ) + static_assert( Extent != nonstd::dynamic_extent && I < Extent, "get<>(span): dynamic extent or index out of range" ); +#endif + return spn[I]; +} + +} // end namespace std + +#endif // span_HAVE( STRUCT_BINDING ) + +#if ! span_USES_STD_SPAN +span_RESTORE_WARNINGS() +#endif // span_USES_STD_SPAN + +#endif // NONSTD_SPAN_HPP_INCLUDED