NekoX/TMessagesProj/jni/libtgvoip3/MediaStreamItf.cpp

251 lines
6.7 KiB
C++

//
// libtgvoip is free and unencumbered public domain software.
// For more information, see http://unlicense.org or the UNLICENSE file
// you should have received with this source code distribution.
//
#include "logging.h"
#include "PrivateDefines.h"
#include "MediaStreamItf.h"
#include "EchoCanceller.h"
#include <cassert>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <utility>
using namespace tgvoip;
void MediaStreamItf::SetCallback(std::function<std::size_t(std::uint8_t*, std::size_t, void*)> callback, void* param)
{
std::lock_guard<std::mutex> lock(m_mutexCallback);
m_callback = std::move(callback);
m_callbackParam = param;
}
std::size_t MediaStreamItf::InvokeCallback(std::uint8_t* data, std::size_t length) const
{
CallbackType callback;
void* callbackParam;
{
std::lock_guard<std::mutex> lock(m_mutexCallback);
callback = m_callback;
callbackParam = m_callbackParam;
}
if (callback != nullptr)
return callback(data, length, callbackParam);
return 0;
}
AudioMixer::AudioMixer()
: m_processedQueue(16)
, m_semaphore(16, 0)
{
}
AudioMixer::~AudioMixer() = default;
void AudioMixer::SetOutput(MediaStreamItf* output)
{
output->SetCallback(OutputCallback, this);
}
void AudioMixer::Start()
{
assert(!m_running);
m_running = true;
m_thread = new Thread(std::bind(&AudioMixer::RunThread, this));
m_thread->SetName("AudioMixer");
m_thread->Start();
}
void AudioMixer::Stop()
{
if (!m_running)
{
LOGE("Tried to stop AudioMixer that wasn't started");
return;
}
m_running = false;
m_semaphore.Release();
m_thread->Join();
delete m_thread;
m_thread = nullptr;
}
void AudioMixer::DoCallback(std::uint8_t* data, std::size_t length)
{
if (m_processedQueue.Size() == 0)
m_semaphore.Release(2);
else
m_semaphore.Release();
Buffer buf = m_processedQueue.GetBlocking();
std::memcpy(data, *buf, 960 * 2);
}
std::size_t AudioMixer::OutputCallback(std::uint8_t* data, std::size_t length, void* arg)
{
reinterpret_cast<AudioMixer*>(arg)->DoCallback(data, length);
return 960 * 2;
}
void AudioMixer::AddInput(MediaStreamItfPtr input)
{
MutexGuard m(m_inputsMutex);
m_inputs.emplace(std::move(input), 1);
}
void AudioMixer::RemoveInput(const MediaStreamItfPtr& input)
{
MutexGuard m(m_inputsMutex);
auto it = m_inputs.find(input);
if (it != m_inputs.end())
m_inputs.erase(it);
}
void AudioMixer::SetInputVolume(const MediaStreamItfPtr& input, float volumeDB)
{
MutexGuard m(m_inputsMutex);
auto it = m_inputs.find(input);
if (it != m_inputs.end())
{
if (volumeDB == -std::numeric_limits<float>::infinity())
it->second = 0;
else
it->second = std::exp(volumeDB / 20.0f * std::log(10.0f));
}
}
void AudioMixer::RunThread()
{
LOGV("AudioMixer thread started");
while (m_running)
{
m_semaphore.Acquire();
if (!m_running)
break;
try
{
Buffer data = m_bufferPool.Get();
MutexGuard m(m_inputsMutex);
std::int16_t* buf = reinterpret_cast<std::int16_t*>(*data);
constexpr std::size_t SIZE = 960;
constexpr std::size_t INT16_SIZE = sizeof(std::int16_t);
std::array<std::int16_t, SIZE> input;
std::array<float, SIZE> out;
out.fill(0);
int usedInputs = 0;
for (auto& [source, multiplier] : m_inputs)
{
std::size_t res = source->InvokeCallback(reinterpret_cast<std::uint8_t*>(input.data()), STD_ARRAY_SIZEOF(input));
if (res == 0 || multiplier == 0)
{
continue;
}
++usedInputs;
for (std::size_t i = 0; i < SIZE; ++i)
{
out[i] += input[i] * multiplier;
}
}
if (usedInputs > 0)
{
for (std::size_t i = 0; i < SIZE; ++i)
{
if (out[i] > 32767.0f)
buf[i] = std::numeric_limits<std::int16_t>::max();
else if (out[i] < -32768.0f)
buf[i] = std::numeric_limits<std::int16_t>::min();
else
buf[i] = static_cast<std::int16_t>(out[i]);
}
}
else
{
std::memset(buf, 0, SIZE * INT16_SIZE);
}
if (m_echoCanceller != nullptr)
m_echoCanceller->SpeakerOutCallback(reinterpret_cast<std::uint8_t*>(buf), SIZE * INT16_SIZE);
m_processedQueue.Put(std::move(data));
}
catch (const std::bad_alloc& exception)
{
LOGE("AudioMixer: no buffers left.\nwhat():\n%s", exception.what());
continue;
}
}
LOGI("======== audio mixer thread exiting =========");
}
void AudioMixer::SetEchoCanceller(EchoCanceller* aec)
{
m_echoCanceller = aec;
}
CallbackWrapper::CallbackWrapper() = default;
CallbackWrapper::~CallbackWrapper() = default;
void CallbackWrapper::Start()
{
}
void CallbackWrapper::Stop()
{
}
AudioLevelMeter::AudioLevelMeter()
{
m_absMax = 0;
m_count = 0;
m_currentLevel = 0;
m_currentLevelFullRange = 0;
}
float AudioLevelMeter::GetLevel()
{
return m_currentLevel / 9.0f;
}
void AudioLevelMeter::Update(std::int16_t* samples, std::size_t count)
{
// Number of bars on the indicator.
// Note that the number of elements is specified because we are indexing it
// in the range of 0-32
const std::array<std::int8_t, 33> permutation =
{
0, 1, 2, 3, 4, 4, 5, 5, 5, 5, 6,
6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
};
std::int16_t absValue = 0;
for (std::size_t k = 0; k < count; ++k)
{
std::int16_t absolute = static_cast<std::int16_t>(std::abs(samples[k]));
if (absolute > absValue)
absValue = absolute;
}
if (absValue > m_absMax)
m_absMax = absValue;
// Update level approximately 10 times per second
if (this->m_count++ == 10)
{
m_currentLevelFullRange = m_absMax;
this->m_count = 0;
// Highest value for a std::int16_t is 0x7fff = 32767
// Divide with 1000 to get in the range of 0-32 which is the range of
// the permutation vector
std::size_t position = static_cast<std::size_t>(m_absMax) / 1000;
// Make it less likely that the bar stays at position 0. I.e. only if
// its in the range 0-250 (instead of 0-1000)
m_currentLevel = permutation[position];
// Decay the absolute maximum (divide by 4)
m_absMax >>= 2;
}
}