jak-project/game/sound/989snd/player.cpp
water111 e81431bd21
Some checks are pending
Build / 🖥️ Windows (push) Waiting to run
Build / 🐧 Linux (push) Waiting to run
Build / 🍎 MacOS (push) Waiting to run
Inform Pages Repo / Generate Documentation (push) Waiting to run
Lint / 📝 Required Checks (push) Waiting to run
Lint / 📝 Optional Checks (push) Waiting to run
Lint / 📝 Formatting (push) Waiting to run
[wip] Jak 3 Overlord (#3567)
2024-07-26 09:42:28 -04:00

384 lines
9.1 KiB
C++

// Copyright: 2021 - 2024, Ziemas
// SPDX-License-Identifier: ISC
#include "player.h"
#include <fstream>
#include "sfxblock.h"
#include "fmt/core.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <combaseapi.h>
#include <windows.h>
#endif
#include "common/log/log.h"
namespace snd {
u8 g_global_excite = 0;
Player::Player() : mVmanager(mSynth) {
InitCubeb();
}
Player::~Player() {
DestroyCubeb();
}
void Player::InitCubeb() {
#ifdef _WIN32
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
m_coinitialized = SUCCEEDED(hr);
if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) {
lg::error("Couldn't initialize COM");
lg::error("Cubeb init failed");
return;
}
#endif
cubeb_init(&mCtx, "OpenGOAL", nullptr);
cubeb_stream_params outparam = {};
outparam.channels = 2;
outparam.format = CUBEB_SAMPLE_S16LE;
outparam.rate = 48000;
outparam.layout = CUBEB_LAYOUT_STEREO;
outparam.prefs = CUBEB_STREAM_PREF_NONE;
s32 err = 0;
u32 latency = 0;
err = cubeb_get_min_latency(mCtx, &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);
if (err != CUBEB_OK) {
lg::error("Cubeb init failed");
return;
}
err = cubeb_stream_start(mStream);
if (err != CUBEB_OK) {
lg::error("Cubeb init failed");
return;
}
}
void Player::DestroyCubeb() {
cubeb_stream_stop(mStream);
cubeb_stream_destroy(mStream);
cubeb_destroy(mCtx);
#ifdef _WIN32
if (m_coinitialized) {
CoUninitialize();
m_coinitialized = false;
}
#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 Player::state_callback([[maybe_unused]] cubeb_stream* stream,
[[maybe_unused]] void* user,
[[maybe_unused]] cubeb_state state) {}
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) {
mTick++;
for (auto it = mHandlers.begin(); it != mHandlers.end();) {
bool done = it->second->Tick();
if (done) {
// fmt::print("erasing handler\n");
mHandleAllocator.FreeId(it->first);
it = mHandlers.erase(it);
} else {
++it;
}
}
htick = 0;
}
if (stick == 48000) {
// fmt::print("{} handlers active\n", m_handlers.size());
stick = 0;
}
stick++;
htick++;
*stream++ = mSynth.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);
if (bank == nullptr) {
lg::error("play_sound: Bank {} does not exist", static_cast<void*>(bank_id));
return 0;
}
auto handler = bank->MakeHandler(mVmanager, sound_id, vol, pan, pm, pb);
if (!handler.has_value()) {
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;
}
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);
SoundBank* bank = nullptr;
if (bank_id == 0 && bank_name != nullptr) {
bank = mLoader.GetBankByName(bank_name);
} else if (bank_id != 0) {
bank = mLoader.GetBankByHandle(bank_id);
} else {
bank = mLoader.GetBankWithSound(sound_name);
}
if (bank == nullptr) {
// lg::error("play_sound_by_name: failed to find bank for sound {}", sound_name);
return 0;
}
auto sound = bank->GetSoundByName(sound_name);
if (sound.has_value()) {
return PlaySound(bank, sound.value(), vol, pan, pm, pb);
}
// lg::error("play_sound_by_name: failed to find sound {}", sound_name);
return 0;
}
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();
// m_handle_allocator.free_id(sound_id);
// m_handlers.erase(sound_id);
}
u32 Player::GetSoundID(u32 sound_handle) {
std::scoped_lock lock(mTickLock);
auto handler = mHandlers.find(sound_handle);
if (handler == mHandlers.end())
return -1;
return handler->second->SoundID();
}
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 = mHandlers.at(sound_id).get();
handler->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;
// fmt::print("sound_still_active {}\n", sound_id);
return true;
}
void Player::SetMasterVolume(u32 group, s32 volume) {
std::scoped_lock lock(mTickLock);
if (volume > 0x400)
volume = 0x400;
if (volume < 0)
volume = 0;
if (group == 15)
return;
mVmanager.SetMasterVol(group, volume);
// Master volume
if (group == 16) {
mSynth.SetMasterVol(0x3ffff * volume / 0x400);
}
}
BankHandle Player::LoadBank(std::span<u8> bank) {
std::scoped_lock lock(mTickLock);
return mLoader.BankLoad(bank);
}
void Player::UnloadBank(BankHandle bank_handle) {
std::scoped_lock lock(mTickLock);
auto* bank = mLoader.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);
} 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);
}
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();
}
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();
}
void Player::PauseAllSoundsInGroup(u8 group) {
std::scoped_lock lock(mTickLock);
for (auto& h : mHandlers) {
if ((1 << h.second->Group()) & group) {
h.second->Pause();
}
}
}
void Player::ContinueAllSoundsInGroup(u8 group) {
std::scoped_lock lock(mTickLock);
for (auto& h : mHandlers) {
if ((1 << h.second->Group()) & group) {
h.second->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())
return;
handler->second->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())
return;
handler->second->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);
}
}
s32 Player::GetSoundUserData(BankHandle block_handle,
char* block_name,
s32 sound_id,
char* sound_name,
SFXUserData* dst) {
std::scoped_lock lock(mTickLock);
SoundBank* bank = nullptr;
if (block_handle == nullptr && block_name != nullptr) {
bank = mLoader.GetBankByName(block_name);
} else if (block_handle != nullptr) {
bank = mLoader.GetBankByHandle(block_handle);
} else {
bank = mLoader.GetBankWithSound(sound_name);
}
if (bank == nullptr) {
return 0;
}
if (sound_id == -1) {
auto sound = bank->GetSoundByName(sound_name);
if (sound.has_value()) {
sound_id = sound.value();
} else {
return 0;
}
}
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];
return 1;
} else {
return 0;
}
return 0;
}
} // namespace snd