2016-05-03 01:16:26 +00:00
|
|
|
#include "amuse/Engine.hpp"
|
|
|
|
#include "amuse/Voice.hpp"
|
2016-05-07 22:10:57 +00:00
|
|
|
#include "amuse/Submix.hpp"
|
2016-05-03 01:16:26 +00:00
|
|
|
#include "amuse/Sequencer.hpp"
|
|
|
|
#include "amuse/IBackendVoice.hpp"
|
|
|
|
#include "amuse/IBackendVoiceAllocator.hpp"
|
|
|
|
#include "amuse/AudioGroupData.hpp"
|
|
|
|
#include "amuse/AudioGroup.hpp"
|
|
|
|
|
|
|
|
namespace amuse
|
|
|
|
{
|
|
|
|
|
|
|
|
Engine::Engine(IBackendVoiceAllocator& backend)
|
|
|
|
: m_backend(backend)
|
|
|
|
{}
|
|
|
|
|
2016-05-07 22:10:57 +00:00
|
|
|
Voice* Engine::_allocateVoice(const AudioGroup& group, double sampleRate,
|
|
|
|
bool dynamicPitch, bool emitter, Submix* smx)
|
2016-05-03 01:16:26 +00:00
|
|
|
{
|
2016-05-07 22:10:57 +00:00
|
|
|
auto it = m_activeVoices.emplace(m_activeVoices.end(), *this, group, m_nextVid++, emitter, smx);
|
2016-05-03 05:16:37 +00:00
|
|
|
m_activeVoices.back().m_backendVoice =
|
|
|
|
m_backend.allocateVoice(m_activeVoices.back(), sampleRate, dynamicPitch);
|
2016-05-06 21:22:39 +00:00
|
|
|
m_activeVoices.back().m_engineIt = it;
|
2016-05-03 05:16:37 +00:00
|
|
|
return &m_activeVoices.back();
|
2016-05-03 01:16:26 +00:00
|
|
|
}
|
|
|
|
|
2016-05-07 22:10:57 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2016-05-06 21:22:39 +00:00
|
|
|
std::list<Voice>::iterator Engine::_destroyVoice(Voice* voice)
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
|
|
|
assert(this == &voice->getEngine());
|
|
|
|
#endif
|
|
|
|
voice->_destroy();
|
|
|
|
return m_activeVoices.erase(voice->m_engineIt);
|
|
|
|
}
|
|
|
|
|
2016-05-07 22:10:57 +00:00
|
|
|
std::list<Submix>::iterator Engine::_destroySubmix(Submix* smx)
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
|
|
|
assert(this == &smx->getEngine());
|
|
|
|
#endif
|
|
|
|
smx->_destroy();
|
|
|
|
return m_activeSubmixes.erase(smx->m_engineIt);
|
|
|
|
}
|
|
|
|
|
2016-05-03 01:16:26 +00:00
|
|
|
AudioGroup* Engine::_findGroupFromSfxId(int sfxId, const AudioGroupSampleDirectory::Entry*& entOut) const
|
|
|
|
{
|
|
|
|
for (const auto& grp : m_audioGroups)
|
|
|
|
{
|
|
|
|
entOut = grp.second->getSfxEntry(sfxId);
|
|
|
|
if (entOut)
|
|
|
|
return grp.second.get();
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioGroup* Engine::_findGroupFromSongId(int songId) const
|
|
|
|
{
|
|
|
|
for (const auto& grp : m_audioGroups)
|
|
|
|
if (grp.second->songInGroup(songId))
|
|
|
|
return grp.second.get();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Update all active audio entities and fill OS audio buffers as needed */
|
|
|
|
void Engine::pumpEngine()
|
|
|
|
{
|
2016-05-04 05:19:13 +00:00
|
|
|
int maxVid = -1;
|
|
|
|
for (Voice& vox : m_activeVoices)
|
|
|
|
{
|
|
|
|
maxVid = std::max(maxVid, vox.vid());
|
|
|
|
}
|
|
|
|
m_nextVid = maxVid + 1;
|
2016-05-03 01:16:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Add audio group data pointers to engine; must remain resident! */
|
2016-05-07 22:10:57 +00:00
|
|
|
const AudioGroup* Engine::addAudioGroup(int groupId, const AudioGroupData& data)
|
2016-05-03 01:16:26 +00:00
|
|
|
{
|
|
|
|
std::unique_ptr<AudioGroup> grp = std::make_unique<AudioGroup>(groupId, data);
|
|
|
|
if (!grp)
|
2016-05-07 22:10:57 +00:00
|
|
|
return nullptr;
|
|
|
|
AudioGroup* ret = grp.get();
|
2016-05-03 01:16:26 +00:00
|
|
|
m_audioGroups.emplace(std::make_pair(groupId, std::move(grp)));
|
2016-05-07 22:10:57 +00:00
|
|
|
return ret;
|
2016-05-03 01:16:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Remove audio group from engine */
|
|
|
|
void Engine::removeAudioGroup(int groupId)
|
|
|
|
{
|
|
|
|
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ;)
|
|
|
|
{
|
2016-05-06 05:19:19 +00:00
|
|
|
if (it->getAudioGroup().groupId() == groupId)
|
2016-05-03 01:16:26 +00:00
|
|
|
{
|
2016-05-07 22:10:57 +00:00
|
|
|
it->_destroy();
|
2016-05-03 01:16:26 +00:00
|
|
|
it = m_activeVoices.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto it = m_activeEmitters.begin() ; it != m_activeEmitters.end() ;)
|
|
|
|
{
|
2016-05-06 05:19:19 +00:00
|
|
|
if (it->getAudioGroup().groupId() == groupId)
|
2016-05-03 01:16:26 +00:00
|
|
|
{
|
2016-05-07 22:10:57 +00:00
|
|
|
it->_destroy();
|
2016-05-03 01:16:26 +00:00
|
|
|
it = m_activeEmitters.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto it = m_activeSequencers.begin() ; it != m_activeSequencers.end() ;)
|
|
|
|
{
|
2016-05-06 05:19:19 +00:00
|
|
|
if (it->getAudioGroup().groupId() == groupId)
|
2016-05-03 01:16:26 +00:00
|
|
|
{
|
2016-05-07 22:10:57 +00:00
|
|
|
it->_destroy();
|
2016-05-03 01:16:26 +00:00
|
|
|
it = m_activeSequencers.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_audioGroups.erase(groupId);
|
|
|
|
}
|
|
|
|
|
2016-05-07 22:10:57 +00:00
|
|
|
/** 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);
|
|
|
|
}
|
|
|
|
|
2016-05-03 01:16:26 +00:00
|
|
|
/** Start soundFX playing from loaded audio groups */
|
2016-05-07 22:10:57 +00:00
|
|
|
Voice* Engine::fxStart(int sfxId, float vol, float pan, Submix* smx)
|
2016-05-03 01:16:26 +00:00
|
|
|
{
|
|
|
|
const AudioGroupSampleDirectory::Entry* entry;
|
|
|
|
AudioGroup* grp = _findGroupFromSfxId(sfxId, entry);
|
|
|
|
if (!grp)
|
|
|
|
return nullptr;
|
|
|
|
|
2016-05-07 22:10:57 +00:00
|
|
|
Voice* ret = _allocateVoice(*grp, entry->m_sampleRate, true, false, smx);
|
2016-05-03 01:16:26 +00:00
|
|
|
ret->setVolume(vol);
|
|
|
|
ret->setPanning(pan);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Start soundFX playing from loaded audio groups, attach to positional emitter */
|
|
|
|
Emitter* Engine::addEmitter(const Vector3f& pos, const Vector3f& dir, float maxDist,
|
2016-05-07 22:10:57 +00:00
|
|
|
float falloff, int sfxId, float minVol, float maxVol, Submix* smx)
|
2016-05-03 01:16:26 +00:00
|
|
|
{
|
|
|
|
const AudioGroupSampleDirectory::Entry* entry;
|
|
|
|
AudioGroup* grp = _findGroupFromSfxId(sfxId, entry);
|
|
|
|
if (!grp)
|
|
|
|
return nullptr;
|
|
|
|
|
2016-05-07 22:10:57 +00:00
|
|
|
Voice* vox = _allocateVoice(*grp, entry->m_sampleRate, true, true, smx);
|
2016-05-06 05:19:19 +00:00
|
|
|
m_activeEmitters.emplace_back(*this, *grp, *vox);
|
2016-05-03 05:16:37 +00:00
|
|
|
Emitter& ret = m_activeEmitters.back();
|
|
|
|
ret.setPos(pos);
|
|
|
|
ret.setDir(dir);
|
|
|
|
ret.setMaxDist(maxDist);
|
|
|
|
ret.setFalloff(falloff);
|
|
|
|
ret.setMinVol(minVol);
|
|
|
|
ret.setMaxVol(maxVol);
|
|
|
|
|
|
|
|
return &ret;
|
2016-05-03 01:16:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Start song playing from loaded audio groups */
|
|
|
|
Sequencer* Engine::seqPlay(int songId, const unsigned char* arrData)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-05-03 05:16:37 +00:00
|
|
|
/** Find voice from VoiceId */
|
|
|
|
Voice* Engine::findVoice(int vid)
|
|
|
|
{
|
|
|
|
for (Voice& vox : m_activeVoices)
|
|
|
|
if (vox.vid() == vid)
|
|
|
|
return &vox;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-05-06 21:22:39 +00:00
|
|
|
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `flag` set */
|
|
|
|
void Engine::killKeygroup(uint8_t kg, uint8_t flag)
|
|
|
|
{
|
|
|
|
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ;)
|
|
|
|
{
|
|
|
|
if (it->m_keygroup == kg)
|
|
|
|
{
|
|
|
|
if (flag)
|
|
|
|
{
|
|
|
|
it = _destroyVoice(&*it);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
it->keyOff();
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Send all voices using `macroId` the message `val` */
|
|
|
|
void Engine::sendMacroMessage(ObjectId macroId, int32_t val)
|
|
|
|
{
|
|
|
|
for (auto it = m_activeVoices.begin() ; it != m_activeVoices.end() ; ++it)
|
|
|
|
if (it->getObjectId() == macroId)
|
|
|
|
it->message(val);
|
|
|
|
}
|
|
|
|
|
2016-05-03 01:16:26 +00:00
|
|
|
}
|