/* ******************************************************************************* * INCLUDE ******************************************************************************* */ #include "AudioMixer.h" #include "System.h" #include 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 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(i0); T* p1 = reinterpret_cast(i1); T* output = reinterpret_cast(o); const size_t count = static_cast(bytes / sizeof(T)); for (size_t i = 0; i < count; ++i) { S a = static_cast(p0[i]); S b = static_cast(p1[i]); const int64_t product = static_cast(a) * b; S c = a + b - static_cast(product >> 0x10); c = (c > MAX) ? MAX : ((c < MIN) ? MIN : c); output[i] = static_cast(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(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_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_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_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(-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(-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())); } } }