mirror of
https://github.com/AxioDL/amuse.git
synced 2025-07-09 14:45:53 +00:00
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/EffectReverb.cpp
|
||||||
lib/EffectChorus.cpp
|
lib/EffectChorus.cpp
|
||||||
lib/EffectDelay.cpp
|
lib/EffectDelay.cpp
|
||||||
lib/SurroundProfiles.cpp
|
|
||||||
lib/ContainerRegistry.cpp
|
lib/ContainerRegistry.cpp
|
||||||
lib/DSPCodec.c
|
lib/DSPCodec.c
|
||||||
lib/N64MusyXCodec.c)
|
lib/N64MusyXCodec.c)
|
||||||
@ -68,7 +67,6 @@ set(HEADERS
|
|||||||
include/amuse/EffectReverb.hpp
|
include/amuse/EffectReverb.hpp
|
||||||
include/amuse/EffectChorus.hpp
|
include/amuse/EffectChorus.hpp
|
||||||
include/amuse/EffectDelay.hpp
|
include/amuse/EffectDelay.hpp
|
||||||
include/amuse/SurroundProfiles.hpp
|
|
||||||
include/amuse/ContainerRegistry.hpp
|
include/amuse/ContainerRegistry.hpp
|
||||||
include/amuse/Common.hpp
|
include/amuse/Common.hpp
|
||||||
include/amuse/amuse.hpp
|
include/amuse/amuse.hpp
|
||||||
|
@ -2,32 +2,51 @@
|
|||||||
#define __AMUSE_EMITTER_HPP__
|
#define __AMUSE_EMITTER_HPP__
|
||||||
|
|
||||||
#include "Entity.hpp"
|
#include "Entity.hpp"
|
||||||
|
#include "Common.hpp"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cfloat>
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
class Voice;
|
class Voice;
|
||||||
|
class Listener;
|
||||||
|
|
||||||
using Vector3f = float[3];
|
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 */
|
/** Voice wrapper with positional-3D level control */
|
||||||
class Emitter : public Entity
|
class Emitter : public Entity
|
||||||
{
|
{
|
||||||
std::shared_ptr<Voice> m_vox;
|
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;
|
friend class Engine;
|
||||||
void _destroy();
|
void _destroy();
|
||||||
|
float _attenuationCurve(float dist) const;
|
||||||
|
void _update();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~Emitter();
|
~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 setVectors(const float* pos, const float* dir);
|
||||||
void setDir(const float* dir);
|
void setMaxVol(float maxVol) { m_maxVol = clamp(0.f, maxVol, 1.f); }
|
||||||
void setMaxDist(float maxDist);
|
|
||||||
void setMaxVol(float maxVol);
|
|
||||||
void setMinVol(float minVol);
|
|
||||||
void setFalloff(float falloff);
|
|
||||||
|
|
||||||
std::shared_ptr<Voice>& getVoice() { return m_vox; }
|
std::shared_ptr<Voice>& getVoice() { return m_vox; }
|
||||||
};
|
};
|
||||||
|
@ -7,9 +7,11 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include "Emitter.hpp"
|
#include "Emitter.hpp"
|
||||||
|
#include "Listener.hpp"
|
||||||
#include "AudioGroupSampleDirectory.hpp"
|
#include "AudioGroupSampleDirectory.hpp"
|
||||||
#include "Sequencer.hpp"
|
#include "Sequencer.hpp"
|
||||||
#include "Studio.hpp"
|
#include "Studio.hpp"
|
||||||
|
#include "IBackendVoiceAllocator.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
@ -42,6 +44,7 @@ class Engine
|
|||||||
std::unordered_map<const AudioGroupData*, std::unique_ptr<AudioGroup>> m_audioGroups;
|
std::unordered_map<const AudioGroupData*, std::unique_ptr<AudioGroup>> m_audioGroups;
|
||||||
std::list<std::shared_ptr<Voice>> m_activeVoices;
|
std::list<std::shared_ptr<Voice>> m_activeVoices;
|
||||||
std::list<std::shared_ptr<Emitter>> m_activeEmitters;
|
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::shared_ptr<Sequencer>> m_activeSequencers;
|
||||||
std::list<std::weak_ptr<Studio>> m_activeStudios; /* lifetime dependent on contributing audio entities */
|
std::list<std::weak_ptr<Studio>> m_activeStudios; /* lifetime dependent on contributing audio entities */
|
||||||
bool m_defaultStudioReady = false;
|
bool m_defaultStudioReady = false;
|
||||||
@ -50,6 +53,7 @@ class Engine
|
|||||||
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
|
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
|
||||||
int m_nextVid = 0;
|
int m_nextVid = 0;
|
||||||
float m_masterVolume = 1.f;
|
float m_masterVolume = 1.f;
|
||||||
|
AudioChannelSet m_channelSet = AudioChannelSet::Unknown;
|
||||||
|
|
||||||
AudioGroup* _addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp);
|
AudioGroup* _addAudioGroup(const AudioGroupData& data, std::unique_ptr<AudioGroup>&& grp);
|
||||||
std::pair<AudioGroup*, const SongGroupIndex*> _findSongGroup(int groupId) const;
|
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 */
|
/** 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,
|
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,
|
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 */
|
/** Start song playing from loaded audio groups */
|
||||||
std::shared_ptr<Sequencer> seqPlay(int groupId, int songId, const unsigned char* arrData,
|
std::shared_ptr<Sequencer> seqPlay(int groupId, int songId, const unsigned char* arrData,
|
||||||
std::weak_ptr<Studio> smx);
|
std::weak_ptr<Studio> smx);
|
||||||
|
@ -1,13 +1,27 @@
|
|||||||
#ifndef __AMUSE_LISTENER_HPP__
|
#ifndef __AMUSE_LISTENER_HPP__
|
||||||
#define __AMUSE_LISTENER_HPP__
|
#define __AMUSE_LISTENER_HPP__
|
||||||
|
|
||||||
#include "Entity.hpp"
|
#include "amuse/Emitter.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
class Listener
|
||||||
class Listener : public Entity
|
|
||||||
{
|
{
|
||||||
|
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 Sequencer;
|
||||||
friend class SoundMacroState;
|
friend class SoundMacroState;
|
||||||
friend class Envelope;
|
friend class Envelope;
|
||||||
|
friend class Emitter;
|
||||||
int m_vid; /**< VoiceID of this voice instance */
|
int m_vid; /**< VoiceID of this voice instance */
|
||||||
bool m_emitter; /**< Voice is part of an Emitter */
|
bool m_emitter; /**< Voice is part of an Emitter */
|
||||||
std::shared_ptr<Studio> m_studio; /**< Studio this voice outputs to */
|
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) */
|
uint32_t m_lastSamplePos = 0; /**< Last sample position (or last loop sample) */
|
||||||
int16_t m_prev1 = 0; /**< DSPADPCM prev sample */
|
int16_t m_prev1 = 0; /**< DSPADPCM prev sample */
|
||||||
int16_t m_prev2 = 0; /**< DSPADPCM prev-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_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) */
|
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 */
|
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,
|
std::shared_ptr<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey,
|
||||||
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
|
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 _setPan(float pan);
|
||||||
void _setSurroundPan(float span);
|
void _setSurroundPan(float span);
|
||||||
|
void _setChannelCoefs(const float coefs[8]);
|
||||||
void _setPitchWheel(float pitchWheel);
|
void _setPitchWheel(float pitchWheel);
|
||||||
void _notifyCtrlChange(uint8_t ctrl, int8_t val);
|
void _notifyCtrlChange(uint8_t ctrl, int8_t val);
|
||||||
|
|
||||||
@ -231,6 +235,9 @@ public:
|
|||||||
/** Set current voice surround-panning immediately */
|
/** Set current voice surround-panning immediately */
|
||||||
void setSurroundPan(float span);
|
void setSurroundPan(float span);
|
||||||
|
|
||||||
|
/** Set current voice channel coefficients immediately */
|
||||||
|
void setChannelCoefs(const float coefs[8]);
|
||||||
|
|
||||||
/** Start volume envelope to specified level */
|
/** Start volume envelope to specified level */
|
||||||
void startEnvelope(double dur, float vol, const Curve* envCurve);
|
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/Emitter.hpp"
|
||||||
|
#include "amuse/Listener.hpp"
|
||||||
#include "amuse/Voice.hpp"
|
#include "amuse/Voice.hpp"
|
||||||
#include "amuse/Engine.hpp"
|
#include "amuse/Engine.hpp"
|
||||||
|
|
||||||
namespace amuse
|
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() {}
|
||||||
|
|
||||||
Emitter::Emitter(Engine& engine, const AudioGroup& group, std::shared_ptr<Voice>&& vox)
|
Emitter::Emitter(Engine& engine, const AudioGroup& group, std::shared_ptr<Voice>&& vox,
|
||||||
: Entity(engine, group, vox->getObjectId()), m_vox(std::move(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();
|
m_vox->kill();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emitter::setPos(const float* pos) {}
|
float Emitter::_attenuationCurve(float dist) const
|
||||||
|
{
|
||||||
void Emitter::setDir(const float* dir) {}
|
if (dist > m_maxDist)
|
||||||
|
return 0.f;
|
||||||
void Emitter::setMaxDist(float maxDist) {}
|
float t = dist / m_maxDist;
|
||||||
|
if (m_falloff < 0.f)
|
||||||
void Emitter::setMaxVol(float maxVol) {}
|
{
|
||||||
|
float tmp = t * 10.f + 1.f;
|
||||||
void Emitter::setMinVol(float minVol) {}
|
tmp = 1.f / (tmp * tmp);
|
||||||
|
return (1.f + m_falloff) * (-t + 1.f) + -m_falloff * tmp;
|
||||||
void Emitter::setFalloff(float falloff) {}
|
}
|
||||||
|
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)
|
void Engine::_on5MsInterval(IBackendVoiceAllocator& engine, double dt)
|
||||||
{
|
{
|
||||||
|
m_channelSet = engine.getAvailableSet();
|
||||||
if (m_midiReader)
|
if (m_midiReader)
|
||||||
m_midiReader->pumpReader(dt);
|
m_midiReader->pumpReader(dt);
|
||||||
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers)
|
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers)
|
||||||
seq->advance(dt);
|
seq->advance(dt);
|
||||||
|
for (std::shared_ptr<Emitter>& emitter : m_activeEmitters)
|
||||||
|
emitter->_update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::_onPumpCycleComplete(IBackendVoiceAllocator& engine)
|
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 */
|
/** 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,
|
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);
|
auto search = m_sfxLookup.find(sfxId);
|
||||||
if (search == m_sfxLookup.end())
|
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 =
|
std::list<std::shared_ptr<Voice>>::iterator vox =
|
||||||
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, true, smx);
|
_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);
|
Emitter& ret = *(*emitIt);
|
||||||
|
|
||||||
ObjectId oid = (grp->getDataFormat() == DataFormat::PC) ? entry->objId : SBig(entry->objId);
|
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);
|
(*vox)->setPan(entry->panning);
|
||||||
ret.setPos(pos);
|
ret.setVectors(pos, dir);
|
||||||
ret.setDir(dir);
|
|
||||||
ret.setMaxDist(maxDist);
|
|
||||||
ret.setFalloff(falloff);
|
|
||||||
ret.setMinVol(minVol);
|
|
||||||
ret.setMaxVol(maxVol);
|
ret.setMaxVol(maxVol);
|
||||||
|
|
||||||
return *emitIt;
|
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 */
|
/** Start song playing from loaded audio groups */
|
||||||
std::shared_ptr<Sequencer> Engine::seqPlay(int groupId, int songId, const unsigned char* arrData,
|
std::shared_ptr<Sequencer> Engine::seqPlay(int groupId, int songId, const unsigned char* arrData,
|
||||||
std::weak_ptr<Studio> smx)
|
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);
|
// fprintf(stderr, "PITCH %d %d \n", cents, slew);
|
||||||
int32_t interval = cents - m_curSample->first.m_pitch * 100;
|
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_sampleRate = m_curSample->first.m_sampleRate * ratio;
|
||||||
m_backendVoice->setPitchRatio(ratio, slew);
|
m_backendVoice->setPitchRatio(ratio, slew);
|
||||||
}
|
}
|
||||||
@ -947,46 +947,113 @@ void Voice::setVolume(float vol)
|
|||||||
vox->setVolume(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)
|
void Voice::_setPan(float pan)
|
||||||
{
|
{
|
||||||
|
if (m_emitter)
|
||||||
|
return;
|
||||||
|
|
||||||
m_curPan = clamp(-1.f, pan, 1.f);
|
m_curPan = clamp(-1.f, pan, 1.f);
|
||||||
float totalPan = clamp(-1.f, m_curPan + m_userPan, 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 totalSpan = clamp(-1.f, m_curSpan + m_userSpan, 1.f);
|
||||||
float coefs[8];
|
float coefs[8] = {};
|
||||||
|
_panLaw(coefs, totalPan, totalPan, totalSpan);
|
||||||
/* Left */
|
_setChannelCoefs(coefs);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Voice::setPan(float pan)
|
void Voice::setPan(float pan)
|
||||||
@ -1011,6 +1078,20 @@ void Voice::setSurroundPan(float span)
|
|||||||
vox->setSurroundPan(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)
|
void Voice::startEnvelope(double dur, float vol, const Curve* envCurve)
|
||||||
{
|
{
|
||||||
m_envelopeTime = 0.f;
|
m_envelopeTime = 0.f;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user