From 45e9c3f37c60be6f11c229a4d5f156eb08be7bbc Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Mon, 13 Nov 2023 12:15:56 +0000 Subject: [PATCH] Indicate audio FIFO underflow/overflow. Don't zero pad audio output, if some audio is available. --- plugins/channelrx/demodwfm/wfmdemod.h | 1 + .../channelrx/demodwfm/wfmdemodbaseband.cpp | 12 ++++++++++ plugins/channelrx/demodwfm/wfmdemodbaseband.h | 5 ++++ plugins/channelrx/demodwfm/wfmdemodgui.cpp | 10 ++++++-- plugins/channelrx/demodwfm/wfmdemodgui.h | 1 + sdrbase/audio/audiofifo.h | 1 + sdrbase/audio/audiooutputdevice.cpp | 23 ++++++++++++++----- 7 files changed, 45 insertions(+), 8 deletions(-) diff --git a/plugins/channelrx/demodwfm/wfmdemod.h b/plugins/channelrx/demodwfm/wfmdemod.h index b25ea127f0..93385b3503 100644 --- a/plugins/channelrx/demodwfm/wfmdemod.h +++ b/plugins/channelrx/demodwfm/wfmdemod.h @@ -95,6 +95,7 @@ class WFMDemod : public BasebandSampleSink, public ChannelAPI { double getMagSq() const { return m_running ? m_basebandSink->getMagSq() : 0.0; } bool getSquelchOpen() const { return m_running && m_basebandSink->getSquelchOpen(); } int getAudioSampleRate() const { return m_running ? m_basebandSink->getAudioSampleRate() : 0; } + QDateTime getAudioFifoErrorDateTime() const { return m_running ? m_basebandSink->getAudioFifoErrorDateTime() : QDateTime(); } void getMagSqLevels(double& avg, double& peak, int& nbSamples) { diff --git a/plugins/channelrx/demodwfm/wfmdemodbaseband.cpp b/plugins/channelrx/demodwfm/wfmdemodbaseband.cpp index 239235ba5b..ca830e7918 100644 --- a/plugins/channelrx/demodwfm/wfmdemodbaseband.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodbaseband.cpp @@ -44,6 +44,8 @@ WFMDemodBaseband::WFMDemodBaseband() m_channelSampleRate = 0; connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + connect(m_sink.getAudioFifo(), &AudioFifo::underflow, this, &WFMDemodBaseband::audioUnderflow); + connect(m_sink.getAudioFifo(), &AudioFifo::overflow, this, &WFMDemodBaseband::audioOverflow); } WFMDemodBaseband::~WFMDemodBaseband() @@ -188,3 +190,13 @@ void WFMDemodBaseband::setBasebandSampleRate(int sampleRate) m_channelizer->setBasebandSampleRate(sampleRate); m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); } + +void WFMDemodBaseband::audioUnderflow() +{ + m_audioFifoErrorDateTime = QDateTime::currentDateTime(); +} + +void WFMDemodBaseband::audioOverflow() +{ + m_audioFifoErrorDateTime = QDateTime::currentDateTime(); +} diff --git a/plugins/channelrx/demodwfm/wfmdemodbaseband.h b/plugins/channelrx/demodwfm/wfmdemodbaseband.h index 16a73a4ad2..14c9de7b12 100644 --- a/plugins/channelrx/demodwfm/wfmdemodbaseband.h +++ b/plugins/channelrx/demodwfm/wfmdemodbaseband.h @@ -20,6 +20,7 @@ #include #include +#include #include "dsp/samplesinkfifo.h" #include "util/message.h" @@ -73,6 +74,7 @@ class WFMDemodBaseband : public QObject void setChannel(ChannelAPI *channel); void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); } void setAudioFifoLabel(const QString& label) { m_sink.setAudioFifoLabel(label); } + QDateTime getAudioFifoErrorDateTime() { return m_audioFifoErrorDateTime; } private: SampleSinkFifo m_sampleFifo; @@ -82,6 +84,7 @@ class WFMDemodBaseband : public QObject MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication WFMDemodSettings m_settings; QRecursiveMutex m_mutex; + QDateTime m_audioFifoErrorDateTime; bool handleMessage(const Message& cmd); void applySettings(const WFMDemodSettings& settings, bool force = false); @@ -89,6 +92,8 @@ class WFMDemodBaseband : public QObject private slots: void handleInputMessages(); void handleData(); //!< Handle data when samples have to be processed + void audioUnderflow(); + void audioOverflow(); }; #endif // INCLUDE_WFMDEMODBASEBAND_H diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.cpp b/plugins/channelrx/demodwfm/wfmdemodgui.cpp index 1cf14ef45a..2d9f5a858a 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodgui.cpp @@ -218,7 +218,8 @@ WFMDemodGUI::WFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_basebandSampleRate(1), m_basicSettingsShown(false), m_squelchOpen(false), - m_audioSampleRate(-1) + m_audioSampleRate(-1), + m_recentAudioFifoError(false) { setAttribute(Qt::WA_DeleteOnClose, true); m_helpURL = "plugins/channelrx/demodwfm/readme.md"; @@ -361,11 +362,15 @@ void WFMDemodGUI::tick() int audioSampleRate = m_wfmDemod->getAudioSampleRate(); bool squelchOpen = m_wfmDemod->getSquelchOpen(); + int secsSinceAudioFifoError = m_wfmDemod->getAudioFifoErrorDateTime().secsTo(QDateTime::currentDateTime()); + bool recentAudioFifoError = (secsSinceAudioFifoError < 1) && squelchOpen; - if ((audioSampleRate != m_audioSampleRate) || (squelchOpen != m_squelchOpen)) + if ((audioSampleRate != m_audioSampleRate) || (squelchOpen != m_squelchOpen) || (recentAudioFifoError != m_recentAudioFifoError)) { if (audioSampleRate < 0) { ui->audioMute->setStyleSheet("QToolButton { background-color : red; }"); + } else if (recentAudioFifoError) { + ui->audioMute->setStyleSheet("QToolButton { background-color : rgb(120,120,0); }"); } else if (squelchOpen) { ui->audioMute->setStyleSheet("QToolButton { background-color : green; }"); } else { @@ -374,6 +379,7 @@ void WFMDemodGUI::tick() m_audioSampleRate = audioSampleRate; m_squelchOpen = squelchOpen; + m_recentAudioFifoError = recentAudioFifoError; } } diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.h b/plugins/channelrx/demodwfm/wfmdemodgui.h index eef5696b72..2f01ec24b0 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.h +++ b/plugins/channelrx/demodwfm/wfmdemodgui.h @@ -58,6 +58,7 @@ public slots: bool m_audioMute; bool m_squelchOpen; int m_audioSampleRate; + bool m_recentAudioFifoError; WFMDemod* m_wfmDemod; MessageQueue m_inputMessageQueue; diff --git a/sdrbase/audio/audiofifo.h b/sdrbase/audio/audiofifo.h index cb9c1ac9a1..9cd478993d 100644 --- a/sdrbase/audio/audiofifo.h +++ b/sdrbase/audio/audiofifo.h @@ -68,6 +68,7 @@ class SDRBASE_API AudioFifo : public QObject { signals: void dataReady(); void overflow(int nsamples); + void underflow(); }; #endif // INCLUDE_AUDIOFIFO_H diff --git a/sdrbase/audio/audiooutputdevice.cpp b/sdrbase/audio/audiooutputdevice.cpp index d6cd997d1a..bb36ab198a 100644 --- a/sdrbase/audio/audiooutputdevice.cpp +++ b/sdrbase/audio/audiooutputdevice.cpp @@ -369,6 +369,13 @@ qint64 AudioOutputDevice::readData(char* data, qint64 maxLen) } } + // See how much data we have available + // If we have less than the requested amount, we only output what we have + // If we have no data, then we output some zeros to avoid underflow + // (bytesAvailable() returns this amount when none available) + unsigned int samplesAvailable = bytesAvailable() / 4; + samplesPerBuffer = std::min(samplesAvailable, samplesPerBuffer); + memset(&m_mixBuffer[0], 0x00, 2 * samplesPerBuffer * sizeof(m_mixBuffer[0])); // start with silence // sum up a block from all fifos @@ -380,10 +387,11 @@ qint64 AudioOutputDevice::readData(char* data, qint64 maxLen) const qint16* src = (const qint16*) data; std::vector::iterator dst = m_mixBuffer.begin(); -// if (samples != framesPerBuffer) -// { -// qDebug("AudioOutputDevice::readData: read %d samples vs %d requested", samples, framesPerBuffer); -// } + if (samples != samplesPerBuffer) + { + //qDebug("AudioOutputDevice::readData: read %d samples vs %d requested", samples, samplesPerBuffer); + emit (*it)->underflow(); + } for (unsigned int i = 0; i < samples; i++) { @@ -541,8 +549,11 @@ qint64 AudioOutputDevice::bytesAvailable() const // If we return 0 from this twice in a row, audio will stop. // So we always return a value, and if we don't have enough data in the FIFOs // when readData is called, that will output silence - if (available == 0) { - available = 2048; // Is there a better value to use? + if (available == 0) + { + // Use a small value, so padding is minimized, but not too small, we get underflow again straightaway + // Could make this a function of sample rate + available = 512; } return available * 2 * 2; // 2 Channels of 16-bit data }