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/Voice.cpp
lib/VolumeLUT.cpp lib/VolumeLUT.cpp
lib/Submix.cpp lib/Submix.cpp
lib/Studio.cpp
lib/EffectBase.cpp lib/EffectBase.cpp
lib/EffectReverb.cpp lib/EffectReverb.cpp
lib/EffectChorus.cpp lib/EffectChorus.cpp
@ -49,6 +50,7 @@ set(HEADERS
include/amuse/SongState.hpp include/amuse/SongState.hpp
include/amuse/Voice.hpp include/amuse/Voice.hpp
include/amuse/Submix.hpp include/amuse/Submix.hpp
include/amuse/Studio.hpp
include/amuse/IBackendSubmix.hpp include/amuse/IBackendSubmix.hpp
include/amuse/IBackendVoice.hpp include/amuse/IBackendVoice.hpp
include/amuse/IBackendVoiceAllocator.hpp include/amuse/IBackendVoiceAllocator.hpp

View File

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

View File

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

View File

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

View File

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

View File

@ -44,7 +44,7 @@ public:
bool dynamicPitch)=0; bool dynamicPitch)=0;
/** Amuse obtains a new submix from the platform this way */ /** 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 */ /** Amuse obtains a list of all MIDI devices this way */
virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices()=0; virtual std::vector<std::pair<std::string, std::string>> enumerateMIDIDevices()=0;

View File

@ -12,7 +12,7 @@
namespace amuse namespace amuse
{ {
class Submix; class Studio;
class Voice; class Voice;
/** State of sequencer over lifetime */ /** 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 SongGroupIndex::MIDISetup* m_midiSetup = nullptr; /**< Selected MIDI setup (may be null) */
const SFXGroupIndex* m_sfxGroup = nullptr; /**< SFX Groups are alternatively referenced here */ 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 */ 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 */ const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */
SongState m_songState; /**< State of current arrangement playback */ SongState m_songState; /**< State of current arrangement playback */
@ -85,15 +85,15 @@ class Sequencer : public Entity
public: public:
~Sequencer(); ~Sequencer();
Sequencer(Engine& engine, const AudioGroup& group, int groupId, 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, 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) */ /** Advance current song data (if any) */
void advance(double dt); void advance(double dt);
/** Obtain pointer to Sequencer's Submix */ /** Obtain pointer to Sequencer's Submix */
Submix* getSubmix() {return m_submix;} std::shared_ptr<Studio> getStudio() {return m_studio;}
/** Get current state of sequencer */ /** Get current state of sequencer */
SequencerState state() const {return m_state;} 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 Voice;
friend class Sequencer; friend class Sequencer;
Engine& m_root; 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::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 */ std::vector<std::unique_ptr<EffectBaseTypeless>> m_effectStack; /**< Ordered list of effects to apply to submix */
bool m_destroyed = false; bool m_destroyed = false;
void _destroy(); void _destroy();
public: public:
Submix(Engine& engine, Submix* smx); Submix(Engine& engine);
~Submix() ~Submix()
{ {
#ifndef NDEBUG #ifndef NDEBUG
@ -39,9 +38,6 @@ public:
#endif #endif
} }
/** Obtain pointer to Submix's parent Submix */
Submix* getParentSubmix() {return m_submix;}
/** Add new effect to effect stack and assume ownership */ /** Add new effect to effect stack and assume ownership */
template <class T, class ...Args> template <class T, class ...Args>
T& makeEffect(Args... args) T& makeEffect(Args... args)

View File

@ -14,7 +14,7 @@
namespace amuse namespace amuse
{ {
class IBackendVoice; class IBackendVoice;
class Submix; class Studio;
struct Keymap; struct Keymap;
struct LayerMapping; struct LayerMapping;
@ -35,7 +35,7 @@ class Voice : public Entity
friend class Envelope; friend class Envelope;
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 */
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 */ std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
SoundMacroState m_state; /**< State container for SoundMacro playback */ SoundMacroState m_state; /**< State container for SoundMacro playback */
@ -164,8 +164,8 @@ class Voice : public Entity
void _notifyCtrlChange(uint8_t ctrl, int8_t val); void _notifyCtrlChange(uint8_t ctrl, int8_t val);
public: public:
~Voice(); ~Voice();
Voice(Engine& engine, const AudioGroup& group, int groupId, 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, Submix* smx); 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 /** Called before each supplyAudio invocation to prepare voice
* backend for possible parameter updates */ * backend for possible parameter updates */
@ -175,8 +175,8 @@ public:
* internally advancing the voice stream */ * internally advancing the voice stream */
size_t supplyAudio(size_t frames, int16_t* data); size_t supplyAudio(size_t frames, int16_t* data);
/** Obtain pointer to Voice's Submix */ /** Obtain pointer to Voice's Studio */
Submix* getSubmix() {return m_submix;} std::shared_ptr<Studio> getStudio() {return m_studio;}
/** Get current state of voice */ /** Get current state of voice */
VoiceState state() const {return m_voxState;} 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)) 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) void BooBackendVoice::resetSampleRate(double sampleRate)
{ {
m_booVoice->resetSampleRate(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) void BooBackendVoice::setPitchRatio(double ratio, bool slew)
@ -88,23 +83,14 @@ void BooBackendSubmix::SubmixCallback::resetOutputSampleRate(double sampleRate)
m_parent.m_clientSmx.resetOutputSampleRate(sampleRate); m_parent.m_clientSmx.resetOutputSampleRate(sampleRate);
} }
BooBackendSubmix::BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx) BooBackendSubmix::BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx, bool mainOut)
: m_clientSmx(clientSmx), m_cb(*this), m_booSubmix(engine.allocateNewSubmix(&m_cb)) : m_clientSmx(clientSmx), m_cb(*this), m_booSubmix(engine.allocateNewSubmix(mainOut, &m_cb))
{} {}
BooBackendSubmix::BooBackendSubmix(boo::IAudioSubmix& parent, Submix& clientSmx) void BooBackendSubmix::setSendLevel(IBackendSubmix* submix, float level, bool slew)
: m_clientSmx(clientSmx), m_cb(*this), m_booSubmix(parent.allocateNewSubmix(&m_cb))
{}
void BooBackendSubmix::setChannelGains(const float gains[8])
{ {
m_booSubmix->setChannelGains(gains); BooBackendSubmix& smx = *reinterpret_cast<BooBackendSubmix*>(submix);
} m_booSubmix->setSendLevel(smx.m_booSubmix.get(), level, slew);
std::unique_ptr<IBackendVoice>
BooBackendSubmix::allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch)
{
return std::make_unique<BooBackendVoice>(*m_booSubmix, clientVox, sampleRate, dynamicPitch);
} }
double BooBackendSubmix::getSampleRate() const 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); 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() 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) for (std::shared_ptr<Sequencer>& seq : m_activeSequencers)
if (!seq->m_destroyed) if (!seq->m_destroyed)
seq->_destroy(); seq->_destroy();
while (m_activeSubmixes.size()) while (m_activeStudios.size())
removeSubmix(&m_activeSubmixes.front()); removeStudio(m_activeStudios.front());
for (std::shared_ptr<Emitter>& emitter : m_activeEmitters) for (std::shared_ptr<Emitter>& emitter : m_activeEmitters)
emitter->_destroy(); emitter->_destroy();
for (std::shared_ptr<Voice>& vox : m_activeVoices) for (std::shared_ptr<Voice>& vox : m_activeVoices)
@ -25,7 +25,7 @@ Engine::~Engine()
} }
Engine::Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode) 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)); backend.register5MsCallback(std::bind(&Engine::_5MsCallback, this, std::placeholders::_1));
m_midiReader = backend.allocateMIDIReader(*this); 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 std::list<std::shared_ptr<Voice>>::iterator
Engine::_allocateVoice(const AudioGroup& group, int groupId, double sampleRate, 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(), auto it = m_activeVoices.emplace(m_activeVoices.end(),
new Voice(*this, group, groupId, m_nextVid++, emitter, smx)); new Voice(*this, group, groupId, m_nextVid++, emitter, studio));
if (smx) m_activeVoices.back()->m_backendVoice =
m_activeVoices.back()->m_backendVoice = m_backend.allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
smx->m_backendSubmix->allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
else
m_activeVoices.back()->m_backendVoice =
m_backend.allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch);
return it; return it;
} }
std::list<std::shared_ptr<Sequencer>>::iterator std::list<std::shared_ptr<Sequencer>>::iterator
Engine::_allocateSequencer(const AudioGroup& group, int groupId, 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); const SongGroupIndex* songGroup = group.getProj().getSongGroupIndex(groupId);
if (songGroup) if (songGroup)
{ {
auto it = m_activeSequencers.emplace(m_activeSequencers.end(), 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; return it;
} }
const SFXGroupIndex* sfxGroup = group.getProj().getSFXGroupIndex(groupId); const SFXGroupIndex* sfxGroup = group.getProj().getSFXGroupIndex(groupId);
if (sfxGroup) if (sfxGroup)
{ {
auto it = m_activeSequencers.emplace(m_activeSequencers.end(), 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 it;
} }
return {}; 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); auto it = m_activeStudios.emplace(m_activeStudios.end(), std::make_shared<Studio>(*this, mainOut));
m_activeSubmixes.back().m_backendSubmix = m_backend.allocateSubmix(m_activeSubmixes.back()); 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; return it;
} }
@ -118,15 +115,15 @@ std::list<std::shared_ptr<Sequencer>>::iterator Engine::_destroySequencer(std::l
return m_activeSequencers.erase(it); 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 #ifndef NDEBUG
assert(this == &it->getEngine()); assert(this == &(*it)->getEngine());
#endif #endif
if (it->m_destroyed) if ((*it)->m_destroyed)
return m_activeSubmixes.begin(); return m_activeStudios.begin();
it->_destroy(); (*it)->_destroy();
return m_activeSubmixes.erase(it); return m_activeStudios.erase(it);
} }
void Engine::_bringOutYourDead() void Engine::_bringOutYourDead()
@ -286,59 +283,47 @@ void Engine::removeAudioGroup(const AudioGroupData& data)
m_audioGroups.erase(search); m_audioGroups.erase(search);
} }
/** Create new Submix (a.k.a 'Studio') within root mix engine */ /** Create new Studio within engine */
Submix* Engine::addSubmix(Submix* smx) 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) for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ; ++it)
{ {
Voice* vox = it->get(); Voice* vox = it->get();
Submix* vsmx = vox->getSubmix(); std::shared_ptr<Studio> vsmx = vox->getStudio();
if (vsmx == &*smx) if (vsmx == *smx)
vox->kill(); 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) for (auto it = m_activeSequencers.begin() ; it != m_activeSequencers.end() ; ++it)
{ {
Sequencer* seq = it->get(); Sequencer* seq = it->get();
Submix* ssmx = seq->getSubmix(); std::shared_ptr<Studio> ssmx = seq->getStudio();
if (ssmx == &*smx) if (ssmx == *smx)
seq->kill(); seq->kill();
} }
/* Delete all submixes bound to submix */ /* Delete studio */
for (auto it = m_activeSubmixes.begin() ; it != m_activeSubmixes.end() ;) return _destroyStudio(smx);
{
Submix* ssmx = it->getParentSubmix();
if (ssmx == &*smx)
{
it = _removeSubmix(it);
continue;
}
++it;
}
/* Delete submix */
return _destroySubmix(smx);
} }
/** Remove Submix and deallocate */ /** 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; return;
for (auto it = m_activeStudios.begin() ; it != m_activeStudios.end() ;)
for (auto it = m_activeSubmixes.begin() ; it != m_activeSubmixes.end() ;)
{ {
if (&*it == &*smx) if (*it == sm)
{ {
it = _removeSubmix(it); it = _removeStudio(it);
break; break;
} }
++it; ++it;
@ -346,7 +331,7 @@ void Engine::removeSubmix(Submix* smx)
} }
/** Start soundFX playing from loaded audio groups */ /** 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); auto search = m_sfxLookup.find(sfxId);
if (search == m_sfxLookup.end()) 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 */ /** 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, 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); auto search = m_sfxLookup.find(sfxId);
if (search == m_sfxLookup.end()) 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 */ /** Start song playing from loaded audio groups */
std::shared_ptr<Sequencer> Engine::seqPlay(int groupId, int songId, 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); std::pair<AudioGroup*, const SongGroupIndex*> songGrp = _findSongGroup(groupId);
if (songGrp.second) if (songGrp.second)

View File

@ -47,36 +47,40 @@ void Sequencer::_bringOutYourDead()
void Sequencer::_destroy() void Sequencer::_destroy()
{ {
Entity::_destroy(); Entity::_destroy();
if (m_submix) if (m_studio)
{ {
m_engine.removeSubmix(m_submix); m_engine.removeStudio(m_studio);
m_submix = nullptr; m_studio.reset();
} }
} }
Sequencer::~Sequencer() Sequencer::~Sequencer()
{ {
if (m_submix) if (m_studio)
{ {
m_engine.removeSubmix(m_submix); m_engine.removeStudio(m_studio);
m_submix = nullptr; m_studio.reset();
} }
} }
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, 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) : Entity(engine, group, groupId), m_songGroup(songGroup)
{ {
auto it = m_songGroup->m_midiSetups.find(setupId); auto it = m_songGroup->m_midiSetups.find(setupId);
if (it != m_songGroup->m_midiSetups.cend()) if (it != m_songGroup->m_midiSetups.cend())
m_midiSetup = it->second->data(); m_midiSetup = it->second->data();
m_submix = m_engine.addSubmix(smx); std::shared_ptr<Studio> st = studio.lock();
m_submix->makeReverbHi(0.2f, 0.3f, 1.f, 0.5f, 0.f, 0.f); 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, 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) : Entity(engine, group, groupId), m_sfxGroup(sfxGroup)
{ {
//m_submix = m_engine.addSubmix(smx); //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 = std::list<std::shared_ptr<Voice>>::iterator ret =
m_parent.m_engine._allocateVoice(m_parent.m_audioGroup, m_parent.m_engine._allocateVoice(m_parent.m_audioGroup,
m_parent.m_groupId, 32000.0, m_parent.m_groupId, 32000.0,
true, false, m_parent.m_submix); true, false, m_parent.m_studio);
if (*ret) if (*ret)
{ {
m_chanVoxs[note] = *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; m_destroyed = true;
} }
Submix::Submix(Engine& engine, Submix* smx) Submix::Submix(Engine& engine)
: m_root(engine), m_submix(smx) : m_root(engine)
{} {}
EffectChorus& Submix::makeChorus(uint32_t baseDelay, uint32_t variation, uint32_t period) 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); //fprintf(stderr, "DEALLOC %d\n", m_vid);
} }
Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, 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_submix(smx) : Entity(engine, group, groupId), m_vid(vid), m_emitter(emitter), m_studio(studio)
{ {
//fprintf(stderr, "ALLOC %d\n", m_vid); //fprintf(stderr, "ALLOC %d\n", m_vid);
} }
Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, 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_submix(smx) : Entity(engine, group, groupId, oid), m_vid(vid), m_emitter(emitter), m_studio(studio)
{ {
//fprintf(stderr, "ALLOC %d\n", m_vid); //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) 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, auto it = m_childVoices.emplace(m_childVoices.end(), new Voice(m_engine, m_audioGroup,
m_groupId, m_engine.m_nextVid++, m_emitter, m_submix)); m_groupId, m_engine.m_nextVid++, m_emitter, m_studio));
if (m_submix) m_childVoices.back()->m_backendVoice =
m_childVoices.back()->m_backendVoice = m_engine.getBackend().allocateVoice(*m_childVoices.back(), sampleRate, dynamicPitch);
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);
return it; return it;
} }
@ -897,11 +893,12 @@ void Voice::_setPan(float pan)
coefs[7] = (totalPan >= 0.f) ? 1.f : (1.f + totalPan); coefs[7] = (totalPan >= 0.f) ? 1.f : (1.f + totalPan);
coefs[7] *= 1.f - std::fabs(totalSpan); 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) for (int i=0 ; i<8 ; ++i)
coefs[i] *= m_curReverbVol; revCoefs[i] = coefs[i] * m_curReverbVol;
m_backendVoice->setSubmixMatrixCoefficients(coefs, true); m_backendVoice->setChannelLevels(m_studio->getAuxA().m_backendSubmix.get(), revCoefs, true);
} }
void Voice::setPan(float pan) void Voice::setPan(float pan)