1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-26 09:48:45 -05:00

Support FileInput plugin on Android, by using QFile rather istream

This commit is contained in:
Jon Beniston 2023-01-02 15:42:34 +00:00
parent 5be2b1674e
commit d574e74908
10 changed files with 771 additions and 543 deletions

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>552</width> <width>441</width>
<height>458</height> <height>458</height>
</rect> </rect>
</property> </property>
@ -18,7 +18,7 @@
</property> </property>
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>552</width> <width>400</width>
<height>102</height> <height>102</height>
</size> </size>
</property> </property>
@ -31,18 +31,18 @@
<property name="windowTitle"> <property name="windowTitle">
<string>File Sink</string> <string>File Sink</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QWidget" name="settingsContainer" native="true"> <widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry"> <property name="sizePolicy">
<rect> <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<x>0</x> <horstretch>0</horstretch>
<y>0</y> <verstretch>0</verstretch>
<width>550</width> </sizepolicy>
<height>100</height>
</rect>
</property> </property>
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>550</width> <width>0</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
@ -514,15 +514,9 @@
</item> </item>
</layout> </layout>
</widget> </widget>
</item>
<item>
<widget class="QWidget" name="verticalWidget" native="true"> <widget class="QWidget" name="verticalWidget" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>100</y>
<width>541</width>
<height>351</height>
</rect>
</property>
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -560,6 +554,8 @@
</item> </item>
</layout> </layout>
</widget> </widget>
</item>
</layout>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@ -100,6 +100,16 @@ void FileInput::openFileStream()
{ {
//stopInput(); //stopInput();
#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()) { if (m_ifstream.is_open()) {
m_ifstream.close(); 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); m_ifstream.open(m_settings.m_fileName.toStdString().c_str(), std::ios::binary | std::ios::ate);
#endif #endif
quint64 fileSize = m_ifstream.tellg(); quint64 fileSize = m_ifstream.tellg();
#endif
if (m_settings.m_fileName.endsWith(".wav")) if (m_settings.m_fileName.endsWith(".wav"))
{ {
WavFileRecord::Header header; 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); m_ifstream.seekg(0, std::ios_base::beg);
bool headerOK = WavFileRecord::readHeader(m_ifstream, header); bool headerOK = WavFileRecord::readHeader(m_ifstream, header);
#endif
m_sampleRate = header.m_sampleRate; m_sampleRate = header.m_sampleRate;
if (header.m_auxiHeader.m_size > 0) if (header.m_auxiHeader.m_size > 0)
{ {
@ -136,7 +152,12 @@ void FileInput::openFileStream()
if (headerOK && (m_sampleRate > 0) && (m_sampleSize > 0)) 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 else
{ {
@ -153,8 +174,13 @@ void FileInput::openFileStream()
else if (fileSize > sizeof(FileRecord::Header)) else if (fileSize > sizeof(FileRecord::Header))
{ {
FileRecord::Header 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); m_ifstream.seekg(0,std::ios_base::beg);
bool crcOK = FileRecord::readHeader(m_ifstream, header); bool crcOK = FileRecord::readHeader(m_ifstream, header);
#endif
m_sampleRate = header.sampleRate; m_sampleRate = header.sampleRate;
m_centerFrequency = header.centerFrequency; m_centerFrequency = header.centerFrequency;
m_startingTimeStamp = header.startTimeStamp; m_startingTimeStamp = header.startTimeStamp;
@ -208,7 +234,11 @@ void FileInput::openFileStream()
} }
if (m_recordLengthMuSec == 0) { if (m_recordLengthMuSec == 0) {
#ifdef ANDROID
m_inputFile.close();
#else
m_ifstream.close(); m_ifstream.close();
#endif
} }
} }
@ -216,14 +246,24 @@ void FileInput::seekFileStream(int seekMillis)
{ {
QMutexLocker mutexLocker(&m_mutex); 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; quint64 seekPoint = ((m_recordLengthMuSec * seekMillis) / 1000) * m_sampleRate;
seekPoint /= 1000000UL; seekPoint /= 1000000UL;
m_fileInputWorker->setSamplesCount(seekPoint); m_fileInputWorker->setSamplesCount(seekPoint);
seekPoint *= (m_sampleSize == 24 ? 8 : 4); // + sizeof(FileRecord::Header) seekPoint *= (m_sampleSize == 24 ? 8 : 4); // + sizeof(FileRecord::Header)
#ifdef ANDROID
m_inputFile.seek(seekPoint + sizeof(FileRecord::Header));
#else
m_ifstream.clear(); m_ifstream.clear();
m_ifstream.seekg(seekPoint + sizeof(FileRecord::Header), std::ios::beg); m_ifstream.seekg(seekPoint + sizeof(FileRecord::Header), std::ios::beg);
#endif
} }
} }
@ -235,7 +275,11 @@ void FileInput::init()
bool FileInput::start() bool FileInput::start()
{ {
#ifdef ANDROID
if (!m_inputFile.isOpen())
#else
if (!m_ifstream.is_open()) if (!m_ifstream.is_open())
#endif
{ {
qWarning("FileInput::start: file not open. not starting"); qWarning("FileInput::start: file not open. not starting");
return false; return false;
@ -244,11 +288,15 @@ bool FileInput::start()
QMutexLocker mutexLocker(&m_mutex); QMutexLocker mutexLocker(&m_mutex);
qDebug() << "FileInput::start"; qDebug() << "FileInput::start";
#ifdef ANDROID
m_inputFile.seek(0);
#else
if (m_ifstream.tellg() != (std::streampos)0) if (m_ifstream.tellg() != (std::streampos)0)
{ {
m_ifstream.clear(); m_ifstream.clear();
m_ifstream.seekg(sizeof(FileRecord::Header), std::ios::beg); m_ifstream.seekg(sizeof(FileRecord::Header), std::ios::beg);
} }
#endif
if (!m_sampleFifo.setSize(m_settings.m_accelerationFactor * m_sampleRate * sizeof(Sample))) if (!m_sampleFifo.setSize(m_settings.m_accelerationFactor * m_sampleRate * sizeof(Sample)))
{ {
@ -256,7 +304,11 @@ bool FileInput::start()
return false; 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); m_fileInputWorker = new FileInputWorker(&m_ifstream, &m_sampleFifo, m_masterTimer, &m_inputMessageQueue);
#endif
m_fileInputWorker->moveToThread(&m_fileInputWorkerThread); 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 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(); startWorker();

View File

@ -28,6 +28,7 @@
#include <QThread> #include <QThread>
#include <QMutex> #include <QMutex>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QFile>
#include "dsp/devicesamplesource.h" #include "dsp/devicesamplesource.h"
#include "fileinputsettings.h" #include "fileinputsettings.h"
@ -334,7 +335,11 @@ public:
DeviceAPI *m_deviceAPI; DeviceAPI *m_deviceAPI;
QMutex m_mutex; QMutex m_mutex;
FileInputSettings m_settings; FileInputSettings m_settings;
#ifdef ANDROID
QFile m_inputFile;
#else
std::ifstream m_ifstream; std::ifstream m_ifstream;
#endif
FileInputWorker* m_fileInputWorker; FileInputWorker* m_fileInputWorker;
QThread m_fileInputWorkerThread; QThread m_fileInputWorkerThread;
QString m_deviceDescription; QString m_deviceDescription;

View File

@ -28,6 +28,7 @@
#include "gui/colormapper.h" #include "gui/colormapper.h"
#include "gui/glspectrum.h" #include "gui/glspectrum.h"
#include "gui/basicdevicesettingsdialog.h" #include "gui/basicdevicesettingsdialog.h"
#include "gui/dialogpositioner.h"
#include "dsp/dspengine.h" #include "dsp/dspengine.h"
#include "dsp/dspcommands.h" #include "dsp/dspcommands.h"
@ -313,7 +314,8 @@ void FileInputGUI::on_showFileDialog_clicked(bool checked)
{ {
(void) checked; (void) checked;
QString fileName = QFileDialog::getOpenFileName(this, 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 != "") if (fileName != "")
{ {
@ -450,6 +452,7 @@ void FileInputGUI::openDeviceSettingsDialog(const QPoint& p)
dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex);
dialog.move(p); dialog.move(p);
new DialogPositioner(&dialog, false);
dialog.exec(); dialog.exec();
m_settings.m_useReverseAPI = dialog.useReverseAPI(); m_settings.m_useReverseAPI = dialog.useReverseAPI();

View File

@ -26,7 +26,12 @@
MESSAGE_CLASS_DEFINITION(FileInputWorker::MsgReportEOF, Message) MESSAGE_CLASS_DEFINITION(FileInputWorker::MsgReportEOF, Message)
FileInputWorker::FileInputWorker(std::ifstream *samplesStream, FileInputWorker::FileInputWorker(
#ifdef ANDROID
QFile *samplesStream,
#else
std::ifstream *samplesStream,
#endif
SampleSinkFifo* sampleFifo, SampleSinkFifo* sampleFifo,
const QTimer& timer, const QTimer& timer,
MessageQueue *fileInputMessageQueue, MessageQueue *fileInputMessageQueue,
@ -69,7 +74,11 @@ void FileInputWorker::startWork()
{ {
qDebug() << "FileInputThread::startWork: "; qDebug() << "FileInputThread::startWork: ";
#ifdef ANDROID
if (m_ifstream->isOpen())
#else
if (m_ifstream->is_open()) if (m_ifstream->is_open())
#endif
{ {
qDebug() << "FileInputThread::startWork: file stream open, starting..."; qDebug() << "FileInputThread::startWork: file stream open, starting...";
m_elapsedTimer.start(); m_elapsedTimer.start();
@ -172,6 +181,23 @@ void FileInputWorker::tick()
setBuffers(m_chunksize); setBuffers(m_chunksize);
} }
#ifdef ANDROID
// read samples directly feeding the SampleFifo (no callback)
qint64 bytesRead = m_ifstream->read(reinterpret_cast<char*>(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) // read samples directly feeding the SampleFifo (no callback)
m_ifstream->read(reinterpret_cast<char*>(m_fileBuf), m_chunksize); m_ifstream->read(reinterpret_cast<char*>(m_fileBuf), m_chunksize);
@ -186,6 +212,7 @@ void FileInputWorker::tick()
writeToSampleFifo(m_fileBuf, (qint32) m_chunksize); writeToSampleFifo(m_fileBuf, (qint32) m_chunksize);
m_samplesCount += m_chunksize / (2 * m_samplebytes); m_samplesCount += m_chunksize / (2 * m_samplebytes);
} }
#endif
} }
} }

View File

@ -50,7 +50,12 @@ public:
{ } { }
}; };
FileInputWorker(std::ifstream *samplesStream, FileInputWorker(
#ifdef ANDROID
QFile *samplesStream,
#else
std::ifstream *samplesStream,
#endif
SampleSinkFifo* sampleFifo, SampleSinkFifo* sampleFifo,
const QTimer& timer, const QTimer& timer,
MessageQueue *fileInputMessageQueue, MessageQueue *fileInputMessageQueue,
@ -68,7 +73,11 @@ public:
private: private:
volatile bool m_running; volatile bool m_running;
#ifdef ANDROID
QFile *m_ifstream;
#else
std::ifstream* m_ifstream; std::ifstream* m_ifstream;
#endif
quint8 *m_fileBuf; quint8 *m_fileBuf;
quint8 *m_convertBuf; quint8 *m_convertBuf;
std::size_t m_bufsize; std::size_t m_bufsize;

View File

@ -114,16 +114,31 @@ bool FileRecord::startRecording()
stopRecording(); stopRecording();
} }
#ifdef ANDROID
if (!m_sampleFile.isOpen())
#else
if (!m_sampleFile.is_open()) if (!m_sampleFile.is_open())
#endif
{ {
qDebug() << "FileRecord::startRecording"; 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); m_sampleFile.open(m_currentFileName.toStdString().c_str(), std::ios::binary);
if (!m_sampleFile.is_open()) if (!m_sampleFile.is_open())
{ {
qWarning() << "FileRecord::startRecording: failed to open file: " << m_currentFileName; qWarning() << "FileRecord::startRecording: failed to open file: " << m_currentFileName;
return false; return false;
} }
#endif
m_recordOn = true; m_recordOn = true;
m_recordStart = true; m_recordStart = true;
m_byteCount = 0; m_byteCount = 0;
@ -135,17 +150,24 @@ bool FileRecord::stopRecording()
{ {
QMutexLocker mutexLocker(&m_mutex); QMutexLocker mutexLocker(&m_mutex);
#ifdef ANDROID
if (m_sampleFile.isOpen())
#else
if (m_sampleFile.is_open()) if (m_sampleFile.is_open())
#endif
{ {
qDebug() << "FileRecord::stopRecording"; qDebug() << "FileRecord::stopRecording";
m_sampleFile.close(); m_sampleFile.close();
m_recordOn = false; m_recordOn = false;
m_recordStart = false; m_recordStart = false;
#ifdef ANDROID
#else
if (m_sampleFile.bad()) if (m_sampleFile.bad())
{ {
qWarning() << "FileRecord::stopRecording: an error occurred while writing to " << m_currentFileName; qWarning() << "FileRecord::stopRecording: an error occurred while writing to " << m_currentFileName;
return false; return false;
} }
#endif
} }
return true; return true;
} }
@ -197,6 +219,14 @@ bool FileRecord::readHeader(std::ifstream& sampleFile, Header& header)
return header.crc32 == crc32.checksum(); 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) void FileRecord::writeHeader(std::ofstream& sampleFile, Header& header)
{ {
boost::crc_32_type crc32; boost::crc_32_type crc32;
@ -204,3 +234,11 @@ void FileRecord::writeHeader(std::ofstream& sampleFile, Header& header)
header.crc32 = crc32.checksum(); header.crc32 = crc32.checksum();
sampleFile.write((const char *) &header, sizeof(Header)); 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));
}

View File

@ -18,6 +18,8 @@
#ifndef INCLUDE_FILERECORD_H #ifndef INCLUDE_FILERECORD_H
#define INCLUDE_FILERECORD_H #define INCLUDE_FILERECORD_H
#include <QFile>
#include <dsp/basebandsamplesink.h> #include <dsp/basebandsamplesink.h>
#include <string> #include <string>
#include <iostream> #include <iostream>
@ -65,7 +67,9 @@ public:
virtual bool isRecording() const { return m_recordOn; } 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(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(std::ofstream& samplefile, Header& header);
static void writeHeader(QFile& samplefile, Header& header);
private: private:
QString m_fileBase; QString m_fileBase;
@ -73,7 +77,11 @@ private:
quint64 m_centerFrequency; quint64 m_centerFrequency;
bool m_recordOn; bool m_recordOn;
bool m_recordStart; bool m_recordStart;
#ifdef ANDROID
QFile m_sampleFile;
#else
std::ofstream m_sampleFile; std::ofstream m_sampleFile;
#endif
QString m_currentFileName; QString m_currentFileName;
quint64 m_byteCount; quint64 m_byteCount;
qint64 m_msShift; qint64 m_msShift;

View File

@ -105,8 +105,8 @@ void WavFileRecord::feed(const SampleVector::const_iterator& begin, const Sample
{ {
// Convert from 24-bit to 16-bit // Convert from 24-bit to 16-bit
int16_t samples[2]; int16_t samples[2];
samples[0] = it->real() >> 8; samples[0] = std::min(32767, std::max(it->real() >> 8, -32768));
samples[1] = it->imag() >> 8; samples[1] = std::min(32767, std::max(it->imag() >> 8, -32768));
m_sampleFile.write(reinterpret_cast<const char*>(&samples), 4); m_sampleFile.write(reinterpret_cast<const char*>(&samples), 4);
m_byteCount += 4; m_byteCount += 4;
} }
@ -154,16 +154,31 @@ bool WavFileRecord::startRecording()
stopRecording(); stopRecording();
} }
#ifdef ANDROID
if (!m_sampleFile.isOpen())
#else
if (!m_sampleFile.is_open()) if (!m_sampleFile.is_open())
#endif
{ {
qDebug() << "WavFileRecord::startRecording"; 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); m_sampleFile.open(m_currentFileName.toStdString().c_str(), std::ios::binary);
if (!m_sampleFile.is_open()) if (!m_sampleFile.is_open())
{ {
qWarning() << "WavFileRecord::startRecording: failed to open file: " << m_currentFileName; qWarning() << "WavFileRecord::startRecording: failed to open file: " << m_currentFileName;
return false; return false;
} }
#endif
m_recordOn = true; m_recordOn = true;
m_recordStart = true; m_recordStart = true;
m_byteCount = 0; m_byteCount = 0;
@ -173,25 +188,41 @@ bool WavFileRecord::startRecording()
bool WavFileRecord::stopRecording() bool WavFileRecord::stopRecording()
{ {
#ifdef ANDROID
if (m_sampleFile.isOpen())
#else
if (m_sampleFile.is_open()) if (m_sampleFile.is_open())
#endif
{ {
qDebug() << "WavFileRecord::stopRecording"; qDebug() << "WavFileRecord::stopRecording";
// Fix up chunk sizes // 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(); long fileSize = m_sampleFile.tellp();
m_sampleFile.seekp(offsetof(Header, m_riffHeader.m_size)); m_sampleFile.seekp(offsetof(Header, m_riffHeader.m_size));
#endif
qint32 size = (fileSize - 8); qint32 size = (fileSize - 8);
m_sampleFile.write((char *)&size, 4); 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)); m_sampleFile.seekp(offsetof(Header, m_dataHeader.m_size));
#endif
size = fileSize - sizeof(Header); size = fileSize - sizeof(Header);
m_sampleFile.write((char *)&size, 4); m_sampleFile.write((char *)&size, 4);
m_sampleFile.close(); m_sampleFile.close();
m_recordOn = false; m_recordOn = false;
m_recordStart = false; m_recordStart = false;
#ifdef ANDROID
#else
if (m_sampleFile.bad()) if (m_sampleFile.bad())
{ {
qWarning() << "WavFileRecord::stopRecording: an error occurred while writing to " << m_currentFileName; qWarning() << "WavFileRecord::stopRecording: an error occurred while writing to " << m_currentFileName;
return false; return false;
} }
#endif
} }
return true; return true;
} }
@ -305,35 +336,7 @@ bool WavFileRecord::readHeader(std::ifstream& sampleFile, Header& header)
return false; return false;
} }
if (strncmp(header.m_riffHeader.m_id, "RIFF", 4)) if (!checkHeader(header)) {
{
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 false;
} }
@ -365,11 +368,89 @@ bool WavFileRecord::readHeader(std::ifstream& sampleFile, Header& header)
return true; 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) void WavFileRecord::writeHeader(std::ofstream& sampleFile, Header& header)
{ {
sampleFile.write((const char *) &header, sizeof(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) bool WavFileRecord::getCenterFrequency(QString fileName, quint64& centerFrequency)
{ {
// Attempt to extract center frequency from filename // Attempt to extract center frequency from filename

View File

@ -27,6 +27,7 @@
#include <ctime> #include <ctime>
#include <QDateTime> #include <QDateTime>
#include <QFile>
#include "dsp/filerecordinterface.h" #include "dsp/filerecordinterface.h"
#include "export.h" #include "export.h"
@ -111,7 +112,9 @@ public:
virtual bool isRecording() const override { return m_recordOn; } virtual bool isRecording() const override { return m_recordOn; }
static bool readHeader(std::ifstream& samplefile, Header& header); 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(std::ofstream& samplefile, Header& header);
static void writeHeader(QFile& samplefile, Header& header);
// These functions guess from the filename, not contents // These functions guess from the filename, not contents
static bool getCenterFrequency(QString fileName, quint64& centerFrequency); static bool getCenterFrequency(QString fileName, quint64& centerFrequency);
@ -123,13 +126,19 @@ private:
quint64 m_centerFrequency; quint64 m_centerFrequency;
bool m_recordOn; bool m_recordOn;
bool m_recordStart; bool m_recordStart;
#ifdef ANDROID
QFile m_sampleFile;
#else
std::ofstream m_sampleFile; std::ofstream m_sampleFile;
#endif
QString m_currentFileName; QString m_currentFileName;
quint64 m_byteCount; quint64 m_byteCount;
qint64 m_msShift; qint64 m_msShift;
int m_nbChannels; int m_nbChannels;
void writeHeader(); void writeHeader();
static bool checkHeader(Header& header);
}; };
#endif // INCLUDE_WAV_FILERECORD_H #endif // INCLUDE_WAV_FILERECORD_H