mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-15 12:51:49 -05:00
File Input and record: 16/24 bit DSP compatibility
This commit is contained in:
parent
810ca8b057
commit
12a97b9644
@ -48,6 +48,7 @@ FileSourceInput::FileSourceInput(DeviceSourceAPI *deviceAPI) :
|
|||||||
m_deviceDescription(),
|
m_deviceDescription(),
|
||||||
m_fileName("..."),
|
m_fileName("..."),
|
||||||
m_sampleRate(0),
|
m_sampleRate(0),
|
||||||
|
m_sampleSize(0),
|
||||||
m_centerFrequency(0),
|
m_centerFrequency(0),
|
||||||
m_recordLength(0),
|
m_recordLength(0),
|
||||||
m_startingTimeStamp(0),
|
m_startingTimeStamp(0),
|
||||||
@ -85,6 +86,7 @@ void FileSourceInput::openFileStream()
|
|||||||
m_sampleRate = header.sampleRate;
|
m_sampleRate = header.sampleRate;
|
||||||
m_centerFrequency = header.centerFrequency;
|
m_centerFrequency = header.centerFrequency;
|
||||||
m_startingTimeStamp = header.startTimeStamp;
|
m_startingTimeStamp = header.startTimeStamp;
|
||||||
|
m_sampleSize = header.sampleSize;
|
||||||
|
|
||||||
if (fileSize > sizeof(FileRecord::Header)) {
|
if (fileSize > sizeof(FileRecord::Header)) {
|
||||||
m_recordLength = (fileSize - sizeof(FileRecord::Header)) / (4 * m_sampleRate);
|
m_recordLength = (fileSize - sizeof(FileRecord::Header)) / (4 * m_sampleRate);
|
||||||
@ -136,7 +138,7 @@ bool FileSourceInput::start()
|
|||||||
m_ifstream.seekg(sizeof(FileRecord::Header), std::ios::beg);
|
m_ifstream.seekg(sizeof(FileRecord::Header), std::ios::beg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!m_sampleFifo.setSize(m_sampleRate * 4)) {
|
if(!m_sampleFifo.setSize(m_sampleRate * sizeof(Sample))) {
|
||||||
qCritical("Could not allocate SampleFifo");
|
qCritical("Could not allocate SampleFifo");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -149,7 +151,7 @@ bool FileSourceInput::start()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_fileSourceThread->setSamplerate(m_sampleRate);
|
m_fileSourceThread->setSampleRateAndSize(m_sampleRate, m_sampleSize);
|
||||||
m_fileSourceThread->connectTimer(m_masterTimer);
|
m_fileSourceThread->connectTimer(m_masterTimer);
|
||||||
m_fileSourceThread->startWork();
|
m_fileSourceThread->startWork();
|
||||||
m_deviceDescription = "FileSource";
|
m_deviceDescription = "FileSource";
|
||||||
|
@ -265,6 +265,7 @@ public:
|
|||||||
QString m_deviceDescription;
|
QString m_deviceDescription;
|
||||||
QString m_fileName;
|
QString m_fileName;
|
||||||
int m_sampleRate;
|
int m_sampleRate;
|
||||||
|
quint32 m_sampleSize;
|
||||||
quint64 m_centerFrequency;
|
quint64 m_centerFrequency;
|
||||||
quint32 m_recordLength; //!< record length in seconds computed from file size
|
quint32 m_recordLength; //!< record length in seconds computed from file size
|
||||||
std::time_t m_startingTimeStamp;
|
std::time_t m_startingTimeStamp;
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
const PluginDescriptor FileSourcePlugin::m_pluginDescriptor = {
|
const PluginDescriptor FileSourcePlugin::m_pluginDescriptor = {
|
||||||
QString("File source input"),
|
QString("File source input"),
|
||||||
QString("3.10.1"),
|
QString("3.11.1"),
|
||||||
QString("(c) Edouard Griffiths, F4EXB"),
|
QString("(c) Edouard Griffiths, F4EXB"),
|
||||||
QString("https://github.com/f4exb/sdrangel"),
|
QString("https://github.com/f4exb/sdrangel"),
|
||||||
true,
|
true,
|
||||||
|
@ -27,12 +27,15 @@ FileSourceThread::FileSourceThread(std::ifstream *samplesStream, SampleSinkFifo*
|
|||||||
QThread(parent),
|
QThread(parent),
|
||||||
m_running(false),
|
m_running(false),
|
||||||
m_ifstream(samplesStream),
|
m_ifstream(samplesStream),
|
||||||
m_buf(0),
|
m_fileBuf(0),
|
||||||
|
m_convertBuf(0),
|
||||||
m_bufsize(0),
|
m_bufsize(0),
|
||||||
m_chunksize(0),
|
m_chunksize(0),
|
||||||
m_sampleFifo(sampleFifo),
|
m_sampleFifo(sampleFifo),
|
||||||
m_samplesCount(0),
|
m_samplesCount(0),
|
||||||
m_samplerate(0),
|
m_samplerate(0),
|
||||||
|
m_samplesize(0),
|
||||||
|
m_samplebytes(0),
|
||||||
m_throttlems(FILESOURCE_THROTTLE_MS),
|
m_throttlems(FILESOURCE_THROTTLE_MS),
|
||||||
m_throttleToggle(false)
|
m_throttleToggle(false)
|
||||||
{
|
{
|
||||||
@ -45,8 +48,12 @@ FileSourceThread::~FileSourceThread()
|
|||||||
stopWork();
|
stopWork();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_buf != 0) {
|
if (m_fileBuf != 0) {
|
||||||
free(m_buf);
|
free(m_fileBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_convertBuf != 0) {
|
||||||
|
free(m_convertBuf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,49 +84,67 @@ void FileSourceThread::stopWork()
|
|||||||
wait();
|
wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSourceThread::setSamplerate(int samplerate)
|
void FileSourceThread::setSampleRateAndSize(int samplerate, quint32 samplesize)
|
||||||
{
|
{
|
||||||
qDebug() << "FileSourceThread::setSamplerate:"
|
qDebug() << "FileSourceThread::setSampleRateAndSize:"
|
||||||
<< " new:" << samplerate
|
<< " new rate:" << samplerate
|
||||||
<< " old:" << m_samplerate;
|
<< " new size:" << samplesize
|
||||||
|
<< " old rate:" << m_samplerate
|
||||||
|
<< " old size:" << m_samplesize;
|
||||||
|
|
||||||
if (samplerate != m_samplerate)
|
if ((samplerate != m_samplerate) || (samplesize != m_samplesize))
|
||||||
{
|
{
|
||||||
if (m_running) {
|
if (m_running) {
|
||||||
stopWork();
|
stopWork();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_samplerate = samplerate;
|
m_samplerate = samplerate;
|
||||||
// TODO: implement FF and slow motion here. 4 corresponds to live. 2 is half speed, 8 is doulbe speed
|
m_samplesize = samplesize;
|
||||||
m_chunksize = (m_samplerate * 4 * m_throttlems) / 1000;
|
m_samplebytes = m_samplesize > 16 ? sizeof(int32_t) : sizeof(int16_t);
|
||||||
|
// TODO: implement FF and slow motion here. 2 corresponds to live. 1 is half speed, 4 is double speed
|
||||||
|
m_chunksize = (m_samplerate * 2 * m_samplebytes * m_throttlems) / 1000;
|
||||||
|
|
||||||
setBuffer(m_chunksize);
|
setBuffers(m_chunksize);
|
||||||
}
|
}
|
||||||
|
|
||||||
//m_samplerate = samplerate;
|
//m_samplerate = samplerate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSourceThread::setBuffer(std::size_t chunksize)
|
void FileSourceThread::setBuffers(std::size_t chunksize)
|
||||||
{
|
{
|
||||||
if (chunksize > m_bufsize)
|
if (chunksize > m_bufsize)
|
||||||
{
|
{
|
||||||
m_bufsize = chunksize;
|
m_bufsize = chunksize;
|
||||||
|
int nbSamples = m_bufsize/(2 * m_samplebytes);
|
||||||
|
|
||||||
if (m_buf == 0)
|
if (m_fileBuf == 0)
|
||||||
{
|
{
|
||||||
qDebug() << "FileSourceThread::setBuffer: Allocate buffer";
|
qDebug() << "FileSourceThread::setBuffers: Allocate file buffer";
|
||||||
m_buf = (quint8*) malloc(m_bufsize);
|
m_fileBuf = (quint8*) malloc(m_bufsize);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
qDebug() << "FileSourceThread::setBuffer: Re-allocate buffer";
|
qDebug() << "FileSourceThread::setBuffers: Re-allocate file buffer";
|
||||||
quint8 *buf = m_buf;
|
quint8 *buf = m_fileBuf;
|
||||||
m_buf = (quint8*) realloc((void*) m_buf, m_bufsize);
|
m_fileBuf = (quint8*) realloc((void*) m_fileBuf, m_bufsize);
|
||||||
if (!m_buf) free(buf);
|
if (!m_fileBuf) free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "FileSourceThread::setBuffer: size: " << m_bufsize
|
if (m_convertBuf == 0)
|
||||||
<< " #samples: " << (m_bufsize/4);
|
{
|
||||||
|
qDebug() << "FileSourceThread::setBuffers: Allocate conversion buffer";
|
||||||
|
m_convertBuf = (quint8*) malloc(nbSamples*sizeof(Sample));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug() << "FileSourceThread::setBuffers: Re-allocate conversion buffer";
|
||||||
|
quint8 *buf = m_convertBuf;
|
||||||
|
m_convertBuf = (quint8*) realloc((void*) m_convertBuf, nbSamples*sizeof(Sample));
|
||||||
|
if (!m_convertBuf) free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "FileSourceThread::setBuffers: size: " << m_bufsize
|
||||||
|
<< " #samples: " << nbSamples;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,17 +176,18 @@ void FileSourceThread::tick()
|
|||||||
if (throttlems != m_throttlems)
|
if (throttlems != m_throttlems)
|
||||||
{
|
{
|
||||||
m_throttlems = throttlems;
|
m_throttlems = throttlems;
|
||||||
m_chunksize = 4 * ((m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000);
|
m_chunksize = 2 * m_samplebytes * ((m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000);
|
||||||
m_throttleToggle = !m_throttleToggle;
|
m_throttleToggle = !m_throttleToggle;
|
||||||
setBuffer(m_chunksize);
|
setBuffers(m_chunksize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// read samples directly feeding the SampleFifo (no callback)
|
// read samples directly feeding the SampleFifo (no callback)
|
||||||
m_ifstream->read(reinterpret_cast<char*>(m_buf), m_chunksize);
|
m_ifstream->read(reinterpret_cast<char*>(m_fileBuf), m_chunksize);
|
||||||
|
|
||||||
if (m_ifstream->eof())
|
if (m_ifstream->eof())
|
||||||
{
|
{
|
||||||
m_sampleFifo->write(m_buf, m_ifstream->gcount());
|
writeToSampleFifo(m_fileBuf, (qint32) m_ifstream->gcount());
|
||||||
|
//m_sampleFifo->write(m_buf, m_ifstream->gcount());
|
||||||
// TODO: handle loop playback situation
|
// TODO: handle loop playback situation
|
||||||
m_ifstream->clear();
|
m_ifstream->clear();
|
||||||
m_ifstream->seekg(sizeof(FileRecord::Header), std::ios::beg);
|
m_ifstream->seekg(sizeof(FileRecord::Header), std::ios::beg);
|
||||||
@ -171,8 +197,55 @@ void FileSourceThread::tick()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_sampleFifo->write(m_buf, m_chunksize);
|
writeToSampleFifo(m_fileBuf, (qint32) m_chunksize);
|
||||||
m_samplesCount += m_chunksize / 4;
|
//m_sampleFifo->write(m_buf, m_chunksize);
|
||||||
|
m_samplesCount += m_chunksize / (2 * m_samplebytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSourceThread::writeToSampleFifo(const quint8* buf, qint32 nbBytes)
|
||||||
|
{
|
||||||
|
if (m_samplesize == 16)
|
||||||
|
{
|
||||||
|
if (SDR_RX_SAMP_SZ == 16)
|
||||||
|
{
|
||||||
|
m_sampleFifo->write(buf, nbBytes);
|
||||||
|
}
|
||||||
|
else if (SDR_RX_SAMP_SZ == 24)
|
||||||
|
{
|
||||||
|
FixReal *convertBuf = (FixReal *) m_convertBuf;
|
||||||
|
const int16_t *fileBuf = (int16_t *) buf;
|
||||||
|
int nbSamples = nbBytes / (2 * m_samplebytes);
|
||||||
|
|
||||||
|
for (int is = 0; is < nbSamples; is++)
|
||||||
|
{
|
||||||
|
convertBuf[2*is] = fileBuf[2*is] << 8;
|
||||||
|
convertBuf[2*is+1] = fileBuf[2*is+1] << 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sampleFifo->write((quint8*) convertBuf, nbSamples*sizeof(Sample));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m_samplesize == 24)
|
||||||
|
{
|
||||||
|
if (SDR_RX_SAMP_SZ == 24)
|
||||||
|
{
|
||||||
|
m_sampleFifo->write(buf, nbBytes);
|
||||||
|
}
|
||||||
|
else if (SDR_RX_SAMP_SZ == 16)
|
||||||
|
{
|
||||||
|
FixReal *convertBuf = (FixReal *) m_convertBuf;
|
||||||
|
const int32_t *fileBuf = (int32_t *) buf;
|
||||||
|
int nbSamples = nbBytes / (2 * m_samplebytes);
|
||||||
|
|
||||||
|
for (int is = 0; is < nbSamples; is++)
|
||||||
|
{
|
||||||
|
convertBuf[2*is] = fileBuf[2*is] >> 8;
|
||||||
|
convertBuf[2*is+1] = fileBuf[2*is+1] >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sampleFifo->write((quint8*) convertBuf, nbSamples*sizeof(Sample));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,8 +41,8 @@ public:
|
|||||||
|
|
||||||
void startWork();
|
void startWork();
|
||||||
void stopWork();
|
void stopWork();
|
||||||
void setSamplerate(int samplerate);
|
void setSampleRateAndSize(int samplerate, quint32 samplesize);
|
||||||
void setBuffer(std::size_t chunksize);
|
void setBuffers(std::size_t chunksize);
|
||||||
bool isRunning() const { return m_running; }
|
bool isRunning() const { return m_running; }
|
||||||
std::size_t getSamplesCount() const { return m_samplesCount; }
|
std::size_t getSamplesCount() const { return m_samplesCount; }
|
||||||
void setSamplesCount(int samplesCount) { m_samplesCount = samplesCount; }
|
void setSamplesCount(int samplesCount) { m_samplesCount = samplesCount; }
|
||||||
@ -55,20 +55,23 @@ private:
|
|||||||
bool m_running;
|
bool m_running;
|
||||||
|
|
||||||
std::ifstream* m_ifstream;
|
std::ifstream* m_ifstream;
|
||||||
quint8 *m_buf;
|
quint8 *m_fileBuf;
|
||||||
|
quint8 *m_convertBuf;
|
||||||
std::size_t m_bufsize;
|
std::size_t m_bufsize;
|
||||||
std::size_t m_chunksize;
|
std::size_t m_chunksize;
|
||||||
SampleSinkFifo* m_sampleFifo;
|
SampleSinkFifo* m_sampleFifo;
|
||||||
std::size_t m_samplesCount;
|
std::size_t m_samplesCount;
|
||||||
|
|
||||||
int m_samplerate;
|
int m_samplerate; //!< File I/Q stream original sample rate
|
||||||
|
quint32 m_samplesize; //!< File effective sample size in bits (I or Q). Ex: 16, 24.
|
||||||
|
quint32 m_samplebytes; //!< Number of bytes used to store a I or Q sample. Ex: 2. 4.
|
||||||
int m_throttlems;
|
int m_throttlems;
|
||||||
QElapsedTimer m_elapsedTimer;
|
QElapsedTimer m_elapsedTimer;
|
||||||
bool m_throttleToggle;
|
bool m_throttleToggle;
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
//void decimate1(SampleVector::iterator* it, const qint16* buf, qint32 len);
|
//void decimate1(SampleVector::iterator* it, const qint16* buf, qint32 len);
|
||||||
//void callback(const qint16* buf, qint32 len);
|
void writeToSampleFifo(const quint8* buf, qint32 nbBytes);
|
||||||
private slots:
|
private slots:
|
||||||
void tick();
|
void tick();
|
||||||
};
|
};
|
||||||
|
@ -122,15 +122,21 @@ void FileRecord::handleConfigure(const std::string& fileName)
|
|||||||
|
|
||||||
void FileRecord::writeHeader()
|
void FileRecord::writeHeader()
|
||||||
{
|
{
|
||||||
m_sampleFile.write((const char *) &m_sampleRate, sizeof(int));
|
m_sampleFile.write((const char *) &m_sampleRate, sizeof(qint32)); // 4 bytes
|
||||||
m_sampleFile.write((const char *) &m_centerFrequency, sizeof(quint64));
|
m_sampleFile.write((const char *) &m_centerFrequency, sizeof(quint64)); // 8 bytes
|
||||||
std::time_t ts = time(0);
|
std::time_t ts = time(0);
|
||||||
m_sampleFile.write((const char *) &ts, sizeof(std::time_t));
|
m_sampleFile.write((const char *) &ts, sizeof(std::time_t)); // 8 bytes
|
||||||
|
quint32 sampleSize = SDR_RX_SAMP_SZ;
|
||||||
|
m_sampleFile.write((const char *) &sampleSize, sizeof(int)); // 4 bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileRecord::readHeader(std::ifstream& sampleFile, Header& header)
|
void FileRecord::readHeader(std::ifstream& sampleFile, Header& header)
|
||||||
{
|
{
|
||||||
sampleFile.read((char *) &(header.sampleRate), sizeof(int));
|
sampleFile.read((char *) &(header.sampleRate), sizeof(qint32));
|
||||||
sampleFile.read((char *) &(header.centerFrequency), sizeof(quint64));
|
sampleFile.read((char *) &(header.centerFrequency), sizeof(quint64));
|
||||||
sampleFile.read((char *) &(header.startTimeStamp), sizeof(std::time_t));
|
sampleFile.read((char *) &(header.startTimeStamp), sizeof(std::time_t));
|
||||||
|
sampleFile.read((char *) &(header.sampleSize), sizeof(quint32));
|
||||||
|
if ((header.sampleSize != 16) || (header.sampleSize != 24)) { // assume 16 bits if garbage (old I/Q file)
|
||||||
|
header.sampleSize = 16;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,10 @@ public:
|
|||||||
|
|
||||||
struct Header
|
struct Header
|
||||||
{
|
{
|
||||||
int sampleRate;
|
qint32 sampleRate;
|
||||||
quint64 centerFrequency;
|
quint64 centerFrequency;
|
||||||
std::time_t startTimeStamp;
|
std::time_t startTimeStamp;
|
||||||
|
quint32 sampleSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
FileRecord();
|
FileRecord();
|
||||||
@ -39,7 +40,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_fileName;
|
std::string m_fileName;
|
||||||
int m_sampleRate;
|
qint32 m_sampleRate;
|
||||||
quint64 m_centerFrequency;
|
quint64 m_centerFrequency;
|
||||||
bool m_recordOn;
|
bool m_recordOn;
|
||||||
bool m_recordStart;
|
bool m_recordStart;
|
||||||
|
Loading…
Reference in New Issue
Block a user