aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2018-02-15 16:32:29 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2018-02-15 16:32:29 +0100
commit7e5367bd6df7fbdb70dd3bd02d033d7bc26417d7 (patch)
tree5d0ecf3deab51269b00e663be64abd7295f0147a
parent278a5c1ee001cff8b33bc001f7719f280c4bb2da (diff)
downloaddabmux-7e5367bd6df7fbdb70dd3bd02d033d7bc26417d7.tar.gz
dabmux-7e5367bd6df7fbdb70dd3bd02d033d7bc26417d7.tar.bz2
dabmux-7e5367bd6df7fbdb70dd3bd02d033d7bc26417d7.zip
Prepare audio level stats for clear-on-read removal
-rw-r--r--src/ManagementServer.cpp143
-rw-r--r--src/ManagementServer.h158
2 files changed, 164 insertions, 137 deletions
diff --git a/src/ManagementServer.cpp b/src/ManagementServer.cpp
index 3300c89..10ba396 100644
--- a/src/ManagementServer.cpp
+++ b/src/ManagementServer.cpp
@@ -2,7 +2,7 @@
Copyright (C) 2009 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2017
+ Copyright (C) 2018
Matthias P. Braendli, matthias.braendli@mpb.li
http://www.opendigitalradio.org
@@ -40,6 +40,8 @@
#include "ManagementServer.h"
#include "Log.h"
+static constexpr auto stats_keep_duration = std::chrono::seconds(30);
+
ManagementServer& get_mgmt_server()
{
static ManagementServer mgmt_server;
@@ -244,9 +246,22 @@ void ManagementServer::update_ptree(const boost::property_tree::ptree& pt)
/************************************************/
-void InputStat::registerAtServer()
+InputStat::InputStat(const std::string& name)
+ : m_name(name)
{
- get_mgmt_server().registerInput(this);
+ /* Statistics */
+ num_underruns = 0;
+ num_overruns = 0;
+
+ /* State handling */
+ time_t now = time(NULL);
+ m_time_last_event = now;
+ m_time_last_buffer_nonempty = 0;
+ m_buffer_empty = true;
+ m_glitch_counter = 0;
+ m_silence_counter = 0;
+
+ reset();
}
InputStat::~InputStat()
@@ -254,6 +269,120 @@ InputStat::~InputStat()
get_mgmt_server().unregisterInput(m_name);
}
+void InputStat::registerAtServer()
+{
+ get_mgmt_server().registerInput(this);
+}
+
+void InputStat::reset()
+{
+ min_fill_buffer = MIN_FILL_BUFFER_UNDEF;
+ max_fill_buffer = 0;
+
+ peaks_left.clear();
+ peaks_right.clear();
+}
+
+void InputStat::notifyBuffer(long bufsize)
+{
+ boost::mutex::scoped_lock lock(m_mutex);
+
+ // Statistics
+ if (bufsize > max_fill_buffer) {
+ max_fill_buffer = bufsize;
+ }
+
+ if (bufsize < min_fill_buffer ||
+ min_fill_buffer == MIN_FILL_BUFFER_UNDEF) {
+ min_fill_buffer = bufsize;
+ }
+
+ // State
+ m_buffer_empty = (bufsize == 0);
+ if (!m_buffer_empty) {
+ m_time_last_buffer_nonempty = time(NULL);
+ }
+}
+
+void InputStat::notifyPeakLevels(int peak_left, int peak_right)
+{
+ boost::mutex::scoped_lock lock(m_mutex);
+
+ peaks_left.push_back(peak_left);
+ peaks_right.push_back(peak_right);
+
+ using namespace std::chrono;
+
+ const auto time_now = steady_clock::now();
+ if (peaks_left.size() > 1) {
+ auto insertion_interval = time_now - time_last_peak_notify;
+ auto peaks_total_length = insertion_interval * peaks_left.size();
+
+ if (peaks_total_length > stats_keep_duration) {
+ peaks_left.pop_front();
+ peaks_right.pop_front();
+ }
+ }
+ time_last_peak_notify = time_now;
+
+ if (peaks_left.empty() or peaks_right.empty()) {
+ throw std::logic_error("Peak statistics empty!");
+ }
+
+ const auto max_left = *max_element(peaks_left.begin(), peaks_left.end());
+ const auto max_right = *max_element(peaks_right.begin(), peaks_right.end());
+
+ // State
+
+ // using the lower of the two channels allows us to detect if only one
+ // channel is silent.
+ const int lower_peak = max_left < max_right ? max_left : max_right;
+
+ const int16_t int16_max = std::numeric_limits<int16_t>::max();
+ int peak_dB = lower_peak ?
+ round(20*log10((double)lower_peak / int16_max)) :
+ -90;
+
+ if (peak_dB < INPUT_AUDIO_LEVEL_THRESHOLD) {
+ if (m_silence_counter < INPUT_AUDIO_LEVEL_COUNT_SATURATION) {
+ m_silence_counter++;
+ }
+ }
+ else {
+ if (m_silence_counter > 0) {
+ m_silence_counter--;
+ }
+ }
+}
+
+void InputStat::notifyUnderrun(void)
+{
+ boost::mutex::scoped_lock lock(m_mutex);
+
+ // Statistics
+ num_underruns++;
+
+ // State
+ m_time_last_event = time(NULL);
+ if (m_glitch_counter < INPUT_UNSTABLE_THRESHOLD) {
+ m_glitch_counter++;
+ }
+}
+
+void InputStat::notifyOverrun(void)
+{
+ boost::mutex::scoped_lock lock(m_mutex);
+
+ // Statistics
+ num_overruns++;
+
+ // State
+ m_time_last_event = time(NULL);
+ if (m_glitch_counter < INPUT_UNSTABLE_THRESHOLD) {
+ m_glitch_counter++;
+ }
+}
+
std::string InputStat::encodeValuesJSON()
{
std::ostringstream ss;
@@ -262,6 +391,14 @@ std::string InputStat::encodeValuesJSON()
boost::mutex::scoped_lock lock(m_mutex);
+ int peak_left = 0;
+ int peak_right = 0;
+
+ if (not peaks_left.empty() and not peaks_right.empty()) {
+ peak_left = *max_element(peaks_left.begin(), peaks_left.end());
+ peak_right = *max_element(peaks_right.begin(), peaks_right.end());
+ }
+
/* convert to dB */
int dB_l = peak_left ? round(20*log10((double)peak_left / int16_max)) : -90;
int dB_r = peak_right ? round(20*log10((double)peak_right / int16_max)) : -90;
diff --git a/src/ManagementServer.h b/src/ManagementServer.h
index e75aed3..fcdbc16 100644
--- a/src/ManagementServer.h
+++ b/src/ManagementServer.h
@@ -2,7 +2,7 @@
Copyright (C) 2009 Her Majesty the Queen in Right of Canada (Communications
Research Center Canada)
- Copyright (C) 2017
+ Copyright (C) 2018
Matthias P. Braendli, matthias.braendli@mpb.li
http://www.opendigitalradio.org
@@ -54,12 +54,14 @@
#include <string>
#include <map>
#include <atomic>
+#include <chrono>
+#include <deque>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <ctime>
-#include <math.h>
+#include <cmath>
#define MIN_FILL_BUFFER_UNDEF (-1)
@@ -70,32 +72,26 @@ enum input_state_t
{
/* The input is waiting for data, all buffers are empty */
NoData,
-
/* The input is running, but has seen many underruns or overruns recently */
Unstable,
-
/* The input is running, but the audio level is too low, or has
* been too low recently
*/
Silence,
-
/* The input is running stable */
Streaming
};
-/* The delay after which the glitch counter is reset
- */
+/* The delay after which the glitch counter is reset */
#define INPUT_COUNTER_RESET_TIME 30 // minutes
/* How many glitches we tolerate in Streaming state before
- * we consider the input Unstable
- */
+ * we consider the input Unstable */
#define INPUT_UNSTABLE_THRESHOLD 3
/* For how long the input buffers must be empty before we move an input to the
- * NoData state.
- */
+ * NoData state. */
#define INPUT_NODATA_TIMEOUT 30 // seconds
/* For silence detection, we count the number of occurrences the audio level
@@ -107,8 +103,7 @@ enum input_state_t
* input will be considered silent after a cut.
*
* If the count reaches a certain value, the input changes state
- * to Silence.
- */
+ * to Silence. */
#define INPUT_AUDIO_LEVEL_THRESHOLD -50 // dB
#define INPUT_AUDIO_LEVEL_SILENCE_COUNT 100 // superframes (120ms)
#define INPUT_AUDIO_LEVEL_COUNT_SATURATION 500 // superframes (120ms)
@@ -150,131 +145,25 @@ enum input_state_t
class InputStat
{
public:
- InputStat(std::string name) : m_name(name)
- {
- /* Statistics */
- num_underruns = 0;
- num_overruns = 0;
-
- /* State handling */
- time_t now = time(NULL);
- m_time_last_event = now;
- m_time_last_buffer_nonempty = 0;
- m_buffer_empty = true;
- m_glitch_counter = 0;
- m_silence_counter = 0;
-
- reset();
- }
-
- void registerAtServer(void);
-
+ InputStat(const std::string& name);
+ InputStat(const InputStat& other) = delete;
+ InputStat& operator=(const InputStat& other) = delete;
~InputStat();
+ void registerAtServer(void);
// Gets called each time the statistics are transmitted,
// and resets the counters to zero
- void reset(void)
- {
- min_fill_buffer = MIN_FILL_BUFFER_UNDEF;
- max_fill_buffer = 0;
+ void reset(void);
- peak_left = 0;
- peak_right = 0;
- }
-
- std::string& get_name(void) { return m_name; }
+ std::string get_name(void) const { return m_name; }
/* This function is called for every frame read by
- * the multiplexer
- */
- void notifyBuffer(long bufsize)
- {
- boost::mutex::scoped_lock lock(m_mutex);
-
- // Statistics
- if (bufsize > max_fill_buffer) {
- max_fill_buffer = bufsize;
- }
-
- if (bufsize < min_fill_buffer ||
- min_fill_buffer == MIN_FILL_BUFFER_UNDEF) {
- min_fill_buffer = bufsize;
- }
-
- // State
- m_buffer_empty = (bufsize == 0);
- if (!m_buffer_empty) {
- m_time_last_buffer_nonempty = time(NULL);
- }
- }
-
- void notifyPeakLevels(int peak_left, int peak_right)
- {
- boost::mutex::scoped_lock lock(m_mutex);
-
- // Statistics
- if (peak_left > this->peak_left) {
- this->peak_left = peak_left;
- }
-
- if (peak_right > this->peak_right) {
- this->peak_right = peak_right;
- }
-
- // State
-
- // using the smallest of the two channels
- // allows us to detect if only one channel
- // is silent.
- int minpeak = peak_left < peak_right ? peak_left : peak_right;
-
- const int16_t int16_max = std::numeric_limits<int16_t>::max();
- int peak_dB = minpeak ?
- round(20*log10((double)minpeak / int16_max)) :
- -90;
-
- if (peak_dB < INPUT_AUDIO_LEVEL_THRESHOLD) {
- if (m_silence_counter < INPUT_AUDIO_LEVEL_COUNT_SATURATION) {
- m_silence_counter++;
- }
- }
- else {
- if (m_silence_counter > 0) {
- m_silence_counter--;
- }
- }
- }
-
- void notifyUnderrun(void)
- {
- boost::mutex::scoped_lock lock(m_mutex);
-
- // Statistics
- num_underruns++;
-
- // State
- m_time_last_event = time(NULL);
- if (m_glitch_counter < INPUT_UNSTABLE_THRESHOLD) {
- m_glitch_counter++;
- }
- }
-
- void notifyOverrun(void)
- {
- boost::mutex::scoped_lock lock(m_mutex);
-
- // Statistics
- num_overruns++;
-
- // State
- m_time_last_event = time(NULL);
- if (m_glitch_counter < INPUT_UNSTABLE_THRESHOLD) {
- m_glitch_counter++;
- }
- }
-
+ * the multiplexer */
+ void notifyBuffer(long bufsize);
+ void notifyPeakLevels(int peak_left, int peak_right);
+ void notifyUnderrun(void);
+ void notifyOverrun(void);
std::string encodeValuesJSON(void);
-
input_state_t determineState(void);
private:
@@ -289,9 +178,11 @@ class InputStat
uint32_t num_underruns;
uint32_t num_overruns;
- // peak audio levels (linear 16-bit PCM) for the two channels
- int peak_left;
- int peak_right;
+ // Peak audio levels (linear 16-bit PCM) for the two channels.
+ // Keep a FIFO of values from the last few seconds
+ std::deque<int> peaks_left;
+ std::deque<int> peaks_right;
+ std::chrono::time_point<std::chrono::steady_clock> time_last_peak_notify;
/************* STATE ***************/
/* Variables used for determining the input state */
@@ -303,7 +194,6 @@ class InputStat
// The mutex that has to be held during all notify and readout
mutable boost::mutex m_mutex;
-
};
class ManagementServer