// // 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 #include #include #include #include using namespace tgvoip; void MediaStreamItf::SetCallback(std::function callback, void* param) { std::lock_guard 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 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(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::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(*data); constexpr std::size_t SIZE = 960; constexpr std::size_t INT16_SIZE = sizeof(std::int16_t); std::array input; std::array out; out.fill(0); int usedInputs = 0; for (auto& [source, multiplier] : m_inputs) { std::size_t res = source->InvokeCallback(reinterpret_cast(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::max(); else if (out[i] < -32768.0f) buf[i] = std::numeric_limits::min(); else buf[i] = static_cast(out[i]); } } else { std::memset(buf, 0, SIZE * INT16_SIZE); } if (m_echoCanceller != nullptr) m_echoCanceller->SpeakerOutCallback(reinterpret_cast(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 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::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(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; } }