mirror of https://github.com/AxioDL/amuse.git
Submix class and initial effects
This commit is contained in:
parent
708662c23e
commit
3cce975977
|
@ -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)
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef __AMUSE_EFFECTBASE_HPP__
|
||||
#define __AMUSE_EFFECTBASE_HPP__
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
class EffectBase
|
||||
{
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_EFFECTBASE_HPP__
|
|
@ -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__
|
|
@ -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__
|
|
@ -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__
|
|
@ -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__
|
|
@ -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);
|
||||
|
|
|
@ -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__
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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__
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue