#include "amuse/Emitter.hpp" #include "amuse/Listener.hpp" #include "amuse/Voice.hpp" #include "amuse/Engine.hpp" namespace amuse { static void Delta(Vector3f& out, const Vector3f& a, const Vector3f& b) { out[0] = a[0] - b[0]; out[1] = a[1] - b[1]; out[2] = a[2] - b[2]; } Emitter::~Emitter() {} Emitter::Emitter(Engine& engine, const AudioGroup& group, ObjToken vox, float maxDist, float minVol, float falloff, bool doppler) : Entity(engine, group, vox->getGroupId(), vox->getObjectId()), m_vox(vox), m_maxDist(maxDist), m_minVol(clamp(0.f, minVol, 1.f)), m_falloff(clamp(-1.f, falloff, 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) { float tmp = t * 10.f + 1.f; tmp = 1.f / (tmp * tmp); return (1.f + m_falloff) * (-t + 1.f) + -m_falloff * tmp; } else if (m_falloff > 0.f) { float tmp = (t - 1.f) * 10.f - 1.f; tmp = -1.f / (tmp * tmp) + 1.f; return (1.f - m_falloff) * (-t + 1.f) + m_falloff * tmp; } else { return -t + 1.f; } } 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; } float coefs[8] = {}; double avgDopplerRatio = 0.0; for (auto& listener : m_engine.m_activeListeners) { Vector3f listenerToEmitter; Delta(listenerToEmitter, m_pos, listener->m_pos); float dist = Length(listenerToEmitter); float panDist = Dot(listenerToEmitter, listener->m_right); float frontPan = clamp(-1.f, panDist / listener->m_frontDiff, 1.f); float backPan = clamp(-1.f, panDist / listener->m_backDiff, 1.f); float spanDist = -Dot(listenerToEmitter, listener->m_heading); float span = clamp(-1.f, spanDist > 0.f ? spanDist / listener->m_backDiff : spanDist / listener->m_frontDiff, 1.f); /* Calculate attenuation */ float att = _attenuationCurve(dist); if (att > FLT_EPSILON) { att = (1.f - att) * m_minVol + att * m_maxVol; /* Apply pan law */ float thisCoefs[8] = {}; m_vox->_panLaw(thisCoefs, frontPan, backPan, span); /* Take maximum coefficient across listeners */ for (int i = 0; i < 8; ++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 */ Vector3f dirDelta; Delta(dirDelta, m_dir, listener->m_dir); Vector3f posDelta; Delta(posDelta, listener->m_pos, m_pos); Normalize(posDelta); 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 (int i=0 ; i<3 ; ++i) { m_pos[i] = pos[i]; m_dir[i] = dir[i]; } m_dirty = true; } }