From b036dbfd7d3357e077ca6736a386db64e30753ac Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Fri, 21 May 2021 20:20:34 +0100 Subject: [PATCH 1/2] Add .wav file support to File Source plugin --- .../channeltx/filesource/filesourcegui.cpp | 2 +- .../channeltx/filesource/filesourcesource.cpp | 71 ++++++++++++++++++- plugins/channeltx/filesource/readme.md | 4 +- 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/plugins/channeltx/filesource/filesourcegui.cpp b/plugins/channeltx/filesource/filesourcegui.cpp index a54277213..154081106 100644 --- a/plugins/channeltx/filesource/filesourcegui.cpp +++ b/plugins/channeltx/filesource/filesourcegui.cpp @@ -427,7 +427,7 @@ void FileSourceGUI::on_showFileDialog_clicked(bool checked) { (void) checked; QString fileName = QFileDialog::getOpenFileName(this, - tr("Open I/Q record file"), ".", tr("SDR I/Q Files (*.sdriq)"), 0, QFileDialog::DontUseNativeDialog); + tr("Open I/Q record file"), ".", tr("SDR I/Q Files (*.sdriq *.wav)"), 0, QFileDialog::DontUseNativeDialog); if (fileName != "") { diff --git a/plugins/channeltx/filesource/filesourcesource.cpp b/plugins/channeltx/filesource/filesourcesource.cpp index df95dab5b..677b29aea 100644 --- a/plugins/channeltx/filesource/filesourcesource.cpp +++ b/plugins/channeltx/filesource/filesourcesource.cpp @@ -27,11 +27,13 @@ #endif #include +#include #include "dsp/dspcommands.h" #include "dsp/devicesamplesink.h" #include "dsp/hbfilterchainconverter.h" #include "dsp/filerecord.h" +#include "dsp/wavfilerecord.h" #include "util/db.h" FileSourceSource::FileSourceSource() : @@ -173,7 +175,74 @@ void FileSourceSource::openFileStream(const QString& fileName) quint64 fileSize = m_ifstream.tellg(); m_samplesCount = 0; - if (fileSize > sizeof(FileRecord::Header)) + if (m_settings.m_fileName.endsWith(".wav")) + { + WavFileRecord::Header header; + m_ifstream.seekg(0, std::ios_base::beg); + bool headerOK = WavFileRecord::readHeader(m_ifstream, header); + m_fileSampleRate = header.m_sampleRate; + if (header.m_auxiHeader.m_size > 0) + { + // Some WAV files written by SDR tools have auxi header + m_centerFrequency = header.m_auxi.m_centerFreq; + m_startingTimeStamp = QDateTime(QDate( + header.m_auxi.m_startTime.m_year, + header.m_auxi.m_startTime.m_month, + header.m_auxi.m_startTime.m_day + ), QTime( + header.m_auxi.m_startTime.m_hour, + header.m_auxi.m_startTime.m_minute, + header.m_auxi.m_startTime.m_second, + header.m_auxi.m_startTime.m_milliseconds + )).toMSecsSinceEpoch() / 1000; + } + else + { + // Attempt to extract time and frequency from filename + QRegExp dateTimeRE("([12][0-9][0-9][0-9]).?([01][0-9]).?([0-3][0-9]).?([0-2][0-9]).?([0-5][0-9]).?([0-5][0-9])"); + if (dateTimeRE.indexIn(m_settings.m_fileName) != -1) + { + m_startingTimeStamp = QDateTime(QDate( + dateTimeRE.capturedTexts()[1].toInt(), + dateTimeRE.capturedTexts()[2].toInt(), + dateTimeRE.capturedTexts()[3].toInt() + ), QTime( + dateTimeRE.capturedTexts()[4].toInt(), + dateTimeRE.capturedTexts()[5].toInt(), + dateTimeRE.capturedTexts()[6].toInt() + )).toMSecsSinceEpoch() / 1000; + } + // Attempt to extract centre frequency from filename + QRegExp freqkRE("(([0-9]+)kHz)"); + QRegExp freqRE("(([0-9]+)Hz)"); + if (freqkRE.indexIn(m_settings.m_fileName)) + { + m_centerFrequency = freqkRE.capturedTexts()[2].toLongLong() * 1000LL; + } + else if (freqRE.indexIn(m_settings.m_fileName)) + { + m_centerFrequency = freqRE.capturedTexts()[2].toLongLong(); + } + } + m_sampleSize = header.m_bitsPerSample; + + if (headerOK && (m_fileSampleRate > 0) && (m_sampleSize > 0)) + { + m_recordLengthMuSec = ((fileSize - m_ifstream.tellg()) * 1000000UL) / ((m_sampleSize == 24 ? 8 : 4) * m_fileSampleRate); + } + else + { + qCritical("FileSourceSource::openFileStream: invalid .wav file"); + m_recordLengthMuSec = 0; + } + + if (getMessageQueueToGUI()) + { + FileSourceReport::MsgReportHeaderCRC *report = FileSourceReport::MsgReportHeaderCRC::create(headerOK); + getMessageQueueToGUI()->push(report); + } + } + else if (fileSize > sizeof(FileRecord::Header)) { FileRecord::Header header; m_ifstream.seekg(0,std::ios_base::beg); diff --git a/plugins/channeltx/filesource/readme.md b/plugins/channeltx/filesource/readme.md index e13f4ec9a..be7ee073d 100644 --- a/plugins/channeltx/filesource/readme.md +++ b/plugins/channeltx/filesource/readme.md @@ -2,7 +2,9 @@

Introduction

-This plugin reads a file of I/Q samples that have been previously saved with the file record button of other sampling source devices. The file starts with a 32 byte header of all unsigned integer of various sizes containing meta data: +This plugin reads a file of I/Q samples that have been previously saved with the file record button of other sampling source devices. File formats supported include SDRangel's `.sdriq` and signed 16-bit PCM `.wav` files. + +`.sqriq` files start with a 32 byte header of all unsigned integer of various sizes containing meta data: From 631b9c256fb5ead1138270805882336737102e68 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Fri, 21 May 2021 21:05:23 +0100 Subject: [PATCH 2/2] Refactor common code into WavFileRecord class --- .../channeltx/filesource/filesourcesource.cpp | 43 +++------------- plugins/samplesource/fileinput/fileinput.cpp | 41 +++------------- sdrbase/dsp/wavfilerecord.cpp | 49 +++++++++++++++++++ sdrbase/dsp/wavfilerecord.h | 8 ++- 4 files changed, 69 insertions(+), 72 deletions(-) diff --git a/plugins/channeltx/filesource/filesourcesource.cpp b/plugins/channeltx/filesource/filesourcesource.cpp index 677b29aea..1b6bf7a7c 100644 --- a/plugins/channeltx/filesource/filesourcesource.cpp +++ b/plugins/channeltx/filesource/filesourcesource.cpp @@ -27,7 +27,6 @@ #endif #include -#include #include "dsp/dspcommands.h" #include "dsp/devicesamplesink.h" @@ -175,7 +174,7 @@ void FileSourceSource::openFileStream(const QString& fileName) quint64 fileSize = m_ifstream.tellg(); m_samplesCount = 0; - if (m_settings.m_fileName.endsWith(".wav")) + if (m_fileName.endsWith(".wav")) { WavFileRecord::Header header; m_ifstream.seekg(0, std::ios_base::beg); @@ -185,44 +184,16 @@ void FileSourceSource::openFileStream(const QString& fileName) { // Some WAV files written by SDR tools have auxi header m_centerFrequency = header.m_auxi.m_centerFreq; - m_startingTimeStamp = QDateTime(QDate( - header.m_auxi.m_startTime.m_year, - header.m_auxi.m_startTime.m_month, - header.m_auxi.m_startTime.m_day - ), QTime( - header.m_auxi.m_startTime.m_hour, - header.m_auxi.m_startTime.m_minute, - header.m_auxi.m_startTime.m_second, - header.m_auxi.m_startTime.m_milliseconds - )).toMSecsSinceEpoch() / 1000; + m_startingTimeStamp = header.getStartTime().toMSecsSinceEpoch() / 1000; } else { - // Attempt to extract time and frequency from filename - QRegExp dateTimeRE("([12][0-9][0-9][0-9]).?([01][0-9]).?([0-3][0-9]).?([0-2][0-9]).?([0-5][0-9]).?([0-5][0-9])"); - if (dateTimeRE.indexIn(m_settings.m_fileName) != -1) - { - m_startingTimeStamp = QDateTime(QDate( - dateTimeRE.capturedTexts()[1].toInt(), - dateTimeRE.capturedTexts()[2].toInt(), - dateTimeRE.capturedTexts()[3].toInt() - ), QTime( - dateTimeRE.capturedTexts()[4].toInt(), - dateTimeRE.capturedTexts()[5].toInt(), - dateTimeRE.capturedTexts()[6].toInt() - )).toMSecsSinceEpoch() / 1000; - } - // Attempt to extract centre frequency from filename - QRegExp freqkRE("(([0-9]+)kHz)"); - QRegExp freqRE("(([0-9]+)Hz)"); - if (freqkRE.indexIn(m_settings.m_fileName)) - { - m_centerFrequency = freqkRE.capturedTexts()[2].toLongLong() * 1000LL; - } - else if (freqRE.indexIn(m_settings.m_fileName)) - { - m_centerFrequency = freqRE.capturedTexts()[2].toLongLong(); + // Attempt to extract start time and frequency from filename + QDateTime startTime; + if (WavFileRecord::getStartTime(m_fileName, startTime)) { + m_startingTimeStamp = startTime.toMSecsSinceEpoch() / 1000; } + WavFileRecord::getCenterFrequency(m_fileName, m_centerFrequency); } m_sampleSize = header.m_bitsPerSample; diff --git a/plugins/samplesource/fileinput/fileinput.cpp b/plugins/samplesource/fileinput/fileinput.cpp index 84233661b..f0dfa1f44 100644 --- a/plugins/samplesource/fileinput/fileinput.cpp +++ b/plugins/samplesource/fileinput/fileinput.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include "SWGDeviceSettings.h" #include "SWGFileInputSettings.h" @@ -111,44 +110,16 @@ void FileInput::openFileStream() { // Some WAV files written by SDR tools have auxi header m_centerFrequency = header.m_auxi.m_centerFreq; - m_startingTimeStamp = QDateTime(QDate( - header.m_auxi.m_startTime.m_year, - header.m_auxi.m_startTime.m_month, - header.m_auxi.m_startTime.m_day - ), QTime( - header.m_auxi.m_startTime.m_hour, - header.m_auxi.m_startTime.m_minute, - header.m_auxi.m_startTime.m_second, - header.m_auxi.m_startTime.m_milliseconds - )).toMSecsSinceEpoch() / 1000; + m_startingTimeStamp = header.getStartTime().toMSecsSinceEpoch() / 1000; } else { - // Attempt to extract time and frequency from filename - QRegExp dateTimeRE("([12][0-9][0-9][0-9]).?([01][0-9]).?([0-3][0-9]).?([0-2][0-9]).?([0-5][0-9]).?([0-5][0-9])"); - if (dateTimeRE.indexIn(m_settings.m_fileName) != -1) - { - m_startingTimeStamp = QDateTime(QDate( - dateTimeRE.capturedTexts()[1].toInt(), - dateTimeRE.capturedTexts()[2].toInt(), - dateTimeRE.capturedTexts()[3].toInt() - ), QTime( - dateTimeRE.capturedTexts()[4].toInt(), - dateTimeRE.capturedTexts()[5].toInt(), - dateTimeRE.capturedTexts()[6].toInt() - )).toMSecsSinceEpoch() / 1000; - } - // Attempt to extract centre frequency from filename - QRegExp freqkRE("(([0-9]+)kHz)"); - QRegExp freqRE("(([0-9]+)Hz)"); - if (freqkRE.indexIn(m_settings.m_fileName)) - { - m_centerFrequency = freqkRE.capturedTexts()[2].toLongLong() * 1000LL; - } - else if (freqRE.indexIn(m_settings.m_fileName)) - { - m_centerFrequency = freqRE.capturedTexts()[2].toLongLong(); + // Attempt to extract start time and frequency from filename + QDateTime startTime; + if (WavFileRecord::getStartTime(m_settings.m_fileName, startTime)) { + m_startingTimeStamp = startTime.toMSecsSinceEpoch() / 1000; } + WavFileRecord::getCenterFrequency(m_settings.m_fileName, m_centerFrequency); } m_sampleSize = header.m_bitsPerSample; diff --git a/sdrbase/dsp/wavfilerecord.cpp b/sdrbase/dsp/wavfilerecord.cpp index 89bb7e114..62f735601 100644 --- a/sdrbase/dsp/wavfilerecord.cpp +++ b/sdrbase/dsp/wavfilerecord.cpp @@ -21,6 +21,7 @@ #include #include +#include #include "dsp/dspcommands.h" #include "util/simpleserializer.h" @@ -340,3 +341,51 @@ void WavFileRecord::writeHeader(std::ofstream& sampleFile, Header& header) { sampleFile.write((const char *) &header, sizeof(Header)); } + +bool WavFileRecord::getCenterFrequency(QString fileName, quint64& centerFrequency) +{ + // Attempt to extract center frequency from filename + QRegExp freqkRE("(([0-9]+)kHz)"); + QRegExp freqRE("(([0-9]+)Hz)"); + if (freqkRE.indexIn(fileName)) + { + centerFrequency = freqkRE.capturedTexts()[2].toLongLong() * 1000LL; + return true; + } + else if (freqRE.indexIn(fileName)) + { + centerFrequency = freqRE.capturedTexts()[2].toLongLong(); + return true; + } + return false; +} + +bool WavFileRecord::getStartTime(QString fileName, QDateTime& startTime) +{ + // Attempt to extract start time from filename + QRegExp dateTimeRE("([12][0-9][0-9][0-9]).?([01][0-9]).?([0-3][0-9]).?([0-2][0-9]).?([0-5][0-9]).?([0-5][0-9])"); + if (dateTimeRE.indexIn(fileName) != -1) + { + startTime = QDateTime(QDate( + dateTimeRE.capturedTexts()[1].toInt(), + dateTimeRE.capturedTexts()[2].toInt(), + dateTimeRE.capturedTexts()[3].toInt()), + QTime( + dateTimeRE.capturedTexts()[4].toInt(), + dateTimeRE.capturedTexts()[5].toInt(), + dateTimeRE.capturedTexts()[6].toInt())); + return true; + } + return false; +} + +QDateTime WavFileRecord::Header::getStartTime() const +{ + return QDateTime(QDate(m_auxi.m_startTime.m_year, + m_auxi.m_startTime.m_month, + m_auxi.m_startTime.m_day), + QTime(m_auxi.m_startTime.m_hour, + m_auxi.m_startTime.m_minute, + m_auxi.m_startTime.m_second, + m_auxi.m_startTime.m_milliseconds)); +} diff --git a/sdrbase/dsp/wavfilerecord.h b/sdrbase/dsp/wavfilerecord.h index ccb5615ab..3392be696 100644 --- a/sdrbase/dsp/wavfilerecord.h +++ b/sdrbase/dsp/wavfilerecord.h @@ -66,7 +66,7 @@ public: quint32 m_unused5; char m_nextFilename[96]; }; - struct Header + struct SDRBASE_API Header { Chunk m_riffHeader; char m_type[4]; // "WAVE" @@ -80,6 +80,8 @@ public: Chunk m_auxiHeader; Auxi m_auxi; Chunk m_dataHeader; + + QDateTime getStartTime() const; }; #pragma pack(pop) @@ -107,6 +109,10 @@ public: static bool readHeader(std::ifstream& samplefile, Header& header); static void writeHeader(std::ofstream& samplefile, Header& header); + // These functions guess from the filename, not contents + static bool getCenterFrequency(QString fileName, quint64& centerFrequency); + static bool getStartTime(QString fileName, QDateTime& startTime); + private: QString m_fileBase; quint32 m_sampleRate;