diff --git a/game/overlord/common/srpc.cpp b/game/overlord/common/srpc.cpp index 5345a78b8a..725a47f7c1 100644 --- a/game/overlord/common/srpc.cpp +++ b/game/overlord/common/srpc.cpp @@ -1,6 +1,7 @@ #include "srpc.h" #include + #include "game/sound/sndshim.h" // added diff --git a/game/overlord/jak2/srpc.cpp b/game/overlord/jak2/srpc.cpp index 8b9a05cec9..a845098490 100644 --- a/game/overlord/jak2/srpc.cpp +++ b/game/overlord/jak2/srpc.cpp @@ -148,9 +148,9 @@ void* RPC_Player(unsigned int /*fno*/, void* data, int size) { } // lg::warn("RPC: PLAY {} v:{}, p:{}", sound->name, GetVolume(sound), GetPan(sound)); - s32 handle = snd_PlaySoundByNameVolPanPMPB(0, nullptr, sound->name, GetVolume(sound), - GetPan(sound), sound->params.pitch_mod, - sound->params.bend); + s32 handle = snd_PlaySoundByNameVolPanPMPB(nullptr, nullptr, sound->name, + GetVolume(sound), GetPan(sound), + sound->params.pitch_mod, sound->params.bend); sound->sound_handle = handle; if (handle != 0) { sound->id = cmd->play.sound_id; diff --git a/game/sound/989snd/ame_handler.cpp b/game/sound/989snd/ame_handler.cpp index c3cd6bdd7a..7f66f53370 100644 --- a/game/sound/989snd/ame_handler.cpp +++ b/game/sound/989snd/ame_handler.cpp @@ -12,13 +12,13 @@ namespace snd { u64 SoundFlavaHack = 0; u8 GlobalExcite = 0; -AmeHandler::AmeHandler(MultiMidi* block, - VoiceManager& vm, - MusicBank::MIDISound& sound, - s32 vol, - s32 pan, - SoundBank& bank) - : m_sound(sound), m_bank(bank), m_header(block), m_vm(vm), m_repeats(sound.Repeats) { +AmeHandler::AmeHandler(SoundHandle oid, + MultiMidi* block, + MusicBank::MIDISound& sound, + s32 vol, + s32 pan, + SoundBank& bank) + : SoundHandler(oid), m_sound(sound), m_bank(bank), m_header(block), m_repeats(sound.Repeats) { if (vol == VOLUME_DONT_CHANGE) { vol = 1024; } @@ -58,8 +58,8 @@ void AmeHandler::StartSegment(u32 id) { // Skip adding if not midi type u32 type = (midi.SoundHandle >> 24) & 0xf; if (type == 1 || type == 3) { - m_midis.emplace(id, std::make_unique(static_cast(&midi), m_vm, m_sound, - m_vol, m_pan, m_bank, this)); + m_midis.emplace(id, std::make_unique(0, static_cast(&midi), m_sound, + m_vol, m_pan, m_bank, this)); } } } diff --git a/game/sound/989snd/ame_handler.h b/game/sound/989snd/ame_handler.h index 0f95ffc431..543ae543cb 100644 --- a/game/sound/989snd/ame_handler.h +++ b/game/sound/989snd/ame_handler.h @@ -5,9 +5,9 @@ #include "loader.h" #include "midi_handler.h" +#include "musicbank.h" #include "sound_handler.h" #include "vagvoice.h" -#include "musicbank.h" #include "common/common_types.h" @@ -23,12 +23,12 @@ class AmeHandler : public SoundHandler { friend class MidiHandler; public: - AmeHandler(MultiMidi* block, - VoiceManager& vm, - MusicBank::MIDISound& sound, - s32 vol, - s32 pan, - SoundBank& bank); + AmeHandler(SoundHandle oid, + MultiMidi* block, + MusicBank::MIDISound& sound, + s32 vol, + s32 pan, + SoundBank& bank); bool Tick() override; SoundBank& Bank() override { return m_bank; }; @@ -66,7 +66,6 @@ class AmeHandler : public SoundHandler { SoundBank& m_bank; MultiMidi* m_header{nullptr}; - VoiceManager& m_vm; s32 m_vol{0}; s32 m_pan{0}; s8 m_repeats{0}; @@ -77,4 +76,10 @@ class AmeHandler : public SoundHandler { std::array m_register{}; std::array m_macro{}; }; + +AmeHandler* AllocAmeSound(MultiMidi* block, + MusicBank::MIDISound& sound, + s32 vol, + s32 pan, + SoundBank& bank); } // namespace snd diff --git a/game/sound/989snd/blocksound_handler.cpp b/game/sound/989snd/blocksound_handler.cpp index e6fca61b73..ba6c8a2a1f 100644 --- a/game/sound/989snd/blocksound_handler.cpp +++ b/game/sound/989snd/blocksound_handler.cpp @@ -5,18 +5,16 @@ #include "util.h" -#include "common/log/log.h" +#include "game/sound/989snd/player.h" namespace snd { std::array g_block_reg{}; -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) { +BlockSoundHandler* BlockSoundHandler::MakeBlockSound(SoundBank& bank, + SFXBlock::SFX& sfx, + s32 sfx_vol, + s32 sfx_pan, + SndPlayParams& params) { s32 vol, pan, pitch_mod, pitch_bend; if (sfx_vol == -1) { sfx_vol = sfx.Vol; @@ -61,42 +59,52 @@ BlockSoundHandler::BlockSoundHandler(SoundBank& bank, pan = sfx_pan; } - m_orig_volume = sfx_vol; - m_orig_pan = sfx_pan; + if (sfx.Flags.solo()) { + StopAllHandlersForSound(sfx); + } + + auto* hnd = AllocBlockSound(bank, sfx, vol); + if (hnd == nullptr) { + return nullptr; + } + + hnd->m_orig_volume = sfx_vol; + hnd->m_orig_pan = sfx_pan; - m_cur_volume = play_vol; - m_cur_pan = pan; - m_cur_pb = pitch_bend; - m_cur_pm = pitch_mod; + hnd->m_cur_volume = play_vol; + hnd->m_cur_pan = pan; + hnd->m_cur_pb = pitch_bend; + hnd->m_cur_pm = pitch_mod; - m_app_volume = vol; - m_app_pan = pan; - m_app_pb = pitch_bend; - m_app_pm = pitch_mod; + hnd->m_app_volume = vol; + hnd->m_app_pan = pan; + hnd->m_app_pb = pitch_bend; + hnd->m_app_pm = pitch_mod; - m_lfo_volume = 0; - m_lfo_pan = 0; - m_lfo_pb = 0; - m_lfo_pm = 0; + hnd->m_lfo_volume = 0; + hnd->m_lfo_pan = 0; + hnd->m_lfo_pb = 0; + hnd->m_lfo_pm = 0; if (params.registers.has_value()) { - m_registers = params.registers.value(); + hnd->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; - // } + // Bug from PS2, this was never set + hnd->m_start_tick = GetTick(); - m_next_grain = 0; - m_countdown = m_sfx.Grains[0].Delay; - while (m_countdown <= 0 && !m_done) { - DoGrain(); + hnd->m_next_grain = 0; + hnd->m_countdown = hnd->m_sfx.Grains[0].Delay; + while (hnd->m_countdown <= 0 && !hnd->m_done) { + hnd->DoGrain(); } + + return hnd; } +BlockSoundHandler::BlockSoundHandler(SoundHandle oid, SoundBank& bank, SFXBlock::SFX& sfx) + : SoundHandler(oid), m_group(sfx.VolGroup), m_sfx(sfx), m_bank(bank) {} + BlockSoundHandler::~BlockSoundHandler() { for (auto& p : m_voices) { auto v = p.lock(); @@ -114,8 +122,9 @@ bool BlockSoundHandler::Tick() { } for (auto it = m_children.begin(); it != m_children.end();) { - bool done = it->get()->Tick(); + bool done = (*it)->Tick(); if (done) { + FreeSound(*it); it = m_children.erase(it); } else { ++it; @@ -154,7 +163,7 @@ void BlockSoundHandler::Pause() { continue; } - m_vm.Pause(voice); + PauseTone(voice); } } @@ -171,7 +180,7 @@ void BlockSoundHandler::Unpause() { continue; } - m_vm.Unpause(voice); + UnpauseTone(voice); } } @@ -232,9 +241,9 @@ void BlockSoundHandler::SetVolPan(s32 vol, s32 pan) { continue; } - 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); + auto volume = MakeVolume(127, 0, m_cur_volume, m_cur_pan, voice->g_vol, voice->g_pan); + auto left = AdjustVolToGroup(volume.left, m_group); + auto right = AdjustVolToGroup(volume.right, m_group); voice->SetVolume(left >> 1, right >> 1); } diff --git a/game/sound/989snd/blocksound_handler.h b/game/sound/989snd/blocksound_handler.h index 52920b3650..8f921ef554 100644 --- a/game/sound/989snd/blocksound_handler.h +++ b/game/sound/989snd/blocksound_handler.h @@ -20,12 +20,13 @@ class BlockSoundVoice : public VagVoice { class BlockSoundHandler : public SoundHandler { public: - BlockSoundHandler(SoundBank& bank, - SFXBlock::SFX& sfx, - VoiceManager& vm, - s32 sfx_vol, - s32 sfx_pan, - SndPlayParams& params); + static BlockSoundHandler* MakeBlockSound(SoundBank& bank, + SFXBlock::SFX& sfx, + s32 sfx_vol, + s32 sfx_pan, + SndPlayParams& params); + + BlockSoundHandler(SoundHandle oid, SoundBank& bank, SFXBlock::SFX& sfx); ~BlockSoundHandler() override; bool Tick() override; @@ -54,11 +55,9 @@ class BlockSoundHandler : public SoundHandler { bool m_skip_grains{false}; SFXBlock::SFX& m_sfx; - VoiceManager& m_vm; std::list> m_voices; - - std::list> m_children; + std::vector m_children; s32 m_orig_volume{0}; s32 m_orig_pan{0}; @@ -86,5 +85,9 @@ class BlockSoundHandler : public SoundHandler { s32 m_countdown{0}; u32 m_next_grain{0}; + u32 m_start_tick{0}; }; + +BlockSoundHandler* AllocBlockSound(SoundBank& bank, SFXBlock::SFX& sfx, s32 sfx_vol); + } // namespace snd diff --git a/game/sound/989snd/loader.cpp b/game/sound/989snd/loader.cpp index fc60528e8e..b249da816a 100644 --- a/game/sound/989snd/loader.cpp +++ b/game/sound/989snd/loader.cpp @@ -16,6 +16,8 @@ namespace snd { +std::vector> gBanks; + enum chunk : u32 { bank, samples, midi }; inline static constexpr u32 fourcc(std::string_view p) { @@ -405,7 +407,7 @@ MusicBank* MusicBank::ReadBank(nonstd::span bank_data, return bank; } -BankHandle Loader::BankLoad(nonstd::span bank) { +BankHandle BankLoad(nonstd::span bank) { BinaryReader reader(bank); FileAttributes fa; fa.Read(reader); @@ -430,12 +432,12 @@ BankHandle Loader::BankLoad(nonstd::span bank) { 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); + gBanks.emplace_back(bank); return bank; } else if (fourcc == snd::fourcc("SBlk")) { auto block = SFXBlock::ReadBlock(bank_data, sample_data); - mBanks.emplace_back(block); + gBanks.emplace_back(block); return block; } @@ -443,18 +445,18 @@ BankHandle Loader::BankLoad(nonstd::span bank) { return nullptr; } -SoundBank* Loader::GetBankByHandle(BankHandle handle) { - auto bank = std::find_if(mBanks.begin(), mBanks.end(), +SoundBank* GetBankByHandle(BankHandle handle) { + auto bank = std::find_if(gBanks.begin(), gBanks.end(), [handle](auto& bank) { return bank.get() == handle; }); - if (bank == mBanks.end()) { + if (bank == gBanks.end()) { return nullptr; } return bank->get(); } -SoundBank* Loader::GetBankByName(const char* name) { - for (auto& b : mBanks) { +SoundBank* GetBankByName(const char* name) { + for (auto& b : gBanks) { auto bankname = b->GetName(); if (bankname.has_value()) { if (bankname->compare(name) == 0) { @@ -466,8 +468,8 @@ SoundBank* Loader::GetBankByName(const char* name) { return nullptr; } -SoundBank* Loader::GetBankWithSound(const char* name) { - for (auto& b : mBanks) { +SoundBank* GetBankWithSound(const char* name) { + for (auto& b : gBanks) { auto sound = b->GetSoundByName(name); if (sound.has_value()) { return b.get(); @@ -477,11 +479,11 @@ SoundBank* Loader::GetBankWithSound(const char* name) { return nullptr; } -void Loader::UnloadBank(BankHandle handle) { - auto bank = std::find_if(mBanks.begin(), mBanks.end(), +void BankLoad(BankHandle handle) { + auto bank = std::find_if(gBanks.begin(), gBanks.end(), [handle](auto& bank) { return bank.get() == handle; }); - if (bank != mBanks.end()) { - mBanks.erase(bank); + if (bank != gBanks.end()) { + gBanks.erase(bank); } } diff --git a/game/sound/989snd/loader.h b/game/sound/989snd/loader.h index 099357ec2d..be6a98ac64 100644 --- a/game/sound/989snd/loader.h +++ b/game/sound/989snd/loader.h @@ -5,8 +5,6 @@ #include #include -#include "soundbank.h" - #include "common/common_types.h" #include "common/util/BinaryReader.h" @@ -14,6 +12,7 @@ namespace snd { +class SoundBank; using BankHandle = SoundBank*; class FileAttributes { @@ -29,17 +28,10 @@ class FileAttributes { void Read(BinaryReader& data); }; -class Loader { - public: - SoundBank* GetBankByHandle(BankHandle id); - SoundBank* GetBankByName(const char* name); - SoundBank* GetBankWithSound(const char* name); - - void UnloadBank(BankHandle id); +SoundBank* GetBankByHandle(BankHandle id); +SoundBank* GetBankByName(const char* name); +SoundBank* GetBankWithSound(const char* name); +void BankLoad(BankHandle id); +BankHandle BankLoad(nonstd::span bank); - BankHandle BankLoad(nonstd::span bank); - - private: - std::vector> mBanks; -}; } // namespace snd diff --git a/game/sound/989snd/midi_handler.cpp b/game/sound/989snd/midi_handler.cpp index aba131ee9a..a9c008179d 100644 --- a/game/sound/989snd/midi_handler.cpp +++ b/game/sound/989snd/midi_handler.cpp @@ -22,13 +22,13 @@ namespace snd { ** */ -MidiHandler::MidiHandler(Midi* block, - VoiceManager& vm, - MusicBank::MIDISound& sound, - s32 vol, - s32 pan, - SoundBank& bank) - : m_sound(sound), m_repeats(sound.Repeats), m_bank(bank), m_header(block), m_vm(vm) { +MidiHandler::MidiHandler(SoundHandle oid, + Midi* block, + MusicBank::MIDISound& sound, + s32 vol, + s32 pan, + SoundBank& bank) + : SoundHandler(oid), m_sound(sound), m_repeats(sound.Repeats), m_bank(bank), m_header(block) { if (vol == VOLUME_DONT_CHANGE) { vol = 1024; } @@ -47,21 +47,21 @@ MidiHandler::MidiHandler(Midi* block, InitMidi(); } -MidiHandler::MidiHandler(Midi* block, - VoiceManager& vm, - MusicBank::MIDISound& sound, - s32 vol, - s32 pan, - SoundBank& bank, - std::optional parent) - : m_parent(parent), +MidiHandler::MidiHandler(SoundHandle oid, + Midi* block, + MusicBank::MIDISound& sound, + s32 vol, + s32 pan, + SoundBank& bank, + std::optional parent) + : SoundHandler(oid), + m_parent(parent), m_sound(sound), m_vol(vol), m_pan(pan), m_repeats(sound.Repeats), m_bank(bank), - m_header(block), - m_vm(vm) { + m_header(block) { InitMidi(); } @@ -99,7 +99,7 @@ void MidiHandler::Pause() { continue; } - m_vm.Pause(voice); + PauseTone(voice); } } @@ -112,7 +112,7 @@ void MidiHandler::Unpause() { continue; } - m_vm.Unpause(voice); + UnpauseTone(voice); } } @@ -162,11 +162,11 @@ void MidiHandler::SetVolPan(s32 vol, s32 pan) { } voice->basevol = - 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); + 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.AdjustVolToGroup(voice->basevol.left, voice->group); - auto right = m_vm.AdjustVolToGroup(voice->basevol.right, voice->group); + auto left = AdjustVolToGroup(voice->basevol.left, voice->group); + auto right = AdjustVolToGroup(voice->basevol.right, voice->group); voice->SetVolume(left >> 1, right >> 1); } } @@ -231,8 +231,8 @@ void MidiHandler::NoteOn() { } 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->basevol = MakeVolumeB(m_vol, (velocity * m_chanvol[channel]) / 0x7f, pan, program.Vol, + program.Pan, t.Vol, t.Pan); voice->note = note; voice->channel = channel; @@ -245,7 +245,7 @@ void MidiHandler::NoteOn() { voice->current_pb = m_cur_pm; voice->group = m_sound.VolGroup; - m_vm.StartTone(voice); + StartTone(voice); m_voices.emplace_front(voice); } } diff --git a/game/sound/989snd/midi_handler.h b/game/sound/989snd/midi_handler.h index 78e13e8b46..992ba6a460 100644 --- a/game/sound/989snd/midi_handler.h +++ b/game/sound/989snd/midi_handler.h @@ -29,20 +29,20 @@ class midi_voice : public VagVoice { class AmeHandler; class MidiHandler : public SoundHandler { public: - MidiHandler(Midi* block, - VoiceManager& vm, - MusicBank::MIDISound& sound, - s32 vol, - s32 pan, - SoundBank& bank); - - MidiHandler(Midi* block, - VoiceManager& vm, - MusicBank::MIDISound& sound, - s32 vol, - s32 pan, - SoundBank& bank, - std::optional parent); + MidiHandler(SoundHandle oid, + Midi* block, + MusicBank::MIDISound& sound, + s32 vol, + s32 pan, + SoundBank& bank); + + MidiHandler(SoundHandle oid, + Midi* block, + MusicBank::MIDISound& sound, + s32 vol, + s32 pan, + SoundBank& bank, + std::optional parent); ~MidiHandler() override { for (auto& p : m_voices) { @@ -115,8 +115,6 @@ class MidiHandler : public SoundHandler { std::array m_programs{}; - VoiceManager& m_vm; - void Step(); void NewDelta(); @@ -131,4 +129,11 @@ class MidiHandler : public SoundHandler { static std::pair ReadVLQ(u8* value); }; + +MidiHandler* AllocMidiSound(Midi* block, + MusicBank::MIDISound& sound, + s32 vol, + s32 pan, + SoundBank& bank); + } // namespace snd diff --git a/game/sound/989snd/musicbank.cpp b/game/sound/989snd/musicbank.cpp index de5f46127a..ec29d43833 100644 --- a/game/sound/989snd/musicbank.cpp +++ b/game/sound/989snd/musicbank.cpp @@ -7,12 +7,7 @@ namespace snd { -std::optional> MusicBank::MakeHandler(VoiceManager& vm, - u32 sound_id, - s32 vol, - s32 pan, - s32 pm, - s32 pb) { +SoundHandler* MusicBank::MakeHandler(u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb) { auto& sound = Sounds[sound_id]; // FIXME: global midi list @@ -22,28 +17,24 @@ std::optional> MusicBank::MakeHandler(VoiceManager 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 AllocMidiSound(&midi, sound, vol, pan, *this); } - return std::nullopt; + return nullptr; } 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 AllocAmeSound(&midi, sound, vol, pan, *this); } - return std::nullopt; + return nullptr; } else { lg::error("Invalid music sound type"); - return std::nullopt; + return nullptr; // error } } -std::optional> MusicBank::MakeHandler(VoiceManager& vm, - u32 sound_id, - s32 vol, - s32 pan, - SndPlayParams& params) { - return std::nullopt; +SoundHandler* MusicBank::MakeHandler(u32 sound_id, s32 vol, s32 pan, SndPlayParams& params) { + return nullptr; } } // namespace snd diff --git a/game/sound/989snd/musicbank.h b/game/sound/989snd/musicbank.h index 4d8250ff30..177ac29ff0 100644 --- a/game/sound/989snd/musicbank.h +++ b/game/sound/989snd/musicbank.h @@ -67,17 +67,7 @@ class MusicBank : public SoundBank { 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> MakeHandler(VoiceManager& vm, - u32 sound_id, - s32 vol, - s32 pan, - SndPlayParams& params) override; + SoundHandler* MakeHandler(u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb) override; + SoundHandler* MakeHandler(u32 sound_id, s32 vol, s32 pan, SndPlayParams& params) override; }; } // namespace snd diff --git a/game/sound/989snd/player.cpp b/game/sound/989snd/player.cpp index bc4f6e1880..fe146186f7 100644 --- a/game/sound/989snd/player.cpp +++ b/game/sound/989snd/player.cpp @@ -4,9 +4,13 @@ #include +#include "ame_handler.h" +#include "loader.h" #include "sfxblock.h" +#include "sound_handler.h" +#include "vagvoice.h" -#include "third-party/fmt/core.h" +#include "third-party/cubeb/cubeb/include/cubeb/cubeb.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -18,16 +22,44 @@ namespace snd { u8 g_global_excite = 0; +std::recursive_mutex gTickLock; // TODO does not need to recursive with some light restructuring +std::vector gHandlers; +Synth gSynth; +s32 gTick{0}; -Player::Player() : mVmanager(mSynth) { +cubeb* gCtx{nullptr}; +cubeb_stream* gStream{nullptr}; + +#ifdef _WIN32 +bool m_coinitialized = false; +#endif + +void InitCubeb(); +void Tick(s16Output* stream, int samples); + +void StartSoundSystem() { InitCubeb(); + VoiceManagerInit(gSynth); } -Player::~Player() { +void StopSoundSystem() { DestroyCubeb(); } -void Player::InitCubeb() { +static long sound_callback([[maybe_unused]] cubeb_stream* stream, + void* user, + [[maybe_unused]] const void* input, + void* output_buffer, + long nframes) { + Tick((s16Output*)output_buffer, nframes); + return nframes; +} + +static void state_callback([[maybe_unused]] cubeb_stream* stream, + [[maybe_unused]] void* user, + [[maybe_unused]] cubeb_state state) {} + +void InitCubeb() { #ifdef _WIN32 HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); m_coinitialized = SUCCEEDED(hr); @@ -38,7 +70,7 @@ void Player::InitCubeb() { } #endif - cubeb_init(&mCtx, "OpenGOAL", nullptr); + cubeb_init(&gCtx, "OpenGOAL", nullptr); cubeb_stream_params outparam = {}; outparam.channels = 2; @@ -49,30 +81,30 @@ void Player::InitCubeb() { s32 err = 0; u32 latency = 0; - err = cubeb_get_min_latency(mCtx, &outparam, &latency); + err = cubeb_get_min_latency(gCtx, &outparam, &latency); if (err != CUBEB_OK) { lg::error("Cubeb init failed"); return; } - err = cubeb_stream_init(mCtx, &mStream, "OpenGOAL", nullptr, nullptr, nullptr, &outparam, - latency, &sound_callback, &state_callback, this); + err = cubeb_stream_init(gCtx, &gStream, "OpenGOAL", nullptr, nullptr, nullptr, &outparam, latency, + &sound_callback, &state_callback, NULL); if (err != CUBEB_OK) { lg::error("Cubeb init failed"); return; } - err = cubeb_stream_start(mStream); + err = cubeb_stream_start(gStream); if (err != CUBEB_OK) { lg::error("Cubeb init failed"); return; } } -void Player::DestroyCubeb() { - cubeb_stream_stop(mStream); - cubeb_stream_destroy(mStream); - cubeb_destroy(mCtx); +void DestroyCubeb() { + cubeb_stream_stop(gStream); + cubeb_stream_destroy(gStream); + cubeb_destroy(gCtx); #ifdef _WIN32 if (m_coinitialized) { CoUninitialize(); @@ -81,35 +113,34 @@ void Player::DestroyCubeb() { #endif } -long Player::sound_callback([[maybe_unused]] cubeb_stream* stream, - void* user, - [[maybe_unused]] const void* input, - void* output_buffer, - long nframes) { - ((Player*)user)->Tick((s16Output*)output_buffer, nframes); - return nframes; -} +void SubmitVoice(std::shared_ptr& voice) { + gSynth.AddVoice(voice); +}; + +s32 GetTick() { + return gTick; +}; -void Player::state_callback([[maybe_unused]] cubeb_stream* stream, - [[maybe_unused]] void* user, - [[maybe_unused]] cubeb_state state) {} +void SetGlobalExcite(u8 value) { + GlobalExcite = value; +}; -void Player::Tick(s16Output* stream, int samples) { - std::scoped_lock lock(mTickLock); +void Tick(s16Output* stream, int samples) { + std::scoped_lock lock(gTickLock); 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) { - mTick++; + gTick++; - for (auto it = mHandlers.begin(); it != mHandlers.end();) { - bool done = it->second->Tick(); + for (auto it = gHandlers.begin(); it != gHandlers.end();) { + bool done = (*it)->Tick(); if (done) { // fmt::print("erasing handler\n"); - mHandleAllocator.FreeId(it->first); - it = mHandlers.erase(it); + FreeSound(*it); + it = gHandlers.erase(it); } else { ++it; } @@ -125,45 +156,42 @@ void Player::Tick(s16Output* stream, int samples) { stick++; htick++; - *stream++ = mSynth.Tick(); + *stream++ = gSynth.Tick(); } } -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); +u32 PlaySound(BankHandle bank_id, u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb) { + std::scoped_lock lock(gTickLock); + auto bank = GetBankByHandle(bank_id); if (bank == nullptr) { lg::error("play_sound: Bank {} does not exist", static_cast(bank_id)); return 0; } - auto handler = bank->MakeHandler(mVmanager, sound_id, vol, pan, pm, pb); - if (!handler.has_value()) { + auto handler = bank->MakeHandler(sound_id, vol, pan, pm, pb); + if (!handler) { return 0; } - u32 handle = mHandleAllocator.GetId(); - mHandlers.emplace(handle, std::move(handler.value())); - // fmt::print("play_sound {}:{} - {}\n", bank_id, sound_id, handle); - - return handle; + gHandlers.push_back(handler); + return handler->Handle(); } -u32 Player::PlaySoundByName(BankHandle bank_id, - char* bank_name, - char* sound_name, - s32 vol, - s32 pan, - s32 pm, - s32 pb) { - std::scoped_lock lock(mTickLock); +u32 PlaySoundByName(BankHandle bank_id, + char* bank_name, + char* sound_name, + s32 vol, + s32 pan, + s32 pm, + s32 pb) { + std::scoped_lock lock(gTickLock); SoundBank* bank = nullptr; if (bank_id == 0 && bank_name != nullptr) { - bank = mLoader.GetBankByName(bank_name); + bank = GetBankByName(bank_name); } else if (bank_id != 0) { - bank = mLoader.GetBankByHandle(bank_id); + bank = GetBankByHandle(bank_id); } else { - bank = mLoader.GetBankWithSound(sound_name); + bank = GetBankWithSound(sound_name); } if (bank == nullptr) { @@ -173,6 +201,7 @@ u32 Player::PlaySoundByName(BankHandle bank_id, auto sound = bank->GetSoundByName(sound_name); if (sound.has_value()) { + // lg::error("play_sound_by_name: playing {}", sound_name); return PlaySound(bank, sound.value(), vol, pan, pm, pb); } @@ -181,41 +210,38 @@ u32 Player::PlaySoundByName(BankHandle bank_id, return 0; } -void Player::StopSound(u32 sound_id) { - std::scoped_lock lock(mTickLock); - auto handler = mHandlers.find(sound_id); - if (handler == mHandlers.end()) +void StopSound(u32 sound_id) { + std::scoped_lock lock(gTickLock); + auto s = GetSound(sound_id); + if (s == nullptr) return; - handler->second->Stop(); - - // m_handle_allocator.free_id(sound_id); - // m_handlers.erase(sound_id); + s->Stop(); } -void Player::SetSoundReg(u32 sound_id, u8 reg, u8 value) { - std::scoped_lock lock(mTickLock); - if (mHandlers.find(sound_id) == mHandlers.end()) { +void SetSoundReg(u32 sound_id, u8 reg, u8 value) { + std::scoped_lock lock(gTickLock); + auto s = GetSound(sound_id); + if (s == nullptr) { // fmt::print("set_midi_reg: Handler {} does not exist\n", sound_id); return; } - auto* handler = mHandlers.at(sound_id).get(); - handler->SetRegister(reg, value); + s->SetRegister(reg, value); } -bool Player::SoundStillActive(u32 sound_id) { - std::scoped_lock lock(mTickLock); - auto handler = mHandlers.find(sound_id); - if (handler == mHandlers.end()) - return false; +u32 SoundStillActive(u32 sound_id) { + std::scoped_lock lock(gTickLock); + auto s = GetSound(sound_id); + if (s == nullptr) { + return 0; + } - // fmt::print("sound_still_active {}\n", sound_id); - return true; + return s->Handle(); } -void Player::SetMasterVolume(u32 group, s32 volume) { - std::scoped_lock lock(mTickLock); +void SetMasterVolume(u32 group, s32 volume) { + std::scoped_lock lock(gTickLock); if (volume > 0x400) volume = 0x400; @@ -225,124 +251,114 @@ void Player::SetMasterVolume(u32 group, s32 volume) { if (group == 15) return; - mVmanager.SetMasterVol(group, volume); + snd::SetMasterVol(group, volume); // Master volume if (group == 16) { - mSynth.SetMasterVol(0x3ffff * volume / 0x400); + gSynth.SetMasterVol(0x3ffff * volume / 0x400); } } -BankHandle Player::LoadBank(nonstd::span bank) { - std::scoped_lock lock(mTickLock); - return mLoader.BankLoad(bank); +BankHandle LoadBank(nonstd::span bank) { + std::scoped_lock lock(gTickLock); + return BankLoad(bank); } -void Player::UnloadBank(BankHandle bank_handle) { - std::scoped_lock lock(mTickLock); - auto* bank = mLoader.GetBankByHandle(bank_handle); +void UnloadBank(BankHandle bank_handle) { + std::scoped_lock lock(gTickLock); + auto* bank = GetBankByHandle(bank_handle); if (bank == nullptr) return; - for (auto it = mHandlers.begin(); it != mHandlers.end();) { - if (&it->second->Bank() == bank_handle) { - mHandleAllocator.FreeId(it->first); - it = mHandlers.erase(it); + for (auto it = gHandlers.begin(); it != gHandlers.end();) { + if (&((*it)->Bank()) == bank) { + FreeSound(*it); + it = gHandlers.erase(it); } else { ++it; } } - mLoader.UnloadBank(bank_handle); -} - -void Player::SetPanTable(VolPair* pantable) { - std::scoped_lock lock(mTickLock); - mVmanager.SetPanTable(pantable); -} - -void Player::SetPlaybackMode(s32 mode) { - std::scoped_lock lock(mTickLock); - mVmanager.SetPlaybackMode(mode); + BankLoad(bank_handle); } -void Player::PauseSound(s32 sound_id) { - std::scoped_lock lock(mTickLock); - auto handler = mHandlers.find(sound_id); - if (handler == mHandlers.end()) +void PauseSound(s32 sound_id) { + std::scoped_lock lock(gTickLock); + auto s = GetSound(sound_id); + if (s == nullptr) return; - handler->second->Pause(); + s->Pause(); } -void Player::ContinueSound(s32 sound_id) { - std::scoped_lock lock(mTickLock); - auto handler = mHandlers.find(sound_id); - if (handler == mHandlers.end()) +void ContinueSound(s32 sound_id) { + std::scoped_lock lock(gTickLock); + auto s = GetSound(sound_id); + if (s == nullptr) return; - handler->second->Unpause(); + s->Unpause(); } -void Player::PauseAllSoundsInGroup(u8 group) { - std::scoped_lock lock(mTickLock); +void PauseAllSoundsInGroup(u8 group) { + std::scoped_lock lock(gTickLock); - for (auto& h : mHandlers) { - if ((1 << h.second->Group()) & group) { - h.second->Pause(); + for (auto& h : gHandlers) { + if ((1 << h->Group()) & group) { + h->Pause(); } } } -void Player::ContinueAllSoundsInGroup(u8 group) { - std::scoped_lock lock(mTickLock); +void ContinueAllSoundsInGroup(u8 group) { + std::scoped_lock lock(gTickLock); - for (auto& h : mHandlers) { - if ((1 << h.second->Group()) & group) { - h.second->Unpause(); + for (auto& h : gHandlers) { + if ((1 << h->Group()) & group) { + h->Unpause(); } } } -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()) +void SetSoundVolPan(s32 sound_id, s32 vol, s32 pan) { + std::scoped_lock lock(gTickLock); + auto s = GetSound(sound_id); + if (s == nullptr) return; - handler->second->SetVolPan(vol, pan); + s->SetVolPan(vol, pan); } -void Player::SetSoundPmod(s32 sound_handle, s32 mod) { - std::scoped_lock lock(mTickLock); - auto handler = mHandlers.find(sound_handle); - if (handler == mHandlers.end()) +void SetSoundPmod(s32 sound_handle, s32 mod) { + std::scoped_lock lock(gTickLock); + auto s = GetSound(sound_handle); + if (s == nullptr) return; - handler->second->SetPMod(mod); + s->SetPMod(mod); } -void Player::StopAllSounds() { - std::scoped_lock lock(mTickLock); - for (auto it = mHandlers.begin(); it != mHandlers.end();) { - mHandleAllocator.FreeId(it->first); - it = mHandlers.erase(it); +void StopAllSounds() { + std::scoped_lock lock(gTickLock); + for (auto it = gHandlers.begin(); it != gHandlers.end();) { + FreeSound(*it); + it = gHandlers.erase(it); } } -s32 Player::GetSoundUserData(BankHandle block_handle, - char* block_name, - s32 sound_id, - char* sound_name, - SFXUserData* dst) { - std::scoped_lock lock(mTickLock); +s32 GetSoundUserData(BankHandle block_handle, + char* block_name, + s32 sound_id, + char* sound_name, + SFXUserData* dst) { + std::scoped_lock lock(gTickLock); SoundBank* bank = nullptr; if (block_handle == nullptr && block_name != nullptr) { - bank = mLoader.GetBankByName(block_name); + bank = GetBankByName(block_name); } else if (block_handle != nullptr) { - bank = mLoader.GetBankByHandle(block_handle); + bank = GetBankByHandle(block_handle); } else { - bank = mLoader.GetBankWithSound(sound_name); + bank = GetBankWithSound(sound_name); } if (bank == nullptr) { @@ -360,10 +376,7 @@ s32 Player::GetSoundUserData(BankHandle block_handle, auto ud = bank->GetSoundUserData(sound_id); if (ud.has_value()) { - dst->data[0] = ud.value()->data[0]; - dst->data[1] = ud.value()->data[1]; - dst->data[2] = ud.value()->data[2]; - dst->data[3] = ud.value()->data[3]; + *dst = *ud.value(); return 1; } else { return 0; diff --git a/game/sound/989snd/player.h b/game/sound/989snd/player.h index 07393c7d25..774e70ebcc 100644 --- a/game/sound/989snd/player.h +++ b/game/sound/989snd/player.h @@ -2,95 +2,56 @@ // SPDX-License-Identifier: ISC #pragma once -#include #include -#include -#include -#include "ame_handler.h" -#include "handle_allocator.h" #include "loader.h" -#include "sound_handler.h" +#include "sfxblock.h" #include "common/common_types.h" -#include "../common/synth.h" -#include "game/sound/989snd/vagvoice.h" +#include "../common/voice.h" -#include "third-party/cubeb/cubeb/include/cubeb/cubeb.h" #include "third-party/span.hpp" namespace snd { -class Player { - public: - Player(); - ~Player(); - Player(const Player&) = delete; - Player operator=(const Player&) = delete; +BankHandle LoadBank(nonstd::span bank); + +void StartSoundSystem(); +void StopSoundSystem(); +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 SetSoundReg(u32 sound_id, u8 reg, u8 value); +void SetGlobalExcite(u8 value); +u32 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); +void SetSoundPmod(s32 sound_handle, s32 mod); +void InitCubeb(); +void DestroyCubeb(); +s32 GetTick(); +void StopAllSounds(); +s32 GetSoundUserData(BankHandle block_handle, + char* block_name, + s32 sound_id, + char* sound_name, + SFXUserData* dst); + +extern std::recursive_mutex gTickLock; - // player(player&& other) noexcept = default; - // player& operator=(player&& other) noexcept = default; - - BankHandle LoadBank(nonstd::span 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 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 mTickLock; // TODO does not need to recursive with some light restructuring - IdAllocator mHandleAllocator; - std::unordered_map> mHandlers; - - void Tick(s16Output* stream, int samples); - -#ifdef _WIN32 - bool m_coinitialized = false; -#endif - - Loader mLoader; - Synth mSynth; - VoiceManager mVmanager; - s32 mTick{0}; - - cubeb* mCtx{nullptr}; - cubeb_stream* mStream{nullptr}; - - static long sound_callback(cubeb_stream* stream, - void* user, - const void* input, - void* output_buffer, - long len); - static void state_callback(cubeb_stream* stream, void* user, cubeb_state state); -}; } // namespace snd diff --git a/game/sound/989snd/sfxblock.cpp b/game/sound/989snd/sfxblock.cpp index 765a2f09b1..053d95a089 100644 --- a/game/sound/989snd/sfxblock.cpp +++ b/game/sound/989snd/sfxblock.cpp @@ -7,18 +7,14 @@ namespace snd { -std::optional> SFXBlock::MakeHandler(VoiceManager& vm, - u32 sound_id, - s32 vol, - s32 pan, - SndPlayParams& params) { +SoundHandler* SFXBlock::MakeHandler(u32 sound_id, s32 vol, s32 pan, SndPlayParams& params) { auto& SFX = Sounds[sound_id]; if (SFX.Grains.empty()) { - return std::nullopt; + return nullptr; } - auto handler = std::make_unique(*this, SFX, vm, vol, pan, params); + auto handler = BlockSoundHandler::MakeBlockSound(*this, SFX, vol, pan, params); return handler; } diff --git a/game/sound/989snd/sfxblock.h b/game/sound/989snd/sfxblock.h index b1bc2dd6bf..3dba02d3f5 100644 --- a/game/sound/989snd/sfxblock.h +++ b/game/sound/989snd/sfxblock.h @@ -48,11 +48,7 @@ class SFXBlock : public SoundBank { 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; + SoundHandler* MakeHandler(u32 sound_id, s32 vol, s32 pan, SndPlayParams& params) override; std::optional GetName() override { return Name; }; std::optional GetSoundByName(const char* name) override; diff --git a/game/sound/989snd/sfxgrain.cpp b/game/sound/989snd/sfxgrain.cpp index 835e677ff5..e68b538e7f 100644 --- a/game/sound/989snd/sfxgrain.cpp +++ b/game/sound/989snd/sfxgrain.cpp @@ -69,10 +69,9 @@ s32 Grain::snd_SFX_GRAIN_TYPE_TONE(BlockSoundHandler& handler) { voice->g_vol = vol; voice->g_pan = pan; - voice->basevol = - handler.m_vm.MakeVolume(127, 0, handler.m_cur_volume, handler.m_cur_pan, vol, pan); + voice->basevol = MakeVolume(127, 0, handler.m_cur_volume, handler.m_cur_pan, vol, pan); - handler.m_vm.StartTone(voice); + StartTone(voice); handler.m_voices.emplace_front(voice); return 0; @@ -158,9 +157,9 @@ s32 Grain::snd_SFX_GRAIN_TYPE_STARTCHILDSOUND(BlockSoundHandler& handler) { s32 index = psp.sound_id; if (index >= 0) { - 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())); + auto child_handler = block.MakeHandler(index, vol, pan, params); + if (child_handler) { + handler.m_children.push_back(child_handler); } return 0; @@ -177,9 +176,10 @@ s32 Grain::snd_SFX_GRAIN_TYPE_STOPCHILDSOUND(BlockSoundHandler& handler) { if (psp.sound_id >= 0) { for (auto it = handler.m_children.begin(); it != handler.m_children.end();) { - auto* sound = static_cast(it->get()); + auto* sound = static_cast(*it); // TODO VERIFY that this works if (&sound->m_sfx == &block.Sounds[psp.sound_id]) { + FreeSound(sound); it = handler.m_children.erase(it); } else { ++it; diff --git a/game/sound/989snd/sndplay.cpp b/game/sound/989snd/sndplay.cpp index 6d3b38a3f9..bb8df0149c 100644 --- a/game/sound/989snd/sndplay.cpp +++ b/game/sound/989snd/sndplay.cpp @@ -14,14 +14,14 @@ #include "common/log/log.h" int main(int argc, char* argv[]) { - snd::Player player; + snd::StartSoundSystem(); fs::path file = argv[1]; auto file_buf = file_util::read_binary_file(file); - auto bankid = player.LoadBank(file_buf); + auto bankid = snd::LoadBank(file_buf); if (argc > 2) { - unsigned sound = player.PlaySound(bankid, atoi(argv[2]), 0x400, 0, 0, 0); + unsigned sound = snd::PlaySound(bankid, atoi(argv[2]), 0x400, 0, 0, 0); lg::info("sound {} started", sound); } @@ -46,20 +46,20 @@ int main(int argc, char* argv[]) { if (parts.size() < 2) { printf("invalid args\n"); } else { - auto id = player.PlaySound(bankid, std::atoi(parts[1].c_str()), 0x400, 0, 0, 0); + auto id = snd::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.PlaySound(bankid, idx, 0x400, 0, 0, 0); + auto id = snd::PlaySound(bankid, idx, 0x400, 0, 0, 0); while (true) { - if (player.SoundStillActive(id)) { + if (snd::SoundStillActive(id)) { sleep(1); } else { idx++; - id = player.PlaySound(bankid, idx, 0x400, 0, 0, 0); + id = snd::PlaySound(bankid, idx, 0x400, 0, 0, 0); } } } @@ -68,14 +68,14 @@ int main(int argc, char* argv[]) { if (parts.size() < 3) { printf("invalid args\n"); } else { - player.SetSoundReg(std::atoi(parts[1].c_str()), std::atoi(parts[2].c_str()), - std::atoi(parts[3].c_str())); + snd::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.StopAllSounds(); + snd::StopAllSounds(); } } diff --git a/game/sound/989snd/sound_handler.cpp b/game/sound/989snd/sound_handler.cpp new file mode 100644 index 0000000000..fb2ab50a06 --- /dev/null +++ b/game/sound/989snd/sound_handler.cpp @@ -0,0 +1,252 @@ +#include "sound_handler.h" + +#include "ame_handler.h" +#include "blocksound_handler.h" +#include "midi_handler.h" + +namespace snd { + +enum { + SOUND_BLOCK = 1, + SOUND_MIDI, + SOUND_AME, +}; + +static constexpr SoundHandle MakeHandle(u8 tag, u8 idx, u16 sequence) { + return ((tag & 0x1f) << 24) | (idx << 16) | sequence; +} + +static constexpr u8 HandleType(SoundHandle handle) { + return (handle >> 24) & 0x1f; +} + +static constexpr u8 HandleIndex(SoundHandle handle) { + return (handle >> 16) & 0xff; +} + +static constexpr u16 HandleSequence(SoundHandle handle) { + return handle & 0xffff; +} + +template +class HandlerPool { + public: + HandlerPool() { + int i = 0; + for (auto& h : mHandlers) { + h.ID = MakeHandle(tag, i, 0); + h.Free = true; + i++; + } + } + + T* GetPtrFromHandle(SoundHandle handle) { + auto idx = HandleIndex(handle); + if (idx >= size) { + return nullptr; + } + + auto& h = mHandlers[idx]; + if (h.Free == false && h.ID == handle) { + return reinterpret_cast(&h.hnd); + } + + return nullptr; + } + + template + T* AllocateHandler(Args&&... args) { + for (auto& h : mHandlers) { + if (h.Free) { + auto idx = HandleIndex(h.ID); + auto sq = HandleSequence(h.ID); + + h.ID = MakeHandle(tag, idx, sq + 1); + h.Free = false; + + auto p = new (&h.hnd) T(h.ID, args...); + + return p; + } + } + + return nullptr; + } + + void FreeHandler(SoundHandle handle) { + auto idx = HandleIndex(handle); + if (idx >= size) { + return; + } + + auto& h = mHandlers[idx]; + if (h.Free == false && h.ID == handle) { + auto* p = reinterpret_cast(&h.hnd); + std::destroy_at(p); + h.Free = true; + } + } + + T* Idx(std::size_t idx) { + if (idx >= size) { + return nullptr; + } + + auto& h = mHandlers[idx]; + if (h.Free == false) { + return reinterpret_cast(&h.hnd); + } + } + + private: + struct HandlerEntry { + SoundHandle ID{0}; + bool Free{true}; + typename std::aligned_storage::type hnd; + }; + + std::array mHandlers; +}; + +HandlerPool gBlockSounds; +HandlerPool gMidiSounds; +HandlerPool gAmeSounds; + +void SoundInit() { + // TODO reset? +} + +void StopAllHandlersForSound(SFXBlock::SFX& sfx) { + for (int i = 0; i < 64; i++) { + auto* s = gBlockSounds.Idx(i); + if (s == nullptr) { + continue; + } + + if (&s->m_sfx == &sfx) { + s->Stop(); + } + } +} + +bool CheckInstanceLimit(SFXBlock::SFX& sfx, s32 sfx_vol, BlockSoundHandler** weakest_out) { + s32 instances = 0; + BlockSoundHandler* weakest = nullptr; + + if (!sfx.InstanceLimit) { + return false; + } + + for (int i = 0; i < 64; i++) { + auto* s = gBlockSounds.Idx(i); + if (s == nullptr) { + continue; + } + + if (&s->m_sfx == &sfx) { + instances++; + if (!weakest) { + weakest = s; + } + + if (sfx.Flags.instlimit_vol() && s->m_app_volume < weakest->m_app_volume) { + weakest = s; + } + + if (sfx.Flags.instlimit_tick() && s->m_start_tick < weakest->m_start_tick) { + weakest = s; + } + } + } + + if (instances > sfx.InstanceLimit) { + if (!weakest) { + return false; + } + + if (sfx.Flags.instlimit_vol() && weakest->m_app_volume >= sfx_vol) { + return false; + } + + *weakest_out = weakest; + } + + return true; +} + +BlockSoundHandler* AllocBlockSound(SoundBank& bank, SFXBlock::SFX& sfx, s32 sfx_vol) { + BlockSoundHandler* weakest = nullptr; + + if (sfx.Flags.has_instlimit()) { + if (!CheckInstanceLimit(sfx, sfx_vol, &weakest)) { + return nullptr; + } + + if (weakest) { + weakest->Stop(); + } + } + + return gBlockSounds.AllocateHandler(bank, sfx); +} + +MidiHandler* AllocMidiSound(Midi* block, + MusicBank::MIDISound& sound, + s32 vol, + s32 pan, + SoundBank& bank) { + return gMidiSounds.AllocateHandler(block, sound, vol, pan, bank); +} + +AmeHandler* AllocAmeSound(MultiMidi* block, + MusicBank::MIDISound& sound, + s32 vol, + s32 pan, + SoundBank& bank) { + return gAmeSounds.AllocateHandler(block, sound, vol, pan, bank); +} + +void FreeSound(SoundHandler* handler) { + auto handle = handler->Handle(); + u8 type = HandleType(handle); + switch (type) { + case SOUND_BLOCK: + gBlockSounds.FreeHandler(handle); + break; + case SOUND_MIDI: + gMidiSounds.FreeHandler(handle); + break; + case SOUND_AME: + gAmeSounds.FreeHandler(handle); + break; + default: + lg::die("Unknown sound type (messed up sound handle) {:x}", handle); + break; + } +} + +SoundHandler* GetSound(SoundHandle handle) { + if (handle == 0) { + return nullptr; + } + + u8 type = HandleType(handle); + switch (type) { + case SOUND_BLOCK: + return gBlockSounds.GetPtrFromHandle(handle); + break; + case SOUND_MIDI: + return gMidiSounds.GetPtrFromHandle(handle); + break; + case SOUND_AME: + return gAmeSounds.GetPtrFromHandle(handle); + break; + default: + lg::die("Unknown sound type (messed up sound handle) {:x}", handle); + break; + } + + return nullptr; +} + +} // namespace snd diff --git a/game/sound/989snd/sound_handler.h b/game/sound/989snd/sound_handler.h index 9fd3b6f1dd..25e8b8eb70 100644 --- a/game/sound/989snd/sound_handler.h +++ b/game/sound/989snd/sound_handler.h @@ -2,6 +2,8 @@ // SPDX-License-Identifier: ISC #pragma once +#include "sfxblock.h" + #include "common/common_types.h" namespace snd { @@ -10,9 +12,11 @@ static constexpr int PAN_DONT_CHANGE = -2; static constexpr int VOLUME_DONT_CHANGE = 0x7fffffff; class SoundBank; +using SoundHandle = u32; class SoundHandler { public: + SoundHandler(SoundHandle OwnerID) : mOwnerId(OwnerID){}; virtual ~SoundHandler() = default; virtual bool Tick() = 0; virtual SoundBank& Bank() = 0; @@ -24,5 +28,14 @@ class SoundHandler { virtual void SetPMod(s32 mod) = 0; virtual void SetPBend(s32 /*mod*/){}; virtual void SetRegister(u8 /*reg*/, u8 /*value*/) {} + + SoundHandle Handle() { return mOwnerId; } + + SoundHandle mOwnerId; }; + +SoundHandler* GetSound(SoundHandle handle); +void FreeSound(SoundHandler* handler); +void StopAllHandlersForSound(SFXBlock::SFX& sfx); + } // namespace snd diff --git a/game/sound/989snd/soundbank.h b/game/sound/989snd/soundbank.h index e2eca4cf05..e0cf522c2a 100644 --- a/game/sound/989snd/soundbank.h +++ b/game/sound/989snd/soundbank.h @@ -2,7 +2,6 @@ #include #include -#include "sound_handler.h" #include "vagvoice.h" #include "common/common_types.h" @@ -32,7 +31,9 @@ enum class BankType { SFX, }; +class SoundHandler; struct SFXUserData; + class SoundBank { public: struct BlockFlags { @@ -53,26 +54,17 @@ class SoundBank { u32 BankID; s8 BankNum; - virtual std::optional> MakeHandler(VoiceManager& vm, - u32 sound_id, - s32 vol, - s32 pan, - s32 pm, - s32 pb) { + virtual SoundHandler* MakeHandler(u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb) { SndPlayParams params{}; params.vol = vol; params.pan = pan; params.pitch_mod = pm; params.pitch_bend = pb; - return MakeHandler(vm, sound_id, -1, -1, params); + return MakeHandler(sound_id, -1, -1, params); }; - virtual std::optional> MakeHandler(VoiceManager& vm, - u32 sound_id, - s32 vol, - s32 pan, - SndPlayParams& params) = 0; + virtual SoundHandler* MakeHandler(u32 sound_id, s32 vol, s32 pan, SndPlayParams& params) = 0; virtual std::optional GetName() { return std::nullopt; }; virtual std::optional GetSoundByName(const char* /*name*/) { return std::nullopt; }; diff --git a/game/sound/989snd/vagvoice.cpp b/game/sound/989snd/vagvoice.cpp index d6ca10e2b3..0e20817626 100644 --- a/game/sound/989snd/vagvoice.cpp +++ b/game/sound/989snd/vagvoice.cpp @@ -4,18 +4,44 @@ #include +#include "player.h" #include "util.h" #include "../common/voice.h" namespace snd { -VoiceManager::VoiceManager(Synth& synth) : mSynth(synth) { - mPanTable = normalPanTable; - mMasterVol.fill(0x400); - mGroupDuck.fill(0x10000); + +std::list> gVoices; +std::array gMasterVol; +std::array gGroupDuck; +const VolPair* gPanTable{nullptr}; +s32 gStereoOrMono{0}; + +// TODO remove +Synth* mSynth; + +void VoiceManagerInit(Synth& synth) { + mSynth = &synth; + gPanTable = normalPanTable; + gMasterVol.fill(0x400); + gGroupDuck.fill(0x10000); +} + +void SetPanTable(VolPair* table) { + std::scoped_lock lock(gTickLock); + gPanTable = table; +}; + +void CleanVoices() { + gVoices.remove_if([](auto& v) { return v.expired(); }); +} + +void SetPlaybackMode(s32 mode) { + std::scoped_lock lock(gTickLock); + gStereoOrMono = mode; } -void VoiceManager::StartTone(std::shared_ptr voice) { +void StartTone(std::shared_ptr voice) { s16 left = AdjustVolToGroup(voice->basevol.left, voice->group); s16 right = AdjustVolToGroup(voice->basevol.right, voice->group); @@ -40,10 +66,10 @@ void VoiceManager::StartTone(std::shared_ptr voice) { voice->KeyOn(); CleanVoices(); - mVoices.emplace_front(voice); - mSynth.AddVoice(voice); + gVoices.emplace_front(voice); + mSynth->AddVoice(voice); } -VolPair VoiceManager::MakeVolume(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) { // 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 @@ -56,7 +82,7 @@ VolPair VoiceManager::MakeVolume(int vol1, int pan1, int vol2, int pan2, int vol return {0, 0}; } - if (mStereoOrMono == 1) { + if (gStereoOrMono == 1) { return {(s16)vol, (s16)vol}; } @@ -84,11 +110,11 @@ VolPair VoiceManager::MakeVolume(int vol1, int pan1, int vol2, int pan2, int vol // it. (For surround audio positioning?) if (total_pan < 180) { - lvol = (mPanTable[total_pan].left * vol) / 0x3fff; - rvol = (mPanTable[total_pan].right * vol) / 0x3fff; + lvol = (gPanTable[total_pan].left * vol) / 0x3fff; + rvol = (gPanTable[total_pan].right * vol) / 0x3fff; } else { - rvol = (mPanTable[total_pan - 180].left * vol) / 0x3fff; - lvol = (mPanTable[total_pan - 180].right * vol) / 0x3fff; + rvol = (gPanTable[total_pan - 180].left * vol) / 0x3fff; + lvol = (gPanTable[total_pan - 180].right * vol) / 0x3fff; } // TODO rest of this function @@ -97,13 +123,13 @@ VolPair VoiceManager::MakeVolume(int vol1, int pan1, int vol2, int pan2, int vol return {lvol, rvol}; } -VolPair VoiceManager::MakeVolumeB(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) { // 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 @@ -117,7 +143,7 @@ VolPair VoiceManager::MakeVolumeB(int sound_vol, return {0, 0}; } - if (mStereoOrMono == 1) { + if (gStereoOrMono == 1) { return {(s16)vol, (s16)vol}; } @@ -145,11 +171,11 @@ VolPair VoiceManager::MakeVolumeB(int sound_vol, // it. (For surround audio positioning?) if (total_pan < 180) { - lvol = (mPanTable[total_pan].left * vol) / 0x3fff; - rvol = (mPanTable[total_pan].right * vol) / 0x3fff; + lvol = (gPanTable[total_pan].left * vol) / 0x3fff; + rvol = (gPanTable[total_pan].right * vol) / 0x3fff; } else { - rvol = (mPanTable[total_pan - 180].left * vol) / 0x3fff; - lvol = (mPanTable[total_pan - 180].right * vol) / 0x3fff; + rvol = (gPanTable[total_pan - 180].left * vol) / 0x3fff; + lvol = (gPanTable[total_pan - 180].right * vol) / 0x3fff; } // TODO rest of this function @@ -158,7 +184,7 @@ VolPair VoiceManager::MakeVolumeB(int sound_vol, return {lvol, rvol}; } -s16 VoiceManager::AdjustVolToGroup(s16 involume, int group) { +s16 AdjustVolToGroup(s16 involume, int group) { s32 volume = involume; // NOTE grou >= 7 in version 2 if (group >= 15) @@ -168,7 +194,7 @@ s16 VoiceManager::AdjustVolToGroup(s16 involume, int group) { volume = 0x7ffe; // NOTE no duckers in version 2 - s32 modifier = (mMasterVol[group] * mGroupDuck[group]) / 0x10000; + s32 modifier = (gMasterVol[group] * gGroupDuck[group]) / 0x10000; volume = (volume * modifier) / 0x400; int sign = 1; if (volume < 0) { @@ -180,10 +206,10 @@ s16 VoiceManager::AdjustVolToGroup(s16 involume, int group) { return retval; } -void VoiceManager::SetMasterVol(u8 group, s32 volume) { - mMasterVol[group] = volume; +void SetMasterVol(u8 group, s32 volume) { + gMasterVol[group] = volume; - for (auto& p : mVoices) { + for (auto& p : gVoices) { auto voice = p.lock(); if (voice == nullptr || voice->paused) { continue; @@ -198,13 +224,13 @@ void VoiceManager::SetMasterVol(u8 group, s32 volume) { } } -void VoiceManager::Pause(std::shared_ptr voice) { +void PauseTone(std::shared_ptr voice) { voice->SetVolume(0, 0); voice->SetPitch(0); voice->paused = true; } -void VoiceManager::Unpause(std::shared_ptr voice) { +void UnpauseTone(std::shared_ptr voice) { s16 left = AdjustVolToGroup(voice->basevol.left, voice->group); s16 right = AdjustVolToGroup(voice->basevol.right, voice->group); diff --git a/game/sound/989snd/vagvoice.h b/game/sound/989snd/vagvoice.h index 728ee90b1b..9841b2d05c 100644 --- a/game/sound/989snd/vagvoice.h +++ b/game/sound/989snd/vagvoice.h @@ -45,43 +45,26 @@ class VagVoice : public Voice { bool paused{false}; }; -class VoiceManager { - public: - 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; }; - - VolPair MakeVolume(int vol1, int pan1, int vol2, int pan2, int vol3, int pan3); - - // This is super silly, but it's what 989snd does - VolPair MakeVolumeB(int sound_vol, - int velocity_volume, - int pan, - int prog_vol, - int prog_pan, - int tone_vol, - int tone_pan); - - void SetMasterVol(u8 group, s32 volume); - void SetPlaybackMode(s32 mode) { mStereoOrMono = mode; } - s16 AdjustVolToGroup(s16 involume, int group); - - private: - Synth& mSynth; - - std::list> mVoices; - void CleanVoices() { - mVoices.remove_if([](auto& v) { return v.expired(); }); - } - - s32 mStereoOrMono{0}; - - std::array mMasterVol; - std::array mGroupDuck; - - const VolPair* mPanTable{nullptr}; -}; +void VoiceManagerInit(Synth& synth); +void StartTone(std::shared_ptr voice); +void PauseTone(std::shared_ptr voice); +void UnpauseTone(std::shared_ptr voice); +void SetPanTable(VolPair* table); + +VolPair MakeVolume(int vol1, int pan1, int vol2, int pan2, int vol3, int pan3); + +// This is super silly, but it's what 989snd does +VolPair MakeVolumeB(int sound_vol, + int velocity_volume, + int pan, + int prog_vol, + int prog_pan, + int tone_vol, + int tone_pan); + +void SetMasterVol(u8 group, s32 volume); +void SetPlaybackMode(s32 mode); +s16 AdjustVolToGroup(s16 involume, int group); +void CleanVoices(); } // namespace snd diff --git a/game/sound/CMakeLists.txt b/game/sound/CMakeLists.txt index 4804d844df..4abc1a940b 100644 --- a/game/sound/CMakeLists.txt +++ b/game/sound/CMakeLists.txt @@ -5,6 +5,7 @@ set(SOUND_SOURCES 989snd/midi_handler.cpp 989snd/ame_handler.cpp 989snd/blocksound_handler.cpp + 989snd/sound_handler.cpp 989snd/musicbank.cpp 989snd/sfxblock.cpp 989snd/sfxgrain.cpp diff --git a/game/sound/sndshim.cpp b/game/sound/sndshim.cpp index f9d3ef2af4..18c12a93f7 100644 --- a/game/sound/sndshim.cpp +++ b/game/sound/sndshim.cpp @@ -9,22 +9,18 @@ #include "989snd/player.h" -std::unique_ptr player; - void snd_StartSoundSystem() { - player = std::make_unique(); + snd::StartSoundSystem(); for (auto& voice : voices) { voice = std::make_shared(snd::Voice::AllocationType::Permanent); voice->SetSample((u16*)spu_memory); - player->SubmitVoice(voice); + snd::SubmitVoice(voice); } } void snd_StopSoundSystem() { - if (player) { - player.reset(); - } + snd::StopSoundSystem(); } // dma is always instant, allocation not required @@ -35,11 +31,7 @@ s32 snd_GetFreeSPUDMA() { void snd_FreeSPUDMA([[maybe_unused]] s32 channel) {} s32 snd_GetTick() { - if (player) { - return player->GetTick(); - } else { - return 0; - } + return snd::GetTick(); } void snd_RegisterIOPMemAllocator(AllocFun, FreeFun) { @@ -78,49 +70,31 @@ void snd_SetReverbDepth(s32 core, s32 left, s32 right) {} void snd_SetReverbType(s32 core, s32 type) {} void snd_SetPanTable(s16* table) { - if (player) { - player->SetPanTable((snd::VolPair*)table); - } + snd::SetPanTable((snd::VolPair*)table); } void snd_SetPlayBackMode(s32 mode) { - if (player) { - player->SetPlaybackMode(mode); - } + snd::SetPlaybackMode(mode); } s32 snd_SoundIsStillPlaying(s32 sound_handle) { - if (player) { - if (player->SoundStillActive(sound_handle)) { - return sound_handle; - } - } - - return 0; + return snd::SoundStillActive(sound_handle); } void snd_StopSound(s32 sound_handle) { - if (player) { - player->StopSound(sound_handle); - } + snd::StopSound(sound_handle); } void snd_SetSoundVolPan(s32 sound_handle, s32 vol, s32 pan) { - if (player) { - player->SetSoundVolPan(sound_handle, vol, pan); - } + snd::SetSoundVolPan(sound_handle, vol, pan); } void snd_SetMasterVolume(s32 which, s32 volume) { - if (player) { - player->SetMasterVolume(which, volume); - } + snd::SetMasterVolume(which, volume); } void snd_UnloadBank(snd::BankHandle bank_handle) { - if (player) { - player->UnloadBank(bank_handle); - } + snd::UnloadBank(bank_handle); } void snd_ResolveBankXREFS() { @@ -128,21 +102,15 @@ void snd_ResolveBankXREFS() { } void snd_ContinueAllSoundsInGroup(u8 groups) { - if (player) { - player->ContinueAllSoundsInGroup(groups); - } + snd::ContinueAllSoundsInGroup(groups); } void snd_PauseAllSoundsInGroup(u8 groups) { - if (player) { - player->PauseAllSoundsInGroup(groups); - } + snd::PauseAllSoundsInGroup(groups); } void snd_SetMIDIRegister(s32 sound_handle, u8 reg, u8 value) { - if (player) { - player->SetSoundReg(sound_handle, reg, value); - } + snd::SetSoundReg(sound_handle, reg, value); } s32 snd_PlaySoundVolPanPMPB(snd::BankHandle bank, @@ -151,11 +119,7 @@ s32 snd_PlaySoundVolPanPMPB(snd::BankHandle bank, s32 pan, s32 pitch_mod, s32 pitch_bend) { - if (player) { - return player->PlaySound(bank, sound, vol, pan, pitch_mod, pitch_bend); - } else { - return 0; - } + return snd::PlaySound(bank, sound, vol, pan, pitch_mod, pitch_bend); } s32 snd_PlaySoundByNameVolPanPMPB(snd::BankHandle bank_handle, @@ -165,36 +129,23 @@ s32 snd_PlaySoundByNameVolPanPMPB(snd::BankHandle bank_handle, s32 pan, s32 pitch_mod, s32 pitch_bend) { - if (player) { - return player->PlaySoundByName(bank_handle, bank_name, sound_name, vol, pan, pitch_mod, - pitch_bend); - } else { - return 0; - } + return snd::PlaySoundByName(bank_handle, bank_name, sound_name, vol, pan, pitch_mod, pitch_bend); } void snd_SetSoundPitchModifier(s32 sound_handle, s32 pitch_mod) { - if (player) { - player->SetSoundPmod(sound_handle, pitch_mod); - } + snd::SetSoundPmod(sound_handle, pitch_mod); } void snd_SetSoundPitchBend(s32 sound_handle, s32 bend) { - // TODO - if (bend != 0) { - } + // lg::warn("unimplemented snd_SetSoundPitchBend({:x}, {})", sound_handle, bend); } void snd_PauseSound(s32 sound_handle) { - if (player) { - player->PauseSound(sound_handle); - } + snd::PauseSound(sound_handle); } void snd_ContinueSound(s32 sound_handle) { - if (player) { - player->ContinueSound(sound_handle); - } + snd::ContinueSound(sound_handle); } void snd_AutoPitch(s32 sound_handle, s32 pitch, s32 delta_time, s32 delta_from) { @@ -210,14 +161,8 @@ snd::BankHandle snd_BankLoadEx(const char* filename, s32 offset, u32 spu_mem_loc, u32 spu_mem_size) { - // printf("snd_BankLoadEx\n"); - if (player) { - // 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; - } + auto file_buf = file_util::read_binary_file(std::string(filename)); + return snd::LoadBank(nonstd::span(file_buf).subspan(offset)); } s32 snd_GetVoiceStatus(s32 voice) { @@ -246,21 +191,14 @@ s32 snd_GetSoundUserData(snd::BankHandle block_handle, s32 sound_id, char* sound_name, SFXUserData* dst) { - if (player) { - return player->GetSoundUserData(block_handle, block_name, sound_id, sound_name, - (snd::SFXUserData*)dst); - } - return 0; + return snd::GetSoundUserData(block_handle, block_name, sound_id, sound_name, + (snd::SFXUserData*)dst); } void snd_SetSoundReg(s32 sound_handle, s32 which, u8 val) { - if (player) { - player->SetSoundReg(sound_handle, which, val); - } + snd::SetSoundReg(sound_handle, which, val); } void snd_SetGlobalExcite(u8 value) { - if (player) { - player->SetGlobalExcite(value); - } + snd::SetGlobalExcite(value); }