Skip to content

Commit

Permalink
[sound] Add instance limits (#3769)
Browse files Browse the repository at this point in the history
Add instance limits to the sound library, and a `dump-info` command to
`sndplay` to print out all the sounds/flags in an SBK file.

This is based on the decompiled 989snd library @Ziemas is working on
(https://github.com/Ziemas/dec989snd/blob/main/src/sndhand.c#L77)

Co-authored-by: water111 <[email protected]>
  • Loading branch information
water111 and water111 authored Dec 7, 2024
1 parent 8690104 commit 1029516
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 33 deletions.
59 changes: 57 additions & 2 deletions game/sound/989snd/blocksound_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ BlockSoundHandler::BlockSoundHandler(SoundBank& bank,
s32 sfx_vol,
s32 sfx_pan,
SndPlayParams& params,
u32 sound_id)
: m_group(sfx.VolGroup), m_sfx(sfx), m_vm(vm), m_bank(bank), m_sound_id(sound_id) {
u32 sound_id,
s32 start_tick)
: m_group(sfx.VolGroup),
m_sfx(sfx),
m_vm(vm),
m_bank(bank),
m_sound_id(sound_id),
m_start_tick(start_tick) {
s32 vol, pan, pitch_mod, pitch_bend;
if (sfx_vol == -1) {
sfx_vol = sfx.Vol;
Expand Down Expand Up @@ -298,4 +304,53 @@ void BlockSoundHandler::DoGrain() {
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) {
return nullptr;
}

if (!m_sfx.Flags.has_instlimit()) {
return nullptr;
}

BlockSoundHandler* weakest = nullptr;
int inst = 0;

for (const auto& [id, handler_ptr] : handlers) {
// Only compare to BlockSoundHandlers
auto* handler = dynamic_cast<BlockSoundHandler*>(handler_ptr.get());
if (!handler) {
continue;
}

// 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) {
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)) {
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())) {
// existing weakest is worst
return weakest;
} else {
// new sound is weakest
return this;
}
} else {
return nullptr;
}
}

} // namespace snd
7 changes: 6 additions & 1 deletion game/sound/989snd/blocksound_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class BlockSoundHandler : public SoundHandler {
s32 sfx_vol,
s32 sfx_pan,
SndPlayParams& params,
u32 sound_id);
u32 sound_id,
s32 start_tick);

~BlockSoundHandler() override;
bool Tick() override;
Expand All @@ -46,6 +47,9 @@ class BlockSoundHandler : public SoundHandler {

void UpdatePitch();

SoundHandler* CheckInstanceLimit(const std::map<u32, std::unique_ptr<SoundHandler>>& handlers,
s32 vol) override;

bool m_paused{false};

u8 m_group{0};
Expand Down Expand Up @@ -90,5 +94,6 @@ class BlockSoundHandler : public SoundHandler {
u32 m_next_grain{0};

u32 m_sound_id{0};
s32 m_start_tick{0};
};
} // namespace snd
11 changes: 4 additions & 7 deletions game/sound/989snd/musicbank.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,8 @@

namespace snd {

std::optional<std::unique_ptr<SoundHandler>> MusicBank::MakeHandler(VoiceManager& vm,
u32 sound_id,
s32 vol,
s32 pan,
s32 pm,
s32 pb) {
std::optional<std::unique_ptr<SoundHandler>>
MusicBank::MakeHandler(VoiceManager& vm, u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb, s32 tick) {
auto& sound = Sounds[sound_id];

// FIXME: global midi list
Expand Down Expand Up @@ -42,7 +38,8 @@ std::optional<std::unique_ptr<SoundHandler>> MusicBank::MakeHandler(VoiceManager
u32 sound_id,
s32 vol,
s32 pan,
SndPlayParams& params) {
SndPlayParams& params,
s32 tick) {
return std::nullopt;
}

Expand Down
11 changes: 4 additions & 7 deletions game/sound/989snd/musicbank.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,14 @@ class MusicBank : public SoundBank {
std::span<u8> samples,
std::span<u8> midi_data);

std::optional<std::unique_ptr<SoundHandler>> MakeHandler(VoiceManager& vm,
u32 sound_id,
s32 vol,
s32 pan,
s32 pm,
s32 pb) override;
std::optional<std::unique_ptr<SoundHandler>>
MakeHandler(VoiceManager& vm, u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb, s32 tick) override;

std::optional<std::unique_ptr<SoundHandler>> MakeHandler(VoiceManager& vm,
u32 sound_id,
s32 vol,
s32 pan,
SndPlayParams& params) override;
SndPlayParams& params,
s32 tick) override;
};
} // namespace snd
20 changes: 19 additions & 1 deletion game/sound/989snd/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,18 +137,36 @@ u32 Player::PlaySound(BankHandle bank_id, u32 sound_id, s32 vol, s32 pan, s32 pm
return 0;
}

auto handler = bank->MakeHandler(mVmanager, sound_id, vol, pan, pm, pb);
auto handler = bank->MakeHandler(mVmanager, sound_id, vol, pan, pm, pb, GetTick());
if (!handler.has_value()) {
return 0;
}

auto handler_to_stop = handler.value()->CheckInstanceLimit(mHandlers, vol);
if (handler_to_stop) {
handler_to_stop->Stop();
if (handler_to_stop == handler.value().get()) {
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;
}

void Player::DebugPrintAllSoundsInBank(BankHandle bank_id) {
std::scoped_lock lock(mTickLock);
auto* bank = mLoader.GetBankByHandle(bank_id);
if (!bank) {
lg::error("DebugPrintAllSoundsInBank: invalid bank");
return;
}
bank->DebugPrintAllSounds();
}

u32 Player::PlaySoundByName(BankHandle bank_id,
char* bank_name,
char* sound_name,
Expand Down
5 changes: 3 additions & 2 deletions game/sound/989snd/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// SPDX-License-Identifier: ISC
#pragma once

#include <map>
#include <memory>
#include <mutex>
#include <span>
#include <unordered_map>
#include <vector>

#include "ame_handler.h"
Expand Down Expand Up @@ -42,6 +42,7 @@ class Player {
s32 pan,
s32 pm,
s32 pb);
void DebugPrintAllSoundsInBank(BankHandle bank);
void SetSoundReg(u32 sound_id, u8 reg, u8 value);
void SetGlobalExcite(u8 value) { GlobalExcite = value; };
bool SoundStillActive(u32 sound_id);
Expand Down Expand Up @@ -71,7 +72,7 @@ class Player {
private:
std::recursive_mutex mTickLock; // TODO does not need to recursive with some light restructuring
IdAllocator mHandleAllocator;
std::unordered_map<u32, std::unique_ptr<SoundHandler>> mHandlers;
std::map<u32, std::unique_ptr<SoundHandler>> mHandlers;

void Tick(s16Output* stream, int samples);

Expand Down
26 changes: 24 additions & 2 deletions game/sound/989snd/sfxblock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,24 @@

#include "common/log/log.h"

#include "third-party/magic_enum.hpp"

namespace snd {

std::optional<std::unique_ptr<SoundHandler>> SFXBlock::MakeHandler(VoiceManager& vm,
u32 sound_id,
s32 vol,
s32 pan,
SndPlayParams& params) {
SndPlayParams& params,
s32 current_tick) {
auto& SFX = Sounds[sound_id];

if (SFX.Grains.empty()) {
return std::nullopt;
}

auto handler = std::make_unique<BlockSoundHandler>(*this, SFX, vm, vol, pan, params, sound_id);
auto handler =
std::make_unique<BlockSoundHandler>(*this, SFX, vm, vol, pan, params, sound_id, current_tick);
return handler;
}

Expand All @@ -31,4 +35,22 @@ std::optional<u32> SFXBlock::GetSoundByName(const char* name) {
return std::nullopt;
}

void SFXBlock::DebugPrintAllSounds() {
for (const auto& [name, id] : Names) {
printf("%s : %d\n", name.c_str(), id);
const auto& sound = Sounds.at(id);
printf(" Vol: %d\n", sound.Vol);
printf(" VolGroup: %d\n", sound.VolGroup);
printf(" Pan: %d\n", sound.Pan);
printf(" InstanceLimit: %d\n", sound.InstanceLimit);
printf(" Flags: 0x%x\n", sound.Flags.flags);
printf(" User: 0x%x 0x%x 0x%x 0x%x\n", sound.UserData.data[0], sound.UserData.data[1],
sound.UserData.data[2], sound.UserData.data[3]);
printf(" Grains\n");
for (const auto& grain : sound.Grains) {
fmt::print(" {} ({})\n", magic_enum::enum_name(grain.Type), (int)grain.Type);
}
}
}

} // namespace snd
4 changes: 3 additions & 1 deletion game/sound/989snd/sfxblock.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,15 @@ class SFXBlock : public SoundBank {
u32 sound_id,
s32 vol,
s32 pan,
SndPlayParams& params) override;
SndPlayParams& params,
s32 current_tick) override;

std::optional<std::string_view> GetName() override { return Name; };
std::optional<u32> GetSoundByName(const char* name) override;
std::optional<const SFXUserData*> GetSoundUserData(u32 sound_id) override {
return &Sounds.at(sound_id).UserData;
};
void DebugPrintAllSounds() override;
};

} // namespace snd
5 changes: 3 additions & 2 deletions game/sound/989snd/sfxgrain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ 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);
auto child_handler =
block.MakeHandler(handler.m_vm, index, vol, pan, params, handler.m_start_tick);
if (child_handler.has_value()) {
handler.m_children.emplace_front(std::move(child_handler.value()));
}
Expand Down Expand Up @@ -257,7 +258,7 @@ s32 Grain::snd_SFX_GRAIN_TYPE_RAND_PLAY(BlockSoundHandler& handler) {
auto cp = std::get<ControlParams>(data);
auto options = cp.param[0];
auto count = cp.param[1];
auto previous = cp.param[2];
auto& previous = cp.param[2];

int rnd = rand() % options;
if (rnd == previous) {
Expand Down
5 changes: 5 additions & 0 deletions game/sound/989snd/sndplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ int main(int argc, char* argv[]) {
printf("commands:\n");
printf(" play [id]\n");
printf(" stop\n");
printf(" dump-info\n");

while (true) {
printf("> ");
Expand Down Expand Up @@ -77,6 +78,10 @@ int main(int argc, char* argv[]) {
printf("stopping all sounds\n");
player.StopAllSounds();
}

if (parts[0] == "dump-info") {
player.DebugPrintAllSoundsInBank(bankid);
}
}

return 0;
Expand Down
11 changes: 11 additions & 0 deletions game/sound/989snd/sound_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// SPDX-License-Identifier: ISC
#pragma once

#include <map>
#include <memory>

#include "common/common_types.h"

namespace snd {
Expand All @@ -25,5 +28,13 @@ class SoundHandler {
virtual void SetPBend(s32 /*mod*/){};
virtual void SetRegister(u8 /*reg*/, u8 /*value*/) {}
virtual u32 SoundID() const { return -1; }

// Check if this handler violates an instance limit. If so, return pointer to the sound that
// should be removed.
virtual SoundHandler* CheckInstanceLimit(
const std::map<u32, std::unique_ptr<SoundHandler>>& handlers,
s32 vol) {
return nullptr;
}
};
} // namespace snd
15 changes: 7 additions & 8 deletions game/sound/989snd/soundbank.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,32 +54,31 @@ class SoundBank {
u32 BankID;
s8 BankNum;

virtual std::optional<std::unique_ptr<SoundHandler>> MakeHandler(VoiceManager& vm,
u32 sound_id,
s32 vol,
s32 pan,
s32 pm,
s32 pb) {
virtual std::optional<std::unique_ptr<SoundHandler>>
MakeHandler(VoiceManager& vm, u32 sound_id, s32 vol, s32 pan, s32 pm, s32 pb, s32 current_tick) {
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(vm, sound_id, -1, -1, params, current_tick);
};

virtual std::optional<std::unique_ptr<SoundHandler>> MakeHandler(VoiceManager& vm,
u32 sound_id,
s32 vol,
s32 pan,
SndPlayParams& params) = 0;
SndPlayParams& params,
s32 current_tick) = 0;

virtual std::optional<std::string_view> GetName() { return std::nullopt; };
virtual std::optional<u32> GetSoundByName(const char* /*name*/) { return std::nullopt; };
virtual std::optional<const SFXUserData*> GetSoundUserData(u32 /*sound_id*/) {
return std::nullopt;
};

virtual void DebugPrintAllSounds() {}
};

} // namespace snd

0 comments on commit 1029516

Please sign in to comment.