mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-22 12:23:37 -05:00
0cf14dfcc9
Fixed buffer sizes are used. Rx use s 3456 x 1st downsample rate x 5 audio frames of buffer space. On Windows this means that each chunk (periodSize()) delivered from the audio stream is our initial DSP processing chunk size, thus matching audio buffer latency exactly with WSJT-X's own front end latency. This should result in optimal resilience to high system loads that might starve the soundcard ADC of buffers to fill and case dropped audio frames. For Tx a buffer sufficient for 1 s of audio is used at present, on Windows the period size will be set to 1/40 of that which gives reasonably low latency and plenty of resilience to high system loads that might starve the soundcard DAC of audio frames to render. Note that a 1 s buffer will make the "Pwr" slider slow to respond, we may have to reduce the Tx audio buffer size if this is seen as a problem.
127 lines
4.6 KiB
C++
127 lines
4.6 KiB
C++
#include "Detector.hpp"
|
|
#include <QDateTime>
|
|
#include <QtAlgorithms>
|
|
#include <QDebug>
|
|
#include <math.h>
|
|
#include "commons.h"
|
|
|
|
#include "moc_Detector.cpp"
|
|
|
|
extern "C" {
|
|
void fil4_(qint16*, qint32*, qint16*, qint32*);
|
|
}
|
|
|
|
extern dec_data_t dec_data;
|
|
|
|
Detector::Detector (unsigned frameRate, double periodLengthInSeconds,
|
|
unsigned downSampleFactor, QObject * parent)
|
|
: AudioDevice (parent)
|
|
, m_frameRate (frameRate)
|
|
, m_period (periodLengthInSeconds)
|
|
, m_downSampleFactor (downSampleFactor)
|
|
, m_samplesPerFFT {max_buffer_size}
|
|
, m_buffer ((downSampleFactor > 1) ?
|
|
new short [max_buffer_size * downSampleFactor] : nullptr)
|
|
, m_bufferPos (0)
|
|
{
|
|
(void)m_frameRate; // quell compiler warning
|
|
clear ();
|
|
}
|
|
|
|
void Detector::setBlockSize (unsigned n)
|
|
{
|
|
m_samplesPerFFT = n;
|
|
}
|
|
|
|
bool Detector::reset ()
|
|
{
|
|
clear ();
|
|
// don't call base call reset because it calls seek(0) which causes
|
|
// a warning
|
|
return isOpen ();
|
|
}
|
|
|
|
void Detector::clear ()
|
|
{
|
|
// set index to roughly where we are in time (1ms resolution)
|
|
// qint64 now (QDateTime::currentMSecsSinceEpoch ());
|
|
// unsigned msInPeriod ((now % 86400000LL) % (m_period * 1000));
|
|
// dec_data.params.kin = qMin ((msInPeriod * m_frameRate) / 1000, static_cast<unsigned> (sizeof (dec_data.d2) / sizeof (dec_data.d2[0])));
|
|
dec_data.params.kin = 0;
|
|
m_bufferPos = 0;
|
|
|
|
// fill buffer with zeros (G4WJS commented out because it might cause decoder hangs)
|
|
// qFill (dec_data.d2, dec_data.d2 + sizeof (dec_data.d2) / sizeof (dec_data.d2[0]), 0);
|
|
}
|
|
|
|
qint64 Detector::writeData (char const * data, qint64 maxSize)
|
|
{
|
|
//qDebug () << "Detector::writeData: size:" << maxSize;
|
|
static unsigned mstr0=999999;
|
|
qint64 ms0 = QDateTime::currentMSecsSinceEpoch() % 86400000;
|
|
unsigned mstr = ms0 % int(1000.0*m_period); // ms into the nominal Tx start time
|
|
if(mstr < mstr0) { //When mstr has wrapped around to 0, restart the buffer
|
|
dec_data.params.kin = 0;
|
|
m_bufferPos = 0;
|
|
}
|
|
mstr0=mstr;
|
|
|
|
// no torn frames
|
|
Q_ASSERT (!(maxSize % static_cast<qint64> (bytesPerFrame ())));
|
|
// these are in terms of input frames (not down sampled)
|
|
size_t framesAcceptable ((sizeof (dec_data.d2) /
|
|
sizeof (dec_data.d2[0]) - dec_data.params.kin) * m_downSampleFactor);
|
|
size_t framesAccepted (qMin (static_cast<size_t> (maxSize /
|
|
bytesPerFrame ()), framesAcceptable));
|
|
|
|
if (framesAccepted < static_cast<size_t> (maxSize / bytesPerFrame ())) {
|
|
qDebug () << "dropped " << maxSize / bytesPerFrame () - framesAccepted
|
|
<< " frames of data on the floor!"
|
|
<< dec_data.params.kin << mstr;
|
|
}
|
|
|
|
for (unsigned remaining = framesAccepted; remaining; ) {
|
|
size_t numFramesProcessed (qMin (m_samplesPerFFT *
|
|
m_downSampleFactor - m_bufferPos, remaining));
|
|
|
|
if(m_downSampleFactor > 1) {
|
|
store (&data[(framesAccepted - remaining) * bytesPerFrame ()],
|
|
numFramesProcessed, &m_buffer[m_bufferPos]);
|
|
m_bufferPos += numFramesProcessed;
|
|
|
|
if(m_bufferPos==m_samplesPerFFT*m_downSampleFactor) {
|
|
qint32 framesToProcess (m_samplesPerFFT * m_downSampleFactor);
|
|
qint32 framesAfterDownSample (m_samplesPerFFT);
|
|
if(m_downSampleFactor > 1 && dec_data.params.kin>=0 &&
|
|
dec_data.params.kin < (NTMAX*12000 - framesAfterDownSample)) {
|
|
fil4_(&m_buffer[0], &framesToProcess, &dec_data.d2[dec_data.params.kin],
|
|
&framesAfterDownSample);
|
|
dec_data.params.kin += framesAfterDownSample;
|
|
} else {
|
|
// qDebug() << "framesToProcess = " << framesToProcess;
|
|
// qDebug() << "dec_data.params.kin = " << dec_data.params.kin;
|
|
// qDebug() << "secondInPeriod = " << secondInPeriod();
|
|
// qDebug() << "framesAfterDownSample" << framesAfterDownSample;
|
|
}
|
|
Q_EMIT framesWritten (dec_data.params.kin);
|
|
m_bufferPos = 0;
|
|
}
|
|
|
|
} else {
|
|
store (&data[(framesAccepted - remaining) * bytesPerFrame ()],
|
|
numFramesProcessed, &dec_data.d2[dec_data.params.kin]);
|
|
m_bufferPos += numFramesProcessed;
|
|
dec_data.params.kin += numFramesProcessed;
|
|
if (m_bufferPos == static_cast<unsigned> (m_samplesPerFFT)) {
|
|
Q_EMIT framesWritten (dec_data.params.kin);
|
|
m_bufferPos = 0;
|
|
}
|
|
}
|
|
remaining -= numFramesProcessed;
|
|
}
|
|
|
|
// we drop any data past the end of the buffer on the floor until
|
|
// the next period starts
|
|
return maxSize;
|
|
}
|