readme
This commit is contained in:
+192
@@ -0,0 +1,192 @@
|
||||
#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();
|
||||
}
|
||||
Reference in New Issue
Block a user