mirror of https://github.com/AxioDL/boo.git
Object tracker list refactor; add object tracker to audio system
This commit is contained in:
parent
3c4f304d5a
commit
03de8811b1
|
@ -269,6 +269,7 @@ add_library(boo
|
|||
lib/audiodev/MIDIDecoder.cpp
|
||||
lib/audiodev/MIDICommon.hpp
|
||||
lib/audiodev/MIDICommon.cpp
|
||||
lib/Common.hpp
|
||||
include/boo/inputdev/IHIDListener.hpp
|
||||
include/boo/IGraphicsContext.hpp
|
||||
include/boo/graphicsdev/IGraphicsDataFactory.hpp
|
||||
|
|
|
@ -78,9 +78,6 @@ struct IAudioVoice
|
|||
|
||||
/** Instructs platform to stop consuming sample data */
|
||||
virtual void stop()=0;
|
||||
|
||||
/** Invalidates this voice by removing it from the AudioVoiceEngine */
|
||||
virtual void unbindVoice()=0;
|
||||
};
|
||||
|
||||
struct IAudioVoiceCallback
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef BOO_INTERNAL_COMMON_HPP
|
||||
#define BOO_INTERNAL_COMMON_HPP
|
||||
|
||||
#include "boo/BooObject.hpp"
|
||||
#include <iterator>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
/** Linked-list iterator shareable by data container types */
|
||||
template<class T>
|
||||
class DataIterator : public std::iterator<std::bidirectional_iterator_tag, T>
|
||||
{
|
||||
T* m_node;
|
||||
public:
|
||||
explicit DataIterator(T* node) : m_node(node) {}
|
||||
T& operator*() const { return *m_node; }
|
||||
bool operator!=(const DataIterator& other) const { return m_node != other.m_node; }
|
||||
DataIterator& operator++() { m_node = m_node->m_next; return *this; }
|
||||
DataIterator& operator--() { m_node = m_node->m_prev; return *this; }
|
||||
};
|
||||
|
||||
/** Linked-list IObj node made part of objects participating in list
|
||||
* Subclasses must implement static methods _getHeadPtr() and _getHeadLock()
|
||||
* to support the common list-management functionality.
|
||||
*/
|
||||
template <class N, class H, class P = IObj>
|
||||
struct ListNode : P
|
||||
{
|
||||
using iterator = DataIterator<N>;
|
||||
iterator begin() { return iterator(static_cast<N*>(this)); }
|
||||
iterator end() { return iterator(nullptr); }
|
||||
|
||||
H m_head;
|
||||
N* m_next;
|
||||
N* m_prev = nullptr;
|
||||
ListNode(H head) : m_head(head)
|
||||
{
|
||||
auto lk = N::_getHeadLock(head);
|
||||
m_next = N::_getHeadPtr(head);
|
||||
if (m_next)
|
||||
m_next->m_prev = static_cast<N*>(this);
|
||||
N::_getHeadPtr(head) = static_cast<N*>(this);
|
||||
}
|
||||
protected:
|
||||
~ListNode()
|
||||
{
|
||||
if (m_prev)
|
||||
{
|
||||
if (m_next)
|
||||
m_next->m_prev = m_prev;
|
||||
m_prev->m_next = m_next;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_next)
|
||||
m_next->m_prev = nullptr;
|
||||
N::_getHeadPtr(m_head) = m_next;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // BOO_INTERNAL_COMMON_HPP
|
|
@ -8,17 +8,21 @@ namespace boo
|
|||
{
|
||||
|
||||
AudioSubmix::AudioSubmix(BaseAudioVoiceEngine& root, IAudioSubmixCallback* cb, int busId, bool mainOut)
|
||||
: m_root(root), m_busId(busId), m_cb(cb), m_mainOut(mainOut)
|
||||
: ListNode<AudioSubmix, BaseAudioVoiceEngine*, IAudioSubmix>(&root), m_busId(busId), m_cb(cb), m_mainOut(mainOut)
|
||||
{
|
||||
if (mainOut)
|
||||
setSendLevel(&m_root.m_mainSubmix, 1.f, false);
|
||||
setSendLevel(m_head->m_mainSubmix.get(), 1.f, false);
|
||||
}
|
||||
|
||||
AudioSubmix::~AudioSubmix()
|
||||
{
|
||||
unbindSubmix();
|
||||
m_head->m_submixesDirty = true;
|
||||
}
|
||||
|
||||
AudioSubmix*& AudioSubmix::_getHeadPtr(BaseAudioVoiceEngine* head) { return head->m_submixHead; }
|
||||
std::unique_lock<std::recursive_mutex> AudioSubmix::_getHeadLock(BaseAudioVoiceEngine* head)
|
||||
{ return std::unique_lock<std::recursive_mutex>{head->m_dataMutex}; }
|
||||
|
||||
bool AudioSubmix::_isDirectDependencyOf(AudioSubmix* send)
|
||||
{
|
||||
return m_sendGains.find(send) != m_sendGains.cend();
|
||||
|
@ -56,13 +60,14 @@ bool AudioSubmix::_mergeC3(std::list<AudioSubmix*>& output,
|
|||
std::list<AudioSubmix*> AudioSubmix::_linearizeC3()
|
||||
{
|
||||
std::vector<std::list<AudioSubmix*>> lists = {{}};
|
||||
for (AudioSubmix* smx : m_root.m_activeSubmixes)
|
||||
{
|
||||
if (smx == this)
|
||||
continue;
|
||||
if (smx->_isDirectDependencyOf(this))
|
||||
lists[0].push_back(smx);
|
||||
}
|
||||
if (m_head->m_submixHead)
|
||||
for (AudioSubmix& smx : *m_head->m_submixHead)
|
||||
{
|
||||
if (&smx == this)
|
||||
continue;
|
||||
if (smx._isDirectDependencyOf(this))
|
||||
lists[0].push_back(&smx);
|
||||
}
|
||||
lists.reserve(lists[0].size() + 1);
|
||||
for (AudioSubmix* smx : lists[0])
|
||||
lists.push_back(smx->_linearizeC3());
|
||||
|
@ -72,86 +77,82 @@ std::list<AudioSubmix*> AudioSubmix::_linearizeC3()
|
|||
return ret;
|
||||
}
|
||||
|
||||
void AudioSubmix::_zeroFill16()
|
||||
template <typename T>
|
||||
void AudioSubmix::_zeroFill()
|
||||
{
|
||||
if (m_scratch16.size())
|
||||
std::fill(m_scratch16.begin(), m_scratch16.end(), 0);
|
||||
if (_getScratch<T>().size())
|
||||
std::fill(_getScratch<T>().begin(), _getScratch<T>().end(), 0);
|
||||
}
|
||||
|
||||
void AudioSubmix::_zeroFill32()
|
||||
template void AudioSubmix::_zeroFill<int16_t>();
|
||||
template void AudioSubmix::_zeroFill<int32_t>();
|
||||
template void AudioSubmix::_zeroFill<float>();
|
||||
|
||||
template <typename T>
|
||||
T* AudioSubmix::_getMergeBuf(size_t frames)
|
||||
{
|
||||
if (m_scratch32.size())
|
||||
std::fill(m_scratch32.begin(), m_scratch32.end(), 0);
|
||||
if (_getRedirect<T>())
|
||||
return _getRedirect<T>();
|
||||
|
||||
size_t sampleCount = frames * m_head->clientMixInfo().m_channelMap.m_channelCount;
|
||||
if (_getScratch<T>().size() < sampleCount)
|
||||
_getScratch<T>().resize(sampleCount);
|
||||
|
||||
return _getScratch<T>().data();
|
||||
}
|
||||
|
||||
void AudioSubmix::_zeroFillFlt()
|
||||
template int16_t* AudioSubmix::_getMergeBuf<int16_t>(size_t frames);
|
||||
template int32_t* AudioSubmix::_getMergeBuf<int32_t>(size_t frames);
|
||||
template float* AudioSubmix::_getMergeBuf<float>(size_t frames);
|
||||
|
||||
template <typename T>
|
||||
static inline T ClampInt(float in)
|
||||
{
|
||||
if (m_scratchFlt.size())
|
||||
std::fill(m_scratchFlt.begin(), m_scratchFlt.end(), 0.f);
|
||||
if (std::is_floating_point<T>())
|
||||
{
|
||||
return in; // Allow subsequent mixing stages to work with over-saturated values
|
||||
}
|
||||
else
|
||||
{
|
||||
constexpr T MAX = std::numeric_limits<T>::max();
|
||||
constexpr T MIN = std::numeric_limits<T>::min();
|
||||
|
||||
if (in < MIN)
|
||||
return MIN;
|
||||
else if (in > MAX)
|
||||
return MAX;
|
||||
else
|
||||
return in;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t* AudioSubmix::_getMergeBuf16(size_t frames)
|
||||
template <typename T>
|
||||
size_t AudioSubmix::_pumpAndMix(size_t frames)
|
||||
{
|
||||
if (m_redirect16)
|
||||
return m_redirect16;
|
||||
|
||||
size_t sampleCount = frames * m_root.clientMixInfo().m_channelMap.m_channelCount;
|
||||
if (m_scratch16.size() < sampleCount)
|
||||
m_scratch16.resize(sampleCount);
|
||||
|
||||
return m_scratch16.data();
|
||||
}
|
||||
|
||||
int32_t* AudioSubmix::_getMergeBuf32(size_t frames)
|
||||
{
|
||||
if (m_redirect32)
|
||||
return m_redirect32;
|
||||
|
||||
size_t sampleCount = frames * m_root.clientMixInfo().m_channelMap.m_channelCount;
|
||||
if (m_scratch32.size() < sampleCount)
|
||||
m_scratch32.resize(sampleCount);
|
||||
|
||||
return m_scratch32.data();
|
||||
}
|
||||
|
||||
float* AudioSubmix::_getMergeBufFlt(size_t frames)
|
||||
{
|
||||
if (m_redirectFlt)
|
||||
return m_redirectFlt;
|
||||
|
||||
size_t sampleCount = frames * m_root.clientMixInfo().m_channelMap.m_channelCount;
|
||||
if (m_scratchFlt.size() < sampleCount)
|
||||
m_scratchFlt.resize(sampleCount);
|
||||
|
||||
return m_scratchFlt.data();
|
||||
}
|
||||
|
||||
size_t AudioSubmix::_pumpAndMix16(size_t frames)
|
||||
{
|
||||
const ChannelMap& chMap = m_root.clientMixInfo().m_channelMap;
|
||||
const ChannelMap& chMap = m_head->clientMixInfo().m_channelMap;
|
||||
size_t chanCount = chMap.m_channelCount;
|
||||
|
||||
if (m_redirect16)
|
||||
if (_getRedirect<T>())
|
||||
{
|
||||
if (m_cb && m_cb->canApplyEffect())
|
||||
m_cb->applyEffect(m_redirect16, frames, chMap, m_root.mixInfo().m_sampleRate);
|
||||
m_redirect16 += chanCount * frames;
|
||||
m_cb->applyEffect(_getRedirect<T>(), frames, chMap, m_head->mixInfo().m_sampleRate);
|
||||
_getRedirect<T>() += chanCount * frames;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t sampleCount = frames * chanCount;
|
||||
if (m_scratch16.size() < sampleCount)
|
||||
m_scratch16.resize(sampleCount);
|
||||
if (_getScratch<T>().size() < sampleCount)
|
||||
_getScratch<T>().resize(sampleCount);
|
||||
if (m_cb && m_cb->canApplyEffect())
|
||||
m_cb->applyEffect(m_scratch16.data(), frames, chMap, m_root.mixInfo().m_sampleRate);
|
||||
m_cb->applyEffect(_getScratch<T>().data(), frames, chMap, m_head->mixInfo().m_sampleRate);
|
||||
|
||||
size_t curSlewFrame = m_slewFrames;
|
||||
for (auto& smx : m_sendGains)
|
||||
{
|
||||
curSlewFrame = m_curSlewFrame;
|
||||
AudioSubmix& sm = *reinterpret_cast<AudioSubmix*>(smx.first);
|
||||
auto it = m_scratch16.begin();
|
||||
int16_t* dataOut = sm._getMergeBuf16(frames);
|
||||
auto it = _getScratch<T>().begin();
|
||||
T* dataOut = sm._getMergeBuf<T>(frames);
|
||||
|
||||
for (size_t f=0 ; f<frames ; ++f)
|
||||
{
|
||||
|
@ -162,7 +163,7 @@ size_t AudioSubmix::_pumpAndMix16(size_t frames)
|
|||
|
||||
for (unsigned c=0 ; c<chanCount ; ++c)
|
||||
{
|
||||
*dataOut = Clamp16(*dataOut + *it * (smx.second[1] * t + smx.second[0] * omt));
|
||||
*dataOut = ClampInt<T>(*dataOut + *it * (smx.second[1] * t + smx.second[0] * omt));
|
||||
++it;
|
||||
++dataOut;
|
||||
}
|
||||
|
@ -173,7 +174,7 @@ size_t AudioSubmix::_pumpAndMix16(size_t frames)
|
|||
{
|
||||
for (unsigned c=0 ; c<chanCount ; ++c)
|
||||
{
|
||||
*dataOut = Clamp16(*dataOut + *it * smx.second[1]);
|
||||
*dataOut = ClampInt<T>(*dataOut + *it * smx.second[1]);
|
||||
++it;
|
||||
++dataOut;
|
||||
}
|
||||
|
@ -186,130 +187,14 @@ size_t AudioSubmix::_pumpAndMix16(size_t frames)
|
|||
return frames;
|
||||
}
|
||||
|
||||
size_t AudioSubmix::_pumpAndMix32(size_t frames)
|
||||
{
|
||||
const ChannelMap& chMap = m_root.clientMixInfo().m_channelMap;
|
||||
size_t chanCount = chMap.m_channelCount;
|
||||
|
||||
if (m_redirect32)
|
||||
{
|
||||
if (m_cb && m_cb->canApplyEffect())
|
||||
m_cb->applyEffect(m_redirect32, frames, chMap, m_root.mixInfo().m_sampleRate);
|
||||
m_redirect32 += chanCount * frames;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t sampleCount = frames * chanCount;
|
||||
if (m_scratch32.size() < sampleCount)
|
||||
m_scratch32.resize(sampleCount);
|
||||
if (m_cb && m_cb->canApplyEffect())
|
||||
m_cb->applyEffect(m_scratch32.data(), frames, chMap, m_root.mixInfo().m_sampleRate);
|
||||
|
||||
size_t curSlewFrame = m_slewFrames;
|
||||
for (auto& smx : m_sendGains)
|
||||
{
|
||||
curSlewFrame = m_curSlewFrame;
|
||||
AudioSubmix& sm = *reinterpret_cast<AudioSubmix*>(smx.first);
|
||||
auto it = m_scratch32.begin();
|
||||
int32_t* dataOut = sm._getMergeBuf32(frames);
|
||||
|
||||
for (size_t f=0 ; f<frames ; ++f)
|
||||
{
|
||||
if (m_slewFrames && curSlewFrame < m_slewFrames)
|
||||
{
|
||||
double t = curSlewFrame / double(m_slewFrames);
|
||||
double omt = 1.0 - t;
|
||||
|
||||
for (unsigned c=0 ; c<chanCount ; ++c)
|
||||
{
|
||||
*dataOut = Clamp32(*dataOut + *it * (smx.second[1] * t + smx.second[0] * omt));
|
||||
++it;
|
||||
++dataOut;
|
||||
}
|
||||
|
||||
++curSlewFrame;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned c=0 ; c<chanCount ; ++c)
|
||||
{
|
||||
*dataOut = Clamp32(*dataOut + *it * smx.second[1]);
|
||||
++it;
|
||||
++dataOut;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_curSlewFrame += curSlewFrame;
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
size_t AudioSubmix::_pumpAndMixFlt(size_t frames)
|
||||
{
|
||||
const ChannelMap& chMap = m_root.clientMixInfo().m_channelMap;
|
||||
size_t chanCount = chMap.m_channelCount;
|
||||
|
||||
if (m_redirectFlt)
|
||||
{
|
||||
if (m_cb && m_cb->canApplyEffect())
|
||||
m_cb->applyEffect(m_redirectFlt, frames, chMap, m_root.mixInfo().m_sampleRate);
|
||||
m_redirectFlt += chanCount * frames;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t sampleCount = frames * chanCount;
|
||||
if (m_scratchFlt.size() < sampleCount)
|
||||
m_scratchFlt.resize(sampleCount);
|
||||
if (m_cb && m_cb->canApplyEffect())
|
||||
m_cb->applyEffect(m_scratchFlt.data(), frames, chMap, m_root.mixInfo().m_sampleRate);
|
||||
|
||||
size_t curSlewFrame = m_slewFrames;
|
||||
for (auto& smx : m_sendGains)
|
||||
{
|
||||
curSlewFrame = m_curSlewFrame;
|
||||
AudioSubmix& sm = *reinterpret_cast<AudioSubmix*>(smx.first);
|
||||
auto it = m_scratchFlt.begin();
|
||||
float* dataOut = sm._getMergeBufFlt(frames);
|
||||
|
||||
for (size_t f=0 ; f<frames ; ++f)
|
||||
{
|
||||
if (m_slewFrames && curSlewFrame < m_slewFrames)
|
||||
{
|
||||
double t = curSlewFrame / double(m_slewFrames);
|
||||
double omt = 1.0 - t;
|
||||
|
||||
for (unsigned c=0 ; c<chanCount ; ++c)
|
||||
{
|
||||
*dataOut = *dataOut + *it * (smx.second[1] * t + smx.second[0] * omt);
|
||||
++it;
|
||||
++dataOut;
|
||||
}
|
||||
|
||||
++curSlewFrame;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned c=0 ; c<chanCount ; ++c)
|
||||
{
|
||||
*dataOut = *dataOut + *it * smx.second[1];
|
||||
++it;
|
||||
++dataOut;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_curSlewFrame += curSlewFrame;
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
template size_t AudioSubmix::_pumpAndMix<int16_t>(size_t frames);
|
||||
template size_t AudioSubmix::_pumpAndMix<int32_t>(size_t frames);
|
||||
template size_t AudioSubmix::_pumpAndMix<float>(size_t frames);
|
||||
|
||||
void AudioSubmix::_resetOutputSampleRate()
|
||||
{
|
||||
if (m_cb)
|
||||
m_cb->resetOutputSampleRate(m_root.mixInfo().m_sampleRate);
|
||||
m_cb->resetOutputSampleRate(m_head->mixInfo().m_sampleRate);
|
||||
}
|
||||
|
||||
void AudioSubmix::resetSendLevels()
|
||||
|
@ -317,7 +202,7 @@ void AudioSubmix::resetSendLevels()
|
|||
if (m_sendGains.empty())
|
||||
return;
|
||||
m_sendGains.clear();
|
||||
m_root.m_submixesDirty = true;
|
||||
m_head->m_submixesDirty = true;
|
||||
}
|
||||
|
||||
void AudioSubmix::setSendLevel(IAudioSubmix* submix, float level, bool slew)
|
||||
|
@ -326,28 +211,19 @@ void AudioSubmix::setSendLevel(IAudioSubmix* submix, float level, bool slew)
|
|||
if (search == m_sendGains.cend())
|
||||
{
|
||||
search = m_sendGains.emplace(submix, std::array<float, 2>{1.f, 1.f}).first;
|
||||
m_root.m_submixesDirty = true;
|
||||
m_head->m_submixesDirty = true;
|
||||
}
|
||||
|
||||
m_slewFrames = slew ? m_root.m_5msFrames : 0;
|
||||
m_slewFrames = slew ? m_head->m_5msFrames : 0;
|
||||
m_curSlewFrame = 0;
|
||||
|
||||
search->second[0] = search->second[1];
|
||||
search->second[1] = level;
|
||||
}
|
||||
|
||||
void AudioSubmix::unbindSubmix()
|
||||
{
|
||||
if (m_bound)
|
||||
{
|
||||
m_root._unbindFrom(m_parentIt);
|
||||
m_bound = false;
|
||||
}
|
||||
}
|
||||
|
||||
const AudioVoiceEngineMixInfo& AudioSubmix::mixInfo() const
|
||||
{
|
||||
return m_root.mixInfo();
|
||||
return m_head->mixInfo();
|
||||
}
|
||||
|
||||
double AudioSubmix::getSampleRate() const
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <vector>
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
#include "Common.hpp"
|
||||
|
||||
#if __SSE__
|
||||
#include <xmmintrin.h>
|
||||
|
@ -22,7 +23,7 @@ class AudioVoice;
|
|||
struct AudioVoiceEngineMixInfo;
|
||||
/* Output gains for each mix-send/channel */
|
||||
|
||||
class AudioSubmix : public IAudioSubmix
|
||||
class AudioSubmix : public ListNode<AudioSubmix, BaseAudioVoiceEngine*, IAudioSubmix>
|
||||
{
|
||||
friend class BaseAudioVoiceEngine;
|
||||
friend class AudioVoiceMono;
|
||||
|
@ -33,16 +34,8 @@ class AudioSubmix : public IAudioSubmix
|
|||
friend struct ::WAVOutVoiceEngine;
|
||||
|
||||
/* Mixer-engine relationships */
|
||||
BaseAudioVoiceEngine& m_root;
|
||||
int m_busId;
|
||||
std::list<AudioSubmix*>::iterator m_parentIt;
|
||||
bool m_mainOut;
|
||||
bool m_bound = false;
|
||||
void bindSubmix(std::list<AudioSubmix*>::iterator pIt)
|
||||
{
|
||||
m_bound = true;
|
||||
m_parentIt = pIt;
|
||||
}
|
||||
|
||||
/* Callback (effect source, optional) */
|
||||
IAudioSubmixCallback* m_cb;
|
||||
|
@ -58,11 +51,13 @@ class AudioSubmix : public IAudioSubmix
|
|||
std::vector<int16_t> m_scratch16;
|
||||
std::vector<int32_t> m_scratch32;
|
||||
std::vector<float> m_scratchFlt;
|
||||
template <typename T> std::vector<T>& _getScratch();
|
||||
|
||||
/* Override scratch buffers with alternate destination */
|
||||
int16_t* m_redirect16 = nullptr;
|
||||
int32_t* m_redirect32 = nullptr;
|
||||
float* m_redirectFlt = nullptr;
|
||||
template <typename T> T*& _getRedirect();
|
||||
|
||||
/* C3-linearization support (to mitigate a potential diamond problem on 'clever' submix routes) */
|
||||
bool _isDirectDependencyOf(AudioSubmix* send);
|
||||
|
@ -71,34 +66,38 @@ class AudioSubmix : public IAudioSubmix
|
|||
std::vector<std::list<AudioSubmix*>>& lists);
|
||||
|
||||
/* Fill scratch buffers with silence for new mix cycle */
|
||||
void _zeroFill16();
|
||||
void _zeroFill32();
|
||||
void _zeroFillFlt();
|
||||
template <typename T> void _zeroFill();
|
||||
|
||||
/* Receive audio from a single voice / submix */
|
||||
int16_t* _getMergeBuf16(size_t frames);
|
||||
int32_t* _getMergeBuf32(size_t frames);
|
||||
float* _getMergeBufFlt(size_t frames);
|
||||
template <typename T> T* _getMergeBuf(size_t frames);
|
||||
|
||||
/* Mix scratch buffers into sends */
|
||||
size_t _pumpAndMix16(size_t frames);
|
||||
size_t _pumpAndMix32(size_t frames);
|
||||
size_t _pumpAndMixFlt(size_t frames);
|
||||
template <typename T> size_t _pumpAndMix(size_t frames);
|
||||
|
||||
void _resetOutputSampleRate();
|
||||
|
||||
public:
|
||||
~AudioSubmix();
|
||||
static AudioSubmix*& _getHeadPtr(BaseAudioVoiceEngine* head);
|
||||
static std::unique_lock<std::recursive_mutex> _getHeadLock(BaseAudioVoiceEngine* head);
|
||||
|
||||
AudioSubmix(BaseAudioVoiceEngine& root, IAudioSubmixCallback* cb, int busId, bool mainOut);
|
||||
~AudioSubmix();
|
||||
|
||||
void resetSendLevels();
|
||||
void setSendLevel(IAudioSubmix* submix, float level, bool slew);
|
||||
void unbindSubmix();
|
||||
const AudioVoiceEngineMixInfo& mixInfo() const;
|
||||
double getSampleRate() const;
|
||||
SubmixFormat getSampleFormat() const;
|
||||
};
|
||||
|
||||
template <> inline std::vector<int16_t>& AudioSubmix::_getScratch() { return m_scratch16; }
|
||||
template <> inline std::vector<int32_t>& AudioSubmix::_getScratch() { return m_scratch32; }
|
||||
template <> inline std::vector<float>& AudioSubmix::_getScratch() { return m_scratchFlt; }
|
||||
|
||||
template <> inline int16_t*& AudioSubmix::_getRedirect<int16_t>() { return m_redirect16; }
|
||||
template <> inline int32_t*& AudioSubmix::_getRedirect<int32_t>() { return m_redirect32; }
|
||||
template <> inline float*& AudioSubmix::_getRedirect<float>() { return m_redirectFlt; }
|
||||
|
||||
}
|
||||
|
||||
#endif // BOO_AUDIOSUBMIX_HPP
|
||||
|
|
|
@ -11,19 +11,24 @@ static AudioMatrixStereo DefaultStereoMtx;
|
|||
|
||||
AudioVoice::AudioVoice(BaseAudioVoiceEngine& root,
|
||||
IAudioVoiceCallback* cb, bool dynamicRate)
|
||||
: m_root(root), m_cb(cb), m_dynamicRate(dynamicRate) {}
|
||||
: ListNode<AudioVoice, BaseAudioVoiceEngine*, IAudioVoice>(&root), m_cb(cb), m_dynamicRate(dynamicRate)
|
||||
{}
|
||||
|
||||
AudioVoice::~AudioVoice()
|
||||
{
|
||||
unbindVoice();
|
||||
soxr_delete(m_src);
|
||||
}
|
||||
|
||||
AudioVoice*& AudioVoice::_getHeadPtr(BaseAudioVoiceEngine* head) { return head->m_voiceHead; }
|
||||
std::unique_lock<std::recursive_mutex> AudioVoice::_getHeadLock(BaseAudioVoiceEngine* head)
|
||||
{ return std::unique_lock<std::recursive_mutex>{head->m_dataMutex}; }
|
||||
|
||||
void AudioVoice::_setPitchRatio(double ratio, bool slew)
|
||||
{
|
||||
if (m_dynamicRate)
|
||||
{
|
||||
soxr_error_t err = soxr_set_io_ratio(m_src, ratio * m_sampleRateIn / m_sampleRateOut, slew ? m_root.m_5msFrames : 0);
|
||||
soxr_error_t err = soxr_set_io_ratio(m_src, ratio * m_sampleRateIn / m_sampleRateOut,
|
||||
slew ? m_head->m_5msFrames : 0);
|
||||
if (err)
|
||||
{
|
||||
Log.report(logvisor::Fatal, "unable to set resampler rate: %s", soxr_strerror(err));
|
||||
|
@ -65,15 +70,6 @@ void AudioVoice::stop()
|
|||
m_running = false;
|
||||
}
|
||||
|
||||
void AudioVoice::unbindVoice()
|
||||
{
|
||||
if (m_bound)
|
||||
{
|
||||
m_root._unbindFrom(m_parentIt);
|
||||
m_bound = false;
|
||||
}
|
||||
}
|
||||
|
||||
AudioVoiceMono::AudioVoiceMono(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb,
|
||||
double sampleRate, bool dynamicRate)
|
||||
: AudioVoice(root, cb, dynamicRate)
|
||||
|
@ -85,8 +81,8 @@ void AudioVoiceMono::_resetSampleRate(double sampleRate)
|
|||
{
|
||||
soxr_delete(m_src);
|
||||
|
||||
double rateOut = m_root.mixInfo().m_sampleRate;
|
||||
soxr_datatype_t formatOut = m_root.mixInfo().m_sampleFormat;
|
||||
double rateOut = m_head->mixInfo().m_sampleRate;
|
||||
soxr_datatype_t formatOut = m_head->mixInfo().m_sampleFormat;
|
||||
soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, formatOut);
|
||||
soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, m_dynamicRate ? SOXR_VR : 0);
|
||||
|
||||
|
@ -110,7 +106,7 @@ void AudioVoiceMono::_resetSampleRate(double sampleRate)
|
|||
|
||||
size_t AudioVoiceMono::SRCCallback(AudioVoiceMono* ctx, int16_t** data, size_t frames)
|
||||
{
|
||||
std::vector<int16_t>& scratchIn = ctx->m_root.m_scratchIn;
|
||||
std::vector<int16_t>& scratchIn = ctx->m_head->m_scratchIn;
|
||||
if (scratchIn.size() < frames)
|
||||
scratchIn.resize(frames);
|
||||
*data = scratchIn.data();
|
||||
|
@ -138,23 +134,24 @@ bool AudioVoiceMono::isSilent() const
|
|||
}
|
||||
}
|
||||
|
||||
size_t AudioVoiceMono::pumpAndMix16(size_t frames)
|
||||
template <typename T>
|
||||
size_t AudioVoiceMono::_pumpAndMix(size_t frames)
|
||||
{
|
||||
if (isSilent())
|
||||
return 0;
|
||||
|
||||
std::vector<int16_t>& scratch16Pre = m_root.m_scratch16Pre;
|
||||
if (scratch16Pre.size() < frames)
|
||||
scratch16Pre.resize(frames);
|
||||
auto& scratchPre = m_head->_getScratchPre<T>();
|
||||
if (scratchPre.size() < frames)
|
||||
scratchPre.resize(frames + 2);
|
||||
|
||||
std::vector<int16_t>& scratch16Post = m_root.m_scratch16Post;
|
||||
if (scratch16Post.size() < frames)
|
||||
scratch16Post.resize(frames);
|
||||
auto& scratchPost = m_head->_getScratchPost<T>();
|
||||
if (scratchPost.size() < frames)
|
||||
scratchPost.resize(frames + 2);
|
||||
|
||||
double dt = frames / m_sampleRateOut;
|
||||
m_cb->preSupplyAudio(*this, dt);
|
||||
_midUpdate();
|
||||
size_t oDone = soxr_output(m_src, scratch16Pre.data(), frames);
|
||||
size_t oDone = soxr_output(m_src, scratchPre.data(), frames);
|
||||
|
||||
if (oDone)
|
||||
{
|
||||
|
@ -163,95 +160,17 @@ size_t AudioVoiceMono::pumpAndMix16(size_t frames)
|
|||
for (auto& mtx : m_sendMatrices)
|
||||
{
|
||||
AudioSubmix& smx = *reinterpret_cast<AudioSubmix*>(mtx.first);
|
||||
m_cb->routeAudio(oDone, 1, dt, smx.m_busId, scratch16Pre.data(), scratch16Post.data());
|
||||
mtx.second.mixMonoSampleData(m_root.clientMixInfo(), scratch16Post.data(), smx._getMergeBuf16(oDone), oDone);
|
||||
m_cb->routeAudio(oDone, 1, dt, smx.m_busId, scratchPre.data(), scratchPost.data());
|
||||
mtx.second.mixMonoSampleData(m_head->clientMixInfo(), scratchPost.data(),
|
||||
smx._getMergeBuf<T>(oDone), oDone);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioSubmix& smx = reinterpret_cast<AudioSubmix&>(m_root.m_mainSubmix);
|
||||
m_cb->routeAudio(oDone, 1, dt, m_root.m_mainSubmix.m_busId, scratch16Pre.data(), scratch16Post.data());
|
||||
DefaultMonoMtx.mixMonoSampleData(m_root.clientMixInfo(), scratch16Post.data(), smx._getMergeBuf16(oDone), oDone);
|
||||
}
|
||||
}
|
||||
|
||||
return oDone;
|
||||
}
|
||||
|
||||
size_t AudioVoiceMono::pumpAndMix32(size_t frames)
|
||||
{
|
||||
if (isSilent())
|
||||
return 0;
|
||||
|
||||
std::vector<int32_t>& scratch32Pre = m_root.m_scratch32Pre;
|
||||
if (scratch32Pre.size() < frames)
|
||||
scratch32Pre.resize(frames);
|
||||
|
||||
std::vector<int32_t>& scratch32Post = m_root.m_scratch32Post;
|
||||
if (scratch32Post.size() < frames)
|
||||
scratch32Post.resize(frames);
|
||||
|
||||
double dt = frames / m_sampleRateOut;
|
||||
m_cb->preSupplyAudio(*this, dt);
|
||||
_midUpdate();
|
||||
size_t oDone = soxr_output(m_src, scratch32Pre.data(), frames);
|
||||
|
||||
if (oDone)
|
||||
{
|
||||
if (m_sendMatrices.size())
|
||||
{
|
||||
for (auto& mtx : m_sendMatrices)
|
||||
{
|
||||
AudioSubmix& smx = *reinterpret_cast<AudioSubmix*>(mtx.first);
|
||||
m_cb->routeAudio(oDone, 1, dt, smx.m_busId, scratch32Pre.data(), scratch32Post.data());
|
||||
mtx.second.mixMonoSampleData(m_root.clientMixInfo(), scratch32Post.data(), smx._getMergeBuf32(oDone), oDone);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioSubmix& smx = reinterpret_cast<AudioSubmix&>(m_root.m_mainSubmix);
|
||||
m_cb->routeAudio(oDone, 1, dt, m_root.m_mainSubmix.m_busId, scratch32Pre.data(), scratch32Post.data());
|
||||
DefaultMonoMtx.mixMonoSampleData(m_root.clientMixInfo(), scratch32Post.data(), smx._getMergeBuf32(oDone), oDone);
|
||||
}
|
||||
}
|
||||
|
||||
return oDone;
|
||||
}
|
||||
|
||||
size_t AudioVoiceMono::pumpAndMixFlt(size_t frames)
|
||||
{
|
||||
if (isSilent())
|
||||
return 0;
|
||||
|
||||
std::vector<float>& scratchFltPre = m_root.m_scratchFltPre;
|
||||
if (scratchFltPre.size() < frames)
|
||||
scratchFltPre.resize(frames + 2);
|
||||
|
||||
std::vector<float>& scratchFltPost = m_root.m_scratchFltPost;
|
||||
if (scratchFltPost.size() < frames)
|
||||
scratchFltPost.resize(frames + 2);
|
||||
|
||||
double dt = frames / m_sampleRateOut;
|
||||
m_cb->preSupplyAudio(*this, dt);
|
||||
_midUpdate();
|
||||
size_t oDone = soxr_output(m_src, scratchFltPre.data(), frames);
|
||||
|
||||
if (oDone)
|
||||
{
|
||||
if (m_sendMatrices.size())
|
||||
{
|
||||
for (auto& mtx : m_sendMatrices)
|
||||
{
|
||||
AudioSubmix& smx = *reinterpret_cast<AudioSubmix*>(mtx.first);
|
||||
m_cb->routeAudio(oDone, 1, dt, smx.m_busId, scratchFltPre.data(), scratchFltPost.data());
|
||||
mtx.second.mixMonoSampleData(m_root.clientMixInfo(), scratchFltPost.data(), smx._getMergeBufFlt(oDone), oDone);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioSubmix& smx = reinterpret_cast<AudioSubmix&>(m_root.m_mainSubmix);
|
||||
m_cb->routeAudio(oDone, 1, dt, m_root.m_mainSubmix.m_busId, scratchFltPre.data(), scratchFltPost.data());
|
||||
DefaultMonoMtx.mixMonoSampleData(m_root.clientMixInfo(), scratchFltPost.data(), smx._getMergeBufFlt(oDone), oDone);
|
||||
AudioSubmix& smx = reinterpret_cast<AudioSubmix&>(m_head->m_mainSubmix);
|
||||
m_cb->routeAudio(oDone, 1, dt, m_head->m_mainSubmix->m_busId, scratchPre.data(), scratchPost.data());
|
||||
DefaultMonoMtx.mixMonoSampleData(m_head->clientMixInfo(), scratchPost.data(),
|
||||
smx._getMergeBuf<T>(oDone), oDone);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -260,19 +179,19 @@ size_t AudioVoiceMono::pumpAndMixFlt(size_t frames)
|
|||
|
||||
void AudioVoiceMono::resetChannelLevels()
|
||||
{
|
||||
m_root.m_submixesDirty = true;
|
||||
m_head->m_submixesDirty = true;
|
||||
m_sendMatrices.clear();
|
||||
}
|
||||
|
||||
void AudioVoiceMono::setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew)
|
||||
{
|
||||
if (!submix)
|
||||
submix = &m_root.m_mainSubmix;
|
||||
submix = m_head->m_mainSubmix.get();
|
||||
|
||||
auto search = m_sendMatrices.find(submix);
|
||||
if (search == m_sendMatrices.cend())
|
||||
search = m_sendMatrices.emplace(submix, AudioMatrixMono{}).first;
|
||||
search->second.setMatrixCoefficients(coefs, slew ? m_root.m_5msFrames : 0);
|
||||
search->second.setMatrixCoefficients(coefs, slew ? m_head->m_5msFrames : 0);
|
||||
}
|
||||
|
||||
void AudioVoiceMono::setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew)
|
||||
|
@ -290,12 +209,12 @@ void AudioVoiceMono::setStereoChannelLevels(IAudioSubmix* submix, const float co
|
|||
};
|
||||
|
||||
if (!submix)
|
||||
submix = &m_root.m_mainSubmix;
|
||||
submix = m_head->m_mainSubmix.get();
|
||||
|
||||
auto search = m_sendMatrices.find(submix);
|
||||
if (search == m_sendMatrices.cend())
|
||||
search = m_sendMatrices.emplace(submix, AudioMatrixMono{}).first;
|
||||
search->second.setMatrixCoefficients(newCoefs, slew ? m_root.m_5msFrames : 0);
|
||||
search->second.setMatrixCoefficients(newCoefs, slew ? m_head->m_5msFrames : 0);
|
||||
}
|
||||
|
||||
AudioVoiceStereo::AudioVoiceStereo(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb,
|
||||
|
@ -309,8 +228,8 @@ void AudioVoiceStereo::_resetSampleRate(double sampleRate)
|
|||
{
|
||||
soxr_delete(m_src);
|
||||
|
||||
double rateOut = m_root.mixInfo().m_sampleRate;
|
||||
soxr_datatype_t formatOut = m_root.mixInfo().m_sampleFormat;
|
||||
double rateOut = m_head->mixInfo().m_sampleRate;
|
||||
soxr_datatype_t formatOut = m_head->mixInfo().m_sampleFormat;
|
||||
soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, formatOut);
|
||||
soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, m_dynamicRate ? SOXR_VR : 0);
|
||||
|
||||
|
@ -334,7 +253,7 @@ void AudioVoiceStereo::_resetSampleRate(double sampleRate)
|
|||
|
||||
size_t AudioVoiceStereo::SRCCallback(AudioVoiceStereo* ctx, int16_t** data, size_t frames)
|
||||
{
|
||||
std::vector<int16_t>& scratchIn = ctx->m_root.m_scratchIn;
|
||||
std::vector<int16_t>& scratchIn = ctx->m_head->m_scratchIn;
|
||||
size_t samples = frames * 2;
|
||||
if (scratchIn.size() < samples)
|
||||
scratchIn.resize(samples);
|
||||
|
@ -363,25 +282,26 @@ bool AudioVoiceStereo::isSilent() const
|
|||
}
|
||||
}
|
||||
|
||||
size_t AudioVoiceStereo::pumpAndMix16(size_t frames)
|
||||
template <typename T>
|
||||
size_t AudioVoiceStereo::_pumpAndMix(size_t frames)
|
||||
{
|
||||
if (isSilent())
|
||||
return 0;
|
||||
|
||||
size_t samples = frames * 2;
|
||||
|
||||
std::vector<int16_t>& scratch16Pre = m_root.m_scratch16Pre;
|
||||
if (scratch16Pre.size() < samples)
|
||||
scratch16Pre.resize(samples);
|
||||
auto& scratchPre = m_head->_getScratchPre<T>();
|
||||
if (scratchPre.size() < samples)
|
||||
scratchPre.resize(samples + 4);
|
||||
|
||||
std::vector<int16_t>& scratch16Post = m_root.m_scratch16Post;
|
||||
if (scratch16Post.size() < samples)
|
||||
scratch16Post.resize(samples);
|
||||
auto& scratchPost = m_head->_getScratchPost<T>();
|
||||
if (scratchPost.size() < samples)
|
||||
scratchPost.resize(samples + 4);
|
||||
|
||||
double dt = frames / m_sampleRateOut;
|
||||
m_cb->preSupplyAudio(*this, dt);
|
||||
_midUpdate();
|
||||
size_t oDone = soxr_output(m_src, scratch16Pre.data(), frames);
|
||||
size_t oDone = soxr_output(m_src, scratchPre.data(), frames);
|
||||
|
||||
if (oDone)
|
||||
{
|
||||
|
@ -390,99 +310,17 @@ size_t AudioVoiceStereo::pumpAndMix16(size_t frames)
|
|||
for (auto& mtx : m_sendMatrices)
|
||||
{
|
||||
AudioSubmix& smx = *reinterpret_cast<AudioSubmix*>(mtx.first);
|
||||
m_cb->routeAudio(oDone, 2, dt, smx.m_busId, scratch16Pre.data(), scratch16Post.data());
|
||||
mtx.second.mixStereoSampleData(m_root.clientMixInfo(), scratch16Post.data(), smx._getMergeBuf16(oDone), oDone);
|
||||
m_cb->routeAudio(oDone, 2, dt, smx.m_busId, scratchPre.data(), scratchPost.data());
|
||||
mtx.second.mixStereoSampleData(m_head->clientMixInfo(), scratchPost.data(),
|
||||
smx._getMergeBuf<T>(oDone), oDone);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioSubmix& smx = reinterpret_cast<AudioSubmix&>(m_root.m_mainSubmix);
|
||||
m_cb->routeAudio(oDone, 2, dt, m_root.m_mainSubmix.m_busId, scratch16Pre.data(), scratch16Post.data());
|
||||
DefaultStereoMtx.mixStereoSampleData(m_root.clientMixInfo(), scratch16Post.data(), smx._getMergeBuf16(oDone), oDone);
|
||||
}
|
||||
}
|
||||
|
||||
return oDone;
|
||||
}
|
||||
|
||||
size_t AudioVoiceStereo::pumpAndMix32(size_t frames)
|
||||
{
|
||||
if (isSilent())
|
||||
return 0;
|
||||
|
||||
size_t samples = frames * 2;
|
||||
|
||||
std::vector<int32_t>& scratch32Pre = m_root.m_scratch32Pre;
|
||||
if (scratch32Pre.size() < samples)
|
||||
scratch32Pre.resize(samples);
|
||||
|
||||
std::vector<int32_t>& scratch32Post = m_root.m_scratch32Post;
|
||||
if (scratch32Post.size() < samples)
|
||||
scratch32Post.resize(samples);
|
||||
|
||||
double dt = frames / m_sampleRateOut;
|
||||
m_cb->preSupplyAudio(*this, dt);
|
||||
_midUpdate();
|
||||
size_t oDone = soxr_output(m_src, scratch32Pre.data(), frames);
|
||||
|
||||
if (oDone)
|
||||
{
|
||||
if (m_sendMatrices.size())
|
||||
{
|
||||
for (auto& mtx : m_sendMatrices)
|
||||
{
|
||||
AudioSubmix& smx = *reinterpret_cast<AudioSubmix*>(mtx.first);
|
||||
m_cb->routeAudio(oDone, 2, dt, smx.m_busId, scratch32Pre.data(), scratch32Post.data());
|
||||
mtx.second.mixStereoSampleData(m_root.clientMixInfo(), scratch32Post.data(), smx._getMergeBuf32(oDone), oDone);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioSubmix& smx = reinterpret_cast<AudioSubmix&>(m_root.m_mainSubmix);
|
||||
m_cb->routeAudio(oDone, 2, dt, m_root.m_mainSubmix.m_busId, scratch32Pre.data(), scratch32Post.data());
|
||||
DefaultStereoMtx.mixStereoSampleData(m_root.clientMixInfo(), scratch32Post.data(), smx._getMergeBuf32(oDone), oDone);
|
||||
}
|
||||
}
|
||||
|
||||
return oDone;
|
||||
}
|
||||
|
||||
size_t AudioVoiceStereo::pumpAndMixFlt(size_t frames)
|
||||
{
|
||||
if (isSilent())
|
||||
return 0;
|
||||
|
||||
size_t samples = frames * 2;
|
||||
|
||||
std::vector<float>& scratchFltPre = m_root.m_scratchFltPre;
|
||||
if (scratchFltPre.size() < samples)
|
||||
scratchFltPre.resize(samples + 4);
|
||||
|
||||
std::vector<float>& scratchFltPost = m_root.m_scratchFltPost;
|
||||
if (scratchFltPost.size() < samples)
|
||||
scratchFltPost.resize(samples + 4);
|
||||
|
||||
double dt = frames / m_sampleRateOut;
|
||||
m_cb->preSupplyAudio(*this, dt);
|
||||
_midUpdate();
|
||||
size_t oDone = soxr_output(m_src, scratchFltPre.data(), frames);
|
||||
|
||||
if (oDone)
|
||||
{
|
||||
if (m_sendMatrices.size())
|
||||
{
|
||||
for (auto& mtx : m_sendMatrices)
|
||||
{
|
||||
AudioSubmix& smx = *reinterpret_cast<AudioSubmix*>(mtx.first);
|
||||
m_cb->routeAudio(oDone, 2, dt, smx.m_busId, scratchFltPre.data(), scratchFltPost.data());
|
||||
mtx.second.mixStereoSampleData(m_root.clientMixInfo(), scratchFltPost.data(), smx._getMergeBufFlt(oDone), oDone);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioSubmix& smx = reinterpret_cast<AudioSubmix&>(m_root.m_mainSubmix);
|
||||
m_cb->routeAudio(oDone, 2, dt, m_root.m_mainSubmix.m_busId, scratchFltPre.data(), scratchFltPost.data());
|
||||
DefaultStereoMtx.mixStereoSampleData(m_root.clientMixInfo(), scratchFltPost.data(), smx._getMergeBufFlt(oDone), oDone);
|
||||
AudioSubmix& smx = reinterpret_cast<AudioSubmix&>(m_head->m_mainSubmix);
|
||||
m_cb->routeAudio(oDone, 2, dt, m_head->m_mainSubmix->m_busId, scratchPre.data(), scratchPost.data());
|
||||
DefaultStereoMtx.mixStereoSampleData(m_head->clientMixInfo(), scratchPost.data(),
|
||||
smx._getMergeBuf<T>(oDone), oDone);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -491,7 +329,7 @@ size_t AudioVoiceStereo::pumpAndMixFlt(size_t frames)
|
|||
|
||||
void AudioVoiceStereo::resetChannelLevels()
|
||||
{
|
||||
m_root.m_submixesDirty = true;
|
||||
m_head->m_submixesDirty = true;
|
||||
m_sendMatrices.clear();
|
||||
}
|
||||
|
||||
|
@ -510,23 +348,23 @@ void AudioVoiceStereo::setMonoChannelLevels(IAudioSubmix* submix, const float co
|
|||
};
|
||||
|
||||
if (!submix)
|
||||
submix = &m_root.m_mainSubmix;
|
||||
submix = m_head->m_mainSubmix.get();
|
||||
|
||||
auto search = m_sendMatrices.find(submix);
|
||||
if (search == m_sendMatrices.cend())
|
||||
search = m_sendMatrices.emplace(submix, AudioMatrixStereo{}).first;
|
||||
search->second.setMatrixCoefficients(newCoefs, slew ? m_root.m_5msFrames : 0);
|
||||
search->second.setMatrixCoefficients(newCoefs, slew ? m_head->m_5msFrames : 0);
|
||||
}
|
||||
|
||||
void AudioVoiceStereo::setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew)
|
||||
{
|
||||
if (!submix)
|
||||
submix = &m_root.m_mainSubmix;
|
||||
submix = m_head->m_mainSubmix.get();
|
||||
|
||||
auto search = m_sendMatrices.find(submix);
|
||||
if (search == m_sendMatrices.cend())
|
||||
search = m_sendMatrices.emplace(submix, AudioMatrixStereo{}).first;
|
||||
search->second.setMatrixCoefficients(coefs, slew ? m_root.m_5msFrames : 0);
|
||||
search->second.setMatrixCoefficients(coefs, slew ? m_head->m_5msFrames : 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include <unordered_map>
|
||||
#include "boo/audiodev/IAudioVoice.hpp"
|
||||
#include "AudioMatrix.hpp"
|
||||
#include "Common.hpp"
|
||||
#include "AudioVoiceEngine.hpp"
|
||||
|
||||
struct AudioUnitVoiceEngine;
|
||||
struct VSTVoiceEngine;
|
||||
|
@ -17,7 +19,7 @@ class BaseAudioVoiceEngine;
|
|||
struct AudioVoiceEngineMixInfo;
|
||||
struct IAudioSubmix;
|
||||
|
||||
class AudioVoice : public IAudioVoice
|
||||
class AudioVoice : public ListNode<AudioVoice, BaseAudioVoiceEngine*, IAudioVoice>
|
||||
{
|
||||
friend class BaseAudioVoiceEngine;
|
||||
friend class AudioSubmix;
|
||||
|
@ -27,16 +29,6 @@ class AudioVoice : public IAudioVoice
|
|||
friend struct ::WAVOutVoiceEngine;
|
||||
|
||||
protected:
|
||||
/* Mixer-engine relationships */
|
||||
BaseAudioVoiceEngine& m_root;
|
||||
std::list<AudioVoice*>::iterator m_parentIt;
|
||||
bool m_bound = false;
|
||||
void bindVoice(std::list<AudioVoice*>::iterator pIt)
|
||||
{
|
||||
m_bound = true;
|
||||
m_parentIt = pIt;
|
||||
}
|
||||
|
||||
/* Callback (audio source) */
|
||||
IAudioVoiceCallback* m_cb;
|
||||
|
||||
|
@ -66,20 +58,27 @@ protected:
|
|||
virtual size_t pumpAndMix16(size_t frames)=0;
|
||||
virtual size_t pumpAndMix32(size_t frames)=0;
|
||||
virtual size_t pumpAndMixFlt(size_t frames)=0;
|
||||
template <typename T> size_t pumpAndMix(size_t frames);
|
||||
|
||||
AudioVoice(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, bool dynamicRate);
|
||||
|
||||
public:
|
||||
static AudioVoice*& _getHeadPtr(BaseAudioVoiceEngine* head);
|
||||
static std::unique_lock<std::recursive_mutex> _getHeadLock(BaseAudioVoiceEngine* head);
|
||||
|
||||
~AudioVoice();
|
||||
void resetSampleRate(double sampleRate);
|
||||
void setPitchRatio(double ratio, bool slew);
|
||||
void start();
|
||||
void stop();
|
||||
void unbindVoice();
|
||||
double getSampleRateIn() const {return m_sampleRateIn;}
|
||||
double getSampleRateOut() const {return m_sampleRateOut;}
|
||||
};
|
||||
|
||||
template <> inline size_t AudioVoice::pumpAndMix<int16_t>(size_t frames) { return pumpAndMix16(frames); }
|
||||
template <> inline size_t AudioVoice::pumpAndMix<int32_t>(size_t frames) { return pumpAndMix32(frames); }
|
||||
template <> inline size_t AudioVoice::pumpAndMix<float>(size_t frames) { return pumpAndMixFlt(frames); }
|
||||
|
||||
class AudioVoiceMono : public AudioVoice
|
||||
{
|
||||
std::unordered_map<IAudioSubmix*, AudioMatrixMono> m_sendMatrices;
|
||||
|
@ -91,9 +90,10 @@ class AudioVoiceMono : public AudioVoice
|
|||
|
||||
bool isSilent() const;
|
||||
|
||||
size_t pumpAndMix16(size_t frames);
|
||||
size_t pumpAndMix32(size_t frames);
|
||||
size_t pumpAndMixFlt(size_t frames);
|
||||
template <typename T> size_t _pumpAndMix(size_t frames);
|
||||
size_t pumpAndMix16(size_t frames) { return _pumpAndMix<int16_t>(frames); }
|
||||
size_t pumpAndMix32(size_t frames) { return _pumpAndMix<int32_t>(frames); }
|
||||
size_t pumpAndMixFlt(size_t frames) { return _pumpAndMix<float>(frames); }
|
||||
|
||||
public:
|
||||
AudioVoiceMono(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb,
|
||||
|
@ -114,9 +114,10 @@ class AudioVoiceStereo : public AudioVoice
|
|||
|
||||
bool isSilent() const;
|
||||
|
||||
size_t pumpAndMix16(size_t frames);
|
||||
size_t pumpAndMix32(size_t frames);
|
||||
size_t pumpAndMixFlt(size_t frames);
|
||||
template <typename T> size_t _pumpAndMix(size_t frames);
|
||||
size_t pumpAndMix16(size_t frames) { return _pumpAndMix<int16_t>(frames); }
|
||||
size_t pumpAndMix32(size_t frames) { return _pumpAndMix<int32_t>(frames); }
|
||||
size_t pumpAndMixFlt(size_t frames) { return _pumpAndMix<float>(frames); }
|
||||
|
||||
public:
|
||||
AudioVoiceStereo(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb,
|
||||
|
|
|
@ -1,34 +1,35 @@
|
|||
#include "AudioVoiceEngine.hpp"
|
||||
#include <cassert>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
BaseAudioVoiceEngine::~BaseAudioVoiceEngine()
|
||||
{
|
||||
while (m_activeVoices.size())
|
||||
m_activeVoices.front()->unbindVoice();
|
||||
while (m_activeSubmixes.size())
|
||||
m_activeSubmixes.front()->unbindSubmix();
|
||||
m_mainSubmix.reset();
|
||||
assert(m_voiceHead == nullptr && "Dangling voices detected");
|
||||
assert(m_submixHead == nullptr && "Dangling submixes detected");
|
||||
}
|
||||
|
||||
void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int16_t* dataOut)
|
||||
template <typename T>
|
||||
void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, T* dataOut)
|
||||
{
|
||||
memset(dataOut, 0, sizeof(int16_t) * frames * m_mixInfo.m_channelMap.m_channelCount);
|
||||
memset(dataOut, 0, sizeof(T) * frames * m_mixInfo.m_channelMap.m_channelCount);
|
||||
if (m_ltRtProcessing)
|
||||
{
|
||||
size_t sampleCount = m_5msFrames * 5;
|
||||
if (m_ltRtIn16.size() < sampleCount)
|
||||
m_ltRtIn16.resize(sampleCount);
|
||||
m_mainSubmix.m_redirect16 = m_ltRtIn16.data();
|
||||
if (_getLtRtIn<T>().size() < sampleCount)
|
||||
_getLtRtIn<T>().resize(sampleCount);
|
||||
m_mainSubmix->_getRedirect<T>() = _getLtRtIn<T>().data();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_mainSubmix.m_redirect16 = dataOut;
|
||||
m_mainSubmix->_getRedirect<T>() = dataOut;
|
||||
}
|
||||
|
||||
if (m_submixesDirty)
|
||||
{
|
||||
m_linearizedSubmixes = m_mainSubmix._linearizeC3();
|
||||
m_linearizedSubmixes = m_mainSubmix->_linearizeC3();
|
||||
m_submixesDirty = false;
|
||||
}
|
||||
|
||||
|
@ -50,22 +51,23 @@ void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int16_t* dataOut)
|
|||
}
|
||||
|
||||
if (m_ltRtProcessing)
|
||||
std::fill(m_ltRtIn16.begin(), m_ltRtIn16.end(), 0);
|
||||
std::fill(_getLtRtIn<T>().begin(), _getLtRtIn<T>().end(), 0.f);
|
||||
|
||||
for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it)
|
||||
(*it)->_zeroFill16();
|
||||
(*it)->_zeroFill<T>();
|
||||
|
||||
for (AudioVoice* vox : m_activeVoices)
|
||||
if (vox->m_running)
|
||||
vox->pumpAndMix16(thisFrames);
|
||||
if (m_voiceHead)
|
||||
for (AudioVoice& vox : *m_voiceHead)
|
||||
if (vox.m_running)
|
||||
vox.pumpAndMix<T>(thisFrames);
|
||||
|
||||
for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it)
|
||||
(*it)->_pumpAndMix16(thisFrames);
|
||||
(*it)->_pumpAndMix<T>(thisFrames);
|
||||
|
||||
if (m_ltRtProcessing)
|
||||
{
|
||||
m_ltRtProcessing->Process(m_ltRtIn16.data(), dataOut, int(thisFrames));
|
||||
m_mainSubmix.m_redirect16 = m_ltRtIn16.data();
|
||||
m_ltRtProcessing->Process(_getLtRtIn<T>().data(), dataOut, int(thisFrames));
|
||||
m_mainSubmix->_getRedirect<T>() = _getLtRtIn<T>().data();
|
||||
}
|
||||
|
||||
size_t sampleCount = thisFrames * m_mixInfo.m_channelMap.m_channelCount;
|
||||
|
@ -80,165 +82,16 @@ void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int16_t* dataOut)
|
|||
m_engineCallback->onPumpCycleComplete(*this);
|
||||
}
|
||||
|
||||
void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int32_t* dataOut)
|
||||
{
|
||||
memset(dataOut, 0, sizeof(int32_t) * frames * m_mixInfo.m_channelMap.m_channelCount);
|
||||
if (m_ltRtProcessing)
|
||||
{
|
||||
size_t sampleCount = m_5msFrames * 5;
|
||||
if (m_ltRtIn32.size() < sampleCount)
|
||||
m_ltRtIn32.resize(sampleCount);
|
||||
m_mainSubmix.m_redirect32 = m_ltRtIn32.data();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_mainSubmix.m_redirect32 = dataOut;
|
||||
}
|
||||
|
||||
if (m_submixesDirty)
|
||||
{
|
||||
m_linearizedSubmixes = m_mainSubmix._linearizeC3();
|
||||
m_submixesDirty = false;
|
||||
}
|
||||
|
||||
size_t remFrames = frames;
|
||||
while (remFrames)
|
||||
{
|
||||
size_t thisFrames;
|
||||
if (remFrames < m_5msFrames)
|
||||
{
|
||||
thisFrames = remFrames;
|
||||
if (m_engineCallback)
|
||||
m_engineCallback->on5MsInterval(*this, thisFrames / double(m_5msFrames) * 5.0 / 1000.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
thisFrames = m_5msFrames;
|
||||
if (m_engineCallback)
|
||||
m_engineCallback->on5MsInterval(*this, 5.0 / 1000.0);
|
||||
}
|
||||
|
||||
if (m_ltRtProcessing)
|
||||
std::fill(m_ltRtIn32.begin(), m_ltRtIn32.end(), 0);
|
||||
|
||||
for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it)
|
||||
(*it)->_zeroFill32();
|
||||
|
||||
for (AudioVoice* vox : m_activeVoices)
|
||||
if (vox->m_running)
|
||||
vox->pumpAndMix32(thisFrames);
|
||||
|
||||
for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it)
|
||||
(*it)->_pumpAndMix32(thisFrames);
|
||||
|
||||
if (m_ltRtProcessing)
|
||||
{
|
||||
m_ltRtProcessing->Process(m_ltRtIn32.data(), dataOut, int(thisFrames));
|
||||
m_mainSubmix.m_redirect32 = m_ltRtIn32.data();
|
||||
}
|
||||
|
||||
size_t sampleCount = thisFrames * m_mixInfo.m_channelMap.m_channelCount;
|
||||
for (size_t i=0 ; i<sampleCount ; ++i)
|
||||
dataOut[i] *= m_totalVol;
|
||||
|
||||
remFrames -= thisFrames;
|
||||
dataOut += sampleCount;
|
||||
}
|
||||
|
||||
if (m_engineCallback)
|
||||
m_engineCallback->onPumpCycleComplete(*this);
|
||||
}
|
||||
|
||||
void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, float* dataOut)
|
||||
{
|
||||
memset(dataOut, 0, sizeof(float) * frames * m_mixInfo.m_channelMap.m_channelCount);
|
||||
if (m_ltRtProcessing)
|
||||
{
|
||||
size_t sampleCount = m_5msFrames * 5;
|
||||
if (m_ltRtInFlt.size() < sampleCount)
|
||||
m_ltRtInFlt.resize(sampleCount);
|
||||
m_mainSubmix.m_redirectFlt = m_ltRtInFlt.data();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_mainSubmix.m_redirectFlt = dataOut;
|
||||
}
|
||||
|
||||
if (m_submixesDirty)
|
||||
{
|
||||
m_linearizedSubmixes = m_mainSubmix._linearizeC3();
|
||||
m_submixesDirty = false;
|
||||
}
|
||||
|
||||
size_t remFrames = frames;
|
||||
while (remFrames)
|
||||
{
|
||||
size_t thisFrames;
|
||||
if (remFrames < m_5msFrames)
|
||||
{
|
||||
thisFrames = remFrames;
|
||||
if (m_engineCallback)
|
||||
m_engineCallback->on5MsInterval(*this, thisFrames / double(m_5msFrames) * 5.0 / 1000.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
thisFrames = m_5msFrames;
|
||||
if (m_engineCallback)
|
||||
m_engineCallback->on5MsInterval(*this, 5.0 / 1000.0);
|
||||
}
|
||||
|
||||
if (m_ltRtProcessing)
|
||||
std::fill(m_ltRtInFlt.begin(), m_ltRtInFlt.end(), 0.f);
|
||||
|
||||
for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it)
|
||||
(*it)->_zeroFillFlt();
|
||||
|
||||
for (AudioVoice* vox : m_activeVoices)
|
||||
if (vox->m_running)
|
||||
vox->pumpAndMixFlt(thisFrames);
|
||||
|
||||
for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it)
|
||||
(*it)->_pumpAndMixFlt(thisFrames);
|
||||
|
||||
if (m_ltRtProcessing)
|
||||
{
|
||||
m_ltRtProcessing->Process(m_ltRtInFlt.data(), dataOut, int(thisFrames));
|
||||
m_mainSubmix.m_redirectFlt = m_ltRtInFlt.data();
|
||||
}
|
||||
|
||||
size_t sampleCount = thisFrames * m_mixInfo.m_channelMap.m_channelCount;
|
||||
for (size_t i=0 ; i<sampleCount ; ++i)
|
||||
dataOut[i] *= m_totalVol;
|
||||
|
||||
remFrames -= thisFrames;
|
||||
dataOut += sampleCount;
|
||||
}
|
||||
|
||||
if (m_engineCallback)
|
||||
m_engineCallback->onPumpCycleComplete(*this);
|
||||
}
|
||||
|
||||
void BaseAudioVoiceEngine::_unbindFrom(std::list<AudioVoice*>::iterator it)
|
||||
{
|
||||
m_activeVoices.erase(it);
|
||||
}
|
||||
|
||||
void BaseAudioVoiceEngine::_unbindFrom(std::list<AudioSubmix*>::iterator it)
|
||||
{
|
||||
m_activeSubmixes.erase(it);
|
||||
m_submixesDirty = true;
|
||||
}
|
||||
template void BaseAudioVoiceEngine::_pumpAndMixVoices<int16_t>(size_t frames, int16_t* dataOut);
|
||||
template void BaseAudioVoiceEngine::_pumpAndMixVoices<int32_t>(size_t frames, int32_t* dataOut);
|
||||
template void BaseAudioVoiceEngine::_pumpAndMixVoices<float>(size_t frames, float* dataOut);
|
||||
|
||||
std::unique_ptr<IAudioVoice>
|
||||
BaseAudioVoiceEngine::allocateNewMonoVoice(double sampleRate,
|
||||
IAudioVoiceCallback* cb,
|
||||
bool dynamicPitch)
|
||||
{
|
||||
std::unique_ptr<IAudioVoice> ret =
|
||||
std::make_unique<AudioVoiceMono>(*this, cb, sampleRate, dynamicPitch);
|
||||
AudioVoiceMono* retMono = static_cast<AudioVoiceMono*>(ret.get());
|
||||
retMono->bindVoice(m_activeVoices.insert(m_activeVoices.end(), retMono));
|
||||
return ret;
|
||||
return std::make_unique<AudioVoiceMono>(*this, cb, sampleRate, dynamicPitch);
|
||||
}
|
||||
|
||||
std::unique_ptr<IAudioVoice>
|
||||
|
@ -246,20 +99,13 @@ BaseAudioVoiceEngine::allocateNewStereoVoice(double sampleRate,
|
|||
IAudioVoiceCallback* cb,
|
||||
bool dynamicPitch)
|
||||
{
|
||||
std::unique_ptr<IAudioVoice> ret =
|
||||
std::make_unique<AudioVoiceStereo>(*this, cb, sampleRate, dynamicPitch);
|
||||
AudioVoiceStereo* retStereo = static_cast<AudioVoiceStereo*>(ret.get());
|
||||
retStereo->bindVoice(m_activeVoices.insert(m_activeVoices.end(), retStereo));
|
||||
return ret;
|
||||
return std::make_unique<AudioVoiceStereo>(*this, cb, sampleRate, dynamicPitch);
|
||||
}
|
||||
|
||||
std::unique_ptr<IAudioSubmix>
|
||||
BaseAudioVoiceEngine::allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb, int busId)
|
||||
{
|
||||
std::unique_ptr<IAudioSubmix> ret = std::make_unique<AudioSubmix>(*this, cb, busId, mainOut);
|
||||
AudioSubmix* retIntern = static_cast<AudioSubmix*>(ret.get());
|
||||
retIntern->bindSubmix(m_activeSubmixes.insert(m_activeSubmixes.end(), retIntern));
|
||||
return ret;
|
||||
return std::make_unique<AudioSubmix>(*this, cb, busId, mainOut);
|
||||
}
|
||||
|
||||
void BaseAudioVoiceEngine::setCallbackInterface(IAudioVoiceEngineCallback* cb)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "AudioVoice.hpp"
|
||||
#include "AudioSubmix.hpp"
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
@ -21,8 +22,9 @@ protected:
|
|||
friend class AudioVoiceStereo;
|
||||
float m_totalVol = 1.f;
|
||||
AudioVoiceEngineMixInfo m_mixInfo;
|
||||
std::list<AudioVoice*> m_activeVoices;
|
||||
std::list<AudioSubmix*> m_activeSubmixes;
|
||||
std::recursive_mutex m_dataMutex;
|
||||
AudioVoice* m_voiceHead = nullptr;
|
||||
AudioSubmix* m_submixHead = nullptr;
|
||||
size_t m_5msFrames = 0;
|
||||
IAudioVoiceEngineCallback* m_engineCallback = nullptr;
|
||||
|
||||
|
@ -31,29 +33,28 @@ protected:
|
|||
std::vector<int16_t> m_scratch16Pre;
|
||||
std::vector<int32_t> m_scratch32Pre;
|
||||
std::vector<float> m_scratchFltPre;
|
||||
template <typename T> std::vector<T>& _getScratchPre();
|
||||
std::vector<int16_t> m_scratch16Post;
|
||||
std::vector<int32_t> m_scratch32Post;
|
||||
std::vector<float> m_scratchFltPost;
|
||||
template <typename T> std::vector<T>& _getScratchPost();
|
||||
|
||||
/* LtRt processing if enabled */
|
||||
std::unique_ptr<LtRtProcessing> m_ltRtProcessing;
|
||||
std::vector<int16_t> m_ltRtIn16;
|
||||
std::vector<int32_t> m_ltRtIn32;
|
||||
std::vector<float> m_ltRtInFlt;
|
||||
template <typename T> std::vector<T>& _getLtRtIn();
|
||||
|
||||
AudioSubmix m_mainSubmix;
|
||||
std::unique_ptr<AudioSubmix> m_mainSubmix;
|
||||
std::list<AudioSubmix*> m_linearizedSubmixes;
|
||||
bool m_submixesDirty = true;
|
||||
|
||||
void _pumpAndMixVoices(size_t frames, int16_t* dataOut);
|
||||
void _pumpAndMixVoices(size_t frames, int32_t* dataOut);
|
||||
void _pumpAndMixVoices(size_t frames, float* dataOut);
|
||||
|
||||
void _unbindFrom(std::list<AudioVoice*>::iterator it);
|
||||
void _unbindFrom(std::list<AudioSubmix*>::iterator it);
|
||||
template <typename T>
|
||||
void _pumpAndMixVoices(size_t frames, T* dataOut);
|
||||
|
||||
public:
|
||||
BaseAudioVoiceEngine() : m_mainSubmix(*this, nullptr, -1, false) {}
|
||||
BaseAudioVoiceEngine() : m_mainSubmix(std::make_unique<AudioSubmix>(*this, nullptr, -1, false)) {}
|
||||
~BaseAudioVoiceEngine();
|
||||
std::unique_ptr<IAudioVoice> allocateNewMonoVoice(double sampleRate,
|
||||
IAudioVoiceCallback* cb,
|
||||
|
@ -76,6 +77,18 @@ public:
|
|||
size_t get5MsFrames() const {return m_5msFrames;}
|
||||
};
|
||||
|
||||
template <> inline std::vector<int16_t>& BaseAudioVoiceEngine::_getScratchPre<int16_t>() { return m_scratch16Pre; }
|
||||
template <> inline std::vector<int32_t>& BaseAudioVoiceEngine::_getScratchPre<int32_t>() { return m_scratch32Pre; }
|
||||
template <> inline std::vector<float>& BaseAudioVoiceEngine::_getScratchPre<float>() { return m_scratchFltPre; }
|
||||
|
||||
template <> inline std::vector<int16_t>& BaseAudioVoiceEngine::_getScratchPost<int16_t>() { return m_scratch16Post; }
|
||||
template <> inline std::vector<int32_t>& BaseAudioVoiceEngine::_getScratchPost<int32_t>() { return m_scratch32Post; }
|
||||
template <> inline std::vector<float>& BaseAudioVoiceEngine::_getScratchPost<float>() { return m_scratchFltPost; }
|
||||
|
||||
template <> inline std::vector<int16_t>& BaseAudioVoiceEngine::_getLtRtIn<int16_t>() { return m_ltRtIn16; }
|
||||
template <> inline std::vector<int32_t>& BaseAudioVoiceEngine::_getLtRtIn<int32_t>() { return m_ltRtIn32; }
|
||||
template <> inline std::vector<float>& BaseAudioVoiceEngine::_getLtRtIn<float>() { return m_ltRtInFlt; }
|
||||
|
||||
}
|
||||
|
||||
#endif // BOO_AUDIOVOICEENGINE_HPP
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#define BOO_AUDIOCOMMON_HPP
|
||||
|
||||
#include <soxr.h>
|
||||
#include "../Common.hpp"
|
||||
#include "boo/audiodev/IAudioVoice.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
|
|
@ -162,10 +162,12 @@ struct WAVOutVoiceEngine : boo::BaseAudioVoiceEngine
|
|||
m_mixInfo.m_sampleRate = sampleRate;
|
||||
_buildAudioRenderClient();
|
||||
|
||||
for (boo::AudioVoice* vox : m_activeVoices)
|
||||
vox->_resetSampleRate(vox->m_sampleRateIn);
|
||||
for (boo::AudioSubmix* smx : m_activeSubmixes)
|
||||
smx->_resetOutputSampleRate();
|
||||
if (m_voiceHead)
|
||||
for (boo::AudioVoice& vox : *m_voiceHead)
|
||||
vox._resetSampleRate(vox.m_sampleRateIn);
|
||||
if (m_submixHead)
|
||||
for (boo::AudioSubmix& smx : *m_submixHead)
|
||||
smx._resetOutputSampleRate();
|
||||
}
|
||||
|
||||
void pumpAndMixVoices()
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <cassert>
|
||||
#include "boo/graphicsdev/IGraphicsDataFactory.hpp"
|
||||
#include "../Common.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
@ -24,34 +26,23 @@ struct GraphicsDataFactoryHead
|
|||
std::recursive_mutex m_dataMutex;
|
||||
BaseGraphicsData* m_dataHead = nullptr;
|
||||
BaseGraphicsPool* m_poolHead = nullptr;
|
||||
};
|
||||
|
||||
/** Linked-list iterator shareable by data container types */
|
||||
template<class T>
|
||||
class DataIterator
|
||||
{
|
||||
T* m_node;
|
||||
public:
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
|
||||
explicit DataIterator(T* node) : m_node(node) {}
|
||||
T& operator*() const { return *m_node; }
|
||||
bool operator!=(const DataIterator& other) const { return m_node != other.m_node; }
|
||||
DataIterator& operator++() { m_node = m_node->m_next; return *this; }
|
||||
DataIterator& operator--() { m_node = m_node->m_prev; return *this; }
|
||||
~GraphicsDataFactoryHead()
|
||||
{
|
||||
assert(m_dataHead == nullptr && "Dangling graphics data pools detected");
|
||||
assert(m_poolHead == nullptr && "Dangling graphics data pools detected");
|
||||
}
|
||||
};
|
||||
|
||||
/** Private generalized data container class.
|
||||
* Keeps head pointers to all graphics objects by type
|
||||
*/
|
||||
struct BaseGraphicsData : IObj
|
||||
struct BaseGraphicsData : ListNode<BaseGraphicsData, GraphicsDataFactoryHead*>
|
||||
{
|
||||
GraphicsDataFactoryHead& m_head;
|
||||
BaseGraphicsData* m_next;
|
||||
BaseGraphicsData* m_prev = nullptr;
|
||||
static BaseGraphicsData*& _getHeadPtr(GraphicsDataFactoryHead* head) { return head->m_dataHead; }
|
||||
static std::unique_lock<std::recursive_mutex> _getHeadLock(GraphicsDataFactoryHead* head)
|
||||
{ return std::unique_lock<std::recursive_mutex>{head->m_dataMutex}; }
|
||||
|
||||
GraphicsDataNode<IShaderPipeline, BaseGraphicsData>* m_SPs = nullptr;
|
||||
GraphicsDataNode<IShaderDataBinding, BaseGraphicsData>* m_SBinds = nullptr;
|
||||
GraphicsDataNode<IGraphicsBufferS, BaseGraphicsData>* m_SBufs = nullptr;
|
||||
|
@ -65,36 +56,11 @@ struct BaseGraphicsData : IObj
|
|||
template<class T> size_t countForward()
|
||||
{ auto* head = getHead<T>(); return head ? head->countForward() : 0; }
|
||||
std::unique_lock<std::recursive_mutex> destructorLock() override
|
||||
{ return std::unique_lock<std::recursive_mutex>{m_head.m_dataMutex}; }
|
||||
{ return std::unique_lock<std::recursive_mutex>{m_head->m_dataMutex}; }
|
||||
|
||||
explicit BaseGraphicsData(GraphicsDataFactoryHead& head)
|
||||
: m_head(head)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lk(m_head.m_dataMutex);
|
||||
m_next = head.m_dataHead;
|
||||
if (m_next)
|
||||
m_next->m_prev = this;
|
||||
head.m_dataHead = this;
|
||||
}
|
||||
~BaseGraphicsData()
|
||||
{
|
||||
if (m_prev)
|
||||
{
|
||||
if (m_next)
|
||||
m_next->m_prev = m_prev;
|
||||
m_prev->m_next = m_next;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_next)
|
||||
m_next->m_prev = nullptr;
|
||||
m_head.m_dataHead = m_next;
|
||||
}
|
||||
}
|
||||
|
||||
using iterator = DataIterator<BaseGraphicsData>;
|
||||
iterator begin() { return iterator(this); }
|
||||
iterator end() { return iterator(nullptr); }
|
||||
: ListNode<BaseGraphicsData, GraphicsDataFactoryHead*>(&head)
|
||||
{}
|
||||
};
|
||||
|
||||
template <> inline GraphicsDataNode<IShaderPipeline, BaseGraphicsData>*&
|
||||
|
@ -119,46 +85,22 @@ BaseGraphicsData::getHead<IVertexFormat>() { return m_VFmts; }
|
|||
/** Private generalized pool container class.
|
||||
* Keeps head pointer to exactly one dynamic buffer while otherwise conforming to BaseGraphicsData
|
||||
*/
|
||||
struct BaseGraphicsPool : IObj
|
||||
struct BaseGraphicsPool : ListNode<BaseGraphicsPool, GraphicsDataFactoryHead*>
|
||||
{
|
||||
GraphicsDataFactoryHead& m_head;
|
||||
BaseGraphicsPool* m_next;
|
||||
BaseGraphicsPool* m_prev = nullptr;
|
||||
static BaseGraphicsPool*& _getHeadPtr(GraphicsDataFactoryHead* head) { return head->m_poolHead; }
|
||||
static std::unique_lock<std::recursive_mutex> _getHeadLock(GraphicsDataFactoryHead* head)
|
||||
{ return std::unique_lock<std::recursive_mutex>{head->m_dataMutex}; }
|
||||
|
||||
GraphicsDataNode<IGraphicsBufferD, BaseGraphicsPool>* m_DBufs = nullptr;
|
||||
template<class T> GraphicsDataNode<T, BaseGraphicsPool>*& getHead();
|
||||
template<class T> size_t countForward()
|
||||
{ auto* head = getHead<T>(); return head ? head->countForward() : 0; }
|
||||
std::unique_lock<std::recursive_mutex> destructorLock() override
|
||||
{ return std::unique_lock<std::recursive_mutex>{m_head.m_dataMutex}; }
|
||||
{ return std::unique_lock<std::recursive_mutex>{m_head->m_dataMutex}; }
|
||||
|
||||
explicit BaseGraphicsPool(GraphicsDataFactoryHead& head)
|
||||
: m_head(head)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lk(m_head.m_dataMutex);
|
||||
m_next = head.m_poolHead;
|
||||
if (m_next)
|
||||
m_next->m_prev = this;
|
||||
head.m_poolHead = this;
|
||||
}
|
||||
~BaseGraphicsPool()
|
||||
{
|
||||
if (m_prev)
|
||||
{
|
||||
if (m_next)
|
||||
m_next->m_prev = m_prev;
|
||||
m_prev->m_next = m_next;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_next)
|
||||
m_next->m_prev = nullptr;
|
||||
m_head.m_poolHead = m_next;
|
||||
}
|
||||
}
|
||||
|
||||
using iterator = DataIterator<BaseGraphicsPool>;
|
||||
iterator begin() { return iterator(this); }
|
||||
iterator end() { return iterator(nullptr); }
|
||||
: ListNode<BaseGraphicsPool, GraphicsDataFactoryHead*>(&head)
|
||||
{}
|
||||
};
|
||||
|
||||
template <> inline GraphicsDataNode<IGraphicsBufferD, BaseGraphicsPool>*&
|
||||
|
@ -169,48 +111,25 @@ BaseGraphicsPool::getHead<IGraphicsBufferD>() { return m_DBufs; }
|
|||
* as well as doubly-linked pointers to same-type sibling objects
|
||||
*/
|
||||
template<class NodeCls, class DataCls>
|
||||
struct GraphicsDataNode : NodeCls
|
||||
struct GraphicsDataNode : ListNode<GraphicsDataNode<NodeCls, DataCls>, ObjToken<DataCls>, NodeCls>
|
||||
{
|
||||
ObjToken<DataCls> m_data;
|
||||
GraphicsDataNode<NodeCls, DataCls>* m_next;
|
||||
GraphicsDataNode<NodeCls, DataCls>* m_prev = nullptr;
|
||||
using base = ListNode<GraphicsDataNode<NodeCls, DataCls>, ObjToken<DataCls>, NodeCls>;
|
||||
static GraphicsDataNode<NodeCls, DataCls>*& _getHeadPtr(ObjToken<DataCls>& head)
|
||||
{ return head->template getHead<NodeCls>(); }
|
||||
static std::unique_lock<std::recursive_mutex> _getHeadLock(ObjToken<DataCls>& head)
|
||||
{ return std::unique_lock<std::recursive_mutex>{head->m_head->m_dataMutex}; }
|
||||
|
||||
std::unique_lock<std::recursive_mutex> destructorLock() override
|
||||
{ return std::unique_lock<std::recursive_mutex>{m_data->m_head.m_dataMutex}; }
|
||||
{ return std::unique_lock<std::recursive_mutex>{base::m_head->m_head->m_dataMutex}; }
|
||||
|
||||
explicit GraphicsDataNode(const ObjToken<DataCls>& data)
|
||||
: m_data(data)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lk(m_data->m_head.m_dataMutex);
|
||||
m_next = data->template getHead<NodeCls>();
|
||||
if (m_next)
|
||||
m_next->m_prev = this;
|
||||
data->template getHead<NodeCls>() = this;
|
||||
}
|
||||
~GraphicsDataNode()
|
||||
{
|
||||
if (m_prev)
|
||||
{
|
||||
if (m_next)
|
||||
m_next->m_prev = m_prev;
|
||||
m_prev->m_next = m_next;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_next)
|
||||
m_next->m_prev = nullptr;
|
||||
m_data->template getHead<NodeCls>() = m_next;
|
||||
}
|
||||
}
|
||||
: ListNode<GraphicsDataNode<NodeCls, DataCls>, ObjToken<DataCls>, NodeCls>(data)
|
||||
{}
|
||||
|
||||
class iterator
|
||||
class iterator : public std::iterator<std::bidirectional_iterator_tag, NodeCls>
|
||||
{
|
||||
GraphicsDataNode<NodeCls, DataCls>* m_node;
|
||||
public:
|
||||
using value_type = NodeCls;
|
||||
using pointer = NodeCls*;
|
||||
using reference = NodeCls&;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
|
||||
explicit iterator(GraphicsDataNode<NodeCls, DataCls>* node) : m_node(node) {}
|
||||
NodeCls& operator*() const { return *m_node; }
|
||||
bool operator!=(const iterator& other) const { return m_node != other.m_node; }
|
||||
|
|
Loading…
Reference in New Issue