Skip to content

Commit

Permalink
Implement new squelch-triggered audio recorder
Browse files Browse the repository at this point in the history
Move wav_sink into receiver_base_cf
Move file name generation to receiver_base_cf
Switch to pwr_squelch
Pull in wavfile_sink and fix gqrx-sdr#1075
Implement new squelch-triggered audio recorder while keeping in mind that
gqrx-sdr#946 would be next.
Add tag processing to wavfile_sink_gqrx.
Implement event-driven GUI updates.
Add GUI options
Make it possible to switch betweensimple_squelch and pwr_squelch implementations
to improve performace on weak systems
Update build dependencies (add libsndfile)
  • Loading branch information
vladisslav2011 committed Aug 20, 2022
1 parent 5fec64d commit 8e420a4
Show file tree
Hide file tree
Showing 25 changed files with 1,485 additions and 138 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
run: |
sudo add-apt-repository -y ppa:gnuradio/gnuradio-releases
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends cmake qt5-default qt5-gtk-platformtheme qttranslations5-l10n libqt5svg5-dev libboost-dev libpulse-dev portaudio19-dev liblog4cpp5-dev gnuradio-dev libairspy-dev libairspyhf-dev libfreesrp-dev libhackrf-dev librtlsdr-dev libsoapysdr-dev soapysdr-module-remote libuhd-dev liborc-0.4-dev libhidapi-dev
sudo apt-get install -y --no-install-recommends cmake qt5-default qt5-gtk-platformtheme qttranslations5-l10n libqt5svg5-dev libboost-dev libpulse-dev portaudio19-dev liblog4cpp5-dev gnuradio-dev libairspy-dev libairspyhf-dev libfreesrp-dev libhackrf-dev librtlsdr-dev libsoapysdr-dev soapysdr-module-remote libuhd-dev liborc-0.4-dev libhidapi-dev libsndfile1-dev
cd /tmp
git clone https://github.com/Nuand/bladeRF.git
Expand Down Expand Up @@ -86,7 +86,7 @@ jobs:
- name: Install dependencies
run: |
brew update
brew install airspy airspyhf boost dylibbundler gnuradio hackrf libbladerf librtlsdr libserialport portaudio pybind11 uhd qt@6
brew install airspy airspyhf boost dylibbundler gnuradio hackrf libbladerf librtlsdr libserialport portaudio pybind11 uhd libsndfile qt@6
brew tap pothosware/homebrew-pothos
brew install soapysdr soapyremote
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Install dependencies
run: sudo apt-get update -qq && sudo apt-get install -y cmake libboost-dev libpulse-dev portaudio19-dev libsndfile-dev liblog4cpp5-dev gnuradio-dev gr-osmosdr liborc-0.4-dev appstream desktop-file-utils
run: sudo apt-get update -qq && sudo apt-get install -y cmake libboost-dev libpulse-dev portaudio19-dev libsndfile-dev liblog4cpp5-dev gnuradio-dev gr-osmosdr liborc-0.4-dev appstream desktop-file-utils libsndfile1-dev
- name: Install Qt5
if: matrix.os != 'ubuntu-22.04'
run: sudo apt-get install -y qt5-default libqt5svg5-dev
Expand Down Expand Up @@ -43,7 +43,7 @@ jobs:
- name: Install dependencies
run: |
brew update
brew install airspy boost gnuradio hackrf libbladerf librtlsdr pybind11 uhd qt@6
brew install airspy boost gnuradio hackrf libbladerf librtlsdr pybind11 uhd libsndfile qt@6
cd /tmp
git clone https://gitea.osmocom.org/sdr/gr-osmosdr.git
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ if(NOT Qt6_FOUND)
endif()
include(FindPkgConfig)
find_package(Gnuradio-osmosdr REQUIRED)
find_package(SNDFILE REQUIRED)

set(GR_REQUIRED_COMPONENTS RUNTIME ANALOG AUDIO BLOCKS DIGITAL FILTER FFT PMT)
find_package(Gnuradio REQUIRED COMPONENTS analog audio blocks digital filter fft network)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ To compile gqrx from source you need the following dependencies:
- Network
- Widgets
- Svg (runtime-only)
- libsndfile
- cmake version >= 3.2.0

Gqrx can be compiled from within Qt Creator or in a terminal:
Expand Down
34 changes: 34 additions & 0 deletions cmake/Modules/FindSNDFILE.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
find_package(PkgConfig)
PKG_CHECK_MODULES(PC_SNDFILE "sndfile")

FIND_PATH(SNDFILE_INCLUDE_DIRS
NAMES sndfile.h
HINTS ${PC_SNDFILE_INCLUDE_DIR}
${CMAKE_INSTALL_PREFIX}/include
PATHS
/usr/local/include
/usr/include
)

FIND_LIBRARY(SNDFILE_LIBRARIES
NAMES sndfile ${SNDFILE_LIBRARY_NAME}
HINTS ${PC_SNDFILE_LIBDIR}
${CMAKE_INSTALL_PREFIX}/lib
${CMAKE_INSTALL_PREFIX}/lib64
PATHS
${SNDFILE_INCLUDE_DIRS}/../lib
/usr/local/lib
/usr/lib
)

INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SNDFILE DEFAULT_MSG SNDFILE_LIBRARIES SNDFILE_INCLUDE_DIRS)
MARK_AS_ADVANCED(SNDFILE_LIBRARIES SNDFILE_INCLUDE_DIRS)

if (SNDFILE_FOUND AND NOT TARGET sndfile::sndfile)
add_library(sndfile::sndfile INTERFACE IMPORTED)
set_target_properties(sndfile::sndfile PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${SNDFILE_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES "${SNDFILE_LIBRARIES}"
)
endif()
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ target_link_libraries(${PROJECT_NAME}
${PULSEAUDIO_LIBRARY}
${PULSE-SIMPLE}
${PORTAUDIO_LIBRARIES}
${SNDFILE_LIBRARIES}
)

if(NOT Gnuradio_VERSION VERSION_LESS "3.10")
Expand Down
96 changes: 77 additions & 19 deletions src/applications/gqrx/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent)
/* create receiver object */
rx = new receiver("", "", 1);
rx->set_rf_freq(144500000.0f);
rx->set_audio_rec_event_handler(std::bind(audio_rec_event, this,
std::placeholders::_1,
std::placeholders::_2));

// remote controller
remote = new RemoteControl();
Expand Down Expand Up @@ -238,12 +241,16 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent)
connect(uiDockAudio, SIGNAL(audioMuteChanged(bool)), this, SLOT(setAudioMute(bool)));
connect(uiDockAudio, SIGNAL(audioStreamingStarted(QString,int,bool)), this, SLOT(startAudioStream(QString,int,bool)));
connect(uiDockAudio, SIGNAL(audioStreamingStopped()), this, SLOT(stopAudioStreaming()));
connect(uiDockAudio, SIGNAL(audioRecStarted(QString)), this, SLOT(startAudioRec(QString)));
connect(uiDockAudio, SIGNAL(audioRecStarted(QString)), remote, SLOT(startAudioRecorder(QString)));
connect(uiDockAudio, SIGNAL(audioRecStopped()), this, SLOT(stopAudioRec()));
connect(uiDockAudio, SIGNAL(audioRecStopped()), remote, SLOT(stopAudioRecorder()));
connect(uiDockAudio, SIGNAL(audioRecStart()), this, SLOT(startAudioRec()));
connect(uiDockAudio, SIGNAL(audioRecStart()), remote, SLOT(startAudioRecorder()));
connect(uiDockAudio, SIGNAL(audioRecStop()), this, SLOT(stopAudioRec()));
connect(uiDockAudio, SIGNAL(audioRecStop()), remote, SLOT(stopAudioRecorder()));
connect(uiDockAudio, SIGNAL(audioPlayStarted(QString)), this, SLOT(startAudioPlayback(QString)));
connect(uiDockAudio, SIGNAL(audioPlayStopped()), this, SLOT(stopAudioPlayback()));
connect(uiDockAudio, SIGNAL(recDirChanged(QString)), this, SLOT(recDirChanged(QString)));
connect(uiDockAudio, SIGNAL(recSquelchTriggeredChanged(bool)), this, SLOT(recSquelchTriggeredChanged(bool)));
connect(uiDockAudio, SIGNAL(recMinTimeChanged(int)), this, SLOT(recMinTimeChanged(int)));
connect(uiDockAudio, SIGNAL(recMaxGapChanged(int)), this, SLOT(recMaxGapChanged(int)));
connect(uiDockAudio, SIGNAL(fftRateChanged(int)), this, SLOT(setAudioFftRate(int)));
connect(uiDockFft, SIGNAL(fftSizeChanged(int)), this, SLOT(setIqFftSize(int)));
connect(uiDockFft, SIGNAL(fftRateChanged(int)), this, SLOT(setIqFftRate(int)));
Expand Down Expand Up @@ -301,8 +308,8 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent)
connect(remote, SIGNAL(newSquelchLevel(double)), this, SLOT(setSqlLevel(double)));
connect(remote, SIGNAL(newSquelchLevel(double)), uiDockRxOpt, SLOT(setSquelchLevel(double)));
connect(uiDockRxOpt, SIGNAL(sqlLevelChanged(double)), remote, SLOT(setSquelchLevel(double)));
connect(remote, SIGNAL(startAudioRecorderEvent()), uiDockAudio, SLOT(startAudioRecorder()));
connect(remote, SIGNAL(stopAudioRecorderEvent()), uiDockAudio, SLOT(stopAudioRecorder()));
connect(remote, SIGNAL(startAudioRecorderEvent()), this, SLOT(startAudioRec()));
connect(remote, SIGNAL(stopAudioRecorderEvent()), this, SLOT(stopAudioRec()));
connect(ui->plotter, SIGNAL(newFilterFreq(int, int)), remote, SLOT(setPassband(int, int)));
connect(remote, SIGNAL(newPassband(int)), this, SLOT(setPassband(int)));
connect(remote, SIGNAL(gainChanged(QString, double)), uiDockInputCtl, SLOT(setGain(QString,double)));
Expand All @@ -311,6 +318,7 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent)

rds_timer = new QTimer(this);
connect(rds_timer, SIGNAL(timeout()), this, SLOT(rdsTimeout()));
connect(this, SIGNAL(sigAudioRecEvent(QString, bool)), this, SLOT(audioRecEvent(QString, bool)), Qt::QueuedConnection);

// enable frequency tooltips on FFT plot
ui->plotter->setTooltipsEnabled(true);
Expand Down Expand Up @@ -1478,11 +1486,47 @@ void MainWindow::rdsTimeout()
}
}

/**
* @brief Set audio recording directory.
* @param dir The directory, where audio files should be created.
*/
void MainWindow::recDirChanged(const QString dir)
{
rx->set_audio_rec_dir(dir.toStdString());
}

/**
* @brief Set audio recording squelch triggered mode.
* @param enabled New state.
*/
void MainWindow::recSquelchTriggeredChanged(const bool enabled)
{
rx->set_audio_rec_sql_triggered(enabled);
}

/**
* @brief Set audio recording squelch triggered minimum time.
* @param time_ms New time in milliseconds.
*/
void MainWindow::recMinTimeChanged(const int time_ms)
{
rx->set_audio_rec_min_time(time_ms);
}

/**
* @brief Set audio recording squelch triggered maximum gap time.
* @param time_ms New time in milliseconds.
*/
void MainWindow::recMaxGapChanged(const int time_ms)
{
rx->set_audio_rec_max_gap(time_ms);
}

/**
* @brief Start audio recorder.
* @param filename The file name into which audio should be recorded.
*/
void MainWindow::startAudioRec(const QString& filename)
void MainWindow::startAudioRec()
{
if (!d_have_audio)
{
Expand All @@ -1494,17 +1538,11 @@ void MainWindow::startAudioRec(const QString& filename)
msg_box.exec();
uiDockAudio->setAudioRecButtonState(false);
}
else if (rx->start_audio_recording(filename.toStdString()))
else if (rx->start_audio_recording())
{
ui->statusBar->showMessage(tr("Error starting audio recorder"));

/* reset state of record button */
uiDockAudio->setAudioRecButtonState(false);
}
else
{
ui->statusBar->showMessage(tr("Recording audio to %1").arg(filename));
}
}

/** Stop audio recorder. */
Expand All @@ -1514,15 +1552,23 @@ void MainWindow::stopAudioRec()
{
/* okay, this one would be weird if it really happened */
ui->statusBar->showMessage(tr("Error stopping audio recorder"));

uiDockAudio->setAudioRecButtonState(true);
}
else
}

/** Audio recording is started or stopped. */
void MainWindow::audioRecEvent(const QString filename, bool is_running)
{
if(is_running)
{
ui->statusBar->showMessage(tr("Recording audio to %1").arg(filename));
uiDockAudio->audioRecStarted(QString(filename));
}else{
/* reset state of record button */
uiDockAudio->audioRecStopped();
ui->statusBar->showMessage(tr("Audio recorder stopped"), 5000);
}
}
}

}

/** Start playback of audio file. */
void MainWindow::startAudioPlayback(const QString& filename)
Expand Down Expand Up @@ -2464,3 +2510,15 @@ void MainWindow::frequencyFocusShortcut()
{
ui->freqCtrl->setFrequencyFocus();
}

/** Called from GNU Radio thread */
void MainWindow::audioRecEventEmitter(std::string filename, bool is_running)
{
emit sigAudioRecEvent(QString(filename.data()), is_running);
}

/** Called from GNU Radio thread */
void MainWindow::audio_rec_event(MainWindow *self, std::string filename, bool is_running)
{
self->audioRecEventEmitter(filename, is_running);
}
12 changes: 11 additions & 1 deletion src/applications/gqrx/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ class MainWindow : public QMainWindow
{
Q_OBJECT

signals:
void sigAudioRecEvent(const QString filename, bool is_running);

public:
explicit MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent = nullptr);
~MainWindow() override;
Expand Down Expand Up @@ -128,6 +131,8 @@ public slots:
const QString &window_title);
/* key shortcut */
void frequencyFocusShortcut();
void audioRecEventEmitter(std::string filename, bool is_running);
static void audio_rec_event(MainWindow *self, std::string filename, bool is_running);

private slots:
/* RecentConfig */
Expand Down Expand Up @@ -171,8 +176,13 @@ private slots:
void setPassband(int bandwidth);

/* audio recording and playback */
void startAudioRec(const QString& filename);
void recDirChanged(const QString dir);
void recSquelchTriggeredChanged(const bool enabled);
void recMinTimeChanged(const int time_ms);
void recMaxGapChanged(const int time_ms);
void startAudioRec();
void stopAudioRec();
void audioRecEvent(const QString filename, bool is_running);
void startAudioPlayback(const QString& filename);
void stopAudioPlayback();

Expand Down
Loading

0 comments on commit 8e420a4

Please sign in to comment.