From d574e74908b23eb73a53dbbed9655493aa1deb44 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Mon, 2 Jan 2023 15:42:34 +0000 Subject: [PATCH] Support FileInput plugin on Android, by using QFile rather istream --- plugins/channelrx/filesink/filesinkgui.ui | 1004 ++++++++--------- plugins/samplesource/fileinput/fileinput.cpp | 58 +- plugins/samplesource/fileinput/fileinput.h | 5 + .../samplesource/fileinput/fileinputgui.cpp | 5 +- .../fileinput/fileinputworker.cpp | 29 +- .../samplesource/fileinput/fileinputworker.h | 11 +- sdrbase/dsp/filerecord.cpp | 40 +- sdrbase/dsp/filerecord.h | 8 + sdrbase/dsp/wavfilerecord.cpp | 145 ++- sdrbase/dsp/wavfilerecord.h | 9 + 10 files changed, 771 insertions(+), 543 deletions(-) diff --git a/plugins/channelrx/filesink/filesinkgui.ui b/plugins/channelrx/filesink/filesinkgui.ui index f8b7227c9..e9fc5b051 100644 --- a/plugins/channelrx/filesink/filesinkgui.ui +++ b/plugins/channelrx/filesink/filesinkgui.ui @@ -6,7 +6,7 @@ 0 0 - 552 + 441 458 @@ -18,7 +18,7 @@ - 552 + 400 102 @@ -31,535 +31,531 @@ File Sink - - - - 0 - 0 - 550 - 100 - - - - - 550 - 0 - - - - Settings - - - - 2 - - - 2 - - - 2 - - - 2 - - - + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Settings + + + + 2 + + + 2 + 2 + + 2 + - - - - 16 - 0 - + + + 2 - - Df - - + + + + + 16 + 0 + + + + Df + + + + + + + true + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Demod shift frequency from center in Hz + + + + + + + Hz + + + + + + + Qt::Vertical + + + + + + + Dec + + + + + + + + 55 + 16777215 + + + + Decimation factor + + + + 1 + + + + + 2 + + + + + 4 + + + + + 8 + + + + + 16 + + + + + 32 + + + + + 64 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + 0 + + + + Sink rate (kS/s) + + + 0000k + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 32 + 0 + + + + Number of captures (files) in recording session updated at end of file + + + #000 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 52 + 0 + + + + Total recording time (HH:MM:SS) + + + 00:00:00 + + + + + + + + 52 + 0 + + + + Total recording size (k: kB, M: MB, G: GB) + + + 999.99M + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + - - - true + + + 10 + + + + Use fixed frequency shift positions for little performance improvement + + + Pos + + + + + + + Center frequency position + + + 2 + + + 1 + + + Qt::Horizontal + + + + + + + + 24 + 0 + + + + Filter chain hash code + + + 000 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + + + + Toggle spectrum squelch recording + + + SQ + + + true + + + + + + + + 24 + 24 + + + + Spectrum squelch level (dB) + + + -120 + + + 0 + + + 1 + + + -50 + + + + + + + Spectrum squelch level (dB) + + + -100 + + + + + + + + 24 + 24 + + + + Pre-recording time (s) + + + 10 + + + 1 + + + + + + + Squelched recoding pre-recording time (s) + + + 10 + + + + + + + + 24 + 24 + + + + Squelched recording post-recording time (s) + + + 10 + + + 1 + + + + + + + Squelched recording post-recording time (s) + + + 10 + + + + + + + Squelched recording enable + + + REC + + + + + + + + + + + + 24 + 16777215 + + + + Start/stop recording + + + + + + + :/record_off.png:/record_off.png + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + Open file + + + + + + + :/preset-load.png:/preset-load.png + + + + + + + true + + + Current recording file + + + ... + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + + + + 0 + 0 + + + + + 10 + + + - + 0 0 - 32 - 16 + 300 + 300 Liberation Mono - 12 + 8 - - PointingHandCursor - - - Qt::StrongFocus - - - Demod shift frequency from center in Hz - - - - Hz - - - - - - - Qt::Vertical - - - - - - - Dec - - - - - - - - 55 - 16777215 - - - - Decimation factor - - - - 1 - - - - - 2 - - - - - 4 - - - - - 8 - - - - - 16 - - - - - 32 - - - - - 64 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 50 - 0 - - - - Sink rate (kS/s) - - - 0000k - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 32 - 0 - - - - Number of captures (files) in recording session updated at end of file - - - #000 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 52 - 0 - - - - Total recording time (HH:MM:SS) - - - 00:00:00 - - - - - - - - 52 - 0 - - - - Total recording size (k: kB, M: MB, G: GB) - - - 999.99M - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + - - - - - 10 - - - - - Use fixed frequency shift positions for little performance improvement - - - Pos - - - - - - - Center frequency position - - - 2 - - - 1 - - - Qt::Horizontal - - - - - - - - 24 - 0 - - - - Filter chain hash code - - - 000 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Qt::Vertical - - - - - - - Toggle spectrum squelch recording - - - SQ - - - true - - - - - - - - 24 - 24 - - - - Spectrum squelch level (dB) - - - -120 - - - 0 - - - 1 - - - -50 - - - - - - - Spectrum squelch level (dB) - - - -100 - - - - - - - - 24 - 24 - - - - Pre-recording time (s) - - - 10 - - - 1 - - - - - - - Squelched recoding pre-recording time (s) - - - 10 - - - - - - - - 24 - 24 - - - - Squelched recording post-recording time (s) - - - 10 - - - 1 - - - - - - - Squelched recording post-recording time (s) - - - 10 - - - - - - - Squelched recording enable - - - REC - - - - - - - - - - - - 24 - 16777215 - - - - Start/stop recording - - - - - - - :/record_off.png:/record_off.png - - - - - - - - 24 - 24 - - - - - 24 - 24 - - - - Open file - - - - - - - :/preset-load.png:/preset-load.png - - - - - - - true - - - Current recording file - - - ... - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - - - - 0 - 100 - 541 - 351 - - - - - 0 - 0 - - - - - 10 - - - - - - 0 - 0 - - - - - 300 - 300 - - - - - Liberation Mono - 8 - - - - - - - - - + + + diff --git a/plugins/samplesource/fileinput/fileinput.cpp b/plugins/samplesource/fileinput/fileinput.cpp index 503bd9100..899eb6de4 100644 --- a/plugins/samplesource/fileinput/fileinput.cpp +++ b/plugins/samplesource/fileinput/fileinput.cpp @@ -100,7 +100,17 @@ void FileInput::openFileStream() { //stopInput(); - if (m_ifstream.is_open()) { +#ifdef ANDROID + if (m_inputFile.isOpen()) { + m_inputFile.close(); + } + + m_inputFile.setFileName(m_settings.m_fileName); + m_inputFile.open(QIODevice::ReadOnly | QIODevice::ExistingOnly); + quint64 fileSize = (quint64) m_inputFile.size(); + +#else + if (m_ifstream.is_open()) { m_ifstream.close(); } @@ -110,12 +120,18 @@ void FileInput::openFileStream() m_ifstream.open(m_settings.m_fileName.toStdString().c_str(), std::ios::binary | std::ios::ate); #endif quint64 fileSize = m_ifstream.tellg(); +#endif if (m_settings.m_fileName.endsWith(".wav")) { WavFileRecord::Header header; +#ifdef ANDROID + m_inputFile.seek(0); + bool headerOK = WavFileRecord::readHeader(m_inputFile, header); +#else m_ifstream.seekg(0, std::ios_base::beg); bool headerOK = WavFileRecord::readHeader(m_ifstream, header); +#endif m_sampleRate = header.m_sampleRate; if (header.m_auxiHeader.m_size > 0) { @@ -136,7 +152,12 @@ void FileInput::openFileStream() if (headerOK && (m_sampleRate > 0) && (m_sampleSize > 0)) { - m_recordLengthMuSec = ((fileSize - m_ifstream.tellg()) * 1000000UL) / ((m_sampleSize == 24 ? 8 : 4) * m_sampleRate); +#ifdef ANDROID + qint64 pos = m_inputFile.pos(); +#else + qint64 pos = m_ifstream.tellg(); +#endif + m_recordLengthMuSec = ((fileSize - pos) * 1000000UL) / ((m_sampleSize == 24 ? 8 : 4) * m_sampleRate); } else { @@ -153,8 +174,13 @@ void FileInput::openFileStream() else if (fileSize > sizeof(FileRecord::Header)) { FileRecord::Header header; +#ifdef ANDROID + m_inputFile.seek(0); + bool crcOK = FileRecord::readHeader(m_inputFile, header); +#else m_ifstream.seekg(0,std::ios_base::beg); bool crcOK = FileRecord::readHeader(m_ifstream, header); +#endif m_sampleRate = header.sampleRate; m_centerFrequency = header.centerFrequency; m_startingTimeStamp = header.startTimeStamp; @@ -208,7 +234,11 @@ void FileInput::openFileStream() } if (m_recordLengthMuSec == 0) { +#ifdef ANDROID + m_inputFile.close(); +#else m_ifstream.close(); +#endif } } @@ -216,14 +246,24 @@ void FileInput::seekFileStream(int seekMillis) { QMutexLocker mutexLocker(&m_mutex); - if ((m_ifstream.is_open()) && m_fileInputWorker && !m_fileInputWorker->isRunning()) + if ( +#ifdef ANDROID + m_inputFile.isOpen() +#else + m_ifstream.is_open() +#endif + && m_fileInputWorker && !m_fileInputWorker->isRunning()) { quint64 seekPoint = ((m_recordLengthMuSec * seekMillis) / 1000) * m_sampleRate; seekPoint /= 1000000UL; m_fileInputWorker->setSamplesCount(seekPoint); seekPoint *= (m_sampleSize == 24 ? 8 : 4); // + sizeof(FileRecord::Header) +#ifdef ANDROID + m_inputFile.seek(seekPoint + sizeof(FileRecord::Header)); +#else m_ifstream.clear(); m_ifstream.seekg(seekPoint + sizeof(FileRecord::Header), std::ios::beg); +#endif } } @@ -235,7 +275,11 @@ void FileInput::init() bool FileInput::start() { +#ifdef ANDROID + if (!m_inputFile.isOpen()) +#else if (!m_ifstream.is_open()) +#endif { qWarning("FileInput::start: file not open. not starting"); return false; @@ -244,11 +288,15 @@ bool FileInput::start() QMutexLocker mutexLocker(&m_mutex); qDebug() << "FileInput::start"; +#ifdef ANDROID + m_inputFile.seek(0); +#else if (m_ifstream.tellg() != (std::streampos)0) { m_ifstream.clear(); m_ifstream.seekg(sizeof(FileRecord::Header), std::ios::beg); } +#endif if (!m_sampleFifo.setSize(m_settings.m_accelerationFactor * m_sampleRate * sizeof(Sample))) { @@ -256,7 +304,11 @@ bool FileInput::start() return false; } +#ifdef ANDROID + m_fileInputWorker = new FileInputWorker(&m_inputFile, &m_sampleFifo, m_masterTimer, &m_inputMessageQueue); +#else m_fileInputWorker = new FileInputWorker(&m_ifstream, &m_sampleFifo, m_masterTimer, &m_inputMessageQueue); +#endif m_fileInputWorker->moveToThread(&m_fileInputWorkerThread); m_fileInputWorker->setSampleRateAndSize(m_settings.m_accelerationFactor * m_sampleRate, m_sampleSize); // Fast Forward: 1 corresponds to live. 1/2 is half speed, 2 is double speed startWorker(); diff --git a/plugins/samplesource/fileinput/fileinput.h b/plugins/samplesource/fileinput/fileinput.h index 08e3e9c81..c372edba1 100644 --- a/plugins/samplesource/fileinput/fileinput.h +++ b/plugins/samplesource/fileinput/fileinput.h @@ -28,6 +28,7 @@ #include #include #include +#include #include "dsp/devicesamplesource.h" #include "fileinputsettings.h" @@ -334,7 +335,11 @@ public: DeviceAPI *m_deviceAPI; QMutex m_mutex; FileInputSettings m_settings; +#ifdef ANDROID + QFile m_inputFile; +#else std::ifstream m_ifstream; +#endif FileInputWorker* m_fileInputWorker; QThread m_fileInputWorkerThread; QString m_deviceDescription; diff --git a/plugins/samplesource/fileinput/fileinputgui.cpp b/plugins/samplesource/fileinput/fileinputgui.cpp index 143925a3b..0de5c2e2f 100644 --- a/plugins/samplesource/fileinput/fileinputgui.cpp +++ b/plugins/samplesource/fileinput/fileinputgui.cpp @@ -28,6 +28,7 @@ #include "gui/colormapper.h" #include "gui/glspectrum.h" #include "gui/basicdevicesettingsdialog.h" +#include "gui/dialogpositioner.h" #include "dsp/dspengine.h" #include "dsp/dspcommands.h" @@ -313,7 +314,8 @@ void FileInputGUI::on_showFileDialog_clicked(bool checked) { (void) checked; QString fileName = QFileDialog::getOpenFileName(this, - tr("Open I/Q record file"), ".", tr("SDR I/Q Files (*.sdriq *.wav)"), 0, QFileDialog::DontUseNativeDialog); + tr("Open I/Q record file"), ".", tr("SDR I/Q Files (*.sdriq *.wav)"), 0); + if (fileName != "") { @@ -450,6 +452,7 @@ void FileInputGUI::openDeviceSettingsDialog(const QPoint& p) dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.move(p); + new DialogPositioner(&dialog, false); dialog.exec(); m_settings.m_useReverseAPI = dialog.useReverseAPI(); diff --git a/plugins/samplesource/fileinput/fileinputworker.cpp b/plugins/samplesource/fileinput/fileinputworker.cpp index f1b61f75d..42a74fbe4 100644 --- a/plugins/samplesource/fileinput/fileinputworker.cpp +++ b/plugins/samplesource/fileinput/fileinputworker.cpp @@ -26,7 +26,12 @@ MESSAGE_CLASS_DEFINITION(FileInputWorker::MsgReportEOF, Message) -FileInputWorker::FileInputWorker(std::ifstream *samplesStream, +FileInputWorker::FileInputWorker( +#ifdef ANDROID + QFile *samplesStream, +#else + std::ifstream *samplesStream, +#endif SampleSinkFifo* sampleFifo, const QTimer& timer, MessageQueue *fileInputMessageQueue, @@ -69,7 +74,11 @@ void FileInputWorker::startWork() { qDebug() << "FileInputThread::startWork: "; +#ifdef ANDROID + if (m_ifstream->isOpen()) +#else if (m_ifstream->is_open()) +#endif { qDebug() << "FileInputThread::startWork: file stream open, starting..."; m_elapsedTimer.start(); @@ -172,6 +181,23 @@ void FileInputWorker::tick() setBuffers(m_chunksize); } + +#ifdef ANDROID + // read samples directly feeding the SampleFifo (no callback) + qint64 bytesRead = m_ifstream->read(reinterpret_cast(m_fileBuf), m_chunksize); + + if (m_ifstream->atEnd()) + { + writeToSampleFifo(m_fileBuf, (qint32) bytesRead); + MsgReportEOF *message = MsgReportEOF::create(); + m_fileInputMessageQueue->push(message); + } + else + { + writeToSampleFifo(m_fileBuf, (qint32) m_chunksize); + m_samplesCount += m_chunksize / (2 * m_samplebytes); + } +#else // read samples directly feeding the SampleFifo (no callback) m_ifstream->read(reinterpret_cast(m_fileBuf), m_chunksize); @@ -186,6 +212,7 @@ void FileInputWorker::tick() writeToSampleFifo(m_fileBuf, (qint32) m_chunksize); m_samplesCount += m_chunksize / (2 * m_samplebytes); } +#endif } } diff --git a/plugins/samplesource/fileinput/fileinputworker.h b/plugins/samplesource/fileinput/fileinputworker.h index c81fc635a..19787b148 100644 --- a/plugins/samplesource/fileinput/fileinputworker.h +++ b/plugins/samplesource/fileinput/fileinputworker.h @@ -50,7 +50,12 @@ public: { } }; - FileInputWorker(std::ifstream *samplesStream, + FileInputWorker( +#ifdef ANDROID + QFile *samplesStream, +#else + std::ifstream *samplesStream, +#endif SampleSinkFifo* sampleFifo, const QTimer& timer, MessageQueue *fileInputMessageQueue, @@ -68,7 +73,11 @@ public: private: volatile bool m_running; +#ifdef ANDROID + QFile *m_ifstream; +#else std::ifstream* m_ifstream; +#endif quint8 *m_fileBuf; quint8 *m_convertBuf; std::size_t m_bufsize; diff --git a/sdrbase/dsp/filerecord.cpp b/sdrbase/dsp/filerecord.cpp index 603e8931c..0b9bcc492 100644 --- a/sdrbase/dsp/filerecord.cpp +++ b/sdrbase/dsp/filerecord.cpp @@ -114,16 +114,31 @@ bool FileRecord::startRecording() stopRecording(); } +#ifdef ANDROID + if (!m_sampleFile.isOpen()) +#else if (!m_sampleFile.is_open()) +#endif { qDebug() << "FileRecord::startRecording"; - m_currentFileName = QString("%1.%2.sdriq").arg(m_fileBase).arg(QDateTime::currentDateTimeUtc().toString("yyyy-MM-ddTHH_mm_ss_zzz")); +#ifdef ANDROID + // FIXME: No idea how to write to a file where the filename doesn't come from the file picker + m_currentFileName = m_fileBase + ".sdriq"; + m_sampleFile.setFileName(m_currentFileName); + if (!m_sampleFile.open(QIODevice::ReadWrite)) + { + qWarning() << "FileRecord::startRecording: failed to open file: " << m_currentFileName << " error " << m_sampleFile.error(); + return false; + } +#else + m_currentFileName = m_fileBase + "." + QDateTime::currentDateTimeUtc().toString("yyyy-MM-ddTHH_mm_ss_zzz") + ".sdriq"; // Don't use QString::arg on Android, as filename can contain %2 m_sampleFile.open(m_currentFileName.toStdString().c_str(), std::ios::binary); if (!m_sampleFile.is_open()) { qWarning() << "FileRecord::startRecording: failed to open file: " << m_currentFileName; return false; } +#endif m_recordOn = true; m_recordStart = true; m_byteCount = 0; @@ -135,17 +150,24 @@ bool FileRecord::stopRecording() { QMutexLocker mutexLocker(&m_mutex); +#ifdef ANDROID + if (m_sampleFile.isOpen()) +#else if (m_sampleFile.is_open()) +#endif { qDebug() << "FileRecord::stopRecording"; m_sampleFile.close(); m_recordOn = false; m_recordStart = false; +#ifdef ANDROID +#else if (m_sampleFile.bad()) { qWarning() << "FileRecord::stopRecording: an error occurred while writing to " << m_currentFileName; return false; } +#endif } return true; } @@ -197,6 +219,14 @@ bool FileRecord::readHeader(std::ifstream& sampleFile, Header& header) return header.crc32 == crc32.checksum(); } +bool FileRecord::readHeader(QFile& sampleFile, Header& header) +{ + sampleFile.read((char *) &header, sizeof(Header)); + boost::crc_32_type crc32; + crc32.process_bytes(&header, 28); + return header.crc32 == crc32.checksum(); +} + void FileRecord::writeHeader(std::ofstream& sampleFile, Header& header) { boost::crc_32_type crc32; @@ -204,3 +234,11 @@ void FileRecord::writeHeader(std::ofstream& sampleFile, Header& header) header.crc32 = crc32.checksum(); sampleFile.write((const char *) &header, sizeof(Header)); } + +void FileRecord::writeHeader(QFile& sampleFile, Header& header) +{ + boost::crc_32_type crc32; + crc32.process_bytes(&header, 28); + header.crc32 = crc32.checksum(); + sampleFile.write((const char *) &header, sizeof(Header)); +} diff --git a/sdrbase/dsp/filerecord.h b/sdrbase/dsp/filerecord.h index 02ba5d706..e2086115c 100644 --- a/sdrbase/dsp/filerecord.h +++ b/sdrbase/dsp/filerecord.h @@ -18,6 +18,8 @@ #ifndef INCLUDE_FILERECORD_H #define INCLUDE_FILERECORD_H +#include + #include #include #include @@ -65,7 +67,9 @@ public: virtual bool isRecording() const { return m_recordOn; } static bool readHeader(std::ifstream& samplefile, Header& header); //!< returns true if CRC checksum is correct else false + static bool readHeader(QFile& samplefile, Header& header); //!< returns true if CRC checksum is correct else false static void writeHeader(std::ofstream& samplefile, Header& header); + static void writeHeader(QFile& samplefile, Header& header); private: QString m_fileBase; @@ -73,7 +77,11 @@ private: quint64 m_centerFrequency; bool m_recordOn; bool m_recordStart; +#ifdef ANDROID + QFile m_sampleFile; +#else std::ofstream m_sampleFile; +#endif QString m_currentFileName; quint64 m_byteCount; qint64 m_msShift; diff --git a/sdrbase/dsp/wavfilerecord.cpp b/sdrbase/dsp/wavfilerecord.cpp index 2cc572f07..2647e69e9 100644 --- a/sdrbase/dsp/wavfilerecord.cpp +++ b/sdrbase/dsp/wavfilerecord.cpp @@ -105,8 +105,8 @@ void WavFileRecord::feed(const SampleVector::const_iterator& begin, const Sample { // Convert from 24-bit to 16-bit int16_t samples[2]; - samples[0] = it->real() >> 8; - samples[1] = it->imag() >> 8; + samples[0] = std::min(32767, std::max(it->real() >> 8, -32768)); + samples[1] = std::min(32767, std::max(it->imag() >> 8, -32768)); m_sampleFile.write(reinterpret_cast(&samples), 4); m_byteCount += 4; } @@ -154,16 +154,31 @@ bool WavFileRecord::startRecording() stopRecording(); } +#ifdef ANDROID + if (!m_sampleFile.isOpen()) +#else if (!m_sampleFile.is_open()) +#endif { qDebug() << "WavFileRecord::startRecording"; - m_currentFileName = QString("%1.%2.wav").arg(m_fileBase).arg(QDateTime::currentDateTimeUtc().toString("yyyy-MM-ddTHH_mm_ss_zzz")); +#ifdef ANDROID + // FIXME: No idea how to write to a file where the filename doesn't come from the file picker + m_currentFileName = m_fileBase + ".wav"; + m_sampleFile.setFileName(m_currentFileName); + if (!m_sampleFile.open(QIODevice::ReadWrite)) + { + qWarning() << "WavFileRecord::startRecording: failed to open file: " << m_currentFileName << " error " << m_sampleFile.error(); + return false; + } +#else + m_currentFileName = m_fileBase + "_" + QDateTime::currentDateTimeUtc().toString("yyyy-MM-ddTHH_mm_ss_zzz") + ".wav"; // Don't use QString::arg on Android, as filename can contain %2 m_sampleFile.open(m_currentFileName.toStdString().c_str(), std::ios::binary); if (!m_sampleFile.is_open()) { qWarning() << "WavFileRecord::startRecording: failed to open file: " << m_currentFileName; return false; } +#endif m_recordOn = true; m_recordStart = true; m_byteCount = 0; @@ -173,25 +188,41 @@ bool WavFileRecord::startRecording() bool WavFileRecord::stopRecording() { +#ifdef ANDROID + if (m_sampleFile.isOpen()) +#else if (m_sampleFile.is_open()) +#endif { qDebug() << "WavFileRecord::stopRecording"; // Fix up chunk sizes +#ifdef ANDROID + long fileSize = (long)m_sampleFile.size(); + m_sampleFile.seek(offsetof(Header, m_riffHeader.m_size)); +#else long fileSize = m_sampleFile.tellp(); m_sampleFile.seekp(offsetof(Header, m_riffHeader.m_size)); +#endif qint32 size = (fileSize - 8); m_sampleFile.write((char *)&size, 4); +#ifdef ANDROID + m_sampleFile.seek(offsetof(Header, m_dataHeader.m_size)); +#else m_sampleFile.seekp(offsetof(Header, m_dataHeader.m_size)); +#endif size = fileSize - sizeof(Header); m_sampleFile.write((char *)&size, 4); m_sampleFile.close(); m_recordOn = false; m_recordStart = false; +#ifdef ANDROID +#else if (m_sampleFile.bad()) { qWarning() << "WavFileRecord::stopRecording: an error occurred while writing to " << m_currentFileName; return false; } +#endif } return true; } @@ -305,35 +336,7 @@ bool WavFileRecord::readHeader(std::ifstream& sampleFile, Header& header) return false; } - if (strncmp(header.m_riffHeader.m_id, "RIFF", 4)) - { - qDebug() << "WavFileRecord::readHeader: No RIFF header"; - return false; - } - if (strncmp(header.m_type, "WAVE", 4)) - { - qDebug() << "WavFileRecord::readHeader: No WAVE header"; - return false; - } - if (strncmp(header.m_fmtHeader.m_id, "fmt ", 4)) - { - qDebug() << "WavFileRecord::readHeader: No fmt header"; - return false; - } - if (header.m_audioFormat != 1) - { - qDebug() << "WavFileRecord::readHeader: Audio format is not PCM"; - return false; - } - if (header.m_numChannels != 2) - { - qDebug() << "WavFileRecord::readHeader: Number of channels is not 2"; - return false; - } - // FileInputWorker can't handle other bits sizes - if (header.m_bitsPerSample != 16) - { - qDebug() << "WavFileRecord::readHeader: Number of bits per sample is not 16"; + if (!checkHeader(header)) { return false; } @@ -365,11 +368,89 @@ bool WavFileRecord::readHeader(std::ifstream& sampleFile, Header& header) return true; } +bool WavFileRecord::readHeader(QFile& sampleFile, Header& header) +{ + memset(&header, 0, sizeof(Header)); + + sampleFile.read((char *) &header, 8+4+8+16); + + if (!checkHeader(header)) { + return false; + } + + Chunk chunkHeader; + bool gotData = false; + while (!gotData) + { + if (sampleFile.read((char *) &chunkHeader, 8) != 8) + { + qDebug() << "WavFileRecord::readHeader: End of file without reading data header"; + return false; + } + + if (!strncmp(chunkHeader.m_id, "auxi", 4)) + { + memcpy(&header.m_auxiHeader, &chunkHeader, sizeof(Chunk)); + if (sampleFile.read((char *) &header.m_auxi, sizeof(Auxi)) != sizeof(Auxi)) { + return false; + } + } + else if (!strncmp(chunkHeader.m_id, "data", 4)) + { + memcpy(&header.m_dataHeader, &chunkHeader, sizeof(Chunk)); + gotData = true; + } + } + + return true; +} + +bool WavFileRecord::checkHeader(Header& header) +{ + if (strncmp(header.m_riffHeader.m_id, "RIFF", 4)) + { + qDebug() << "WavFileRecord::readHeader: No RIFF header"; + return false; + } + if (strncmp(header.m_type, "WAVE", 4)) + { + qDebug() << "WavFileRecord::readHeader: No WAVE header"; + return false; + } + if (strncmp(header.m_fmtHeader.m_id, "fmt ", 4)) + { + qDebug() << "WavFileRecord::readHeader: No fmt header"; + return false; + } + if (header.m_audioFormat != 1) + { + qDebug() << "WavFileRecord::readHeader: Audio format is not PCM"; + return false; + } + if (header.m_numChannels != 2) + { + qDebug() << "WavFileRecord::readHeader: Number of channels is not 2"; + return false; + } + // FileInputWorker can't handle other bits sizes + if (header.m_bitsPerSample != 16) + { + qDebug() << "WavFileRecord::readHeader: Number of bits per sample is not 16"; + return false; + } + return true; +} + void WavFileRecord::writeHeader(std::ofstream& sampleFile, Header& header) { sampleFile.write((const char *) &header, sizeof(Header)); } +void WavFileRecord::writeHeader(QFile& sampleFile, Header& header) +{ + sampleFile.write((const char *) &header, sizeof(Header)); +} + bool WavFileRecord::getCenterFrequency(QString fileName, quint64& centerFrequency) { // Attempt to extract center frequency from filename diff --git a/sdrbase/dsp/wavfilerecord.h b/sdrbase/dsp/wavfilerecord.h index 6e1e49317..9cf2db22b 100644 --- a/sdrbase/dsp/wavfilerecord.h +++ b/sdrbase/dsp/wavfilerecord.h @@ -27,6 +27,7 @@ #include #include +#include #include "dsp/filerecordinterface.h" #include "export.h" @@ -111,7 +112,9 @@ public: virtual bool isRecording() const override { return m_recordOn; } static bool readHeader(std::ifstream& samplefile, Header& header); + static bool readHeader(QFile& samplefile, Header& header); static void writeHeader(std::ofstream& samplefile, Header& header); + static void writeHeader(QFile& samplefile, Header& header); // These functions guess from the filename, not contents static bool getCenterFrequency(QString fileName, quint64& centerFrequency); @@ -123,13 +126,19 @@ private: quint64 m_centerFrequency; bool m_recordOn; bool m_recordStart; +#ifdef ANDROID + QFile m_sampleFile; +#else std::ofstream m_sampleFile; +#endif QString m_currentFileName; quint64 m_byteCount; qint64 m_msShift; int m_nbChannels; void writeHeader(); + + static bool checkHeader(Header& header); }; #endif // INCLUDE_WAV_FILERECORD_H