mirror of
https://github.com/open-goal/jak-project
synced 2026-08-21 14:54:53 -04:00
[jak3] Add BRANCH grain, make handler IDs unique (#3950)
Two changes that fix some Jak 3 audio bugs. In some areas, sound effects would seem to cut off randomly. This was caused by the sound code reusing IDs, but overlord didn't realize. It would try to kill a sound effect by ID that had already died, and the ID was reassigned to a new sound. To fix this, I made the handle IDs always increment. I also tested starting the ID at large numbers and confirmed that worked. I also added support for the BRANCH grain. This makes the `wastelander-shot` and possibly other sound effects work. It doesn't support all the features, but this is probably enough. Fun fact: the wastelander shot is supposed to have 7 variations, but you always get the same one because they set the sound mask wrong. I added a line you can uncomment if you want to experience the other 6 variations. --------- Co-authored-by: water111 <awaterford1111445@gmail.com>
This commit is contained in:
@@ -19,7 +19,8 @@ BlockSoundHandler::BlockSoundHandler(SoundBank& bank,
|
||||
u32 sound_id,
|
||||
s32 start_tick)
|
||||
: m_group(sfx.VolGroup),
|
||||
m_sfx(sfx),
|
||||
m_sfx(&sfx),
|
||||
m_orig_sfx(&sfx),
|
||||
m_vm(vm),
|
||||
m_bank(bank),
|
||||
m_sound_id(sound_id),
|
||||
@@ -98,7 +99,7 @@ BlockSoundHandler::BlockSoundHandler(SoundBank& bank,
|
||||
// }
|
||||
|
||||
m_next_grain = 0;
|
||||
m_countdown = m_sfx.Grains[0].Delay;
|
||||
m_countdown = m_sfx->Grains[0].Delay;
|
||||
while (m_countdown <= 0 && !m_done) {
|
||||
DoGrain();
|
||||
}
|
||||
@@ -209,7 +210,7 @@ void BlockSoundHandler::SetVolPan(s32 vol, s32 pan) {
|
||||
}
|
||||
|
||||
if (pan == PAN_RESET) {
|
||||
m_app_pan = m_sfx.Pan;
|
||||
m_app_pan = m_sfx->Pan;
|
||||
} else if (pan != PAN_DONT_CHANGE) {
|
||||
m_app_pan = pan;
|
||||
}
|
||||
@@ -283,7 +284,7 @@ void BlockSoundHandler::SetPBend(s32 bend) {
|
||||
}
|
||||
|
||||
void BlockSoundHandler::DoGrain() {
|
||||
auto& grain = m_sfx.Grains[m_next_grain];
|
||||
auto& grain = m_sfx->Grains[m_next_grain];
|
||||
|
||||
s32 ret = grain(*this);
|
||||
|
||||
@@ -296,22 +297,23 @@ void BlockSoundHandler::DoGrain() {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
SoundHandler* BlockSoundHandler::CheckInstanceLimit(
|
||||
const std::map<u32, std::unique_ptr<SoundHandler>>& handlers,
|
||||
s32 vol) {
|
||||
if (!m_sfx.InstanceLimit) {
|
||||
s32 vol,
|
||||
bool parent) {
|
||||
if (!m_sfx->InstanceLimit) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!m_sfx.Flags.has_instlimit()) {
|
||||
if (!m_sfx->Flags.has_instlimit()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -326,22 +328,20 @@ SoundHandler* BlockSoundHandler::CheckInstanceLimit(
|
||||
}
|
||||
|
||||
// See if this is playing the same sound
|
||||
// 989snd checks both an orig_sound and a SH.Sound, but we never change the sound.
|
||||
// We'd need to revisit this if we eventually support BRANCH grains.
|
||||
if (&handler->m_sfx == &m_sfx) {
|
||||
if ((parent && handler->m_orig_sfx == m_sfx) || handler->m_sfx == m_sfx) {
|
||||
inst++;
|
||||
if (!weakest || //
|
||||
(m_sfx.Flags.instlimit_vol() && handler->m_app_volume < weakest->m_app_volume) || //
|
||||
(m_sfx.Flags.instlimit_tick() && handler->m_start_tick < weakest->m_start_tick)) {
|
||||
if (!weakest || //
|
||||
(m_sfx->Flags.instlimit_vol() && handler->m_app_volume < weakest->m_app_volume) || //
|
||||
(m_sfx->Flags.instlimit_tick() && handler->m_start_tick < weakest->m_start_tick)) {
|
||||
weakest = handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See if this handler would cause us to exceed the limit
|
||||
if (m_sfx.InstanceLimit - 1 < inst) {
|
||||
if (weakest && ((m_sfx.Flags.instlimit_vol() && weakest->m_app_volume < vol) ||
|
||||
m_sfx.Flags.instlimit_tick())) {
|
||||
if (m_sfx->InstanceLimit - 1 < inst) {
|
||||
if (weakest && ((m_sfx->Flags.instlimit_vol() && weakest->m_app_volume < vol) ||
|
||||
m_sfx->Flags.instlimit_tick())) {
|
||||
// existing weakest is worst
|
||||
return weakest;
|
||||
} else {
|
||||
|
||||
@@ -48,7 +48,8 @@ class BlockSoundHandler : public SoundHandler {
|
||||
void UpdatePitch();
|
||||
|
||||
SoundHandler* CheckInstanceLimit(const std::map<u32, std::unique_ptr<SoundHandler>>& handlers,
|
||||
s32 vol) override;
|
||||
s32 vol,
|
||||
bool parent) override;
|
||||
|
||||
bool m_paused{false};
|
||||
|
||||
@@ -59,7 +60,8 @@ class BlockSoundHandler : public SoundHandler {
|
||||
u32 m_grains_to_skip{0};
|
||||
bool m_skip_grains{false};
|
||||
|
||||
SFXBlock::SFX& m_sfx;
|
||||
SFXBlock::SFX* m_sfx = nullptr;
|
||||
SFXBlock::SFX* m_orig_sfx = nullptr;
|
||||
VoiceManager& m_vm;
|
||||
|
||||
std::list<std::weak_ptr<BlockSoundVoice>> m_voices;
|
||||
|
||||
@@ -2,31 +2,19 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace snd {
|
||||
|
||||
class IdAllocator {
|
||||
public:
|
||||
u32 GetId() {
|
||||
u32 id = 0;
|
||||
if (mFreeIds.empty()) {
|
||||
id = mNextId++;
|
||||
} else {
|
||||
id = mFreeIds.front();
|
||||
mFreeIds.pop();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void FreeId(u32 id) { mFreeIds.push(id); }
|
||||
// Use a unique ID each time - the Overlord will sometimes do the wrong thing if handles are
|
||||
// reused. The real 989snd also includes type + handle index in this.
|
||||
u32 GetId() { return mNextId++; }
|
||||
void FreeId(u32) {}
|
||||
|
||||
private:
|
||||
u32 mNextId{1};
|
||||
std::queue<u32> mFreeIds;
|
||||
};
|
||||
|
||||
} // namespace snd
|
||||
|
||||
@@ -19,7 +19,7 @@ void LFOTracker::Init() {
|
||||
|
||||
void LFOTracker::CalcDepth() {
|
||||
if (m_target == LFOTarget::VOLUME) {
|
||||
m_range = (m_handler.m_sfx.Vol * m_depth) >> 10;
|
||||
m_range = (m_handler.m_sfx->Vol * m_depth) >> 10;
|
||||
}
|
||||
if (m_target == LFOTarget::PAN) {
|
||||
m_range = (180 * m_depth) >> 10;
|
||||
|
||||
@@ -142,7 +142,7 @@ u32 Player::PlaySound(BankHandle bank_id, u32 sound_id, s32 vol, s32 pan, s32 pm
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto handler_to_stop = handler.value()->CheckInstanceLimit(mHandlers, vol);
|
||||
auto handler_to_stop = handler.value()->CheckInstanceLimit(mHandlers, vol, true);
|
||||
if (handler_to_stop) {
|
||||
handler_to_stop->Stop();
|
||||
if (handler_to_stop == handler.value().get()) {
|
||||
|
||||
@@ -180,7 +180,7 @@ s32 Grain::snd_SFX_GRAIN_TYPE_STOPCHILDSOUND(BlockSoundHandler& handler) {
|
||||
for (auto it = handler.m_children.begin(); it != handler.m_children.end();) {
|
||||
auto* sound = static_cast<BlockSoundHandler*>(it->get());
|
||||
// TODO VERIFY that this works
|
||||
if (&sound->m_sfx == &block.Sounds[psp.sound_id]) {
|
||||
if (sound->m_sfx == &block.Sounds[psp.sound_id]) {
|
||||
it = handler.m_children.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
@@ -201,6 +201,46 @@ s32 Grain::snd_SFX_GRAIN_TYPE_PLUGIN_MESSAGE(BlockSoundHandler& handler) {
|
||||
}
|
||||
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_BRANCH(BlockSoundHandler& handler) {
|
||||
auto psp = std::get<PlaySoundParams>(data);
|
||||
s32 index = psp.sound_id;
|
||||
if (index <= -1) {
|
||||
lg::error("Unsupported BRANCH sound not specified by ID");
|
||||
// unsure exactly how this case worked
|
||||
return 0;
|
||||
}
|
||||
|
||||
// kill voices
|
||||
for (auto& p : handler.m_voices) {
|
||||
auto v = p.lock();
|
||||
if (v == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
v->KeyOff();
|
||||
v->SetVolumeL(0);
|
||||
v->SetVolumeR(0);
|
||||
}
|
||||
|
||||
auto& block = static_cast<SFXBlock&>(handler.m_bank);
|
||||
auto* new_sfx = &block.Sounds[index];
|
||||
if (new_sfx->Flags.has_instlimit()) {
|
||||
// TODO: not entirely straightforward.
|
||||
}
|
||||
|
||||
handler.m_sfx = new_sfx;
|
||||
handler.m_orig_volume = new_sfx->Vol;
|
||||
handler.m_orig_pan = new_sfx->Pan;
|
||||
handler.m_cur_volume = (handler.m_app_volume * new_sfx->Vol) >> 10;
|
||||
handler.m_group = new_sfx->VolGroup;
|
||||
handler.m_countdown = new_sfx->Grains[0].Delay;
|
||||
handler.m_next_grain = -1;
|
||||
handler.m_skip_grains = false;
|
||||
handler.m_grains_to_play = 0;
|
||||
handler.m_grains_to_skip = 0;
|
||||
// the ID case only supports BRANCHing to sfx in the same block
|
||||
// (this would need to be a pointer, not a reference)
|
||||
// handler.m_bank = block;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -219,7 +259,7 @@ s32 Grain::snd_SFX_GRAIN_TYPE_LOOP_START(BlockSoundHandler& handler) {
|
||||
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 == GrainType::LOOP_START) {
|
||||
if (handler.m_sfx->Grains[i].Type == GrainType::LOOP_START) {
|
||||
handler.m_next_grain = i - 1;
|
||||
found = true;
|
||||
}
|
||||
@@ -234,8 +274,8 @@ s32 Grain::snd_SFX_GRAIN_TYPE_LOOP_END(BlockSoundHandler& 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 == GrainType::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;
|
||||
}
|
||||
@@ -416,9 +456,9 @@ s32 Grain::snd_SFX_GRAIN_TYPE_GOTO_MARKER(BlockSoundHandler& handler) {
|
||||
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 == GrainType::MARKER) {
|
||||
auto mcp = std::get<ControlParams>(handler.m_sfx.Grains.at(i).data);
|
||||
for (int i = 0; i < (int)handler.m_sfx->Grains.size() && !found; i++) {
|
||||
if (handler.m_sfx->Grains.at(i).Type == GrainType::MARKER) {
|
||||
auto mcp = std::get<ControlParams>(handler.m_sfx->Grains.at(i).data);
|
||||
auto mark = mcp.param[0];
|
||||
|
||||
if (mark == target_mark) {
|
||||
@@ -444,9 +484,9 @@ s32 Grain::snd_SFX_GRAIN_TYPE_GOTO_RANDOM_MARKER(BlockSoundHandler& handler) {
|
||||
s32 range = upper_bound - lower_bound + 1;
|
||||
s32 target_mark = (rand() % range) + lower_bound;
|
||||
|
||||
for (int i = 0; i < (int)handler.m_sfx.Grains.size() && !found; i++) {
|
||||
if (handler.m_sfx.Grains.at(i).Type == GrainType::MARKER) {
|
||||
auto mcp = std::get<ControlParams>(handler.m_sfx.Grains.at(i).data);
|
||||
for (int i = 0; i < (int)handler.m_sfx->Grains.size() && !found; i++) {
|
||||
if (handler.m_sfx->Grains.at(i).Type == GrainType::MARKER) {
|
||||
auto mcp = std::get<ControlParams>(handler.m_sfx->Grains.at(i).data);
|
||||
auto mark = mcp.param[0];
|
||||
|
||||
if (mark == target_mark) {
|
||||
@@ -534,7 +574,7 @@ s32 Grain::snd_SFX_GRAIN_TYPE_KILL_VOICES(BlockSoundHandler& handler) {
|
||||
}
|
||||
|
||||
s32 Grain::snd_SFX_GRAIN_TYPE_ON_STOP_MARKER(BlockSoundHandler& handler) {
|
||||
handler.m_next_grain = handler.m_sfx.Grains.size() - 1;
|
||||
handler.m_next_grain = handler.m_sfx->Grains.size() - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ class SoundHandler {
|
||||
// should be removed.
|
||||
virtual SoundHandler* CheckInstanceLimit(
|
||||
const std::map<u32, std::unique_ptr<SoundHandler>>& handlers,
|
||||
s32 vol) {
|
||||
s32 vol,
|
||||
bool parent) {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -532,6 +532,8 @@
|
||||
(set! (-> s4-1 volume) 1024)
|
||||
(set! (-> s4-1 pitch-mod) 0)
|
||||
(set! (-> s4-1 reg 0) (the-as uint (-> this shot-sound)))
|
||||
;; uncomment the following line to enable the wastelander shot variations.
|
||||
;; (logior! (-> s4-1 mask) (sound-mask reg0))
|
||||
(s5-2 s4-1 (new-sound-id) (-> this root trans))
|
||||
)
|
||||
0
|
||||
|
||||
Reference in New Issue
Block a user