244 lines
6.9 KiB
C++
244 lines
6.9 KiB
C++
/*
|
|
*******************************************************************************
|
|
* INCLUDE
|
|
*******************************************************************************
|
|
*/
|
|
#include "AudioMixer.h"
|
|
#include "System.h"
|
|
#include <cstdint>
|
|
|
|
namespace Video
|
|
{
|
|
namespace Core
|
|
{
|
|
/*
|
|
*******************************************************************************
|
|
* CLASS DEFINE
|
|
*******************************************************************************
|
|
*/
|
|
AudioMixer::Channel::Channel(AudioMixer::Ptr pMixer, uint32_t id)
|
|
: AudioAdaptablePipe(Poco::format(pMixer->name() + "/CH#%u", id)), _pMixer(pMixer), _id(id)
|
|
{
|
|
setAttach(false);
|
|
setInput(nullptr);
|
|
}
|
|
|
|
AudioMixer::Channel::~Channel()
|
|
{
|
|
}
|
|
|
|
void AudioMixer::Channel::start()
|
|
{
|
|
}
|
|
|
|
void AudioMixer::Channel::stop()
|
|
{
|
|
}
|
|
|
|
void AudioMixer::Channel::onConfigChanged(ParameterNames& names)
|
|
{
|
|
if (names.exists(_Input))
|
|
unlink(true, false);
|
|
|
|
if (names.exists(_Attach))
|
|
{
|
|
auto pInput = getInput();
|
|
if (pInput)
|
|
{
|
|
if (getAttach())
|
|
{
|
|
getInput()->link(sharedFromThis());
|
|
link(mixer());
|
|
}
|
|
else
|
|
getInput()->unlink(sharedFromThis());
|
|
}
|
|
}
|
|
|
|
serialCodeWatcher().reset();
|
|
}
|
|
|
|
/*
|
|
*******************************************************************************
|
|
* CLASS DEFINE
|
|
*******************************************************************************
|
|
*/
|
|
AudioMixer::AudioMixer(const std::string& name): AbstractAudioInput(name)
|
|
{
|
|
_mixFrame = new Frame();
|
|
for (int i = 0; i < Frame::DATA_LINES; i++)
|
|
_mixFrame->data[i] = new uint8_t[1024 * 32];
|
|
}
|
|
|
|
AudioMixer::~AudioMixer()
|
|
{
|
|
}
|
|
|
|
AudioMixer::Channel::Ptr AudioMixer::getChannel(uint32_t id)
|
|
{
|
|
if (_channels.find(id) == _channels.end())
|
|
{
|
|
Channel::Ptr pChannel = new Channel(Ptr(this, true), id);
|
|
syncConfigToChannel(pChannel);
|
|
_channels[id] = pChannel;
|
|
}
|
|
|
|
return _channels[id];
|
|
}
|
|
|
|
void AudioMixer::syncConfigToChannel(AudioMixer::Channel::Ptr pChannel)
|
|
{
|
|
pChannel->setSampleRate(getSampleRate());
|
|
pChannel->setChannelLayout(getChannelLayout());
|
|
pChannel->setSampleFormat(getSampleFormat());
|
|
pChannel->setFrameSize(getFrameSize());
|
|
}
|
|
|
|
template <typename T, typename S>
|
|
static inline void mix(const S MIN, const S MAX, uint8_t* i0, uint8_t* i1, uint8_t* o, uint32_t bytes)
|
|
{
|
|
T* p0 = reinterpret_cast<T*>(i0);
|
|
T* p1 = reinterpret_cast<T*>(i1);
|
|
T* output = reinterpret_cast<T*>(o);
|
|
|
|
const size_t count = static_cast<size_t>(bytes / sizeof(T));
|
|
for (size_t i = 0; i < count; ++i)
|
|
{
|
|
S a = static_cast<S>(p0[i]);
|
|
S b = static_cast<S>(p1[i]);
|
|
|
|
const int64_t product = static_cast<int64_t>(a) * b;
|
|
S c = a + b - static_cast<S>(product >> 0x10);
|
|
|
|
c = (c > MAX) ? MAX : ((c < MIN) ? MIN : c);
|
|
output[i] = static_cast<T>(c);
|
|
}
|
|
}
|
|
|
|
void AudioMixer::start()
|
|
{
|
|
refreshOutputParams();
|
|
run();
|
|
}
|
|
|
|
void AudioMixer::stop()
|
|
{
|
|
cancel();
|
|
}
|
|
|
|
void AudioMixer::onConfigChanged(ParameterNames& names)
|
|
{
|
|
for (auto& m : _channels)
|
|
{
|
|
auto pChannel = m.second;
|
|
syncConfigToChannel(pChannel);
|
|
pChannel->commit();
|
|
}
|
|
|
|
AbstractAudioInput::onConfigChanged(names);
|
|
}
|
|
|
|
Frame::Ptr AudioMixer::readFrame()
|
|
{
|
|
Frame::Ptr pFrame = nullptr;
|
|
uint64_t pts = 0;
|
|
uint32_t index = 0;
|
|
for (auto& m : _channels)
|
|
{
|
|
auto pChannel = m.second;
|
|
Frame::Ptr pReadFrame;
|
|
|
|
try
|
|
{
|
|
pReadFrame = pChannel->readFrame(32);
|
|
}
|
|
catch (...)
|
|
{
|
|
pReadFrame = nullptr;
|
|
}
|
|
|
|
if (!pReadFrame.isNull())
|
|
{
|
|
if (pts == 0 || pts > pReadFrame->timestamp)
|
|
pts = pReadFrame->timestamp;
|
|
|
|
if (pFrame == nullptr)
|
|
pFrame = pReadFrame;
|
|
else
|
|
{
|
|
#define AUDIO_DATA(i) index > 0 ? _mixFrame->data[i].get() : pFrame->data[i].get()
|
|
switch (getSampleFormat())
|
|
{
|
|
case SampleFormat::U8:
|
|
case SampleFormat::U8P:
|
|
for (uint32_t i = 0; i < getChannels(); i++)
|
|
mix<uint8_t, uint16_t>(0, UINT8_MAX, AUDIO_DATA(i), pReadFrame->data[i].get(),
|
|
_mixFrame->data[i].get(), pFrame->length);
|
|
break;
|
|
|
|
case SampleFormat::S16:
|
|
case SampleFormat::S16P:
|
|
for (uint32_t i = 0; i < getChannels(); i++)
|
|
mix<int16_t, int32_t>(INT16_MIN, INT16_MAX, AUDIO_DATA(i), pReadFrame->data[i].get(),
|
|
_mixFrame->data[i].get(), pFrame->length);
|
|
break;
|
|
|
|
case SampleFormat::S32:
|
|
case SampleFormat::S32P:
|
|
for (uint32_t i = 0; i < getChannels(); i++)
|
|
mix<int32_t, int64_t>(INT32_MIN, INT32_MAX, AUDIO_DATA(i), pReadFrame->data[i].get(),
|
|
_mixFrame->data[i].get(), pFrame->length);
|
|
break;
|
|
|
|
case SampleFormat::S64:
|
|
case SampleFormat::S64P:
|
|
for (uint32_t i = 0; i < getChannels(); i++)
|
|
mix<int64_t, int64_t>(INT64_MIN, INT64_MAX, AUDIO_DATA(i), pReadFrame->data[i].get(),
|
|
_mixFrame->data[i].get(), pFrame->length);
|
|
break;
|
|
|
|
case SampleFormat::FLT:
|
|
case SampleFormat::FLTP:
|
|
for (uint32_t i = 0; i < getChannels(); i++)
|
|
mix<float, float>(-1.0f, 1.0f, AUDIO_DATA(i), pReadFrame->data[i].get(),
|
|
_mixFrame->data[i].get(), pFrame->length);
|
|
break;
|
|
|
|
case SampleFormat::DBL:
|
|
case SampleFormat::DBLP:
|
|
for (uint32_t i = 0; i < getChannels(); i++)
|
|
mix<double, double>(-1.0, 1.0, AUDIO_DATA(i), pReadFrame->data[i].get(),
|
|
_mixFrame->data[i].get(), pFrame->length);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
index++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pFrame.isNull())
|
|
return nullptr;
|
|
|
|
_traffic++;
|
|
if (index > 0)
|
|
{
|
|
_mixFrame->timestamp = pts;
|
|
_mixFrame->length = pFrame->length;
|
|
_mixFrame->code = pFrame->code;
|
|
return _mixFrame;
|
|
}
|
|
else
|
|
return pFrame;
|
|
}
|
|
|
|
void AudioMixer::refreshOutputParams()
|
|
{
|
|
setFrameDuration((float)1000000 / getSampleRate() * getFrameSize());
|
|
setChannels(channelLayoutChannels(getChannelLayout()));
|
|
setLineSize(sampleFormatCalcLineSize(getSampleFormat(), getChannelLayout(), getFrameSize()));
|
|
}
|
|
}
|
|
}
|