mirror of https://github.com/AxioDL/amuse.git
Implement listener, emitter, and -3dB pan law
This commit is contained in:
parent
aef2b2a707
commit
c6781df90a
|
@ -37,7 +37,6 @@ set(SOURCES
|
|||
lib/EffectReverb.cpp
|
||||
lib/EffectChorus.cpp
|
||||
lib/EffectDelay.cpp
|
||||
lib/SurroundProfiles.cpp
|
||||
lib/ContainerRegistry.cpp
|
||||
lib/DSPCodec.c
|
||||
lib/N64MusyXCodec.c)
|
||||
|
@ -68,7 +67,6 @@ set(HEADERS
|
|||
include/amuse/EffectReverb.hpp
|
||||
include/amuse/EffectChorus.hpp
|
||||
include/amuse/EffectDelay.hpp
|
||||
include/amuse/SurroundProfiles.hpp
|
||||
include/amuse/ContainerRegistry.hpp
|
||||
include/amuse/Common.hpp
|
||||
include/amuse/amuse.hpp
|
||||
|
|
|
@ -2,32 +2,51 @@
|
|||
#define __AMUSE_EMITTER_HPP__
|
||||
|
||||
#include "Entity.hpp"
|
||||
#include "Common.hpp"
|
||||
#include <memory>
|
||||
#include <cmath>
|
||||
#include <cfloat>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
class Voice;
|
||||
class Listener;
|
||||
|
||||
using Vector3f = float[3];
|
||||
|
||||
static float Dot(const Vector3f& a, const Vector3f& b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; }
|
||||
|
||||
static float Length(const Vector3f& a)
|
||||
{
|
||||
if (std::fabs(a[0]) <= FLT_EPSILON && std::fabs(a[1]) <= FLT_EPSILON && std::fabs(a[2]) <= FLT_EPSILON)
|
||||
return 0.f;
|
||||
return std::sqrt(Dot(a, a));
|
||||
}
|
||||
|
||||
/** Voice wrapper with positional-3D level control */
|
||||
class Emitter : public Entity
|
||||
{
|
||||
std::shared_ptr<Voice> m_vox;
|
||||
Vector3f m_pos = {};
|
||||
Vector3f m_dir = {};
|
||||
float m_maxDist;
|
||||
float m_maxVol = 1.f;
|
||||
float m_minVol;
|
||||
float m_falloff;
|
||||
bool m_doppler;
|
||||
|
||||
friend class Engine;
|
||||
void _destroy();
|
||||
float _attenuationCurve(float dist) const;
|
||||
void _update();
|
||||
|
||||
public:
|
||||
~Emitter();
|
||||
Emitter(Engine& engine, const AudioGroup& group, std::shared_ptr<Voice>&& vox);
|
||||
Emitter(Engine& engine, const AudioGroup& group, std::shared_ptr<Voice>&& vox,
|
||||
float maxDist, float minVol, float falloff, bool doppler);
|
||||
|
||||
void setPos(const float* pos);
|
||||
void setDir(const float* dir);
|
||||
void setMaxDist(float maxDist);
|
||||
void setMaxVol(float maxVol);
|
||||
void setMinVol(float minVol);
|
||||
void setFalloff(float falloff);
|
||||
void setVectors(const float* pos, const float* dir);
|
||||
void setMaxVol(float maxVol) { m_maxVol = clamp(0.f, maxVol, 1.f); }
|
||||
|
||||
std::shared_ptr<Voice>& getVoice() { return m_vox; }
|
||||
};
|
||||
|
|
|
@ -7,9 +7,11 @@
|
|||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include "Emitter.hpp"
|
||||
#include "Listener.hpp"
|
||||
#include "AudioGroupSampleDirectory.hpp"
|
||||
#include "Sequencer.hpp"
|
||||
#include "Studio.hpp"
|
||||
#include "IBackendVoiceAllocator.hpp"
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
@ -42,6 +44,7 @@ class Engine
|
|||
std::unordered_map<const AudioGroupData*, std::unique_ptr<AudioGroup>> m_audioGroups;
|
||||
std::list<std::shared_ptr<Voice>> m_activeVoices;
|
||||
std::list<std::shared_ptr<Emitter>> m_activeEmitters;
|
||||
std::list<std::shared_ptr<Listener>> m_activeListeners;
|
||||
std::list<std::shared_ptr<Sequencer>> m_activeSequencers;
|
||||
std::list<std::weak_ptr<Studio>> m_activeStudios; /* lifetime dependent on contributing audio entities */
|
||||
bool m_defaultStudioReady = false;
|
||||
|
@ -50,6 +53,7 @@ class Engine
|
|||
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
|
||||
int m_nextVid = 0;
|
||||
float m_masterVolume = 1.f;
|
||||
AudioChannelSet m_channelSet = AudioChannelSet::Unknown;
|
||||
|
||||
AudioGroup* _addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp);
|
||||
std::pair<AudioGroup*, const SongGroupIndex*> _findSongGroup(int groupId) const;
|
||||
|
@ -94,13 +98,21 @@ public:
|
|||
|
||||
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
||||
std::shared_ptr<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
|
||||
int sfxId, float minVol, float maxVol, std::weak_ptr<Studio> smx);
|
||||
int sfxId, float minVol, float maxVol, bool doppler,
|
||||
std::weak_ptr<Studio> smx);
|
||||
std::shared_ptr<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
|
||||
int sfxId, float minVol, float maxVol)
|
||||
int sfxId, float minVol, float maxVol, bool doppler)
|
||||
{
|
||||
return addEmitter(pos, dir, maxDist, falloff, sfxId, minVol, maxVol, m_defaultStudio);
|
||||
return addEmitter(pos, dir, maxDist, falloff, sfxId, minVol, maxVol, doppler, m_defaultStudio);
|
||||
}
|
||||
|
||||
/** Build listener and add to engine's listener list */
|
||||
std::shared_ptr<Listener> addListener(const float* pos, const float* dir, const float* heading, const float* up,
|
||||
float frontDiff, float backDiff, float soundSpeed, float volume);
|
||||
|
||||
/** Remove listener from engine's listener list */
|
||||
void removeListener(Listener* listener);
|
||||
|
||||
/** Start song playing from loaded audio groups */
|
||||
std::shared_ptr<Sequencer> seqPlay(int groupId, int songId, const unsigned char* arrData,
|
||||
std::weak_ptr<Studio> smx);
|
||||
|
|
|
@ -1,13 +1,27 @@
|
|||
#ifndef __AMUSE_LISTENER_HPP__
|
||||
#define __AMUSE_LISTENER_HPP__
|
||||
|
||||
#include "Entity.hpp"
|
||||
#include "amuse/Emitter.hpp"
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
class Listener : public Entity
|
||||
class Listener
|
||||
{
|
||||
friend class Emitter;
|
||||
Vector3f m_pos = {};
|
||||
Vector3f m_dir = {};
|
||||
Vector3f m_heading = {};
|
||||
Vector3f m_up = {};
|
||||
Vector3f m_right = {};
|
||||
float m_volume;
|
||||
float m_frontDiff;
|
||||
float m_backDiff;
|
||||
float m_soundSpeed;
|
||||
public:
|
||||
Listener(float volume, float frontDiff, float backDiff, float soundSpeed)
|
||||
: m_volume(clamp(0.f, volume, 1.f)), m_frontDiff(frontDiff), m_backDiff(backDiff), m_soundSpeed(soundSpeed) {}
|
||||
void setVectors(const float* pos, const float* dir, const float* heading, const float* up);
|
||||
void setVolume(float vol) { m_volume = clamp(0.f, vol, 1.f); }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
#ifndef __AMUSE_SURROUNDPROFILES_HPP__
|
||||
#define __AMUSE_SURROUNDPROFILES_HPP__
|
||||
|
||||
#include "IBackendVoice.hpp"
|
||||
#include "IBackendVoiceAllocator.hpp"
|
||||
#include "Emitter.hpp"
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
struct ReferenceVector;
|
||||
|
||||
/** Support class for attenuating channel audio based on speaker 'positions' */
|
||||
class SurroundProfiles
|
||||
{
|
||||
static void SetupRefs(float matOut[8], const ChannelMap& map, const Vector3f& listenEmit,
|
||||
const ReferenceVector refs[]);
|
||||
|
||||
public:
|
||||
static void SetupMatrix(float matOut[8], const ChannelMap& map, AudioChannelSet set, const Vector3f& emitPos,
|
||||
const Vector3f& listenPos, const Vector3f& listenDir, const Vector3f& listenUp);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __AMUSE_SURROUNDPROFILES_HPP__
|
|
@ -33,6 +33,7 @@ class Voice : public Entity
|
|||
friend class Sequencer;
|
||||
friend class SoundMacroState;
|
||||
friend class Envelope;
|
||||
friend class Emitter;
|
||||
int m_vid; /**< VoiceID of this voice instance */
|
||||
bool m_emitter; /**< Voice is part of an Emitter */
|
||||
std::shared_ptr<Studio> m_studio; /**< Studio this voice outputs to */
|
||||
|
@ -62,6 +63,7 @@ class Voice : public Entity
|
|||
uint32_t m_lastSamplePos = 0; /**< Last sample position (or last loop sample) */
|
||||
int16_t m_prev1 = 0; /**< DSPADPCM prev sample */
|
||||
int16_t m_prev2 = 0; /**< DSPADPCM prev-prev sample */
|
||||
double m_dopplerRatio = 1.0; /**< Current ratio to mix with chromatic pitch for doppler effects */
|
||||
double m_sampleRate = NativeSampleRate; /**< Current sample rate computed from relative sample key or SETPITCH */
|
||||
double m_voiceTime = 0.0; /**< Current seconds of voice playback (per-sample resolution) */
|
||||
uint64_t m_voiceSamples = 0; /**< Count of samples processed over voice's lifetime */
|
||||
|
@ -166,8 +168,10 @@ class Voice : public Entity
|
|||
std::shared_ptr<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey,
|
||||
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
|
||||
|
||||
void _panLaw(float coefsOut[8], float frontPan, float backPan, float totalSpan) const;
|
||||
void _setPan(float pan);
|
||||
void _setSurroundPan(float span);
|
||||
void _setChannelCoefs(const float coefs[8]);
|
||||
void _setPitchWheel(float pitchWheel);
|
||||
void _notifyCtrlChange(uint8_t ctrl, int8_t val);
|
||||
|
||||
|
@ -231,6 +235,9 @@ public:
|
|||
/** Set current voice surround-panning immediately */
|
||||
void setSurroundPan(float span);
|
||||
|
||||
/** Set current voice channel coefficients immediately */
|
||||
void setChannelCoefs(const float coefs[8]);
|
||||
|
||||
/** Start volume envelope to specified level */
|
||||
void startEnvelope(double dur, float vol, const Curve* envCurve);
|
||||
|
||||
|
|
108
lib/Emitter.cpp
108
lib/Emitter.cpp
|
@ -1,14 +1,24 @@
|
|||
#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, std::shared_ptr<Voice>&& vox)
|
||||
: Entity(engine, group, vox->getObjectId()), m_vox(std::move(vox))
|
||||
Emitter::Emitter(Engine& engine, const AudioGroup& group, std::shared_ptr<Voice>&& vox,
|
||||
float maxDist, float minVol, float falloff, bool doppler)
|
||||
: Entity(engine, group, vox->getObjectId()), m_vox(std::move(vox)), m_maxDist(maxDist),
|
||||
m_minVol(clamp(0.f, minVol, 1.f)), m_falloff(clamp(-1.f, falloff, 1.f)), m_doppler(doppler)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -18,15 +28,87 @@ void Emitter::_destroy()
|
|||
m_vox->kill();
|
||||
}
|
||||
|
||||
void Emitter::setPos(const float* pos) {}
|
||||
|
||||
void Emitter::setDir(const float* dir) {}
|
||||
|
||||
void Emitter::setMaxDist(float maxDist) {}
|
||||
|
||||
void Emitter::setMaxVol(float maxVol) {}
|
||||
|
||||
void Emitter::setMinVol(float minVol) {}
|
||||
|
||||
void Emitter::setFalloff(float falloff) {}
|
||||
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()
|
||||
{
|
||||
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);
|
||||
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, listener->m_dir, m_dir);
|
||||
float sign = -Dot(listener->m_dir, m_dir);
|
||||
if (listener->m_soundSpeed != 0.f)
|
||||
avgDopplerRatio += 1.0 + std::copysign(Length(dirDelta), sign) / 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());
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -174,10 +174,13 @@ void Engine::_bringOutYourDead()
|
|||
|
||||
void Engine::_on5MsInterval(IBackendVoiceAllocator& engine, double dt)
|
||||
{
|
||||
m_channelSet = engine.getAvailableSet();
|
||||
if (m_midiReader)
|
||||
m_midiReader->pumpReader(dt);
|
||||
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers)
|
||||
seq->advance(dt);
|
||||
for (std::shared_ptr<Emitter>& emitter : m_activeEmitters)
|
||||
emitter->_update();
|
||||
}
|
||||
|
||||
void Engine::_onPumpCycleComplete(IBackendVoiceAllocator& engine)
|
||||
|
@ -320,7 +323,8 @@ std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, std::wea
|
|||
|
||||
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
||||
std::shared_ptr<Emitter> Engine::addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
|
||||
int sfxId, float minVol, float maxVol, std::weak_ptr<Studio> smx)
|
||||
int sfxId, float minVol, float maxVol, bool doppler,
|
||||
std::weak_ptr<Studio> smx)
|
||||
{
|
||||
auto search = m_sfxLookup.find(sfxId);
|
||||
if (search == m_sfxLookup.end())
|
||||
|
@ -333,7 +337,8 @@ std::shared_ptr<Emitter> Engine::addEmitter(const float* pos, const float* dir,
|
|||
|
||||
std::list<std::shared_ptr<Voice>>::iterator vox =
|
||||
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, true, smx);
|
||||
auto emitIt = m_activeEmitters.emplace(m_activeEmitters.end(), new Emitter(*this, *grp, std::move(*vox)));
|
||||
auto emitIt = m_activeEmitters.emplace(m_activeEmitters.end(),
|
||||
new Emitter(*this, *grp, std::move(*vox), maxDist, minVol, falloff, doppler));
|
||||
Emitter& ret = *(*emitIt);
|
||||
|
||||
ObjectId oid = (grp->getDataFormat() == DataFormat::PC) ? entry->objId : SBig(entry->objId);
|
||||
|
@ -345,16 +350,36 @@ std::shared_ptr<Emitter> Engine::addEmitter(const float* pos, const float* dir,
|
|||
}
|
||||
|
||||
(*vox)->setPan(entry->panning);
|
||||
ret.setPos(pos);
|
||||
ret.setDir(dir);
|
||||
ret.setMaxDist(maxDist);
|
||||
ret.setFalloff(falloff);
|
||||
ret.setMinVol(minVol);
|
||||
ret.setVectors(pos, dir);
|
||||
ret.setMaxVol(maxVol);
|
||||
|
||||
return *emitIt;
|
||||
}
|
||||
|
||||
/** Build listener and add to engine's listener list */
|
||||
std::shared_ptr<Listener> Engine::addListener(const float* pos, const float* dir, const float* heading, const float* up,
|
||||
float frontDiff, float backDiff, float soundSpeed, float volume)
|
||||
{
|
||||
auto listenerIt = m_activeListeners.emplace(m_activeListeners.end(),
|
||||
new Listener(volume, frontDiff, backDiff, soundSpeed));
|
||||
Listener& ret = *(*listenerIt);
|
||||
ret.setVectors(pos, dir, heading, up);
|
||||
return *listenerIt;
|
||||
}
|
||||
|
||||
/** Remove listener from engine's listener list */
|
||||
void Engine::removeListener(Listener* listener)
|
||||
{
|
||||
for (auto it = m_activeListeners.begin() ; it != m_activeListeners.end() ; ++it)
|
||||
{
|
||||
if (it->get() == listener)
|
||||
{
|
||||
m_activeListeners.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Start song playing from loaded audio groups */
|
||||
std::shared_ptr<Sequencer> Engine::seqPlay(int groupId, int songId, const unsigned char* arrData,
|
||||
std::weak_ptr<Studio> smx)
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
#include "amuse/Listener.hpp"
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
static void Cross(Vector3f& out, const Vector3f& a, const Vector3f& b)
|
||||
{
|
||||
out[0] = a[1] * b[2] - a[2] * b[1];
|
||||
out[1] = a[2] * b[0] - a[0] * b[2];
|
||||
out[2] = a[0] * b[1] - a[1] * b[0];
|
||||
}
|
||||
|
||||
static float Normalize(Vector3f& out)
|
||||
{
|
||||
float dist = Length(out);
|
||||
if (dist == 0.f)
|
||||
return 0.f;
|
||||
out[0] /= dist;
|
||||
out[1] /= dist;
|
||||
out[2] /= dist;
|
||||
return dist;
|
||||
}
|
||||
|
||||
void Listener::setVectors(const float* pos, const float* dir, const float* heading, const float* up)
|
||||
{
|
||||
for (int i=0 ; i<3 ; ++i)
|
||||
{
|
||||
m_pos[i] = pos[i];
|
||||
m_dir[i] = dir[i];
|
||||
m_heading[i] = heading[i];
|
||||
m_up[i] = up[i];
|
||||
}
|
||||
Normalize(m_heading);
|
||||
Normalize(m_up);
|
||||
Cross(m_right, m_heading, m_up);
|
||||
Normalize(m_right);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,155 +0,0 @@
|
|||
#include "amuse/SurroundProfiles.hpp"
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <cfloat>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
static float Dot(const Vector3f& a, const Vector3f& b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; }
|
||||
|
||||
static float Length(const Vector3f& a)
|
||||
{
|
||||
if (std::fabs(a[0]) <= FLT_EPSILON && std::fabs(a[1]) <= FLT_EPSILON && std::fabs(a[2]) <= FLT_EPSILON)
|
||||
return 0.f;
|
||||
return std::sqrt(Dot(a, a));
|
||||
}
|
||||
|
||||
static float Normalize(Vector3f& out, const Vector3f& in)
|
||||
{
|
||||
out[0] = in[0];
|
||||
out[1] = in[1];
|
||||
out[2] = in[2];
|
||||
float dist = Length(out);
|
||||
out[0] /= dist;
|
||||
out[1] /= dist;
|
||||
out[2] /= dist;
|
||||
return dist;
|
||||
}
|
||||
|
||||
static void Cross(Vector3f& out, const Vector3f& a, const Vector3f& b)
|
||||
{
|
||||
out[0] = a[1] * b[2] - a[2] * b[1];
|
||||
out[1] = a[2] * b[0] - a[0] * b[2];
|
||||
out[2] = a[0] * b[1] - a[1] * b[0];
|
||||
}
|
||||
|
||||
class SimpleMatrix
|
||||
{
|
||||
Vector3f m_mat[3];
|
||||
|
||||
public:
|
||||
SimpleMatrix(const Vector3f& dir, const Vector3f& up)
|
||||
{
|
||||
Vector3f temp;
|
||||
Normalize(temp, dir);
|
||||
m_mat[0][1] = temp[0];
|
||||
m_mat[1][1] = temp[1];
|
||||
m_mat[2][1] = temp[2];
|
||||
|
||||
Normalize(temp, up);
|
||||
m_mat[0][2] = temp[0];
|
||||
m_mat[1][2] = temp[1];
|
||||
m_mat[2][2] = temp[2];
|
||||
|
||||
Cross(temp, dir, up);
|
||||
m_mat[0][0] = temp[0];
|
||||
m_mat[1][0] = temp[1];
|
||||
m_mat[2][0] = temp[2];
|
||||
}
|
||||
|
||||
void vecMult(Vector3f& out, const Vector3f& in)
|
||||
{
|
||||
out[0] = Dot(m_mat[0], in);
|
||||
out[1] = Dot(m_mat[1], in);
|
||||
out[2] = Dot(m_mat[2], in);
|
||||
}
|
||||
};
|
||||
|
||||
struct ReferenceVector
|
||||
{
|
||||
Vector3f vec;
|
||||
float bias;
|
||||
bool valid = false;
|
||||
ReferenceVector() = default;
|
||||
ReferenceVector(float x, float y, float z, float thres)
|
||||
{
|
||||
vec[0] = x;
|
||||
vec[1] = y;
|
||||
vec[2] = z;
|
||||
bias = thres;
|
||||
valid = true;
|
||||
}
|
||||
};
|
||||
|
||||
static const ReferenceVector StereoVectors[8] = {
|
||||
{-0.80901f, 0.58778f, 0.f, 0.3f}, {0.80901f, 0.58778f, 0.f, 0.3f},
|
||||
};
|
||||
|
||||
static const ReferenceVector QuadVectors[8] = {
|
||||
{-0.70710f, 0.70710f, 0.f, 0.1f},
|
||||
{0.70710f, 0.70710f, 0.f, 0.1f},
|
||||
{-0.70710f, -0.70710f, 0.f, 0.1f},
|
||||
{0.70710f, -0.70710f, 0.f, 0.1f},
|
||||
};
|
||||
|
||||
static const ReferenceVector Sur51Vectors[8] = {
|
||||
{-0.70710f, 0.70710f, 0.f, 0.1f}, {0.70710f, 0.70710f, 0.f, 0.1f}, {-0.70710f, -0.70710f, 0.f, 0.1f},
|
||||
{0.70710f, -0.70710f, 0.f, 0.1f}, {0.0f, 1.0f, 0.f, 0.1f}, {0.0f, 1.0f, 0.f, 1.0f},
|
||||
};
|
||||
|
||||
static const ReferenceVector Sur71Vectors[8] = {
|
||||
{-0.70710f, 0.70710f, 0.f, 0.1f}, {0.70710f, 0.70710f, 0.f, 0.1f}, {-0.70710f, -0.70710f, 0.f, 0.1f},
|
||||
{0.70710f, -0.70710f, 0.f, 0.1f}, {0.0f, 1.0f, 0.f, 0.1f}, {0.0f, 1.0f, 0.f, 1.0f},
|
||||
{-1.f, 0.0f, 0.f, 0.1f}, {1.f, 0.0f, 0.f, 0.1f},
|
||||
};
|
||||
|
||||
void SurroundProfiles::SetupRefs(float matOut[8], const ChannelMap& map, const Vector3f& listenEmit,
|
||||
const ReferenceVector refs[])
|
||||
{
|
||||
for (unsigned i = 0; i < map.m_channelCount && i < 8; ++i)
|
||||
{
|
||||
matOut[i] = 0.f;
|
||||
if (map.m_channels[i] == AudioChannel::Unknown)
|
||||
continue;
|
||||
const ReferenceVector& refVec = refs[int(map.m_channels[i])];
|
||||
if (!refVec.valid)
|
||||
continue;
|
||||
matOut[i] = std::max(1.f, Dot(listenEmit, refVec.vec) + refVec.bias);
|
||||
}
|
||||
}
|
||||
|
||||
void SurroundProfiles::SetupMatrix(float matOut[8], const ChannelMap& map, AudioChannelSet set, const Vector3f& emitPos,
|
||||
const Vector3f& listenPos, const Vector3f& listenHeading, const Vector3f& listenUp)
|
||||
{
|
||||
Vector3f listenDelta;
|
||||
listenDelta[0] = emitPos[0] - listenPos[0];
|
||||
listenDelta[1] = emitPos[1] - listenPos[1];
|
||||
listenDelta[2] = emitPos[2] - listenPos[2];
|
||||
|
||||
Vector3f listenNorm;
|
||||
float dist = Normalize(listenNorm, listenDelta);
|
||||
|
||||
SimpleMatrix listenerMat(listenHeading, listenUp);
|
||||
Vector3f listenEmit;
|
||||
listenerMat.vecMult(listenEmit, listenNorm);
|
||||
|
||||
/* Factor for each channel in set */
|
||||
switch (set)
|
||||
{
|
||||
case AudioChannelSet::Stereo:
|
||||
default:
|
||||
SetupRefs(matOut, map, listenEmit, StereoVectors);
|
||||
break;
|
||||
case AudioChannelSet::Quad:
|
||||
SetupRefs(matOut, map, listenEmit, QuadVectors);
|
||||
break;
|
||||
case AudioChannelSet::Surround51:
|
||||
SetupRefs(matOut, map, listenEmit, Sur51Vectors);
|
||||
break;
|
||||
case AudioChannelSet::Surround71:
|
||||
SetupRefs(matOut, map, listenEmit, Sur71Vectors);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
153
lib/Voice.cpp
153
lib/Voice.cpp
|
@ -117,7 +117,7 @@ void Voice::_setTotalPitch(int32_t cents, bool slew)
|
|||
{
|
||||
// fprintf(stderr, "PITCH %d %d \n", cents, slew);
|
||||
int32_t interval = cents - m_curSample->first.m_pitch * 100;
|
||||
double ratio = std::exp2(interval / 1200.0);
|
||||
double ratio = std::exp2(interval / 1200.0) * m_dopplerRatio;
|
||||
m_sampleRate = m_curSample->first.m_sampleRate * ratio;
|
||||
m_backendVoice->setPitchRatio(ratio, slew);
|
||||
}
|
||||
|
@ -947,46 +947,113 @@ void Voice::setVolume(float vol)
|
|||
vox->setVolume(vol);
|
||||
}
|
||||
|
||||
void Voice::_panLaw(float coefs[8], float frontPan, float backPan, float totalSpan) const
|
||||
{
|
||||
/* -3dB panning law for various channel configs */
|
||||
switch (m_engine.m_channelSet)
|
||||
{
|
||||
case AudioChannelSet::Stereo:
|
||||
default:
|
||||
/* Left */
|
||||
coefs[0] = -frontPan * 0.5f + 0.5f;
|
||||
|
||||
/* Right */
|
||||
coefs[1] = frontPan * 0.5f + 0.5f;
|
||||
|
||||
break;
|
||||
|
||||
case AudioChannelSet::Quad:
|
||||
/* Left */
|
||||
coefs[0] = -frontPan * 0.5f + 0.5f;
|
||||
coefs[0] *= -totalSpan * 0.5f + 0.5f;
|
||||
|
||||
/* Right */
|
||||
coefs[1] = frontPan * 0.5f + 0.5f;
|
||||
coefs[1] *= -totalSpan * 0.5f + 0.5f;
|
||||
|
||||
/* Back Left */
|
||||
coefs[2] = -backPan * 0.5f + 0.5f;
|
||||
coefs[2] *= totalSpan * 0.5f + 0.5f;
|
||||
|
||||
/* Back Right */
|
||||
coefs[3] = backPan * 0.5f + 0.5f;
|
||||
coefs[3] *= totalSpan * 0.5f + 0.5f;
|
||||
|
||||
break;
|
||||
|
||||
case AudioChannelSet::Surround51:
|
||||
/* Left */
|
||||
coefs[0] = (frontPan <= 0.f) ? -frontPan : 0.f;
|
||||
coefs[0] *= -totalSpan * 0.5f + 0.5f;
|
||||
|
||||
/* Right */
|
||||
coefs[1] = (frontPan >= 0.f) ? frontPan : 0.f;
|
||||
coefs[1] *= -totalSpan * 0.5f + 0.5f;
|
||||
|
||||
/* Back Left */
|
||||
coefs[2] = -backPan * 0.5f + 0.5f;
|
||||
coefs[2] *= totalSpan * 0.5f + 0.5f;
|
||||
|
||||
/* Back Right */
|
||||
coefs[3] = backPan * 0.5f + 0.5f;
|
||||
coefs[3] *= totalSpan * 0.5f + 0.5f;
|
||||
|
||||
/* Center */
|
||||
coefs[4] = 1.f - std::fabs(frontPan);
|
||||
coefs[4] *= -totalSpan * 0.5f + 0.5f;
|
||||
|
||||
/* LFE */
|
||||
coefs[5] = 1.f;
|
||||
|
||||
break;
|
||||
|
||||
case AudioChannelSet::Surround71:
|
||||
/* Left */
|
||||
coefs[0] = (frontPan <= 0.f) ? -frontPan : 0.f;
|
||||
coefs[0] *= (totalSpan <= 0.f) ? -totalSpan : 0.f;
|
||||
|
||||
/* Right */
|
||||
coefs[1] = (frontPan >= 0.f) ? frontPan : 0.f;
|
||||
coefs[1] *= (totalSpan <= 0.f) ? -totalSpan : 0.f;
|
||||
|
||||
/* Back Left */
|
||||
coefs[2] = -backPan * 0.5f + 0.5f;
|
||||
coefs[2] *= (totalSpan >= 0.f) ? totalSpan : 0.f;
|
||||
|
||||
/* Back Right */
|
||||
coefs[3] = backPan * 0.5f + 0.5f;
|
||||
coefs[3] *= (totalSpan >= 0.f) ? totalSpan : 0.f;
|
||||
|
||||
/* Center */
|
||||
coefs[4] = 1.f - std::fabs(frontPan);
|
||||
coefs[4] *= (totalSpan <= 0.f) ? -totalSpan : 0.f;
|
||||
|
||||
/* LFE */
|
||||
coefs[5] = 1.f;
|
||||
|
||||
/* Side Left */
|
||||
coefs[6] = (backPan <= 0.f) ? -backPan : 0.f;
|
||||
coefs[6] *= 1.f - std::fabs(totalSpan);
|
||||
|
||||
/* Side Right */
|
||||
coefs[7] = (backPan >= 0.f) ? backPan : 0.f;
|
||||
coefs[7] *= 1.f - std::fabs(totalSpan);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Voice::_setPan(float pan)
|
||||
{
|
||||
if (m_emitter)
|
||||
return;
|
||||
|
||||
m_curPan = clamp(-1.f, pan, 1.f);
|
||||
float totalPan = clamp(-1.f, m_curPan + m_userPan, 1.f);
|
||||
float totalSpan = clamp(-1.f, m_curSpan + m_userSpan, 1.f);
|
||||
float coefs[8];
|
||||
|
||||
/* Left */
|
||||
coefs[0] = (totalPan <= 0.f) ? 1.f : (1.f - totalPan);
|
||||
coefs[0] *= (totalSpan <= 0.f) ? 1.f : (1.f - totalSpan);
|
||||
|
||||
/* Right */
|
||||
coefs[1] = (totalPan >= 0.f) ? 1.f : (1.f + totalPan);
|
||||
coefs[1] *= (totalSpan <= 0.f) ? 1.f : (1.f - totalSpan);
|
||||
|
||||
/* Back Left */
|
||||
coefs[2] = (totalPan <= 0.f) ? 1.f : (1.f - totalPan);
|
||||
coefs[2] *= (totalSpan >= 0.f) ? 1.f : (1.f + totalSpan);
|
||||
|
||||
/* Back Right */
|
||||
coefs[3] = (totalPan >= 0.f) ? 1.f : (1.f + totalPan);
|
||||
coefs[3] *= (totalSpan >= 0.f) ? 1.f : (1.f + totalSpan);
|
||||
|
||||
/* Center */
|
||||
coefs[4] = 1.f - std::fabs(totalPan);
|
||||
|
||||
/* LFE */
|
||||
coefs[5] = 1.f;
|
||||
|
||||
/* Side Left */
|
||||
coefs[6] = (totalPan <= 0.f) ? 1.f : (1.f - totalPan);
|
||||
coefs[6] *= 1.f - std::fabs(totalSpan);
|
||||
|
||||
/* Side Right */
|
||||
coefs[7] = (totalPan >= 0.f) ? 1.f : (1.f + totalPan);
|
||||
coefs[7] *= 1.f - std::fabs(totalSpan);
|
||||
|
||||
m_backendVoice->setChannelLevels(m_studio->getMaster().m_backendSubmix.get(), coefs, true);
|
||||
m_backendVoice->setChannelLevels(m_studio->getAuxA().m_backendSubmix.get(), coefs, true);
|
||||
m_backendVoice->setChannelLevels(m_studio->getAuxB().m_backendSubmix.get(), coefs, true);
|
||||
float coefs[8] = {};
|
||||
_panLaw(coefs, totalPan, totalPan, totalSpan);
|
||||
_setChannelCoefs(coefs);
|
||||
}
|
||||
|
||||
void Voice::setPan(float pan)
|
||||
|
@ -1011,6 +1078,20 @@ void Voice::setSurroundPan(float span)
|
|||
vox->setSurroundPan(span);
|
||||
}
|
||||
|
||||
void Voice::_setChannelCoefs(const float coefs[8])
|
||||
{
|
||||
m_backendVoice->setChannelLevels(m_studio->getMaster().m_backendSubmix.get(), coefs, true);
|
||||
m_backendVoice->setChannelLevels(m_studio->getAuxA().m_backendSubmix.get(), coefs, true);
|
||||
m_backendVoice->setChannelLevels(m_studio->getAuxB().m_backendSubmix.get(), coefs, true);
|
||||
}
|
||||
|
||||
void Voice::setChannelCoefs(const float coefs[8])
|
||||
{
|
||||
_setChannelCoefs(coefs);
|
||||
for (std::shared_ptr<Voice>& vox : m_childVoices)
|
||||
vox->setChannelCoefs(coefs);
|
||||
}
|
||||
|
||||
void Voice::startEnvelope(double dur, float vol, const Curve* envCurve)
|
||||
{
|
||||
m_envelopeTime = 0.f;
|
||||
|
|
Loading…
Reference in New Issue