diff --git a/src/utils/Obs_VolumeMeter.cpp b/src/utils/Obs_VolumeMeter.cpp index 92fbe3cf..9fa75eec 100644 --- a/src/utils/Obs_VolumeMeter.cpp +++ b/src/utils/Obs_VolumeMeter.cpp @@ -1,354 +1,353 @@ -/* -obs-websocket -Copyright (C) 2014 by Leonhard Oelke -Copyright (C) 2016-2021 Stephane Lepin -Copyright (C) 2020-2021 Kyle Manning - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see -*/ - -#include -#include - -#include "Obs.h" -#include "Obs_VolumeMeter.h" -#include "Obs_VolumeMeter_Helpers.h" -#include "../obs-websocket.h" - -Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input) : - PeakMeterType(SAMPLE_PEAK_METER), - _input(obs_source_get_weak_source(input)), - _channels(0), - _lastUpdate(0), - _volume(obs_source_get_volume(input)) -{ - signal_handler_t *sh = obs_source_get_signal_handler(input); - signal_handler_connect(sh, "volume", Meter::InputVolumeCallback, this); - - obs_source_add_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this); - - blog_debug("[Utils::Obs::VolumeMeter::Meter::Meter] Meter created for input: %s", obs_source_get_name(input)); -} - -Utils::Obs::VolumeMeter::Meter::~Meter() -{ - OBSSourceAutoRelease input = obs_weak_source_get_source(_input); - if (!input) { - blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::~Meter] Failed to get strong reference to input. Has it been destroyed?"); - return; - } - - signal_handler_t *sh = obs_source_get_signal_handler(input); - signal_handler_disconnect(sh, "volume", Meter::InputVolumeCallback, this); - - obs_source_remove_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this); - - blog_debug("[Utils::Obs::VolumeMeter::Meter::~Meter] Meter destroyed for input: %s", obs_source_get_name(input)); -} - -bool Utils::Obs::VolumeMeter::Meter::InputValid() -{ - // return !obs_weak_source_expired(_input); - return true; -} - -json Utils::Obs::VolumeMeter::Meter::GetMeterData() -{ - json ret; - - OBSSourceAutoRelease input = obs_weak_source_get_source(_input); - if (!input) { - blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?"); - return ret; - } - - std::vector> levels; - const float volume = _muted ? 0.0f : _volume.load(); - - std::unique_lock l(_mutex); - - if (_lastUpdate != 0 && (os_gettime_ns() - _lastUpdate) * 0.000000001 > 0.3) - ResetAudioLevels(); - - for (int channel = 0; channel < _channels; channel++) { - std::vector level; - level.push_back(_magnitude[channel] * volume); - level.push_back(_peak[channel] * volume); - level.push_back(_peak[channel]); - - levels.push_back(level); - } - l.unlock(); - - ret["inputName"] = obs_source_get_name(input); - ret["inputLevelsMul"] = levels; - - return ret; -} - -// MUST HOLD LOCK -void Utils::Obs::VolumeMeter::Meter::ResetAudioLevels() -{ - _lastUpdate = 0; - for (int channelNumber = 0; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++) { - _magnitude[channelNumber] = 0; - _peak[channelNumber] = 0; - } -} - -// MUST HOLD LOCK -void Utils::Obs::VolumeMeter::Meter::ProcessAudioChannels(const struct audio_data *data) -{ - int channels = 0; - for (int i = 0; i < MAX_AV_PLANES; i++) { - if (data->data[i]) - channels++; - } - - bool channelsChanged = _channels != channels; - _channels = std::clamp(channels, 0, MAX_AUDIO_CHANNELS); - - if (channelsChanged) - ResetAudioLevels(); -} - -// MUST HOLD LOCK -void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data) -{ - size_t sampleCount = data->frames; - int channelNumber = 0; - - for (int planeNumber = 0; channelNumber < _channels; planeNumber++) { - float *samples = (float*)data->data[planeNumber]; - if (!samples) - continue; - - if (((uintptr_t)samples & 0xf) > 0) { - _peak[channelNumber] = 1.0f; - channelNumber++; - continue; - } - - __m128 previousSamples = _mm_loadu_ps(_previousSamples[channelNumber]); - - float peak; - switch (PeakMeterType) { - default: - case SAMPLE_PEAK_METER: - peak = GetSamplePeak(previousSamples, samples, sampleCount); - break; - case TRUE_PEAK_METER: - peak = GetTruePeak(previousSamples, samples, sampleCount); - break; - } - - switch (sampleCount) { - case 0: - break; - case 1: - _previousSamples[channelNumber][0] = _previousSamples[channelNumber][1]; - _previousSamples[channelNumber][1] = _previousSamples[channelNumber][2]; - _previousSamples[channelNumber][2] = _previousSamples[channelNumber][3]; - _previousSamples[channelNumber][3] = samples[sampleCount - 1]; - break; - case 2: - _previousSamples[channelNumber][0] = _previousSamples[channelNumber][2]; - _previousSamples[channelNumber][1] = _previousSamples[channelNumber][3]; - _previousSamples[channelNumber][2] = samples[sampleCount - 2]; - _previousSamples[channelNumber][3] = samples[sampleCount - 1]; - break; - case 3: - _previousSamples[channelNumber][0] = _previousSamples[channelNumber][3]; - _previousSamples[channelNumber][1] = samples[sampleCount - 3]; - _previousSamples[channelNumber][2] = samples[sampleCount - 2]; - _previousSamples[channelNumber][3] = samples[sampleCount - 1]; - break; - default: - _previousSamples[channelNumber][0] = samples[sampleCount - 4]; - _previousSamples[channelNumber][1] = samples[sampleCount - 3]; - _previousSamples[channelNumber][2] = samples[sampleCount - 2]; - _previousSamples[channelNumber][3] = samples[sampleCount - 1]; - } - - _peak[channelNumber] = peak; - - channelNumber++; - } - - for (; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++) - _peak[channelNumber] = 0.0; -} - -// MUST HOLD LOCK -void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(const struct audio_data *data) -{ - size_t sampleCount = data->frames; - - int channelNumber = 0; - for (int planeNumber = 0; channelNumber < _channels; planeNumber++) { - float *samples = (float*)data->data[planeNumber]; - if (!samples) - continue; - - float sum = 0.0; - for (size_t i = 0; i < sampleCount; i++) { - float sample = samples[i]; - sum += sample * sample; - } - - _magnitude[channelNumber] = std::sqrt(sum / sampleCount); - - channelNumber++; - } -} - -void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data, obs_source_t *, const struct audio_data *data, bool muted) -{ - auto c = static_cast(priv_data); - - std::unique_lock l(c->_mutex); - - c->_muted = muted; - c->ProcessAudioChannels(data); - c->ProcessPeak(data); - c->ProcessMagnitude(data); - - c->_lastUpdate = os_gettime_ns(); -} - -void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, calldata_t *cd) -{ - auto c = static_cast(priv_data); - - c->_volume = (float)calldata_float(cd, "volume"); -} - -Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeriod) : - _updateCallback(cb), - _updatePeriod(updatePeriod), - _running(false) -{ - signal_handler_t *sh = obs_get_signal_handler(); - if (!sh) - return; - - auto enumProc = [](void *priv_data, obs_source_t *input) { - auto c = static_cast(priv_data); - - if (!obs_source_active(input)) - return true; - - uint32_t flags = obs_source_get_output_flags(input); - if ((flags & OBS_SOURCE_AUDIO) == 0) - return true; - - c->_meters.emplace_back(std::move(new Meter(input))); - - return true; - }; - obs_enum_sources(enumProc, this); - - signal_handler_connect(sh, "source_activate", Handler::InputActivateCallback, this); - signal_handler_connect(sh, "source_deactivate", Handler::InputDeactivateCallback, this); - - _running = true; - _updateThread = std::thread(&Handler::UpdateThread, this); - - blog_debug("[Utils::Obs::VolumeMeter::Handler::Handler] Handler created."); -} - -Utils::Obs::VolumeMeter::Handler::~Handler() -{ - signal_handler_t *sh = obs_get_signal_handler(); - if (!sh) - return; - - signal_handler_disconnect(sh, "source_activate", Handler::InputActivateCallback, this); - signal_handler_disconnect(sh, "source_deactivate", Handler::InputDeactivateCallback, this); - - if (_running) { - _running = false; - _cond.notify_all(); - } - - if (_updateThread.joinable()) - _updateThread.join(); - - blog_debug("[Utils::Obs::VolumeMeter::Handler::~Handler] Handler destroyed."); -} - -void Utils::Obs::VolumeMeter::Handler::UpdateThread() -{ - blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread started."); - while (_running) { - { - std::unique_lock l(_mutex); - if (_cond.wait_for(l, std::chrono::milliseconds(_updatePeriod), [this]{ return !_running; })) - break; - } - - std::vector inputs; - std::unique_lock l(_meterMutex); - for (auto &meter : _meters) { - if (meter->InputValid()) - inputs.push_back(meter->GetMeterData()); - } - l.unlock(); - - if (_updateCallback) - _updateCallback(inputs); - } - blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread stopped."); -} - -void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data, calldata_t *cd) -{ - auto c = static_cast(priv_data); - - obs_source_t *input = GetCalldataPointer(cd, "source"); - if (!input) - return; - - if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT) - return; - - uint32_t flags = obs_source_get_output_flags(input); - if ((flags & OBS_SOURCE_AUDIO) == 0) - return; - - std::unique_lock l(c->_meterMutex); - c->_meters.emplace_back(std::move(new Meter(input))); -} - -void Utils::Obs::VolumeMeter::Handler::InputDeactivateCallback(void *priv_data, calldata_t *cd) -{ - auto c = static_cast(priv_data); - - obs_source_t *input = GetCalldataPointer(cd, "source"); - if (!input) - return; - - if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT) - return; - - // Don't ask me why, but using std::remove_if segfaults trying this. - std::unique_lock l(c->_meterMutex); - std::vector::iterator iter; - for (iter = c->_meters.begin(); iter != c->_meters.end();) { - if (obs_weak_source_references_source(iter->get()->GetWeakInput(), input)) - iter = c->_meters.erase(iter); - else - ++iter; - } -} +/* +obs-websocket +Copyright (C) 2014 by Leonhard Oelke +Copyright (C) 2016-2021 Stephane Lepin +Copyright (C) 2020-2021 Kyle Manning + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program. If not, see +*/ + +#include +#include + +#include "Obs.h" +#include "Obs_VolumeMeter.h" +#include "Obs_VolumeMeter_Helpers.h" +#include "../obs-websocket.h" + +Utils::Obs::VolumeMeter::Meter::Meter(obs_source_t *input) : + PeakMeterType(SAMPLE_PEAK_METER), + _input(obs_source_get_weak_source(input)), + _channels(0), + _lastUpdate(0), + _volume(obs_source_get_volume(input)) +{ + signal_handler_t *sh = obs_source_get_signal_handler(input); + signal_handler_connect(sh, "volume", Meter::InputVolumeCallback, this); + + obs_source_add_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this); + + blog_debug("[Utils::Obs::VolumeMeter::Meter::Meter] Meter created for input: %s", obs_source_get_name(input)); +} + +Utils::Obs::VolumeMeter::Meter::~Meter() +{ + OBSSourceAutoRelease input = obs_weak_source_get_source(_input); + if (!input) { + blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::~Meter] Failed to get strong reference to input. Has it been destroyed?"); + return; + } + + signal_handler_t *sh = obs_source_get_signal_handler(input); + signal_handler_disconnect(sh, "volume", Meter::InputVolumeCallback, this); + + obs_source_remove_audio_capture_callback(input, Meter::InputAudioCaptureCallback, this); + + blog_debug("[Utils::Obs::VolumeMeter::Meter::~Meter] Meter destroyed for input: %s", obs_source_get_name(input)); +} + +bool Utils::Obs::VolumeMeter::Meter::InputValid() +{ + return !obs_weak_source_expired(_input); +} + +json Utils::Obs::VolumeMeter::Meter::GetMeterData() +{ + json ret; + + OBSSourceAutoRelease input = obs_weak_source_get_source(_input); + if (!input) { + blog(LOG_WARNING, "[Utils::Obs::VolumeMeter::Meter::GetMeterData] Failed to get strong reference to input. Has it been destroyed?"); + return ret; + } + + std::vector> levels; + const float volume = _muted ? 0.0f : _volume.load(); + + std::unique_lock l(_mutex); + + if (_lastUpdate != 0 && (os_gettime_ns() - _lastUpdate) * 0.000000001 > 0.3) + ResetAudioLevels(); + + for (int channel = 0; channel < _channels; channel++) { + std::vector level; + level.push_back(_magnitude[channel] * volume); + level.push_back(_peak[channel] * volume); + level.push_back(_peak[channel]); + + levels.push_back(level); + } + l.unlock(); + + ret["inputName"] = obs_source_get_name(input); + ret["inputLevelsMul"] = levels; + + return ret; +} + +// MUST HOLD LOCK +void Utils::Obs::VolumeMeter::Meter::ResetAudioLevels() +{ + _lastUpdate = 0; + for (int channelNumber = 0; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++) { + _magnitude[channelNumber] = 0; + _peak[channelNumber] = 0; + } +} + +// MUST HOLD LOCK +void Utils::Obs::VolumeMeter::Meter::ProcessAudioChannels(const struct audio_data *data) +{ + int channels = 0; + for (int i = 0; i < MAX_AV_PLANES; i++) { + if (data->data[i]) + channels++; + } + + bool channelsChanged = _channels != channels; + _channels = std::clamp(channels, 0, MAX_AUDIO_CHANNELS); + + if (channelsChanged) + ResetAudioLevels(); +} + +// MUST HOLD LOCK +void Utils::Obs::VolumeMeter::Meter::ProcessPeak(const struct audio_data *data) +{ + size_t sampleCount = data->frames; + int channelNumber = 0; + + for (int planeNumber = 0; channelNumber < _channels; planeNumber++) { + float *samples = (float*)data->data[planeNumber]; + if (!samples) + continue; + + if (((uintptr_t)samples & 0xf) > 0) { + _peak[channelNumber] = 1.0f; + channelNumber++; + continue; + } + + __m128 previousSamples = _mm_loadu_ps(_previousSamples[channelNumber]); + + float peak; + switch (PeakMeterType) { + default: + case SAMPLE_PEAK_METER: + peak = GetSamplePeak(previousSamples, samples, sampleCount); + break; + case TRUE_PEAK_METER: + peak = GetTruePeak(previousSamples, samples, sampleCount); + break; + } + + switch (sampleCount) { + case 0: + break; + case 1: + _previousSamples[channelNumber][0] = _previousSamples[channelNumber][1]; + _previousSamples[channelNumber][1] = _previousSamples[channelNumber][2]; + _previousSamples[channelNumber][2] = _previousSamples[channelNumber][3]; + _previousSamples[channelNumber][3] = samples[sampleCount - 1]; + break; + case 2: + _previousSamples[channelNumber][0] = _previousSamples[channelNumber][2]; + _previousSamples[channelNumber][1] = _previousSamples[channelNumber][3]; + _previousSamples[channelNumber][2] = samples[sampleCount - 2]; + _previousSamples[channelNumber][3] = samples[sampleCount - 1]; + break; + case 3: + _previousSamples[channelNumber][0] = _previousSamples[channelNumber][3]; + _previousSamples[channelNumber][1] = samples[sampleCount - 3]; + _previousSamples[channelNumber][2] = samples[sampleCount - 2]; + _previousSamples[channelNumber][3] = samples[sampleCount - 1]; + break; + default: + _previousSamples[channelNumber][0] = samples[sampleCount - 4]; + _previousSamples[channelNumber][1] = samples[sampleCount - 3]; + _previousSamples[channelNumber][2] = samples[sampleCount - 2]; + _previousSamples[channelNumber][3] = samples[sampleCount - 1]; + } + + _peak[channelNumber] = peak; + + channelNumber++; + } + + for (; channelNumber < MAX_AUDIO_CHANNELS; channelNumber++) + _peak[channelNumber] = 0.0; +} + +// MUST HOLD LOCK +void Utils::Obs::VolumeMeter::Meter::ProcessMagnitude(const struct audio_data *data) +{ + size_t sampleCount = data->frames; + + int channelNumber = 0; + for (int planeNumber = 0; channelNumber < _channels; planeNumber++) { + float *samples = (float*)data->data[planeNumber]; + if (!samples) + continue; + + float sum = 0.0; + for (size_t i = 0; i < sampleCount; i++) { + float sample = samples[i]; + sum += sample * sample; + } + + _magnitude[channelNumber] = std::sqrt(sum / sampleCount); + + channelNumber++; + } +} + +void Utils::Obs::VolumeMeter::Meter::InputAudioCaptureCallback(void *priv_data, obs_source_t *, const struct audio_data *data, bool muted) +{ + auto c = static_cast(priv_data); + + std::unique_lock l(c->_mutex); + + c->_muted = muted; + c->ProcessAudioChannels(data); + c->ProcessPeak(data); + c->ProcessMagnitude(data); + + c->_lastUpdate = os_gettime_ns(); +} + +void Utils::Obs::VolumeMeter::Meter::InputVolumeCallback(void *priv_data, calldata_t *cd) +{ + auto c = static_cast(priv_data); + + c->_volume = (float)calldata_float(cd, "volume"); +} + +Utils::Obs::VolumeMeter::Handler::Handler(UpdateCallback cb, uint64_t updatePeriod) : + _updateCallback(cb), + _updatePeriod(updatePeriod), + _running(false) +{ + signal_handler_t *sh = obs_get_signal_handler(); + if (!sh) + return; + + auto enumProc = [](void *priv_data, obs_source_t *input) { + auto c = static_cast(priv_data); + + if (!obs_source_active(input)) + return true; + + uint32_t flags = obs_source_get_output_flags(input); + if ((flags & OBS_SOURCE_AUDIO) == 0) + return true; + + c->_meters.emplace_back(std::move(new Meter(input))); + + return true; + }; + obs_enum_sources(enumProc, this); + + signal_handler_connect(sh, "source_activate", Handler::InputActivateCallback, this); + signal_handler_connect(sh, "source_deactivate", Handler::InputDeactivateCallback, this); + + _running = true; + _updateThread = std::thread(&Handler::UpdateThread, this); + + blog_debug("[Utils::Obs::VolumeMeter::Handler::Handler] Handler created."); +} + +Utils::Obs::VolumeMeter::Handler::~Handler() +{ + signal_handler_t *sh = obs_get_signal_handler(); + if (!sh) + return; + + signal_handler_disconnect(sh, "source_activate", Handler::InputActivateCallback, this); + signal_handler_disconnect(sh, "source_deactivate", Handler::InputDeactivateCallback, this); + + if (_running) { + _running = false; + _cond.notify_all(); + } + + if (_updateThread.joinable()) + _updateThread.join(); + + blog_debug("[Utils::Obs::VolumeMeter::Handler::~Handler] Handler destroyed."); +} + +void Utils::Obs::VolumeMeter::Handler::UpdateThread() +{ + blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread started."); + while (_running) { + { + std::unique_lock l(_mutex); + if (_cond.wait_for(l, std::chrono::milliseconds(_updatePeriod), [this]{ return !_running; })) + break; + } + + std::vector inputs; + std::unique_lock l(_meterMutex); + for (auto &meter : _meters) { + if (meter->InputValid()) + inputs.push_back(meter->GetMeterData()); + } + l.unlock(); + + if (_updateCallback) + _updateCallback(inputs); + } + blog_debug("[Utils::Obs::VolumeMeter::Handler::UpdateThread] Thread stopped."); +} + +void Utils::Obs::VolumeMeter::Handler::InputActivateCallback(void *priv_data, calldata_t *cd) +{ + auto c = static_cast(priv_data); + + obs_source_t *input = GetCalldataPointer(cd, "source"); + if (!input) + return; + + if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT) + return; + + uint32_t flags = obs_source_get_output_flags(input); + if ((flags & OBS_SOURCE_AUDIO) == 0) + return; + + std::unique_lock l(c->_meterMutex); + c->_meters.emplace_back(std::move(new Meter(input))); +} + +void Utils::Obs::VolumeMeter::Handler::InputDeactivateCallback(void *priv_data, calldata_t *cd) +{ + auto c = static_cast(priv_data); + + obs_source_t *input = GetCalldataPointer(cd, "source"); + if (!input) + return; + + if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT) + return; + + // Don't ask me why, but using std::remove_if segfaults trying this. + std::unique_lock l(c->_meterMutex); + std::vector::iterator iter; + for (iter = c->_meters.begin(); iter != c->_meters.end();) { + if (obs_weak_source_references_source(iter->get()->GetWeakInput(), input)) + iter = c->_meters.erase(iter); + else + ++iter; + } +} diff --git a/src/utils/Obs_VolumeMeter.h b/src/utils/Obs_VolumeMeter.h index 9058fb2d..e2b20a6e 100644 --- a/src/utils/Obs_VolumeMeter.h +++ b/src/utils/Obs_VolumeMeter.h @@ -1,99 +1,99 @@ -/* -obs-websocket -Copyright (C) 2016-2021 Stephane Lepin -Copyright (C) 2020-2021 Kyle Manning - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see -*/ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "Obs.h" -#include "Json.h" - -namespace Utils { - namespace Obs { - namespace VolumeMeter { - // Some code copied from https://github.com/obsproject/obs-studio/blob/master/libobs/obs-audio-controls.c - // Keeps a running tally of the current audio levels, for a specific input - class Meter { - public: - Meter(obs_source_t *input); - ~Meter(); - - bool InputValid(); - obs_weak_source_t *GetWeakInput() { return _input; } - json GetMeterData(); - - std::atomic PeakMeterType; - - private: - OBSWeakSourceAutoRelease _input; - - // All values in mul - std::mutex _mutex; - bool _muted; - int _channels; - float _magnitude[MAX_AUDIO_CHANNELS]; - float _peak[MAX_AUDIO_CHANNELS]; - float _previousSamples[MAX_AUDIO_CHANNELS][4]; - - std::atomic _lastUpdate; - std::atomic _volume; - - void ResetAudioLevels(); - void ProcessAudioChannels(const struct audio_data *data); - void ProcessPeak(const struct audio_data *data); - void ProcessMagnitude(const struct audio_data *data); - - static void InputAudioCaptureCallback(void *priv_data, obs_source_t *source, const struct audio_data *data, bool muted); - static void InputVolumeCallback(void *priv_data, calldata_t *cd); - }; - - // Maintains an array of active inputs - class Handler { - typedef std::function)> UpdateCallback; - typedef std::unique_ptr MeterPtr; - - public: - Handler(UpdateCallback cb, uint64_t updatePeriod = 50); - ~Handler(); - - private: - UpdateCallback _updateCallback; - - std::mutex _meterMutex; - std::vector _meters; - uint64_t _updatePeriod; - - std::mutex _mutex; - std::condition_variable _cond; - std::atomic _running; - std::thread _updateThread; - - void UpdateThread(); - static void InputActivateCallback(void *priv_data, calldata_t *cd); - static void InputDeactivateCallback(void *priv_data, calldata_t *cd); - }; - } - } -} +/* +obs-websocket +Copyright (C) 2016-2021 Stephane Lepin +Copyright (C) 2020-2021 Kyle Manning + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program. If not, see +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "Obs.h" +#include "Json.h" + +namespace Utils { + namespace Obs { + namespace VolumeMeter { + // Some code copied from https://github.com/obsproject/obs-studio/blob/master/libobs/obs-audio-controls.c + // Keeps a running tally of the current audio levels, for a specific input + class Meter { + public: + Meter(obs_source_t *input); + ~Meter(); + + bool InputValid(); + obs_weak_source_t *GetWeakInput() { return _input; } + json GetMeterData(); + + std::atomic PeakMeterType; + + private: + OBSWeakSourceAutoRelease _input; + + // All values in mul + std::mutex _mutex; + bool _muted; + int _channels; + float _magnitude[MAX_AUDIO_CHANNELS]; + float _peak[MAX_AUDIO_CHANNELS]; + float _previousSamples[MAX_AUDIO_CHANNELS][4]; + + std::atomic _lastUpdate; + std::atomic _volume; + + void ResetAudioLevels(); + void ProcessAudioChannels(const struct audio_data *data); + void ProcessPeak(const struct audio_data *data); + void ProcessMagnitude(const struct audio_data *data); + + static void InputAudioCaptureCallback(void *priv_data, obs_source_t *source, const struct audio_data *data, bool muted); + static void InputVolumeCallback(void *priv_data, calldata_t *cd); + }; + + // Maintains an array of active inputs + class Handler { + typedef std::function)> UpdateCallback; + typedef std::unique_ptr MeterPtr; + + public: + Handler(UpdateCallback cb, uint64_t updatePeriod = 50); + ~Handler(); + + private: + UpdateCallback _updateCallback; + + std::mutex _meterMutex; + std::vector _meters; + uint64_t _updatePeriod; + + std::mutex _mutex; + std::condition_variable _cond; + std::atomic _running; + std::thread _updateThread; + + void UpdateThread(); + static void InputActivateCallback(void *priv_data, calldata_t *cd); + static void InputDeactivateCallback(void *priv_data, calldata_t *cd); + }; + } + } +} diff --git a/src/utils/Obs_VolumeMeter_Helpers.h b/src/utils/Obs_VolumeMeter_Helpers.h index e69bfc6f..7af42ac3 100644 --- a/src/utils/Obs_VolumeMeter_Helpers.h +++ b/src/utils/Obs_VolumeMeter_Helpers.h @@ -56,7 +56,7 @@ with this program. If not, see r = fmaxf(r, x4_mem[3]); \ } while (false) -float GetSamplePeak(__m128 previousSamples, const float *samples, size_t sampleCount) +static float GetSamplePeak(__m128 previousSamples, const float *samples, size_t sampleCount) { __m128 peak = previousSamples; for (size_t i = 0; (i + 3) < sampleCount; i += 4) { @@ -69,7 +69,7 @@ float GetSamplePeak(__m128 previousSamples, const float *samples, size_t sampleC return ret; } -float GetTruePeak(__m128 previousSamples, const float *samples, size_t sampleCount) +static float GetTruePeak(__m128 previousSamples, const float *samples, size_t sampleCount) { const __m128 m3 = _mm_set_ps(-0.155915f, 0.935489f, 0.233872f, -0.103943f); const __m128 m1 = _mm_set_ps(-0.216236f, 0.756827f, 0.504551f, -0.189207f);