mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-22 16:08:39 -05:00
Support FileInput plugin on Android, by using QFile rather istream
This commit is contained in:
parent
5be2b1674e
commit
d574e74908
File diff suppressed because it is too large
Load Diff
@ -100,7 +100,17 @@ void FileInput::openFileStream()
|
|||||||
{
|
{
|
||||||
//stopInput();
|
//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();
|
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();
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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));
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user