Files
combattest/SoundSystem.cpp
T
2026-11-08 07:45:01 -05:00

192 lines
6.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "SoundSystem.h"
#include <algorithm>
#include <iostream>
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
void SoundSystem::CheckError(FMOD_RESULT result, const char* context)
{
if (result != FMOD_OK) {
std::cout << "FMOD error [" << context << "]: "
<< FMOD_ErrorString(result) << "\n";
}
}
FMOD_VECTOR SoundSystem::ToFMOD(const glm::vec3& v)
{
return { v.x, v.y, v.z };
}
// ---------------------------------------------------------------------------
// Construction / destruction
// ---------------------------------------------------------------------------
SoundSystem::SoundSystem()
{
FMOD_RESULT result;
result = FMOD::System_Create(&system);
CheckError(result, "System_Create");
// 512 max channels – raise if you need more simultaneous voices.
result = system->init(512, FMOD_INIT_NORMAL | FMOD_INIT_3D_RIGHTHANDED, nullptr);
CheckError(result, "system->init");
// One unit is one meter. Rolloff is per sound, see SetDistances.
system->set3DSettings(1.0f, // doppler scale
1.0f, // distance factor (metres per unit)
1.0f); // rolloff scale, 1 is real world falloff between a sound's min and max distance
// Default listener orientation (matches XAudio2 original).
SetListenerPosition({ 0.0f, 0.0f, 0.0f },
{ 0.0f, 0.0f, 1.0f },
{ 0.0f, 1.0f, 0.0f });
}
SoundSystem::~SoundSystem()
{
for (auto& [key, sd] : cache) {
if (sd && sd->sound)
sd->sound->release();
delete sd;
}
cache.clear();
if (system) {
system->close();
system->release();
}
}
// ---------------------------------------------------------------------------
// Asset loading
// ---------------------------------------------------------------------------
SoundData* SoundSystem::LoadSound(const std::string& filename, bool is3D)
{
// Return cached version if already loaded.
auto it = cache.find(filename);
if (it != cache.end()) return it->second;
FMOD_MODE mode = FMOD_DEFAULT | FMOD_CREATECOMPRESSEDSAMPLE;
// Inverse falloff like real sound, tapering to silence at max distance so far off sounds don't linger at a murmur
if (is3D) mode |= FMOD_3D | FMOD_3D_INVERSETAPEREDROLLOFF;
else mode |= FMOD_2D;
SoundData* sd = new SoundData();
sd->is3D = is3D;
FMOD_RESULT result = system->createSound(filename.c_str(), mode, nullptr, &sd->sound);
CheckError(result, "createSound");
if (result != FMOD_OK) {
delete sd;
return nullptr;
}
if (is3D)
sd->sound->set3DMinMaxDistance(DEFAULT_MIN_DISTANCE, DEFAULT_MAX_DISTANCE);
cache[filename] = sd;
return sd;
}
void SoundSystem::SetDistances(SoundData* data, float minDistance, float maxDistance)
{
if (!data || !data->sound || !data->is3D) return;
minDistance = std::max(minDistance, 0.01f);
CheckError(data->sound->set3DMinMaxDistance(minDistance, std::max(maxDistance, minDistance + 0.01f)), "set3DMinMaxDistance");
}
// ---------------------------------------------------------------------------
// Playback
// ---------------------------------------------------------------------------
// Looping set on the channel, so one play of a sound looping doesn't make every other play of it loop.
// Keeps the rest of the sound's mode, like being 3D and how it rolls off.
static void SetChannelLooping(FMOD::Sound* sound, FMOD::Channel* channel, bool loop)
{
FMOD_MODE mode = 0;
sound->getMode(&mode);
mode &= ~(FMOD_LOOP_OFF | FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI);
mode |= loop ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF;
channel->setMode(mode);
channel->setLoopCount(loop ? -1 : 0);
}
FMOD::Channel* SoundSystem::PlaySound(SoundData* data, bool loop)
{
if (!data || !data->sound) return nullptr;
FMOD::Channel* channel = nullptr;
FMOD_RESULT result = system->playSound(data->sound, nullptr, true, &channel);
CheckError(result, "playSound");
if (result != FMOD_OK || !channel) return nullptr;
SetChannelLooping(data->sound, channel, loop);
channel->setPaused(false);
return channel;
}
FMOD::Channel* SoundSystem::PlaySound3D(SoundData* data,
const glm::vec3& position,
bool loop)
{
if (!data || !data->sound) return nullptr;
// Start paused so we can set position before the first audio callback.
FMOD::Channel* channel = nullptr;
FMOD_RESULT result = system->playSound(data->sound, nullptr, true, &channel);
CheckError(result, "playSound3D");
if (result != FMOD_OK || !channel) return nullptr;
FMOD_VECTOR pos = ToFMOD(position);
FMOD_VECTOR vel = { 0.0f, 0.0f, 0.0f };
channel->set3DAttributes(&pos, &vel);
SetChannelLooping(data->sound, channel, loop);
channel->setPaused(false);
return channel;
}
// ---------------------------------------------------------------------------
// Listener
// ---------------------------------------------------------------------------
void SoundSystem::SetListenerPosition(const glm::vec3& position,
const glm::vec3& forward,
const glm::vec3& up,
const glm::vec3& velocity)
{
FMOD_VECTOR pos = ToFMOD(position);
FMOD_VECTOR vel = ToFMOD(velocity);
FMOD_VECTOR fwd = ToFMOD(forward);
FMOD_VECTOR up_ = ToFMOD(up);
system->set3DListenerAttributes(0, &pos, &vel, &fwd, &up_);
}
// ---------------------------------------------------------------------------
// Per-channel 3D update
// ---------------------------------------------------------------------------
void SoundSystem::SetChannelPosition(FMOD::Channel* channel,
const glm::vec3& position,
const glm::vec3& velocity)
{
if (!channel) return;
FMOD_VECTOR pos = ToFMOD(position);
FMOD_VECTOR vel = ToFMOD(velocity);
channel->set3DAttributes(&pos, &vel);
}
// ---------------------------------------------------------------------------
// Frame update – must be called once per game loop tick
// ---------------------------------------------------------------------------
void SoundSystem::Update()
{
system->update();
}