2020-02-21 18:18:11 +02:00
|
|
|
// last time modified by Igor UA3DJY on 20200128
|
2020-02-21 11:05:07 +02:00
|
|
|
|
|
|
|
|
#include "Modulator.hpp"
|
|
|
|
|
#include <limits>
|
|
|
|
|
#include <qmath.h>
|
2020-10-20 14:16:01 +03:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
|
|
|
|
#include <QRandomGenerator>
|
|
|
|
|
#endif
|
2020-02-21 11:05:07 +02:00
|
|
|
#include <QDebug>
|
|
|
|
|
#include "mainwindow.h"
|
|
|
|
|
#include "soundout.h"
|
|
|
|
|
#include "commons.h"
|
|
|
|
|
|
|
|
|
|
#include "moc_Modulator.cpp"
|
|
|
|
|
|
|
|
|
|
extern float gran(); // Noise generator (for tests only)
|
|
|
|
|
|
2020-02-21 18:18:11 +02:00
|
|
|
//#define RAMP_INCREMENT 64 // MUST be an integral factor of 2^16
|
|
|
|
|
#define RAMP_INCREMENT 65536 // MUST be an integral factor of 2^16
|
2020-02-21 11:05:07 +02:00
|
|
|
|
|
|
|
|
#if defined (WSJT_SOFT_KEYING)
|
|
|
|
|
# define SOFT_KEYING WSJT_SOFT_KEYING
|
|
|
|
|
#else
|
|
|
|
|
# define SOFT_KEYING 1
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
double constexpr Modulator::m_twoPi;
|
|
|
|
|
|
2020-04-11 13:39:03 +03:00
|
|
|
Modulator::Modulator (unsigned frameRate, double periodLengthInSeconds, JTDXDateTime * jtdxtime, QObject * parent)
|
2020-02-21 11:05:07 +02:00
|
|
|
: AudioDevice {parent}
|
|
|
|
|
, m_quickClose {false}
|
|
|
|
|
, m_phi {0.0}
|
|
|
|
|
, m_toneSpacing {0.0}
|
|
|
|
|
, m_fSpread {0.0}
|
|
|
|
|
, m_period {periodLengthInSeconds}
|
|
|
|
|
, m_frameRate {frameRate}
|
|
|
|
|
, m_state {Idle}
|
|
|
|
|
, m_tuning {false}
|
|
|
|
|
, m_cwLevel {false}
|
|
|
|
|
, m_j0 {-1}
|
|
|
|
|
, m_toneFrequency0 {1500.0}
|
2020-04-11 13:39:03 +03:00
|
|
|
, m_jtdxtime {jtdxtime}
|
2020-04-21 19:52:35 +03:00
|
|
|
, debug_file_ {QDir(QStandardPaths::writableLocation (QStandardPaths::DataLocation)).absoluteFilePath ("jtdx_debug.txt").toStdString()}
|
2020-02-21 11:05:07 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
|
|
|
|
|
double frequency, double toneSpacing,
|
|
|
|
|
SoundOutput * stream, Channel channel,
|
|
|
|
|
bool synchronize, double dBSNR, double TRperiod)
|
|
|
|
|
{
|
2020-04-17 09:31:57 +03:00
|
|
|
QThread::currentThread()->setPriority(QThread::HighPriority);
|
2020-02-21 11:05:07 +02:00
|
|
|
Q_ASSERT (stream);
|
2020-04-21 19:52:35 +03:00
|
|
|
#if JTDX_DEBUG_TO_FILE
|
|
|
|
|
FILE * pFile = fopen (debug_file_.c_str(),"a");
|
2021-11-10 18:23:32 +02:00
|
|
|
fprintf (pFile,"\n");
|
2020-04-21 19:52:35 +03:00
|
|
|
fclose (pFile);
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-02-21 11:05:07 +02:00
|
|
|
// Time according to this computer which becomes our base time
|
2020-04-11 13:39:03 +03:00
|
|
|
qint64 ms0 = m_jtdxtime->currentMSecsSinceEpoch2() % 86400000;
|
2021-11-10 18:23:32 +02:00
|
|
|
// qDebug() << "ModStart" << m_jtdxtime->currentDateTimeUtc().toString("hh:mm:ss.sss");
|
2020-02-21 11:05:07 +02:00
|
|
|
unsigned mstr = ms0 % int(1000.0*m_period); // ms into the nominal Tx start time
|
|
|
|
|
if (m_state != Idle) stop ();
|
|
|
|
|
m_quickClose = false;
|
|
|
|
|
m_symbolsLength = symbolsLength;
|
|
|
|
|
m_isym0 = std::numeric_limits<unsigned>::max (); // big number
|
|
|
|
|
m_frequency0 = 0.;
|
|
|
|
|
m_phi = 0.;
|
|
|
|
|
m_addNoise = dBSNR < 0.;
|
|
|
|
|
m_nsps = framesPerSymbol;
|
|
|
|
|
m_frequency = frequency;
|
2020-02-21 18:18:11 +02:00
|
|
|
m_amp = std::numeric_limits<qint32>::max ();
|
2020-02-21 11:05:07 +02:00
|
|
|
m_toneSpacing = toneSpacing;
|
|
|
|
|
m_TRperiod=TRperiod;
|
|
|
|
|
unsigned delay_ms=1000;
|
|
|
|
|
if(m_nsps==1920) delay_ms=500; //FT8
|
2020-04-17 09:31:57 +03:00
|
|
|
else if(m_nsps==576) {
|
|
|
|
|
delay_ms=500; //FT4
|
|
|
|
|
}
|
2020-02-21 11:05:07 +02:00
|
|
|
// noise generator parameters
|
|
|
|
|
if (m_addNoise) {
|
|
|
|
|
m_snr = qPow (10.0, 0.05 * (dBSNR - 6.0));
|
|
|
|
|
m_fac = 3000.0;
|
|
|
|
|
if (m_snr > 1.0) m_fac = 3000.0 / m_snr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// round up to an exact portion of a second that allows for startup delays
|
2020-04-17 09:31:57 +03:00
|
|
|
//m_ic = (mstr / delay_ms) * m_frameRate * delay_ms / 1000;
|
|
|
|
|
auto mstr2 = mstr - delay_ms;
|
|
|
|
|
if (mstr <= delay_ms) {
|
|
|
|
|
m_ic = 0;
|
|
|
|
|
} else {
|
|
|
|
|
m_ic = mstr2 * (m_frameRate / 1000);
|
|
|
|
|
}
|
2020-02-21 11:05:07 +02:00
|
|
|
m_silentFrames = 0;
|
|
|
|
|
// calculate number of silent frames to send
|
2020-04-17 09:31:57 +03:00
|
|
|
if (m_ic == 0 && synchronize && !m_tuning) {
|
|
|
|
|
m_silentFrames = m_frameRate / (1000 / delay_ms) - (mstr * (m_frameRate / 1000));
|
2020-02-21 11:05:07 +02:00
|
|
|
}
|
2021-11-10 18:23:32 +02:00
|
|
|
// printf ("%s(%0.1f) Modulator startdelay_ms=%d m_frameRate=%d mstr=%d mstr2 = %d m_ic=%d m_silentFrames=%lld \n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),delay_ms,m_frameRate,mstr,mstr2,m_ic,m_silentFrames);
|
2020-04-21 19:52:35 +03:00
|
|
|
#if JTDX_DEBUG_TO_FILE
|
|
|
|
|
pFile = fopen (debug_file_.c_str(),"a");
|
2021-11-10 18:23:32 +02:00
|
|
|
fprintf (pFile,"%s(%0.1f) Modulator startdelay_ms=%d m_frameRate=%d mstr=%d mstr2 = %d m_ic=%d m_silentFrames=%lld \n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),delay_ms,m_frameRate,mstr,mstr2,m_ic,m_silentFrames);
|
2020-04-21 19:52:35 +03:00
|
|
|
fclose (pFile);
|
|
|
|
|
#endif
|
2020-02-21 11:05:07 +02:00
|
|
|
initialize (QIODevice::ReadOnly, channel);
|
|
|
|
|
Q_EMIT stateChanged ((m_state = (synchronize && m_silentFrames) ?
|
|
|
|
|
Synchronizing : Active));
|
|
|
|
|
m_stream = stream;
|
|
|
|
|
if (m_stream) m_stream->restart (this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Modulator::tune (bool newState)
|
|
|
|
|
{
|
2021-11-10 18:23:32 +02:00
|
|
|
// printf("%s(%0.1f) Modulator tune %d ->%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),m_tuning,newState);
|
|
|
|
|
#if JTDX_DEBUG_TO_FILE
|
|
|
|
|
FILE * pFile = fopen (debug_file_.c_str(),"a");
|
|
|
|
|
fprintf (pFile,"%s(%0.1f) Modulator tune %d ->%d\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),m_tuning,newState);
|
|
|
|
|
fclose (pFile);
|
|
|
|
|
#endif
|
2020-02-21 11:05:07 +02:00
|
|
|
m_tuning = newState;
|
|
|
|
|
if (!m_tuning) stop (true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Modulator::stop (bool quick)
|
|
|
|
|
{
|
|
|
|
|
m_quickClose = quick;
|
|
|
|
|
close ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Modulator::close ()
|
|
|
|
|
{
|
|
|
|
|
if(m_stream) {
|
|
|
|
|
if(m_quickClose) { m_stream->reset (); }
|
|
|
|
|
else { m_stream->stop (); }
|
|
|
|
|
}
|
|
|
|
|
if(m_state != Idle) Q_EMIT stateChanged ((m_state = Idle));
|
|
|
|
|
AudioDevice::close ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qint64 Modulator::readData (char * data, qint64 maxSize)
|
|
|
|
|
{
|
|
|
|
|
double toneFrequency=1500.0;
|
|
|
|
|
if(m_nsps==6) {
|
|
|
|
|
toneFrequency=1000.0;
|
|
|
|
|
m_frequency=1000.0;
|
|
|
|
|
m_frequency0=1000.0;
|
|
|
|
|
}
|
|
|
|
|
if(maxSize==0) return 0;
|
|
|
|
|
Q_ASSERT (!(maxSize % qint64 (bytesPerFrame ()))); // no torn frames
|
|
|
|
|
Q_ASSERT (isOpen ());
|
|
|
|
|
|
|
|
|
|
qint64 numFrames (maxSize / bytesPerFrame ());
|
2020-02-21 18:18:11 +02:00
|
|
|
qint32 * samples (reinterpret_cast<qint32 *> (data));
|
|
|
|
|
qint32 * end (samples + numFrames * (bytesPerFrame () / sizeof (qint32)));
|
2020-02-21 11:05:07 +02:00
|
|
|
qint64 framesGenerated (0);
|
|
|
|
|
|
|
|
|
|
switch (m_state)
|
|
|
|
|
{
|
|
|
|
|
case Synchronizing:
|
|
|
|
|
{
|
|
|
|
|
if (m_silentFrames) { // send silence up to first second
|
|
|
|
|
framesGenerated = qMin (m_silentFrames, numFrames);
|
|
|
|
|
for ( ; samples != end; samples = load (0, samples)) { // silence
|
|
|
|
|
}
|
|
|
|
|
m_silentFrames -= framesGenerated;
|
|
|
|
|
return framesGenerated * bytesPerFrame ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Q_EMIT stateChanged ((m_state = Active));
|
|
|
|
|
m_cwLevel = false;
|
|
|
|
|
m_ramp = 0; // prepare for CW wave shaping
|
|
|
|
|
}
|
|
|
|
|
// fall through
|
|
|
|
|
|
|
|
|
|
case Active:
|
|
|
|
|
{
|
|
|
|
|
unsigned int isym=0;
|
2020-02-21 18:18:11 +02:00
|
|
|
qint32 sample=0;
|
2020-02-21 11:05:07 +02:00
|
|
|
if(!m_tuning) isym=m_ic/(4.0*m_nsps); // Actual fsample=48000
|
|
|
|
|
bool slowCwId=((isym >= m_symbolsLength) && (icw[0] > 0));
|
|
|
|
|
m_nspd=2560; // 22.5 WPM
|
|
|
|
|
|
|
|
|
|
if(m_TRperiod > 16.0 && slowCwId) { // Transmit CW ID?
|
|
|
|
|
m_dphi = m_twoPi*m_frequency/m_frameRate;
|
|
|
|
|
unsigned ic0 = m_symbolsLength * 4 * m_nsps;
|
|
|
|
|
unsigned j(0);
|
|
|
|
|
|
|
|
|
|
while (samples != end) {
|
|
|
|
|
j = (m_ic - ic0)/m_nspd + 1; // symbol of this sample
|
|
|
|
|
bool level {bool (icw[j])};
|
|
|
|
|
m_phi += m_dphi;
|
|
|
|
|
if (m_phi > m_twoPi) m_phi -= m_twoPi;
|
|
|
|
|
sample=0;
|
2020-02-21 18:18:11 +02:00
|
|
|
float amp=2147483647.0;
|
2020-02-21 11:05:07 +02:00
|
|
|
float x=0.0;
|
|
|
|
|
if(m_ramp!=0) {
|
|
|
|
|
x=qSin(float(m_phi));
|
|
|
|
|
if(SOFT_KEYING) {
|
|
|
|
|
amp=qAbs(qint32(m_ramp));
|
2020-02-21 18:18:11 +02:00
|
|
|
if(amp>2147483647.0) amp=2147483647.0;
|
2020-02-21 11:05:07 +02:00
|
|
|
}
|
|
|
|
|
sample=round(amp*x);
|
|
|
|
|
}
|
|
|
|
|
if (int (j) <= icw[0] && j < NUM_CW_SYMBOLS) { // stop condition
|
|
|
|
|
samples = load (postProcessSample (sample), samples);
|
|
|
|
|
++framesGenerated;
|
|
|
|
|
++m_ic;
|
|
|
|
|
} else {
|
2021-11-18 16:13:15 +02:00
|
|
|
// printf("%s(%0.1f) Modulator Idle1 %lld frames generated\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),framesGenerated);
|
2021-11-10 18:23:32 +02:00
|
|
|
#if JTDX_DEBUG_TO_FILE
|
|
|
|
|
FILE * pFile = fopen (debug_file_.c_str(),"a");
|
|
|
|
|
fprintf (pFile,"%s(%0.1f) Modulator Idle1 %lld frames generated\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),framesGenerated);
|
|
|
|
|
fclose (pFile);
|
|
|
|
|
#endif
|
2020-02-21 11:05:07 +02:00
|
|
|
Q_EMIT stateChanged ((m_state = Idle));
|
|
|
|
|
return framesGenerated * bytesPerFrame ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// adjust ramp
|
2020-02-21 18:18:11 +02:00
|
|
|
if ((m_ramp != 0 && m_ramp != std::numeric_limits<qint32>::min ()) || level != m_cwLevel) {
|
2020-02-21 11:05:07 +02:00
|
|
|
// either ramp has terminated at max/min or direction has changed
|
|
|
|
|
m_ramp += RAMP_INCREMENT; // ramp
|
|
|
|
|
}
|
|
|
|
|
m_cwLevel = level;
|
|
|
|
|
}
|
|
|
|
|
return framesGenerated * bytesPerFrame ();
|
|
|
|
|
} //End of code for CW ID
|
|
|
|
|
|
|
|
|
|
double const baud (12000.0 / m_nsps);
|
|
|
|
|
// fade out parameters (no fade out for tuning)
|
|
|
|
|
unsigned int i0,i1;
|
|
|
|
|
if(m_tuning) {
|
|
|
|
|
i1 = i0 = 9999 * m_nsps;
|
|
|
|
|
} else {
|
|
|
|
|
i0=(m_symbolsLength - 0.017) * 4.0 * m_nsps;
|
|
|
|
|
i1= m_symbolsLength * 4.0 * m_nsps;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sample=0;
|
|
|
|
|
for (unsigned i = 0; i < numFrames && m_ic <= i1; ++i) {
|
|
|
|
|
if(m_TRperiod > 16.0 || m_tuning) {
|
|
|
|
|
isym=0;
|
|
|
|
|
if(!m_tuning) isym=m_ic / (4.0 * m_nsps); //Actual fsample=48000
|
|
|
|
|
if (isym != m_isym0 || m_frequency != m_frequency0) {
|
|
|
|
|
if(itone[0]>=100) {
|
|
|
|
|
m_toneFrequency0=itone[0];
|
|
|
|
|
} else {
|
|
|
|
|
if(m_toneSpacing==0.0) {
|
|
|
|
|
m_toneFrequency0=m_frequency + itone[isym]*baud;
|
|
|
|
|
} else {
|
|
|
|
|
m_toneFrequency0=m_frequency + itone[isym]*m_toneSpacing;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// qDebug() << "B" << m_ic << numFrames << isym << itone[isym] << toneFrequency0 << m_nsps;
|
|
|
|
|
m_dphi = m_twoPi * m_toneFrequency0 / m_frameRate;
|
|
|
|
|
m_isym0 = isym;
|
|
|
|
|
m_frequency0 = m_frequency; //???
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int j=m_ic/480;
|
|
|
|
|
if(m_fSpread>0.0 and j!=m_j0) {
|
2020-10-20 14:16:01 +03:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
|
|
|
|
float x1=QRandomGenerator::global ()->generateDouble ();
|
|
|
|
|
float x2=QRandomGenerator::global ()->generateDouble ();
|
|
|
|
|
#else
|
|
|
|
|
float x1=(float)qrand()/RAND_MAX;
|
|
|
|
|
float x2=(float)qrand()/RAND_MAX;
|
|
|
|
|
#endif
|
2020-02-21 11:05:07 +02:00
|
|
|
toneFrequency = m_toneFrequency0 + 0.5*m_fSpread*(x1+x2-1.0);
|
|
|
|
|
m_dphi = m_twoPi * toneFrequency / m_frameRate;
|
|
|
|
|
m_j0=j;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_phi += m_dphi;
|
|
|
|
|
if (m_phi > m_twoPi) m_phi -= m_twoPi;
|
|
|
|
|
//ramp for first tone
|
|
|
|
|
if (m_ic==0) m_amp = m_amp * 0.008144735;
|
|
|
|
|
if (m_ic > 0 and m_ic < 191) m_amp = m_amp / 0.975;
|
|
|
|
|
//ramp for last tone
|
|
|
|
|
if (m_ic > i0) m_amp = 0.99 * m_amp;
|
|
|
|
|
if (m_ic > i1) m_amp = 0.0;
|
|
|
|
|
sample=qRound(m_amp*qSin(m_phi));
|
|
|
|
|
}
|
|
|
|
|
//transmit from a precomputed FT8 wave[] array:
|
2020-02-21 18:18:11 +02:00
|
|
|
if(!m_tuning and (m_toneSpacing < 0.0)) { m_amp=2147483647.0; sample=qRound(m_amp*foxcom_.wave[m_ic]); }
|
2020-02-21 11:05:07 +02:00
|
|
|
samples = load (postProcessSample (sample), samples);
|
|
|
|
|
++framesGenerated; ++m_ic;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_amp == 0.0) { // TODO G4WJS: compare double with zero might not be wise
|
|
|
|
|
if (icw[0] == 0) {
|
|
|
|
|
// no CW ID to send
|
2021-11-18 16:13:15 +02:00
|
|
|
// printf("%s(%0.1f) Modulator Idle2 %lld frames generated\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),framesGenerated);
|
2021-11-10 18:23:32 +02:00
|
|
|
#if JTDX_DEBUG_TO_FILE
|
|
|
|
|
FILE * pFile = fopen (debug_file_.c_str(),"a");
|
|
|
|
|
fprintf (pFile,"%s(%0.1f) Modulator Idle2 %lld frames generated\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),framesGenerated);
|
|
|
|
|
fclose (pFile);
|
|
|
|
|
#endif
|
2020-02-21 11:05:07 +02:00
|
|
|
Q_EMIT stateChanged ((m_state = Idle));
|
|
|
|
|
return framesGenerated * bytesPerFrame ();
|
|
|
|
|
}
|
|
|
|
|
m_phi = 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_frequency0 = m_frequency;
|
|
|
|
|
// done for this chunk - continue on next call
|
2021-11-10 18:23:32 +02:00
|
|
|
if (samples != end && framesGenerated) {
|
2021-11-18 16:13:15 +02:00
|
|
|
// printf("%s(%0.1f) Modulator Idle3 %lld frames generated\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),framesGenerated);
|
2021-11-10 18:23:32 +02:00
|
|
|
#if JTDX_DEBUG_TO_FILE
|
|
|
|
|
FILE * pFile = fopen (debug_file_.c_str(),"a");
|
|
|
|
|
fprintf (pFile,"%s(%0.1f) Modulator Idle3 %lld frames generated\n",m_jtdxtime->currentDateTimeUtc2().toString("hh:mm:ss.zzz").toStdString().c_str(),m_jtdxtime->GetOffset(),framesGenerated);
|
|
|
|
|
fclose (pFile);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2020-02-21 11:05:07 +02:00
|
|
|
while (samples != end) { // pad block with silence
|
|
|
|
|
samples = load (0, samples);
|
|
|
|
|
++framesGenerated;
|
|
|
|
|
}
|
|
|
|
|
return framesGenerated * bytesPerFrame ();
|
|
|
|
|
}
|
|
|
|
|
// fall through
|
|
|
|
|
|
|
|
|
|
case Idle:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Q_ASSERT (Idle == m_state);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-21 18:18:11 +02:00
|
|
|
qint32 Modulator::postProcessSample (qint32 sample) const
|
2020-02-21 11:05:07 +02:00
|
|
|
{
|
|
|
|
|
if (m_addNoise) { // Test frame, we'll add noise
|
|
|
|
|
qint32 s = m_fac * (gran () + sample * m_snr / 32768.0);
|
2020-02-21 18:18:11 +02:00
|
|
|
if (s > std::numeric_limits<qint32>::max ()) {
|
|
|
|
|
s = std::numeric_limits<qint32>::max ();
|
2020-02-21 11:05:07 +02:00
|
|
|
}
|
2020-02-21 18:18:11 +02:00
|
|
|
if (s < std::numeric_limits<qint32>::min ()) {
|
|
|
|
|
s = std::numeric_limits<qint32>::min ();
|
2020-02-21 11:05:07 +02:00
|
|
|
}
|
|
|
|
|
sample = s;
|
|
|
|
|
}
|
|
|
|
|
return sample;
|
|
|
|
|
}
|