amuse/lib/Emitter.cpp

123 lines
3.6 KiB
C++

#include "amuse/Emitter.hpp"
#include "amuse/Engine.hpp"
#include "amuse/Listener.hpp"
#include "amuse/Voice.hpp"
namespace amuse {
static constexpr Vector3f Delta(const Vector3f& a, const Vector3f& b) {
return {a[0] - b[0], a[1] - b[1], a[2] - b[2]};
}
Emitter::~Emitter() = default;
Emitter::Emitter(Engine& engine, const AudioGroup& group, ObjToken<Voice> vox, float maxDist, float minVol,
float falloff, bool doppler)
: Entity(engine, group, vox->getGroupId(), vox->getObjectId())
, m_vox(vox)
, m_maxDist(maxDist)
, m_minVol(std::clamp(minVol, 0.f, 1.f))
, m_falloff(std::clamp(falloff, -1.f, 1.f))
, m_doppler(doppler) {}
void Emitter::_destroy() {
Entity::_destroy();
m_vox->kill();
}
float Emitter::_attenuationCurve(float dist) const {
if (dist > m_maxDist)
return 0.f;
float t = dist / m_maxDist;
if (m_falloff >= 0.f) {
return 1.f - (m_falloff * t * t + (1.f - m_falloff) * t);
} else {
float omt = 1.f - t;
return 1.f - ((1.f + m_falloff) * t - (1.f - omt * omt) * m_falloff);
}
}
void Emitter::_update() {
if (!m_dirty) {
/* Ensure that all listeners are also not dirty */
bool dirty = false;
for (auto& listener : m_engine.m_activeListeners) {
if (listener->m_dirty) {
dirty = true;
break;
}
}
if (!dirty)
return;
}
std::array<float, 8> coefs{};
double avgDopplerRatio = 0.0;
for (auto& listener : m_engine.m_activeListeners) {
const Vector3f listenerToEmitter = Delta(m_pos, listener->m_pos);
const float dist = Length(listenerToEmitter);
const float panDist = Dot(listenerToEmitter, listener->m_right);
const float frontPan = std::clamp(panDist / listener->m_frontDiff, -1.f, 1.f);
const float backPan = std::clamp(panDist / listener->m_backDiff, -1.f, 1.f);
const float spanDist = -Dot(listenerToEmitter, listener->m_heading);
const float span =
std::clamp(spanDist > 0.f ? spanDist / listener->m_backDiff : spanDist / listener->m_frontDiff, -1.f, 1.f);
/* Calculate attenuation */
float att = _attenuationCurve(dist);
att = (m_maxVol - m_minVol) * att + m_minVol;
att = m_attCache.getVolume(att, false);
if (att > FLT_EPSILON) {
/* Apply pan law */
const std::array<float, 8> thisCoefs = m_vox->_panLaw(frontPan, backPan, span);
/* Take maximum coefficient across listeners */
for (size_t i = 0; i < coefs.size(); ++i) {
coefs[i] = std::max(coefs[i], thisCoefs[i] * att * listener->m_volume);
}
}
/* Calculate doppler */
if (m_doppler) {
/* Positive values indicate emitter and listener closing in */
const Vector3f dirDelta = Delta(m_dir, listener->m_dir);
const Vector3f posDelta = Normalize(Delta(listener->m_pos, m_pos));
const float deltaSpeed = Dot(dirDelta, posDelta);
if (listener->m_soundSpeed != 0.f) {
avgDopplerRatio += 1.0 + deltaSpeed / listener->m_soundSpeed;
} else {
avgDopplerRatio += 1.0;
}
}
}
if (m_engine.m_activeListeners.size() != 0) {
m_vox->setChannelCoefs(coefs);
if (m_doppler) {
m_vox->m_dopplerRatio = avgDopplerRatio / float(m_engine.m_activeListeners.size());
m_vox->m_pitchDirty = true;
}
}
m_dirty = false;
}
void Emitter::setVectors(const float* pos, const float* dir) {
for (size_t i = 0; i < m_pos.size(); ++i) {
if (!std::isnan(pos[i])) {
m_pos[i] = pos[i];
} else {
m_pos[i] = 0.f;
}
if (!std::isnan(dir[i])) {
m_dir[i] = dir[i];
} else {
m_dir[i] = 0.f;
}
}
m_dirty = true;
}
} // namespace amuse