Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Indicate audio FIFO underflow/overflow in GUI #1887

Merged
merged 2 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions plugins/channelrx/demodwfm/wfmdemod.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
12 changes: 12 additions & 0 deletions plugins/channelrx/demodwfm/wfmdemodbaseband.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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();
}
5 changes: 5 additions & 0 deletions plugins/channelrx/demodwfm/wfmdemodbaseband.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <QObject>
#include <QRecursiveMutex>
#include <QDateTime>

#include "dsp/samplesinkfifo.h"
#include "util/message.h"
Expand Down Expand Up @@ -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;
Expand All @@ -82,13 +84,16 @@ 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);

private slots:
void handleInputMessages();
void handleData(); //!< Handle data when samples have to be processed
void audioUnderflow();
void audioOverflow();
};

#endif // INCLUDE_WFMDEMODBASEBAND_H
10 changes: 8 additions & 2 deletions plugins/channelrx/demodwfm/wfmdemodgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -362,11 +363,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 {
Expand All @@ -375,6 +380,7 @@ void WFMDemodGUI::tick()

m_audioSampleRate = audioSampleRate;
m_squelchOpen = squelchOpen;
m_recentAudioFifoError = recentAudioFifoError;
}
}

Expand Down
1 change: 1 addition & 0 deletions plugins/channelrx/demodwfm/wfmdemodgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public slots:
bool m_audioMute;
bool m_squelchOpen;
int m_audioSampleRate;
bool m_recentAudioFifoError;

WFMDemod* m_wfmDemod;
MessageQueue m_inputMessageQueue;
Expand Down
1 change: 1 addition & 0 deletions sdrbase/audio/audiofifo.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class SDRBASE_API AudioFifo : public QObject {
signals:
void dataReady();
void overflow(int nsamples);
void underflow();
};

#endif // INCLUDE_AUDIOFIFO_H
23 changes: 17 additions & 6 deletions sdrbase/audio/audiooutputdevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -380,10 +387,11 @@ qint64 AudioOutputDevice::readData(char* data, qint64 maxLen)
const qint16* src = (const qint16*) data;
std::vector<qint32>::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++)
{
Expand Down Expand Up @@ -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
}
Expand Down