[sound] Add instance limits

This commit is contained in:
water111 2024-11-17 11:04:51 -05:00
parent 7543acfb8a
commit 81df24660b
12 changed files with 146 additions and 33 deletions

View File

@ -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;
@ -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

View File

@ -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;
@ -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};
@ -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

View File

@ -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
@ -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;
}

View File

@ -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

View File

@ -137,11 +137,19 @@ 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);
@ -149,6 +157,16 @@ u32 Player::PlaySound(BankHandle bank_id, u32 sound_id, s32 vol, s32 pan, s32 pm
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,

View File

@ -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"
@ -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);
@ -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);

View File

@ -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;
}
@ -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

View File

@ -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

View File

@ -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()));
}
@ -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) {

View File

@ -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("> ");
@ -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;

View File

@ -2,6 +2,9 @@
// SPDX-License-Identifier: ISC
#pragma once
#include <map>
#include <memory>
#include "common/common_types.h"
namespace snd {
@ -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

View File

@ -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