Submix class and initial effects

This commit is contained in:
Jack Andersen 2016-05-07 12:10:57 -10:00
parent 708662c23e
commit 3cce975977
23 changed files with 419 additions and 34 deletions

View File

@ -10,7 +10,13 @@ set(SOURCES
lib/Listener.cpp
lib/Sequencer.cpp
lib/SoundMacroState.cpp
lib/Voice.cpp)
lib/Voice.cpp
lib/Submix.cpp
lib/EffectBase.cpp
lib/EffectReverbHi.cpp
lib/EffectReverbStd.cpp
lib/EffectChorus.cpp
lib/EffectDelay.cpp)
set(HEADERS
include/amuse/AudioGroup.hpp
@ -26,8 +32,15 @@ set(HEADERS
include/amuse/Sequencer.hpp
include/amuse/SoundMacroState.hpp
include/amuse/Voice.hpp
include/amuse/Submix.hpp
include/amuse/IBackendSubmix.hpp
include/amuse/IBackendVoice.hpp
include/amuse/IBackendVoiceAllocator.hpp
include/amuse/EffectBase.hpp
include/amuse/EffectReverbHi.hpp
include/amuse/EffectReverbStd.hpp
include/amuse/EffectChorus.hpp
include/amuse/EffectDelay.hpp
include/amuse/Common.hpp
include/amuse/amuse.hpp)

View File

@ -2,7 +2,9 @@
#define __AMUSE_BOO_BACKEND_HPP__
#include <boo/audiodev/IAudioVoiceEngine.hpp>
#include <boo/audiodev/IAudioSubmix.hpp>
#include "IBackendVoice.hpp"
#include "IBackendSubmix.hpp"
#include "IBackendVoiceAllocator.hpp"
namespace amuse
@ -23,12 +25,36 @@ class BooBackendVoice : public IBackendVoice
public:
BooBackendVoice(boo::IAudioVoiceEngine& engine, Voice& clientVox,
double sampleRate, bool dynamicPitch);
BooBackendVoice(boo::IAudioSubmix& submix, Voice& clientVox,
double sampleRate, bool dynamicPitch);
void setMatrixCoefficients(const float coefs[8]);
void setPitchRatio(double ratio);
void start();
void stop();
};
/** Backend submix implementation for boo mixer */
class BooBackendSubmix : public IBackendSubmix
{
friend class BooBackendVoiceAllocator;
Submix& m_clientSmx;
struct SubmixCallback : boo::IAudioSubmixCallback
{
BooBackendSubmix& m_parent;
bool canApplyEffect() const;
void applyEffect(int16_t* audio, const boo::ChannelMap& chanMap, double sampleRate) const;
void applyEffect(int32_t* audio, const boo::ChannelMap& chanMap, double sampleRate) const;
void applyEffect(float* audio, const boo::ChannelMap& chanMap, double sampleRate) const;
SubmixCallback(BooBackendSubmix& parent) : m_parent(parent) {}
} m_cb;
std::unique_ptr<boo::IAudioSubmix> m_booSubmix;
public:
BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx);
BooBackendSubmix(boo::IAudioSubmix& parent, Submix& clientSmx);
void setChannelGains(const float gains[8]);
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
};
/** Backend voice allocator implementation for boo mixer */
class BooBackendVoiceAllocator : public IBackendVoiceAllocator
{
@ -36,6 +62,7 @@ class BooBackendVoiceAllocator : public IBackendVoiceAllocator
public:
BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine);
std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch);
std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx);
};
}

View File

@ -0,0 +1,13 @@
#ifndef __AMUSE_EFFECTBASE_HPP__
#define __AMUSE_EFFECTBASE_HPP__
namespace amuse
{
class EffectBase
{
};
}
#endif // __AMUSE_EFFECTBASE_HPP__

View File

@ -0,0 +1,20 @@
#ifndef __AMUSE_EFFECTCHORUS_HPP__
#define __AMUSE_EFFECTCHORUS_HPP__
#include "EffectBase.hpp"
#include <stdint.h>
namespace amuse
{
/** Mixes the audio back into itself after continuously-varying delay */
class EffectChorus : public EffectBase
{
uint32_t m_baseDelay; /**< [5, 15] minimum value (in ms) for computed delay */
uint32_t m_variation; /**< [0, 5] time error (in ms) to set delay within */
uint32_t m_period; /**< [500, 10000] time (in ms) of one delay-shift cycle */
};
}
#endif // __AMUSE_EFFECTCHORUS_HPP__

View File

@ -0,0 +1,20 @@
#ifndef __AMUSE_EFFECTDELAY_HPP__
#define __AMUSE_EFFECTDELAY_HPP__
#include "EffectBase.hpp"
#include <stdint.h>
namespace amuse
{
/** Mixes the audio back into itself after specified delay */
class EffectDelay : public EffectBase
{
uint32_t m_delay[8]; /**< [10, 5000] time in ms of each channel's delay */
uint32_t m_feedback[8]; /**< [0, 100] percent to mix delayed signal with input signal */
uint32_t m_output[8]; /**< [0, 100] total output percent */
};
}
#endif // __AMUSE_EFFECTDELAY_HPP__

View File

@ -0,0 +1,22 @@
#ifndef __AMUSE_EFFECTREVERBHI_HPP__
#define __AMUSE_EFFECTREVERBHI_HPP__
#include "EffectBase.hpp"
namespace amuse
{
/** Reverb effect with configurable reflection filtering and channel-crosstalk */
class EffectReverbHi : public EffectBase
{
float m_coloration; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */
float m_mix; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
float m_time; /**< [0.01, 10.0] time in seconds for reflection decay */
float m_damping; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
float m_preDelay; /**< [0.0, 0.1] time in seconds before initial reflection heard */
float m_crosstalk; /**< [0.0, 100.0] factor defining how much reflections are allowed to bleed to other channels */
};
}
#endif // __AMUSE_EFFECTREVERBHI_HPP__

View File

@ -0,0 +1,21 @@
#ifndef __AMUSE_EFFECTREVERBSTD_HPP__
#define __AMUSE_EFFECTREVERBSTD_HPP__
#include "EffectBase.hpp"
namespace amuse
{
/** Reverb effect with configurable reflection filtering */
class EffectReverbStd : public EffectBase
{
float m_coloration; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */
float m_mix; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
float m_time; /**< [0.01, 10.0] time in seconds for reflection decay */
float m_damping; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
float m_preDelay; /**< [0.0, 0.1] time in seconds before initial reflection heard */
};
}
#endif // __AMUSE_EFFECTREVERBSTD_HPP__

View File

@ -13,6 +13,7 @@ namespace amuse
{
class IBackendVoiceAllocator;
class Voice;
class Submix;
class Emitter;
class Sequencer;
class AudioGroup;
@ -26,10 +27,14 @@ class Engine
std::list<Voice> m_activeVoices;
std::list<Emitter> m_activeEmitters;
std::list<Sequencer> m_activeSequencers;
std::list<Submix> m_activeSubmixes;
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
int m_nextVid = 0;
Voice* _allocateVoice(const AudioGroup& group, double sampleRate, bool dynamicPitch, bool emitter);
Voice* _allocateVoice(const AudioGroup& group, double sampleRate,
bool dynamicPitch, bool emitter, Submix* smx);
Submix* _allocateSubmix(Submix* smx);
std::list<Voice>::iterator _destroyVoice(Voice* voice);
std::list<Submix>::iterator _destroySubmix(Submix* smx);
AudioGroup* _findGroupFromSfxId(int sfxId, const AudioGroupSampleDirectory::Entry*& entOut) const;
AudioGroup* _findGroupFromSongId(int songId) const;
public:
@ -39,17 +44,24 @@ public:
void pumpEngine();
/** Add audio group data pointers to engine; must remain resident! */
bool addAudioGroup(int groupId, const AudioGroupData& data);
const AudioGroup* addAudioGroup(int groupId, const AudioGroupData& data);
/** Remove audio group from engine */
void removeAudioGroup(int groupId);
/** Create new Submix (a.k.a 'Studio') within root mix engine */
Submix* addSubmix(Submix* parent=nullptr);
/** Remove Submix and deallocate */
void removeSubmix(Submix* smx);
/** Start soundFX playing from loaded audio groups */
Voice* fxStart(int sfxId, float vol, float pan);
Voice* fxStart(int sfxId, float vol, float pan, Submix* smx=nullptr);
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
Emitter* addEmitter(const Vector3f& pos, const Vector3f& dir, float maxDist,
float falloff, int sfxId, float minVol, float maxVol);
float falloff, int sfxId, float minVol, float maxVol,
Submix* smx=nullptr);
/** Start song playing from loaded audio groups */
Sequencer* seqPlay(int songId, const unsigned char* arrData);

View File

@ -0,0 +1,28 @@
#ifndef __AMUSE_IBACKENDSUBMIX_HPP__
#define __AMUSE_IBACKENDSUBMIX_HPP__
#include <memory>
namespace amuse
{
class IBackendVoice;
class Voice;
/**
* @brief Client-implemented submix instance
*/
class IBackendSubmix
{
public:
virtual ~IBackendSubmix() = default;
/** Set channel-gains for submix (AudioChannel enum for array index) */
virtual void setChannelGains(const float gains[8])=0;
/** Amuse obtains a new voice from the platform outputting to this submix */
virtual std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch)=0;
};
}
#endif // __AMUSE_IBACKENDSUBMIX_HPP__

View File

@ -18,6 +18,13 @@ enum class AudioChannel
Unknown = 0xff
};
/** Same structure from boo, used to represent interleaved speaker layout */
struct ChannelMap
{
unsigned m_channelCount = 0;
AudioChannel m_channels[8] = {};
};
/**
* @brief Client-implemented voice instance
*/

View File

@ -6,7 +6,9 @@
namespace amuse
{
class IBackendVoice;
class IBackendSubmix;
class Voice;
class Submix;
/**
* @brief Client-implemented voice allocator
@ -18,6 +20,9 @@ public:
/** Amuse obtains a new voice from the platform this way */
virtual std::unique_ptr<IBackendVoice> allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch)=0;
/** Amuse obtains a new submix from the platform this way */
virtual std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx)=0;
};
}

53
include/amuse/Submix.hpp Normal file
View File

@ -0,0 +1,53 @@
#ifndef __AMUSE_SUBMIX_HPP__
#define __AMUSE_SUBMIX_HPP__
#include <memory>
#include "SoundMacroState.hpp"
#include "IBackendSubmix.hpp"
#include "IBackendVoice.hpp"
#include <unordered_set>
namespace amuse
{
class IBackendSubmix;
/** Intermediate mix of voices for applying auxiliary effects */
class Submix
{
friend class Engine;
friend class Voice;
Engine& m_root;
Submix* m_submix = nullptr; /**< Parent submix of this submix (or NULL if mixing to main output) */
std::list<Submix>::iterator m_engineIt; /**< Iterator to self within Engine's list for quick deletion */
std::unique_ptr<IBackendSubmix> m_backendSubmix; /**< Handle to client-implemented backend submix */
std::unordered_set<Voice*> m_activeVoices; /**< Secondary index of Voices within Submix */
std::unordered_set<Submix*> m_activeSubmixes; /**< Secondary index of Submixes within Submix */
bool m_destroyed = false;
void _destroy();
public:
Submix(Engine& engine, Submix* smx);
~Submix()
{
#ifndef NDEBUG
/* Ensure proper destruction procedure followed */
assert(m_destroyed);
#endif
}
/** Obtain pointer to Submix's parent Submix */
Submix* getParentSubmix() {return m_submix;}
/** Returns true when an effect callback is bound */
bool canApplyEffect() const;
/** in/out transformation entry for audio effect */
void applyEffect(int16_t* audio, const ChannelMap& chanMap, double sampleRate) const;
void applyEffect(int32_t* audio, const ChannelMap& chanMap, double sampleRate) const;
void applyEffect(float* audio, const ChannelMap& chanMap, double sampleRate) const;
Engine& getEngine() {return m_root;}
};
}
#endif // __AMUSE_SUBMIX_HPP__

View File

@ -11,6 +11,7 @@
namespace amuse
{
class IBackendVoice;
class Submix;
/** State of voice over lifetime */
enum class VoiceState
@ -24,27 +25,29 @@ enum class VoiceState
class Voice : public Entity
{
friend class Engine;
template <class U, class A>
friend class std::list;
int m_vid; /**< VoiceID of this voice instance */
bool m_emitter; /**< Voice is part of an Emitter */
Submix* m_submix = nullptr; /**< Submix this voice outputs to (or NULL for the main output mix) */
std::list<Voice>::iterator m_engineIt; /**< Iterator to self within Engine's list for quick deletion */
std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
SoundMacroState m_state; /**< State container for SoundMacro playback */
Voice *m_nextSibling = nullptr, *m_prevSibling = nullptr; /**< Sibling voice links for PLAYMACRO usage */
std::list<Voice> m_childVoices; /**< Child voices for PLAYMACRO usage */
uint8_t m_lastNote = 0; /**< Last MIDI semitone played by voice */
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
void _destroy();
public:
Voice(Engine& engine, const AudioGroup& group, int vid, bool emitter);
Voice(Engine& engine, const AudioGroup& group, ObjectId oid, int vid, bool emitter);
Voice(Engine& engine, const AudioGroup& group, int vid, bool emitter, Submix* smx);
Voice(Engine& engine, const AudioGroup& group, ObjectId oid, int vid, bool emitter, Submix* smx);
/** Request specified count of audio frames (samples) from voice,
* internally advancing the voice stream */
size_t supplyAudio(size_t frames, int16_t* data);
/** Obtain pointer to Voice's Submix */
Submix* getSubmix() {return m_submix;}
/** Get current state of voice */
VoiceState state() const;
@ -52,7 +55,7 @@ public:
int vid() const {return m_vid;}
/** Allocate parallel macro and tie to voice for possible emitter influence */
Voice* startSiblingMacro(int8_t addNote, ObjectId macroId, int macroStep);
Voice* startChildMacro(int8_t addNote, ObjectId macroId, int macroStep);
/** Load specified SoundMacro ID of within group into voice */
bool loadSoundMacro(ObjectId macroId, int macroStep=0, bool pushPc=false);

View File

@ -1,5 +1,6 @@
#include "amuse/BooBackend.hpp"
#include "amuse/Voice.hpp"
#include "amuse/Submix.hpp"
namespace amuse
{
@ -16,6 +17,12 @@ BooBackendVoice::BooBackendVoice(boo::IAudioVoiceEngine& engine, Voice& clientVo
m_booVoice(engine.allocateNewMonoVoice(sampleRate, &m_cb, dynamicPitch))
{}
BooBackendVoice::BooBackendVoice(boo::IAudioSubmix& submix, Voice& clientVox,
double sampleRate, bool dynamicPitch)
: m_clientVox(clientVox), m_cb(*this),
m_booVoice(submix.allocateNewMonoVoice(sampleRate, &m_cb, dynamicPitch))
{}
void BooBackendVoice::setMatrixCoefficients(const float coefs[8])
{
m_booVoice->setMonoMatrixCoefficients(coefs);
@ -36,6 +43,48 @@ void BooBackendVoice::stop()
m_booVoice->stop();
}
bool BooBackendSubmix::SubmixCallback::canApplyEffect() const
{
return m_parent.m_clientSmx.canApplyEffect();
}
void BooBackendSubmix::SubmixCallback::applyEffect(int16_t* audio, const boo::ChannelMap& chanMap,
double sampleRate) const
{
return m_parent.m_clientSmx.applyEffect(audio, reinterpret_cast<const ChannelMap&>(chanMap), sampleRate);
}
void BooBackendSubmix::SubmixCallback::applyEffect(int32_t* audio, const boo::ChannelMap& chanMap,
double sampleRate) const
{
return m_parent.m_clientSmx.applyEffect(audio, reinterpret_cast<const ChannelMap&>(chanMap), sampleRate);
}
void BooBackendSubmix::SubmixCallback::applyEffect(float* audio, const boo::ChannelMap& chanMap,
double sampleRate) const
{
return m_parent.m_clientSmx.applyEffect(audio, reinterpret_cast<const ChannelMap&>(chanMap), sampleRate);
}
BooBackendSubmix::BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx)
: m_clientSmx(clientSmx), m_cb(*this), m_booSubmix(engine.allocateNewSubmix(&m_cb))
{}
BooBackendSubmix::BooBackendSubmix(boo::IAudioSubmix& parent, Submix& clientSmx)
: m_clientSmx(clientSmx), m_cb(*this), m_booSubmix(parent.allocateNewSubmix(&m_cb))
{}
void BooBackendSubmix::setChannelGains(const float gains[8])
{
m_booSubmix->setChannelGains(gains);
}
std::unique_ptr<IBackendVoice>
BooBackendSubmix::allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch)
{
return std::make_unique<BooBackendVoice>(*m_booSubmix, clientVox, sampleRate, dynamicPitch);
}
BooBackendVoiceAllocator::BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine)
: m_booEngine(booEngine)
{}
@ -46,4 +95,9 @@ BooBackendVoiceAllocator::allocateVoice(Voice& clientVox, double sampleRate, boo
return std::make_unique<BooBackendVoice>(m_booEngine, clientVox, sampleRate, dynamicPitch);
}
std::unique_ptr<IBackendSubmix> BooBackendVoiceAllocator::allocateSubmix(Submix& clientSmx)
{
return std::make_unique<BooBackendSubmix>(m_booEngine, clientSmx);
}
}

0
lib/EffectBase.cpp Normal file
View File

0
lib/EffectChorus.cpp Normal file
View File

0
lib/EffectDelay.cpp Normal file
View File

0
lib/EffectReverbHi.cpp Normal file
View File

0
lib/EffectReverbStd.cpp Normal file
View File

View File

@ -1,5 +1,6 @@
#include "amuse/Engine.hpp"
#include "amuse/Voice.hpp"
#include "amuse/Submix.hpp"
#include "amuse/Sequencer.hpp"
#include "amuse/IBackendVoice.hpp"
#include "amuse/IBackendVoiceAllocator.hpp"
@ -13,15 +14,24 @@ Engine::Engine(IBackendVoiceAllocator& backend)
: m_backend(backend)
{}
Voice* Engine::_allocateVoice(const AudioGroup& group, double sampleRate, bool dynamicPitch, bool emitter)
Voice* Engine::_allocateVoice(const AudioGroup& group, double sampleRate,
bool dynamicPitch, bool emitter, Submix* smx)
{
auto it = m_activeVoices.emplace(m_activeVoices.end(), *this, group, m_nextVid++, emitter);
auto it = m_activeVoices.emplace(m_activeVoices.end(), *this, group, m_nextVid++, emitter, smx);
m_activeVoices.back().m_backendVoice =
m_backend.allocateVoice(m_activeVoices.back(), sampleRate, dynamicPitch);
m_activeVoices.back().m_engineIt = it;
return &m_activeVoices.back();
}
Submix* Engine::_allocateSubmix(Submix* smx)
{
auto it = m_activeSubmixes.emplace(m_activeSubmixes.end(), *this, smx);
m_activeSubmixes.back().m_backendSubmix = m_backend.allocateSubmix(m_activeSubmixes.back());
m_activeSubmixes.back().m_engineIt = it;
return &m_activeSubmixes.back();
}
std::list<Voice>::iterator Engine::_destroyVoice(Voice* voice)
{
#ifndef NDEBUG
@ -31,6 +41,15 @@ std::list<Voice>::iterator Engine::_destroyVoice(Voice* voice)
return m_activeVoices.erase(voice->m_engineIt);
}
std::list<Submix>::iterator Engine::_destroySubmix(Submix* smx)
{
#ifndef NDEBUG
assert(this == &smx->getEngine());
#endif
smx->_destroy();
return m_activeSubmixes.erase(smx->m_engineIt);
}
AudioGroup* Engine::_findGroupFromSfxId(int sfxId, const AudioGroupSampleDirectory::Entry*& entOut) const
{
for (const auto& grp : m_audioGroups)
@ -62,13 +81,14 @@ void Engine::pumpEngine()
}
/** Add audio group data pointers to engine; must remain resident! */
bool Engine::addAudioGroup(int groupId, const AudioGroupData& data)
const AudioGroup* Engine::addAudioGroup(int groupId, const AudioGroupData& data)
{
std::unique_ptr<AudioGroup> grp = std::make_unique<AudioGroup>(groupId, data);
if (!grp)
return false;
return nullptr;
AudioGroup* ret = grp.get();
m_audioGroups.emplace(std::make_pair(groupId, std::move(grp)));
return true;
return ret;
}
/** Remove audio group from engine */
@ -78,6 +98,7 @@ void Engine::removeAudioGroup(int groupId)
{
if (it->getAudioGroup().groupId() == groupId)
{
it->_destroy();
it = m_activeVoices.erase(it);
continue;
}
@ -88,6 +109,7 @@ void Engine::removeAudioGroup(int groupId)
{
if (it->getAudioGroup().groupId() == groupId)
{
it->_destroy();
it = m_activeEmitters.erase(it);
continue;
}
@ -98,6 +120,7 @@ void Engine::removeAudioGroup(int groupId)
{
if (it->getAudioGroup().groupId() == groupId)
{
it->_destroy();
it = m_activeSequencers.erase(it);
continue;
}
@ -107,15 +130,54 @@ void Engine::removeAudioGroup(int groupId)
m_audioGroups.erase(groupId);
}
/** Create new Submix (a.k.a 'Studio') within root mix engine */
Submix* Engine::addSubmix(Submix* smx)
{
return _allocateSubmix(smx);
}
/** Remove Submix and deallocate */
void Engine::removeSubmix(Submix* smx)
{
/* Delete all voices bound to submix */
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ;)
{
Submix* vsmx = it->getSubmix();
if (vsmx && vsmx == smx)
{
it->_destroy();
it = m_activeVoices.erase(it);
continue;
}
++it;
}
/* Delete all submixes bound to submix */
for (auto it = m_activeSubmixes.begin() ; it != m_activeSubmixes.end() ;)
{
Submix* ssmx = it->getParentSubmix();
if (ssmx && ssmx == smx)
{
it->_destroy();
it = m_activeSubmixes.erase(it);
continue;
}
++it;
}
/* Delete submix */
_destroySubmix(smx);
}
/** Start soundFX playing from loaded audio groups */
Voice* Engine::fxStart(int sfxId, float vol, float pan)
Voice* Engine::fxStart(int sfxId, float vol, float pan, Submix* smx)
{
const AudioGroupSampleDirectory::Entry* entry;
AudioGroup* grp = _findGroupFromSfxId(sfxId, entry);
if (!grp)
return nullptr;
Voice* ret = _allocateVoice(*grp, entry->m_sampleRate, true, false);
Voice* ret = _allocateVoice(*grp, entry->m_sampleRate, true, false, smx);
ret->setVolume(vol);
ret->setPanning(pan);
return ret;
@ -123,14 +185,14 @@ Voice* Engine::fxStart(int sfxId, float vol, float pan)
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
Emitter* Engine::addEmitter(const Vector3f& pos, const Vector3f& dir, float maxDist,
float falloff, int sfxId, float minVol, float maxVol)
float falloff, int sfxId, float minVol, float maxVol, Submix* smx)
{
const AudioGroupSampleDirectory::Entry* entry;
AudioGroup* grp = _findGroupFromSfxId(sfxId, entry);
if (!grp)
return nullptr;
Voice* vox = _allocateVoice(*grp, entry->m_sampleRate, true, true);
Voice* vox = _allocateVoice(*grp, entry->m_sampleRate, true, true, smx);
m_activeEmitters.emplace_back(*this, *grp, *vox);
Emitter& ret = m_activeEmitters.back();
ret.setPos(pos);

View File

@ -438,7 +438,7 @@ bool SoundMacroState::advance(Voice& vox, float dt)
//int8_t priority = cmd.m_data[5];
//int8_t maxVoices = cmd.m_data[6];
Voice* sibVox = vox.startSiblingMacro(addNote, macroId, macroStep);
Voice* sibVox = vox.startChildMacro(addNote, macroId, macroStep);
if (sibVox)
m_lastPlayMacroVid = sibVox->vid();

20
lib/Submix.cpp Normal file
View File

@ -0,0 +1,20 @@
#include "amuse/Submix.hpp"
namespace amuse
{
void Submix::_destroy()
{
m_destroyed = true;
if (m_submix)
m_submix->m_activeSubmixes.erase(this);
}
Submix::Submix(Engine& engine, Submix* smx)
: m_root(engine), m_submix(smx)
{
if (m_submix)
m_submix->m_activeSubmixes.insert(this);
}
}

View File

@ -1,31 +1,36 @@
#include "amuse/Voice.hpp"
#include "amuse/Submix.hpp"
#include "amuse/IBackendVoice.hpp"
namespace amuse
{
Voice::Voice(Engine& engine, const AudioGroup& group, int vid, bool emitter)
: Entity(engine, group), m_vid(vid), m_emitter(emitter)
{}
Voice::Voice(Engine& engine, const AudioGroup& group, ObjectId oid, int vid, bool emitter)
: Entity(engine, group, oid), m_vid(vid), m_emitter(emitter)
{}
void Voice::_destroy()
{
Entity::_destroy();
if (m_prevSibling)
m_prevSibling->m_nextSibling = m_nextSibling;
if (m_nextSibling)
m_nextSibling->m_prevSibling = m_prevSibling;
if (m_submix)
m_submix->m_activeVoices.erase(this);
}
Voice::Voice(Engine& engine, const AudioGroup& group, int vid, bool emitter, Submix* smx)
: Entity(engine, group), m_vid(vid), m_emitter(emitter), m_submix(smx)
{
if (m_submix)
m_submix->m_activeVoices.insert(this);
}
Voice::Voice(Engine& engine, const AudioGroup& group, ObjectId oid, int vid, bool emitter, Submix* smx)
: Entity(engine, group, oid), m_vid(vid), m_emitter(emitter), m_submix(smx)
{
if (m_submix)
m_submix->m_activeVoices.insert(this);
}
size_t Voice::supplyAudio(size_t frames, int16_t* data)
{
}
Voice* Voice::startSiblingMacro(int8_t addNote, ObjectId macroId, int macroStep)
Voice* Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep)
{
}