Initial multiple-referencing submix refactor

This commit is contained in:
Jack Andersen 2016-07-12 17:04:55 -10:00
parent 596bc66ce6
commit d3d5595422
16 changed files with 264 additions and 157 deletions

View File

@ -22,6 +22,7 @@ set(SOURCES
lib/Voice.cpp
lib/VolumeLUT.cpp
lib/Submix.cpp
lib/Studio.cpp
lib/EffectBase.cpp
lib/EffectReverb.cpp
lib/EffectChorus.cpp
@ -49,6 +50,7 @@ set(HEADERS
include/amuse/SongState.hpp
include/amuse/Voice.hpp
include/amuse/Submix.hpp
include/amuse/Studio.hpp
include/amuse/IBackendSubmix.hpp
include/amuse/IBackendVoice.hpp
include/amuse/IBackendVoiceAllocator.hpp

View File

@ -30,11 +30,10 @@ 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 resetSampleRate(double sampleRate);
void setMatrixCoefficients(const float coefs[8], bool slew);
void setSubmixMatrixCoefficients(const float coefs[8], bool slew);
void resetChannelLevels();
void setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew);
void setPitchRatio(double ratio, bool slew);
void start();
void stop();
@ -44,6 +43,7 @@ public:
class BooBackendSubmix : public IBackendSubmix
{
friend class BooBackendVoiceAllocator;
friend class BooBackendVoice;
Submix& m_clientSmx;
struct SubmixCallback : boo::IAudioSubmixCallback
{
@ -60,10 +60,8 @@ class BooBackendSubmix : public IBackendSubmix
} 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);
BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx, bool mainOut);
void setSendLevel(IBackendSubmix* submix, float level, bool slew);
double getSampleRate() const;
SubmixFormat getSampleFormat() const;
};
@ -124,7 +122,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);
std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut);
std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices();
std::unique_ptr<IMIDIReader> allocateMIDIReader(Engine& engine, const char* name=nullptr);
void register5MsCallback(std::function<void(double)>&& callback);

View File

@ -9,6 +9,7 @@
#include "Emitter.hpp"
#include "AudioGroupSampleDirectory.hpp"
#include "Sequencer.hpp"
#include "Studio.hpp"
namespace amuse
{
@ -37,11 +38,12 @@ class Engine
IBackendVoiceAllocator& m_backend;
AmplitudeMode m_ampMode;
std::unique_ptr<IMIDIReader> m_midiReader;
std::shared_ptr<Studio> m_defaultStudio;
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<Sequencer>> m_activeSequencers;
std::list<Submix> m_activeSubmixes;
std::list<std::shared_ptr<Studio>> m_activeStudios;
std::unordered_map<uint16_t, std::tuple<AudioGroup*, int, const SFXGroupIndex::SFXEntry*>> m_sfxLookup;
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
int m_nextVid = 0;
@ -52,15 +54,15 @@ class Engine
std::list<std::shared_ptr<Voice>>::iterator
_allocateVoice(const AudioGroup& group, int groupId, double sampleRate,
bool dynamicPitch, bool emitter, Submix* smx);
bool dynamicPitch, bool emitter, std::weak_ptr<Studio> studio);
std::list<std::shared_ptr<Sequencer>>::iterator
_allocateSequencer(const AudioGroup& group, int groupId,
int setupId, Submix* smx);
std::list<Submix>::iterator _allocateSubmix(Submix* smx);
int setupId, std::weak_ptr<Studio> studio);
std::list<std::shared_ptr<Studio>>::iterator _allocateStudio(bool mainOut);
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it);
std::list<std::shared_ptr<Sequencer>>::iterator _destroySequencer(std::list<std::shared_ptr<Sequencer>>::iterator it);
std::list<Submix>::iterator _destroySubmix(std::list<Submix>::iterator it);
std::list<Submix>::iterator _removeSubmix(std::list<Submix>::iterator it);
std::list<std::shared_ptr<Studio>>::iterator _destroyStudio(std::list<std::shared_ptr<Studio>>::iterator it);
std::list<std::shared_ptr<Studio>>::iterator _removeStudio(std::list<std::shared_ptr<Studio>>::iterator it);
void _bringOutYourDead();
void _5MsCallback(double dt);
public:
@ -79,23 +81,34 @@ public:
/** Remove audio group from engine */
void removeAudioGroup(const AudioGroupData& data);
/** Create new Submix (a.k.a 'Studio') within root mix engine */
Submix* addSubmix(Submix* parent=nullptr);
/** Access engine's default studio */
std::shared_ptr<Studio> getDefaultStudio() {return m_defaultStudio;}
/** Remove Submix and deallocate */
void removeSubmix(Submix* smx);
/** Create new Studio within engine */
std::shared_ptr<Studio> addStudio(bool mainOut);
/** Remove Studio from engine */
void removeStudio(std::weak_ptr<Studio> studio);
/** Start soundFX playing from loaded audio groups */
std::shared_ptr<Voice> fxStart(int sfxId, float vol, float pan, Submix* smx=nullptr);
std::shared_ptr<Voice> fxStart(int sfxId, float vol, float pan, std::weak_ptr<Studio> smx);
std::shared_ptr<Voice> fxStart(int sfxId, float vol, float pan)
{
return fxStart(sfxId, vol, pan, m_defaultStudio);
}
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
std::shared_ptr<Emitter> addEmitter(const Vector3f& pos, const Vector3f& dir, float maxDist,
float falloff, int sfxId, float minVol, float maxVol,
Submix* smx=nullptr);
std::weak_ptr<Studio> smx);
/** Start song playing from loaded audio groups */
std::shared_ptr<Sequencer> seqPlay(int groupId, int songId, const unsigned char* arrData,
Submix* smx=nullptr);
std::weak_ptr<Studio> smx);
std::shared_ptr<Sequencer> seqPlay(int groupId, int songId, const unsigned char* arrData)
{
return seqPlay(groupId, songId, arrData, m_defaultStudio);
}
/** Find voice from VoiceId */
std::shared_ptr<Voice> findVoice(int vid);

View File

@ -21,11 +21,8 @@ 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;
/** Set send level for submix (AudioChannel enum for array index) */
virtual void setSendLevel(IBackendSubmix* submix, float level, bool slew)=0;
/** Amuse gets fixed sample rate of submix this way */
virtual double getSampleRate() const=0;

View File

@ -3,6 +3,7 @@
namespace amuse
{
class IBackendSubmix;
/** Same channel enums from boo, used for matrix coefficient table index */
enum class AudioChannel
@ -34,11 +35,11 @@ public:
/** Set new sample rate into platform voice (may result in artifacts while playing) */
virtual void resetSampleRate(double sampleRate)=0;
/** Set channel-gains for audio source (AudioChannel enum for array index) */
virtual void setMatrixCoefficients(const float coefs[8], bool slew)=0;
/** Reset channel-gains to silence and unbind all submixes */
virtual void resetChannelLevels()=0;
/** Set submix-channel-gains for audio source (AudioChannel enum for array index) */
virtual void setSubmixMatrixCoefficients(const float coefs[8], bool slew)=0;
/** Set channel-gains for audio source (AudioChannel enum for array index) */
virtual void setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew)=0;
/** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */
virtual void setPitchRatio(double ratio, bool slew)=0;

View File

@ -44,7 +44,7 @@ public:
bool dynamicPitch)=0;
/** Amuse obtains a new submix from the platform this way */
virtual std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx)=0;
virtual std::unique_ptr<IBackendSubmix> allocateSubmix(Submix& clientSmx, bool mainOut)=0;
/** Amuse obtains a list of all MIDI devices this way */
virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices()=0;

View File

@ -12,7 +12,7 @@
namespace amuse
{
class Submix;
class Studio;
class Voice;
/** State of sequencer over lifetime */
@ -31,7 +31,7 @@ class Sequencer : public Entity
const SongGroupIndex::MIDISetup* m_midiSetup = nullptr; /**< Selected MIDI setup (may be null) */
const SFXGroupIndex* m_sfxGroup = nullptr; /**< SFX Groups are alternatively referenced here */
std::vector<const SFXGroupIndex::SFXEntry*> m_sfxMappings; /**< SFX entries are mapped to MIDI keys this via this */
Submix* m_submix = nullptr; /**< Submix this sequencer outputs to (or NULL for the main output mix) */
std::shared_ptr<Studio> m_studio; /**< Studio this sequencer outputs to */
const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */
SongState m_songState; /**< State of current arrangement playback */
@ -85,15 +85,15 @@ class Sequencer : public Entity
public:
~Sequencer();
Sequencer(Engine& engine, const AudioGroup& group, int groupId,
const SongGroupIndex* songGroup, int setupId, Submix* smx);
const SongGroupIndex* songGroup, int setupId, std::weak_ptr<Studio> studio);
Sequencer(Engine& engine, const AudioGroup& group, int groupId,
const SFXGroupIndex* sfxGroup, Submix* smx);
const SFXGroupIndex* sfxGroup, std::weak_ptr<Studio> studio);
/** Advance current song data (if any) */
void advance(double dt);
/** Obtain pointer to Sequencer's Submix */
Submix* getSubmix() {return m_submix;}
std::shared_ptr<Studio> getStudio() {return m_studio;}
/** Get current state of sequencer */
SequencerState state() const {return m_state;}

62
include/amuse/Studio.hpp Normal file
View File

@ -0,0 +1,62 @@
#ifndef __AMUSE_STUDIO_HPP__
#define __AMUSE_STUDIO_HPP__
#include <memory>
#include <list>
#include "Entity.hpp"
#include "Voice.hpp"
#include "Submix.hpp"
#include <type_traits>
namespace amuse
{
class Studio
{
friend class Engine;
Engine& m_engine;
Submix m_auxA;
Submix m_auxB;
struct StudioSend
{
std::weak_ptr<Studio> m_targetStudio;
float m_dryLevel;
float m_auxALevel;
float m_auxBLevel;
StudioSend(std::weak_ptr<Studio> studio, float dry, float auxA, float auxB)
: m_targetStudio(studio), m_dryLevel(dry), m_auxALevel(auxA), m_auxBLevel(auxB) {}
};
std::list<StudioSend> m_studiosOut;
bool m_destroyed = false;
void _destroy();
void _bringOutYourDead();
#ifndef NDEBUG
bool _cyclicCheck(Studio* leaf);
#endif
public:
Studio(Engine& engine, bool mainOut);
/** Register a target Studio to send this Studio's mixing busses */
void addStudioSend(std::weak_ptr<Studio> studio, float dry, float auxA, float auxB);
~Studio()
{
#ifndef NDEBUG
/* Ensure proper destruction procedure followed */
assert(m_destroyed);
#endif
}
/** advice submixes of changing sample rate */
void resetOutputSampleRate(double sampleRate);
Submix& getAuxA() {return m_auxA;}
Submix& getAuxB() {return m_auxB;}
Engine& getEngine() {return m_engine;}
};
}
#endif // __AMUSE_STUDIO_HPP__

View File

@ -23,14 +23,13 @@ class Submix
friend class Voice;
friend class Sequencer;
Engine& m_root;
Submix* m_submix = nullptr; /**< Parent submix of this submix (or NULL if mixing to main output) */
std::unique_ptr<IBackendSubmix> m_backendSubmix; /**< Handle to client-implemented backend submix */
std::vector<std::unique_ptr<EffectBaseTypeless>> m_effectStack; /**< Ordered list of effects to apply to submix */
bool m_destroyed = false;
void _destroy();
public:
Submix(Engine& engine, Submix* smx);
Submix(Engine& engine);
~Submix()
{
#ifndef NDEBUG
@ -39,9 +38,6 @@ public:
#endif
}
/** Obtain pointer to Submix's parent Submix */
Submix* getParentSubmix() {return m_submix;}
/** Add new effect to effect stack and assume ownership */
template <class T, class ...Args>
T& makeEffect(Args... args)

View File

@ -14,7 +14,7 @@
namespace amuse
{
class IBackendVoice;
class Submix;
class Studio;
struct Keymap;
struct LayerMapping;
@ -35,7 +35,7 @@ class Voice : public Entity
friend class Envelope;
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::shared_ptr<Studio> m_studio; /**< Studio this voice outputs to */
std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
SoundMacroState m_state; /**< State container for SoundMacro playback */
@ -164,8 +164,8 @@ class Voice : public Entity
void _notifyCtrlChange(uint8_t ctrl, int8_t val);
public:
~Voice();
Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, Submix* smx);
Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, Submix* smx);
Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, std::weak_ptr<Studio> studio);
Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, std::weak_ptr<Studio> studio);
/** Called before each supplyAudio invocation to prepare voice
* backend for possible parameter updates */
@ -175,8 +175,8 @@ public:
* 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;}
/** Obtain pointer to Voice's Studio */
std::shared_ptr<Studio> getStudio() {return m_studio;}
/** Get current state of voice */
VoiceState state() const {return m_voxState;}

View File

@ -24,25 +24,20 @@ 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::resetSampleRate(double sampleRate)
{
m_booVoice->resetSampleRate(sampleRate);
}
void BooBackendVoice::setMatrixCoefficients(const float coefs[8], bool slew)
void BooBackendVoice::resetChannelLevels()
{
m_booVoice->setMonoMatrixCoefficients(coefs, slew);
m_booVoice->resetChannelLevels();
}
void BooBackendVoice::setSubmixMatrixCoefficients(const float coefs[8], bool slew)
void BooBackendVoice::setChannelLevels(IBackendSubmix* submix, const float coefs[8], bool slew)
{
m_booVoice->setMonoSubmixMatrixCoefficients(coefs, slew);
BooBackendSubmix& smx = *reinterpret_cast<BooBackendSubmix*>(submix);
m_booVoice->setMonoChannelLevels(smx.m_booSubmix.get(), coefs, slew);
}
void BooBackendVoice::setPitchRatio(double ratio, bool slew)
@ -88,23 +83,14 @@ void BooBackendSubmix::SubmixCallback::resetOutputSampleRate(double sampleRate)
m_parent.m_clientSmx.resetOutputSampleRate(sampleRate);
}
BooBackendSubmix::BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx)
: m_clientSmx(clientSmx), m_cb(*this), m_booSubmix(engine.allocateNewSubmix(&m_cb))
BooBackendSubmix::BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx, bool mainOut)
: m_clientSmx(clientSmx), m_cb(*this), m_booSubmix(engine.allocateNewSubmix(mainOut, &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])
void BooBackendSubmix::setSendLevel(IBackendSubmix* submix, float level, bool slew)
{
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);
BooBackendSubmix& smx = *reinterpret_cast<BooBackendSubmix*>(submix);
m_booSubmix->setSendLevel(smx.m_booSubmix.get(), level, slew);
}
double BooBackendSubmix::getSampleRate() const
@ -327,9 +313,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)
std::unique_ptr<IBackendSubmix> BooBackendVoiceAllocator::allocateSubmix(Submix& clientSmx, bool mainOut)
{
return std::make_unique<BooBackendSubmix>(m_booEngine, clientSmx);
return std::make_unique<BooBackendSubmix>(m_booEngine, clientSmx, mainOut);
}
std::vector<std::pair<std::string, std::string>> BooBackendVoiceAllocator::enumerateMIDIDevices()

View File

@ -16,8 +16,8 @@ Engine::~Engine()
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers)
if (!seq->m_destroyed)
seq->_destroy();
while (m_activeSubmixes.size())
removeSubmix(&m_activeSubmixes.front());
while (m_activeStudios.size())
removeStudio(m_activeStudios.front());
for (std::shared_ptr<Emitter>& emitter : m_activeEmitters)
emitter->_destroy();
for (std::shared_ptr<Voice>& vox : m_activeVoices)
@ -25,7 +25,7 @@ Engine::~Engine()
}
Engine::Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode)
: m_backend(backend), m_ampMode(ampMode)
: m_backend(backend), m_ampMode(ampMode), m_defaultStudio(std::make_shared<Studio>(*this, true))
{
backend.register5MsCallback(std::bind(&Engine::_5MsCallback, this, std::placeholders::_1));
m_midiReader = backend.allocateMIDIReader(*this);
@ -55,44 +55,41 @@ std::pair<AudioGroup*, const SFXGroupIndex*> Engine::_findSFXGroup(int groupId)
std::list<std::shared_ptr<Voice>>::iterator
Engine::_allocateVoice(const AudioGroup& group, int groupId, double sampleRate,
bool dynamicPitch, bool emitter, Submix* smx)
bool dynamicPitch, bool emitter, std::weak_ptr<Studio> studio)
{
auto it = m_activeVoices.emplace(m_activeVoices.end(),
new Voice(*this, group, groupId, m_nextVid++, emitter, smx));
if (smx)
m_activeVoices.back()->m_backendVoice =
smx->m_backendSubmix->allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
else
m_activeVoices.back()->m_backendVoice =
m_backend.allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
new Voice(*this, group, groupId, m_nextVid++, emitter, studio));
m_activeVoices.back()->m_backendVoice =
m_backend.allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
return it;
}
std::list<std::shared_ptr<Sequencer>>::iterator
Engine::_allocateSequencer(const AudioGroup& group, int groupId,
int setupId, Submix* smx)
int setupId, std::weak_ptr<Studio> studio)
{
const SongGroupIndex* songGroup = group.getProj().getSongGroupIndex(groupId);
if (songGroup)
{
auto it = m_activeSequencers.emplace(m_activeSequencers.end(),
new Sequencer(*this, group, groupId, songGroup, setupId, smx));
new Sequencer(*this, group, groupId, songGroup, setupId, studio));
return it;
}
const SFXGroupIndex* sfxGroup = group.getProj().getSFXGroupIndex(groupId);
if (sfxGroup)
{
auto it = m_activeSequencers.emplace(m_activeSequencers.end(),
new Sequencer(*this, group, groupId, sfxGroup, smx));
new Sequencer(*this, group, groupId, sfxGroup, studio));
return it;
}
return {};
}
std::list<Submix>::iterator Engine::_allocateSubmix(Submix* smx)
std::list<std::shared_ptr<Studio>>::iterator Engine::_allocateStudio(bool mainOut)
{
auto it = m_activeSubmixes.emplace(m_activeSubmixes.end(), *this, smx);
m_activeSubmixes.back().m_backendSubmix = m_backend.allocateSubmix(m_activeSubmixes.back());
auto it = m_activeStudios.emplace(m_activeStudios.end(), std::make_shared<Studio>(*this, mainOut));
m_activeStudios.back()->m_auxA.m_backendSubmix = m_backend.allocateSubmix(m_activeStudios.back()->m_auxA, mainOut);
m_activeStudios.back()->m_auxB.m_backendSubmix = m_backend.allocateSubmix(m_activeStudios.back()->m_auxB, mainOut);
return it;
}
@ -118,15 +115,15 @@ std::list<std::shared_ptr<Sequencer>>::iterator Engine::_destroySequencer(std::l
return m_activeSequencers.erase(it);
}
std::list<Submix>::iterator Engine::_destroySubmix(std::list<Submix>::iterator it)
std::list<std::shared_ptr<Studio>>::iterator Engine::_destroyStudio(std::list<std::shared_ptr<Studio>>::iterator it)
{
#ifndef NDEBUG
assert(this == &it->getEngine());
assert(this == &(*it)->getEngine());
#endif
if (it->m_destroyed)
return m_activeSubmixes.begin();
it->_destroy();
return m_activeSubmixes.erase(it);
if ((*it)->m_destroyed)
return m_activeStudios.begin();
(*it)->_destroy();
return m_activeStudios.erase(it);
}
void Engine::_bringOutYourDead()
@ -286,59 +283,47 @@ void Engine::removeAudioGroup(const AudioGroupData& data)
m_audioGroups.erase(search);
}
/** Create new Submix (a.k.a 'Studio') within root mix engine */
Submix* Engine::addSubmix(Submix* smx)
/** Create new Studio within engine */
std::shared_ptr<Studio> Engine::addStudio(bool mainOut)
{
return &*_allocateSubmix(smx);
return *_allocateStudio(mainOut);
}
std::list<Submix>::iterator Engine::_removeSubmix(std::list<Submix>::iterator smx)
std::list<std::shared_ptr<Studio>>::iterator Engine::_removeStudio(std::list<std::shared_ptr<Studio>>::iterator smx)
{
/* Delete all voices bound to submix */
/* Delete all voices bound to studio */
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ; ++it)
{
Voice* vox = it->get();
Submix* vsmx = vox->getSubmix();
if (vsmx == &*smx)
std::shared_ptr<Studio> vsmx = vox->getStudio();
if (vsmx == *smx)
vox->kill();
}
/* Delete all sequencers bound to submix */
/* Delete all sequencers bound to studio */
for (auto it = m_activeSequencers.begin() ; it != m_activeSequencers.end() ; ++it)
{
Sequencer* seq = it->get();
Submix* ssmx = seq->getSubmix();
if (ssmx == &*smx)
std::shared_ptr<Studio> ssmx = seq->getStudio();
if (ssmx == *smx)
seq->kill();
}
/* Delete all submixes bound to submix */
for (auto it = m_activeSubmixes.begin() ; it != m_activeSubmixes.end() ;)
{
Submix* ssmx = it->getParentSubmix();
if (ssmx == &*smx)
{
it = _removeSubmix(it);
continue;
}
++it;
}
/* Delete submix */
return _destroySubmix(smx);
/* Delete studio */
return _destroyStudio(smx);
}
/** Remove Submix and deallocate */
void Engine::removeSubmix(Submix* smx)
void Engine::removeStudio(std::weak_ptr<Studio> smx)
{
if (!smx)
std::shared_ptr<Studio> sm = smx.lock();
if (sm == m_defaultStudio)
return;
for (auto it = m_activeSubmixes.begin() ; it != m_activeSubmixes.end() ;)
for (auto it = m_activeStudios.begin() ; it != m_activeStudios.end() ;)
{
if (&*it == &*smx)
if (*it == sm)
{
it = _removeSubmix(it);
it = _removeStudio(it);
break;
}
++it;
@ -346,7 +331,7 @@ void Engine::removeSubmix(Submix* smx)
}
/** Start soundFX playing from loaded audio groups */
std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, Submix* smx)
std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, std::weak_ptr<Studio> smx)
{
auto search = m_sfxLookup.find(sfxId);
if (search == m_sfxLookup.end())
@ -375,7 +360,8 @@ std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, Submix*
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
std::shared_ptr<Emitter> Engine::addEmitter(const Vector3f& pos, const Vector3f& dir, float maxDist,
float falloff, int sfxId, float minVol, float maxVol, Submix* smx)
float falloff, int sfxId, float minVol, float maxVol,
std::weak_ptr<Studio> smx)
{
auto search = m_sfxLookup.find(sfxId);
if (search == m_sfxLookup.end())
@ -413,7 +399,7 @@ std::shared_ptr<Emitter> Engine::addEmitter(const Vector3f& pos, const Vector3f&
/** Start song playing from loaded audio groups */
std::shared_ptr<Sequencer> Engine::seqPlay(int groupId, int songId,
const unsigned char* arrData, Submix* smx)
const unsigned char* arrData, std::weak_ptr<Studio> smx)
{
std::pair<AudioGroup*, const SongGroupIndex*> songGrp = _findSongGroup(groupId);
if (songGrp.second)

View File

@ -47,36 +47,40 @@ void Sequencer::_bringOutYourDead()
void Sequencer::_destroy()
{
Entity::_destroy();
if (m_submix)
if (m_studio)
{
m_engine.removeSubmix(m_submix);
m_submix = nullptr;
m_engine.removeStudio(m_studio);
m_studio.reset();
}
}
Sequencer::~Sequencer()
{
if (m_submix)
if (m_studio)
{
m_engine.removeSubmix(m_submix);
m_submix = nullptr;
m_engine.removeStudio(m_studio);
m_studio.reset();
}
}
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId,
const SongGroupIndex* songGroup, int setupId, Submix* smx)
const SongGroupIndex* songGroup, int setupId, std::weak_ptr<Studio> studio)
: Entity(engine, group, groupId), m_songGroup(songGroup)
{
auto it = m_songGroup->m_midiSetups.find(setupId);
if (it != m_songGroup->m_midiSetups.cend())
m_midiSetup = it->second->data();
m_submix = m_engine.addSubmix(smx);
m_submix->makeReverbHi(0.2f, 0.3f, 1.f, 0.5f, 0.f, 0.f);
std::shared_ptr<Studio> st = studio.lock();
m_studio = m_engine.addStudio(st ? false : true);
if (st)
m_studio->addStudioSend(studio, 1.f, 0.f, 0.f);
m_studio->getAuxA().makeReverbHi(0.2f, 0.3f, 1.f, 0.5f, 0.f, 0.f);
m_studio->getAuxB().makeChorus(10, 5, 1000);
}
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId,
const SFXGroupIndex* sfxGroup, Submix* smx)
const SFXGroupIndex* sfxGroup, std::weak_ptr<Studio> studio)
: Entity(engine, group, groupId), m_sfxGroup(sfxGroup)
{
//m_submix = m_engine.addSubmix(smx);
@ -217,7 +221,7 @@ std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo
std::list<std::shared_ptr<Voice>>::iterator ret =
m_parent.m_engine._allocateVoice(m_parent.m_audioGroup,
m_parent.m_groupId, 32000.0,
true, false, m_parent.m_submix);
true, false, m_parent.m_studio);
if (*ret)
{
m_chanVoxs[note] = *ret;

65
lib/Studio.cpp Normal file
View File

@ -0,0 +1,65 @@
#include "amuse/Studio.hpp"
#include "amuse/Engine.hpp"
namespace amuse
{
void Studio::_destroy()
{
m_destroyed = true;
}
void Studio::_bringOutYourDead()
{
for (auto it = m_studiosOut.begin() ; it != m_studiosOut.end() ;)
{
std::shared_ptr<Studio> studio = it->m_targetStudio.lock();
if (!studio)
it = m_studiosOut.erase(it);
else
++it;
}
}
#ifndef NDEBUG
bool Studio::_cyclicCheck(Studio* leaf)
{
for (auto it = m_studiosOut.begin() ; it != m_studiosOut.end() ;)
{
if (std::shared_ptr<Studio> studio = it->m_targetStudio.lock())
{
if (leaf == studio.get() || studio->_cyclicCheck(leaf))
return true;
++it;
}
else
it = m_studiosOut.erase(it);
}
return false;
}
#endif
Studio::Studio(Engine& engine, bool mainOut)
: m_engine(engine), m_auxA(engine), m_auxB(engine)
{
if (mainOut)
addStudioSend(engine.getDefaultStudio(), 1.f, 1.f, 1.f);
}
void Studio::addStudioSend(std::weak_ptr<Studio> studio, float dry, float auxA, float auxB)
{
#ifndef NDEBUG
/* Cyclic check */
assert(!_cyclicCheck(this));
#endif
m_studiosOut.emplace_back(studio, dry, auxA, auxB);
}
void Studio::resetOutputSampleRate(double sampleRate)
{
m_auxA.resetOutputSampleRate(sampleRate);
m_auxB.resetOutputSampleRate(sampleRate);
}
}

View File

@ -8,8 +8,8 @@ void Submix::_destroy()
m_destroyed = true;
}
Submix::Submix(Engine& engine, Submix* smx)
: m_root(engine), m_submix(smx)
Submix::Submix(Engine& engine)
: m_root(engine)
{}
EffectChorus& Submix::makeChorus(uint32_t baseDelay, uint32_t variation, uint32_t period)

View File

@ -27,14 +27,14 @@ Voice::~Voice()
//fprintf(stderr, "DEALLOC %d\n", m_vid);
}
Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, Submix* smx)
: Entity(engine, group, groupId), m_vid(vid), m_emitter(emitter), m_submix(smx)
Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, std::weak_ptr<Studio> studio)
: Entity(engine, group, groupId), m_vid(vid), m_emitter(emitter), m_studio(studio)
{
//fprintf(stderr, "ALLOC %d\n", m_vid);
}
Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, Submix* smx)
: Entity(engine, group, groupId, oid), m_vid(vid), m_emitter(emitter), m_submix(smx)
Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, std::weak_ptr<Studio> studio)
: Entity(engine, group, groupId, oid), m_vid(vid), m_emitter(emitter), m_studio(studio)
{
//fprintf(stderr, "ALLOC %d\n", m_vid);
}
@ -174,13 +174,9 @@ std::unique_ptr<int8_t[]>& Voice::_ensureCtrlVals()
std::list<std::shared_ptr<Voice>>::iterator Voice::_allocateVoice(double sampleRate, bool dynamicPitch)
{
auto it = m_childVoices.emplace(m_childVoices.end(), new Voice(m_engine, m_audioGroup,
m_groupId, m_engine.m_nextVid++, m_emitter, m_submix));
if (m_submix)
m_childVoices.back()->m_backendVoice =
m_submix->m_backendSubmix->allocateVoice(*m_childVoices.back(), sampleRate, dynamicPitch);
else
m_childVoices.back()->m_backendVoice =
m_engine.getBackend().allocateVoice(*m_childVoices.back(), sampleRate, dynamicPitch);
m_groupId, m_engine.m_nextVid++, m_emitter, m_studio));
m_childVoices.back()->m_backendVoice =
m_engine.getBackend().allocateVoice(*m_childVoices.back(), sampleRate, dynamicPitch);
return it;
}
@ -897,11 +893,12 @@ void Voice::_setPan(float pan)
coefs[7] = (totalPan >= 0.f) ? 1.f : (1.f + totalPan);
coefs[7] *= 1.f - std::fabs(totalSpan);
m_backendVoice->setMatrixCoefficients(coefs, true);
m_backendVoice->setChannelLevels(nullptr, coefs, true);
float revCoefs[8];
for (int i=0 ; i<8 ; ++i)
coefs[i] *= m_curReverbVol;
m_backendVoice->setSubmixMatrixCoefficients(coefs, true);
revCoefs[i] = coefs[i] * m_curReverbVol;
m_backendVoice->setChannelLevels(m_studio->getAuxA().m_backendSubmix.get(), revCoefs, true);
}
void Voice::setPan(float pan)