mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-25 13:48:42 -05:00
Qt 5 Audio replaces PortAudio.
Currently only Qt5 or above is known to work with this code. It may be possible to backport it to Qt4 if required. Audio output goes back to a separate thread to try and minimize stutters in streaming on Windows particularly. A crash on Linux due to mishandling of stereo audio output has been fixed and both left and right channels are now correctly synthesised with identical contents. Rigs are enumerated directly from hamlib API rather than running a sub process reading output of rigctl -l. This was initially done to get rid of some GUI thread blocking in the configuration dialog, but is generally a better way of doing it anyway. Some refactoring in MainWindow to accomodate the audio streaming, modulation and detecting classes. Exit handling for application refactored to use signals rather than brute force event loop exit. This was required to get correct thread shutdown semantics. The GUI update timer is now stopped during application shutdown which is necessary to stop crashes when shutting down gracefully with signals and window close() calls. There is an outstanding issue with Linux audio streams; the QAudio Input/Output classes create a new stream name each time a stream is started. This doesn't play well with PulseAudio utilities such as pavucontrol to set stream volume as settings are lost every tx period. I have tried to keep a single stream for all output but there are problems restarting it that haven't been resolved yet. The QtCreator project file has been rearranged a little because it passes all the object files to the linker rather than using an archive library. Since the GNU linker is single pass; the object files need to be in a logical order with definitions appearing afer references to them. This was required to avoid a linking error. The lib/Makefile.linux has been enhanced to use the fortran compiler to locate the correct version of the Fortran library to use. This is necessary on the latest Linux distros because the unversioned symlink to compiler support libraries is no longer provided. This only an issue with mixed programming language links where the linker driver for one language has to link support libraraies for another language. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@3532 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
parent
fd0c056861
commit
ef586e4b58
188
Detector.cpp
188
Detector.cpp
@ -1,93 +1,95 @@
|
|||||||
#include "Detector.hpp"
|
#include "Detector.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <QDateTime>
|
||||||
|
#include <QtAlgorithms>
|
||||||
#include <QDateTime>
|
#include <QDebug>
|
||||||
#include <QDebug>
|
|
||||||
|
#include "commons.h"
|
||||||
#include "commons.h"
|
|
||||||
|
Detector::Detector (unsigned frameRate, unsigned periodLengthInSeconds, unsigned bytesPerSignal, QObject * parent)
|
||||||
Detector::Detector (unsigned frameRate, unsigned periodLengthInSeconds, unsigned bytesPerSignal, QObject * parent)
|
: QIODevice (parent)
|
||||||
: QIODevice (parent)
|
, m_frameRate (frameRate)
|
||||||
, m_frameRate (frameRate)
|
, m_period (periodLengthInSeconds)
|
||||||
, m_period (periodLengthInSeconds)
|
, m_bytesPerSignal (bytesPerSignal)
|
||||||
, m_bytesPerSignal (bytesPerSignal)
|
, m_monitoring (false)
|
||||||
, m_monitoring (false)
|
, m_starting (false)
|
||||||
, m_starting (false)
|
{
|
||||||
{
|
clear ();
|
||||||
clear ();
|
}
|
||||||
}
|
|
||||||
|
bool Detector::reset ()
|
||||||
bool Detector::reset ()
|
{
|
||||||
{
|
clear ();
|
||||||
clear ();
|
return QIODevice::reset ();
|
||||||
return QIODevice::reset ();
|
}
|
||||||
}
|
|
||||||
|
void Detector::clear ()
|
||||||
void Detector::clear ()
|
{
|
||||||
{
|
// set index to roughly where we are in time (1ms resolution)
|
||||||
// set index to roughly where we are in time (1s resolution)
|
// qint64 now (QDateTime::currentMSecsSinceEpoch ());
|
||||||
jt9com_.kin = secondInPeriod () * m_frameRate;
|
// unsigned msInPeriod ((now % 86400000LL) % (m_period * 1000));
|
||||||
|
// jt9com_.kin = qMin ((msInPeriod * m_frameRate) / 1000, static_cast<unsigned> (sizeof (jt9com_.d2) / sizeof (jt9com_.d2[0])));
|
||||||
// fill buffer with zeros
|
jt9com_.kin = 0;
|
||||||
std::fill (jt9com_.d2, jt9com_.d2 + sizeof (jt9com_.d2) / sizeof (jt9com_.d2[0]), 0);
|
|
||||||
}
|
// fill buffer with zeros
|
||||||
|
qFill (jt9com_.d2, jt9com_.d2 + sizeof (jt9com_.d2) / sizeof (jt9com_.d2[0]), 0);
|
||||||
qint64 Detector::writeData (char const * data, qint64 maxSize)
|
}
|
||||||
{
|
|
||||||
Q_ASSERT (!(maxSize % sizeof (jt9com_.d2[0]))); // no torn frames
|
qint64 Detector::writeData (char const * data, qint64 maxSize)
|
||||||
Q_ASSERT (!(reinterpret_cast<size_t> (data) % __alignof__ (frame_t))); // data is aligned as frame_t would be
|
{
|
||||||
|
Q_ASSERT (!(maxSize % static_cast<qint64> (sizeof (frame_t)))); // no torn frames
|
||||||
frame_t const * frames (reinterpret_cast<frame_t const *> (data));
|
Q_ASSERT (!(reinterpret_cast<size_t> (data) % __alignof__ (frame_t))); // data is aligned as frame_t would be
|
||||||
|
|
||||||
qint64 framesAcceptable (sizeof (jt9com_.d2) / sizeof (jt9com_.d2[0]) - jt9com_.kin);
|
frame_t const * frames (reinterpret_cast<frame_t const *> (data));
|
||||||
qint64 framesAccepted (std::min (maxSize / sizeof (jt9com_.d2[0]), framesAcceptable));
|
|
||||||
|
qint64 framesAcceptable (sizeof (jt9com_.d2) / sizeof (jt9com_.d2[0]) - jt9com_.kin);
|
||||||
if (framesAccepted < maxSize / sizeof (jt9com_.d2[0]))
|
qint64 framesAccepted (qMin (static_cast<qint64> (maxSize / sizeof (jt9com_.d2[0])), framesAcceptable));
|
||||||
{
|
|
||||||
qDebug () << "dropped " << maxSize / sizeof (jt9com_.d2[0]) - framesAccepted << " frames of data on the floor!\n";
|
if (framesAccepted < static_cast<qint64> (maxSize / sizeof (jt9com_.d2[0])))
|
||||||
}
|
{
|
||||||
|
qDebug () << "dropped " << maxSize / sizeof (jt9com_.d2[0]) - framesAccepted << " frames of data on the floor!";
|
||||||
std::copy (frames, frames + framesAccepted, &jt9com_.d2[jt9com_.kin]);
|
}
|
||||||
|
|
||||||
unsigned lastSignalIndex (jt9com_.kin * sizeof (jt9com_.d2[0]) / m_bytesPerSignal);
|
qCopy (frames, frames + framesAccepted, &jt9com_.d2[jt9com_.kin]);
|
||||||
jt9com_.kin += framesAccepted;
|
|
||||||
unsigned currentSignalIndex (jt9com_.kin * sizeof (jt9com_.d2[0]) / m_bytesPerSignal);
|
unsigned lastSignalIndex (jt9com_.kin * sizeof (jt9com_.d2[0]) / m_bytesPerSignal);
|
||||||
|
jt9com_.kin += framesAccepted;
|
||||||
if (currentSignalIndex != lastSignalIndex && m_monitoring)
|
unsigned currentSignalIndex (jt9com_.kin * sizeof (jt9com_.d2[0]) / m_bytesPerSignal);
|
||||||
{
|
|
||||||
Q_EMIT bytesWritten (currentSignalIndex * m_bytesPerSignal);
|
if (currentSignalIndex != lastSignalIndex && m_monitoring)
|
||||||
}
|
{
|
||||||
|
Q_EMIT bytesWritten (currentSignalIndex * m_bytesPerSignal);
|
||||||
if (!secondInPeriod ())
|
}
|
||||||
{
|
|
||||||
if (!m_starting)
|
if (!secondInPeriod ())
|
||||||
{
|
{
|
||||||
// next samples will be in new period so wrap around to
|
if (!m_starting)
|
||||||
// start of buffer
|
{
|
||||||
//
|
// next samples will be in new period so wrap around to
|
||||||
// we don't bother calling reset () since we expect to fill
|
// start of buffer
|
||||||
// the whole buffer and don't need to waste cycles zeroing
|
//
|
||||||
jt9com_.kin = 0;
|
// we don't bother calling reset () since we expect to fill
|
||||||
m_starting = true;
|
// the whole buffer and don't need to waste cycles zeroing
|
||||||
}
|
jt9com_.kin = 0;
|
||||||
}
|
m_starting = true;
|
||||||
else if (m_starting)
|
}
|
||||||
{
|
}
|
||||||
m_starting = false;
|
else if (m_starting)
|
||||||
}
|
{
|
||||||
|
m_starting = false;
|
||||||
return maxSize; // we drop any data past the end of
|
}
|
||||||
// the buffer on the floor until the
|
|
||||||
// next period starts
|
return maxSize; // we drop any data past the end of
|
||||||
}
|
// the buffer on the floor until the
|
||||||
|
// next period starts
|
||||||
unsigned Detector::secondInPeriod () const
|
}
|
||||||
{
|
|
||||||
// we take the time of the data as the following assuming no latency
|
unsigned Detector::secondInPeriod () const
|
||||||
// delivering it to us (not true but close enough for us)
|
{
|
||||||
qint64 now (QDateTime::currentMSecsSinceEpoch ());
|
// we take the time of the data as the following assuming no latency
|
||||||
|
// delivering it to us (not true but close enough for us)
|
||||||
unsigned secondInToday ((now % 86400000LL) / 1000);
|
qint64 now (QDateTime::currentMSecsSinceEpoch ());
|
||||||
return secondInToday % m_period;
|
|
||||||
}
|
unsigned secondInToday ((now % 86400000LL) / 1000);
|
||||||
|
return secondInToday % m_period;
|
||||||
|
}
|
||||||
|
134
Detector.hpp
134
Detector.hpp
@ -1,67 +1,67 @@
|
|||||||
#ifndef DETECTOR_HPP__
|
#ifndef DETECTOR_HPP__
|
||||||
#define DETECTOR_HPP__
|
#define DETECTOR_HPP__
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
|
|
||||||
//
|
//
|
||||||
// output device that distributes data in predefined chunks via a signal
|
// output device that distributes data in predefined chunks via a signal
|
||||||
//
|
//
|
||||||
// the underlying device for this abstraction is just the buffer that
|
// the underlying device for this abstraction is just the buffer that
|
||||||
// stores samples throughout a receiving period
|
// stores samples throughout a receiving period
|
||||||
//
|
//
|
||||||
class Detector : public QIODevice
|
class Detector : public QIODevice
|
||||||
{
|
{
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
|
|
||||||
Q_PROPERTY (bool monitoring READ isMonitoring WRITE setMonitoring);
|
Q_PROPERTY (bool monitoring READ isMonitoring WRITE setMonitoring);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_DISABLE_COPY (Detector);
|
Q_DISABLE_COPY (Detector);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//
|
//
|
||||||
// if the data buffer were not global storage and fixed size then we
|
// if the data buffer were not global storage and fixed size then we
|
||||||
// might want maximum size passed as constructor arguments
|
// might want maximum size passed as constructor arguments
|
||||||
//
|
//
|
||||||
Detector (unsigned frameRate, unsigned periodLengthInSeconds, unsigned bytesPerSignal, QObject * parent = 0);
|
Detector (unsigned frameRate, unsigned periodLengthInSeconds, unsigned bytesPerSignal, QObject * parent = 0);
|
||||||
|
|
||||||
bool open ()
|
bool open ()
|
||||||
{
|
{
|
||||||
// we only support data consumption and want it as fast as possible
|
// we only support data consumption and want it as fast as possible
|
||||||
return QIODevice::open (QIODevice::WriteOnly | QIODevice::Unbuffered);
|
return QIODevice::open (QIODevice::WriteOnly | QIODevice::Unbuffered);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSequential () const
|
bool isSequential () const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isMonitoring () const {return m_monitoring;}
|
bool isMonitoring () const {return m_monitoring;}
|
||||||
void setMonitoring (bool newState) {m_monitoring = newState;}
|
void setMonitoring (bool newState) {m_monitoring = newState;}
|
||||||
|
|
||||||
bool reset ();
|
bool reset ();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
qint64 readData (char * /* data */, qint64 /* maxSize */)
|
qint64 readData (char * /* data */, qint64 /* maxSize */)
|
||||||
{
|
{
|
||||||
return -1; // we don't produce data
|
return -1; // we don't produce data
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 writeData (char const * data, qint64 maxSize);
|
qint64 writeData (char const * data, qint64 maxSize);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef int16_t frame_t;
|
typedef qint16 frame_t;
|
||||||
|
|
||||||
void clear (); // discard buffer contents
|
void clear (); // discard buffer contents
|
||||||
unsigned secondInPeriod () const;
|
unsigned secondInPeriod () const;
|
||||||
|
|
||||||
unsigned m_frameRate;
|
unsigned m_frameRate;
|
||||||
unsigned m_period;
|
unsigned m_period;
|
||||||
unsigned m_bytesPerSignal;
|
unsigned m_bytesPerSignal;
|
||||||
bool m_monitoring;
|
bool m_monitoring;
|
||||||
bool m_starting;
|
bool m_starting;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
457
Modulator.cpp
457
Modulator.cpp
@ -1,195 +1,262 @@
|
|||||||
#include "Modulator.hpp"
|
#include "Modulator.hpp"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <limits>
|
||||||
#include <cmath>
|
|
||||||
#include <algorithm>
|
#include <qmath.h>
|
||||||
#include <limits>
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
#include <QDateTime>
|
|
||||||
|
#include "mainwindow.h"
|
||||||
extern float gran(); // Noise generator (for tests only)
|
|
||||||
|
extern float gran(); // Noise generator (for tests only)
|
||||||
double const Modulator::m_twoPi = 2.0 * 3.141592653589793238462;
|
|
||||||
|
double const Modulator::m_twoPi = 2.0 * 3.141592653589793238462;
|
||||||
// float wpm=20.0;
|
|
||||||
// unsigned m_nspd=1.2*48000.0/wpm;
|
// float wpm=20.0;
|
||||||
// m_nspd=3072; //18.75 WPM
|
// unsigned m_nspd=1.2*48000.0/wpm;
|
||||||
unsigned const Modulator::m_nspd = 2048 + 512; // 22.5 WPM
|
// m_nspd=3072; //18.75 WPM
|
||||||
|
unsigned const Modulator::m_nspd = 2048 + 512; // 22.5 WPM
|
||||||
Modulator::Modulator (unsigned frameRate, unsigned periodLengthInSeconds, QObject * parent)
|
|
||||||
: QIODevice (parent)
|
Modulator::Modulator (unsigned frameRate, unsigned periodLengthInSeconds, QObject * parent)
|
||||||
, m_frameRate (frameRate)
|
: QIODevice (parent)
|
||||||
, m_period (periodLengthInSeconds)
|
, m_frameRate (frameRate)
|
||||||
, m_state (Idle)
|
, m_period (periodLengthInSeconds)
|
||||||
, m_phi (0.)
|
, m_framesSent (0)
|
||||||
, m_ic (0)
|
, m_state (Idle)
|
||||||
, m_isym0 (std::numeric_limits<unsigned>::max ()) // ensure we set up first symbol tone
|
, m_tuning (false)
|
||||||
{
|
, m_muted (false)
|
||||||
}
|
, m_phi (0.)
|
||||||
|
{
|
||||||
bool Modulator::open (std::vector<int> const * symbols, std::vector<int> const * cw, double framesPerSymbol, unsigned frequency, double dBSNR)
|
qsrand (QDateTime::currentMSecsSinceEpoch()); // Initialize random seed
|
||||||
{
|
}
|
||||||
m_symbols.reset (symbols); // take over ownership (cannot throw)
|
|
||||||
m_cw.reset (cw); // take over ownership (cannot throw)
|
void Modulator::send (unsigned symbolsLength, double framesPerSymbol, unsigned frequency, bool synchronize, double dBSNR)
|
||||||
m_addNoise = dBSNR < 0.;
|
{
|
||||||
m_nsps = framesPerSymbol;
|
// Time according to this computer which becomes our base time
|
||||||
m_frequency = frequency;
|
qint64 ms0 = QDateTime::currentMSecsSinceEpoch() % 86400000;
|
||||||
m_amp = std::numeric_limits<frame_t>::max ();
|
|
||||||
m_state = Idle;
|
m_symbolsLength = symbolsLength;
|
||||||
|
|
||||||
// noise generator parameters
|
m_framesSent = 0;
|
||||||
if (m_addNoise)
|
m_isym0 = std::numeric_limits<unsigned>::max (); // ensure we set up first symbol tone
|
||||||
{
|
m_addNoise = dBSNR < 0.;
|
||||||
m_snr = std::pow (10.0, 0.05 * (dBSNR - 6.0));
|
m_nsps = framesPerSymbol;
|
||||||
m_fac = 3000.0;
|
m_frequency = frequency;
|
||||||
if (m_snr > 1.0)
|
m_amp = std::numeric_limits<qint16>::max ();
|
||||||
{
|
|
||||||
m_fac = 3000.0 / m_snr;
|
// noise generator parameters
|
||||||
}
|
if (m_addNoise)
|
||||||
}
|
{
|
||||||
|
m_snr = qPow (10.0, 0.05 * (dBSNR - 6.0));
|
||||||
return QIODevice::open (QIODevice::ReadOnly);
|
m_fac = 3000.0;
|
||||||
}
|
if (m_snr > 1.0)
|
||||||
|
{
|
||||||
qint64 Modulator::readData (char * data, qint64 maxSize)
|
m_fac = 3000.0 / m_snr;
|
||||||
{
|
}
|
||||||
frame_t * frames (reinterpret_cast<frame_t *> (data));
|
}
|
||||||
unsigned numFrames (maxSize / sizeof (frame_t));
|
|
||||||
|
unsigned mstr = ms0 % (1000 * m_period); // ms in period
|
||||||
switch (m_state)
|
m_ic = (mstr / 1000) * m_frameRate; // we start exactly N seconds
|
||||||
{
|
// into period where N is the
|
||||||
case Idle:
|
// next whole second
|
||||||
{
|
|
||||||
// Time according to this computer
|
m_silentFrames = 0;
|
||||||
qint64 ms = QDateTime::currentMSecsSinceEpoch() % 86400000;
|
if (synchronize && !m_tuning) // calculate number of silent frames to send
|
||||||
unsigned mstr = ms % (1000 * m_period);
|
{
|
||||||
if (mstr < 1000) // send silence up to first second
|
m_silentFrames = m_ic + m_frameRate - (mstr * m_frameRate / 1000);
|
||||||
{
|
}
|
||||||
std::fill (frames, frames + numFrames, 0); // silence
|
|
||||||
return numFrames * sizeof (frame_t);
|
qDebug () << "Modulator: starting at " << m_ic / m_frameRate << " sec, sending " << m_silentFrames << " silent frames";
|
||||||
}
|
|
||||||
m_ic = (mstr - 1000) * 48;
|
Q_EMIT stateChanged ((m_state = (synchronize && m_silentFrames) ? Synchronizing : Active));
|
||||||
|
}
|
||||||
std::srand (mstr); // Initialize random seed
|
|
||||||
|
qint64 Modulator::readData (char * data, qint64 maxSize)
|
||||||
m_state = Active;
|
{
|
||||||
}
|
Q_ASSERT (!(maxSize % static_cast<qint64> (sizeof (frame_t)))); // no torn frames
|
||||||
// fall through
|
Q_ASSERT (!(reinterpret_cast<size_t> (data) % __alignof__ (frame_t))); // data is aligned as frame_t would be
|
||||||
|
|
||||||
case Active:
|
frame_t * frames (reinterpret_cast<frame_t *> (data));
|
||||||
{
|
qint64 numFrames (maxSize / sizeof (frame_t));
|
||||||
unsigned isym (m_tuning ? 0 : m_ic / (4.0 * m_nsps)); // Actual fsample=48000
|
|
||||||
|
qDebug () << "Modulator: " << numFrames << " requested, m_ic = " << m_ic << ", tune mode is " << m_tuning;
|
||||||
if (isym >= m_symbols->size () && (*m_cw)[0] > 0)
|
|
||||||
{
|
switch (m_state)
|
||||||
// Output the CW ID
|
{
|
||||||
m_dphi = m_twoPi * m_frequency / m_frameRate;
|
case Synchronizing:
|
||||||
|
{
|
||||||
unsigned const ic0 = m_symbols->size () * 4 * m_nsps;
|
if (m_silentFrames) // send silence up to first second
|
||||||
unsigned j (0);
|
{
|
||||||
for (unsigned i = 0; i < numFrames; ++i)
|
frame_t frame;
|
||||||
{
|
for (unsigned c = 0; c < NUM_CHANNELS; ++c)
|
||||||
m_phi += m_dphi;
|
{
|
||||||
if (m_phi > m_twoPi)
|
frame.channel[c] = 0; // silence
|
||||||
{
|
}
|
||||||
m_phi -= m_twoPi;
|
|
||||||
}
|
numFrames = qMin (m_silentFrames, numFrames);
|
||||||
frame_t frame = std::numeric_limits<frame_t>::max () * std::sin (m_phi);
|
qFill (frames, frames + numFrames, frame);
|
||||||
j = (m_ic - ic0) / m_nspd + 1;
|
m_silentFrames -= numFrames;
|
||||||
if (!(*m_cw)[j])
|
return numFrames * sizeof (frame_t);
|
||||||
{
|
}
|
||||||
frame = 0;
|
|
||||||
}
|
Q_EMIT stateChanged ((m_state = Active));
|
||||||
|
}
|
||||||
frame = postProcessFrame (frame);
|
// fall through
|
||||||
|
|
||||||
*frames++ = frame; //left
|
case Active:
|
||||||
++m_ic;
|
{
|
||||||
}
|
unsigned isym (m_tuning ? 0 : m_ic / (4.0 * m_nsps)); // Actual fsample=48000
|
||||||
if (j > static_cast<unsigned> ((*m_cw)[0]))
|
|
||||||
{
|
if (isym >= m_symbolsLength && icw[0] > 0) // start CW condition
|
||||||
m_state = Done;
|
{
|
||||||
}
|
// Output the CW ID
|
||||||
return numFrames * sizeof (frame_t);
|
m_dphi = m_twoPi * m_frequency / m_frameRate;
|
||||||
}
|
|
||||||
|
unsigned const ic0 = m_symbolsLength * 4 * m_nsps;
|
||||||
double const baud (12000.0 / m_nsps);
|
unsigned j (0);
|
||||||
|
qint64 framesGenerated (0);
|
||||||
// fade out parameters (no fade out for tuning)
|
for (unsigned i = 0; i < numFrames; ++i)
|
||||||
unsigned const i0 = m_tuning ? 999 * m_nsps : (m_symbols->size () - 0.017) * 4.0 * m_nsps;
|
{
|
||||||
unsigned const i1 = m_tuning ? 999 * m_nsps : m_symbols->size () * 4.0 * m_nsps;
|
m_phi += m_dphi;
|
||||||
|
if (m_phi > m_twoPi)
|
||||||
for (unsigned i = 0; i < numFrames; ++i)
|
{
|
||||||
{
|
m_phi -= m_twoPi;
|
||||||
isym = m_tuning ? 0 : m_ic / (4.0 * m_nsps); //Actual fsample=48000
|
}
|
||||||
if (isym != m_isym0)
|
|
||||||
{
|
frame_t frame;
|
||||||
double toneFrequency = m_frequency + (*m_symbols)[isym] * baud;
|
for (unsigned c = 0; c < NUM_CHANNELS; ++c)
|
||||||
m_dphi = m_twoPi * toneFrequency / m_frameRate;
|
{
|
||||||
m_isym0 = isym;
|
frame.channel[c] = std::numeric_limits<qint16>::max () * qSin (m_phi);
|
||||||
}
|
}
|
||||||
m_phi += m_dphi;
|
|
||||||
if (m_phi > m_twoPi)
|
j = (m_ic - ic0) / m_nspd + 1;
|
||||||
{
|
if (j < NUM_CW_SYMBOLS) // stop condition
|
||||||
m_phi -= m_twoPi;
|
{
|
||||||
}
|
if (!icw[j])
|
||||||
if (m_ic > i0)
|
{
|
||||||
{
|
for (unsigned c = 0; c < NUM_CHANNELS; ++c)
|
||||||
m_amp = 0.98 * m_amp;
|
{
|
||||||
}
|
frame.channel[c] = 0;
|
||||||
if (m_ic > i1)
|
}
|
||||||
{
|
}
|
||||||
m_amp = 0.0;
|
|
||||||
}
|
frame = postProcessFrame (frame);
|
||||||
frame_t frame (m_amp * std::sin (m_phi));
|
|
||||||
frame = postProcessFrame (frame);
|
*frames++ = frame;
|
||||||
*frames++ = frame; //left
|
++framesGenerated;
|
||||||
++m_ic;
|
|
||||||
}
|
++m_ic;
|
||||||
|
}
|
||||||
if (m_amp == 0.0) // TODO G4WJS: compare double with zero might not be wise
|
}
|
||||||
{
|
|
||||||
if ((*m_cw)[0] == 0)
|
if (j > static_cast<unsigned> (icw[0]))
|
||||||
{
|
{
|
||||||
// no CW ID to send
|
Q_EMIT stateChanged ((m_state = Idle));
|
||||||
m_state = Done;
|
}
|
||||||
return numFrames * sizeof (frame_t);
|
|
||||||
}
|
m_framesSent += framesGenerated;
|
||||||
|
return framesGenerated * sizeof (frame_t);
|
||||||
m_phi = 0.0;
|
}
|
||||||
}
|
|
||||||
|
double const baud (12000.0 / m_nsps);
|
||||||
// done for this chunk - continue on next call
|
|
||||||
return numFrames * sizeof (frame_t);
|
// fade out parameters (no fade out for tuning)
|
||||||
}
|
unsigned const i0 = m_tuning ? 999 * m_nsps : (m_symbolsLength - 0.017) * 4.0 * m_nsps;
|
||||||
|
unsigned const i1 = m_tuning ? 999 * m_nsps : m_symbolsLength * 4.0 * m_nsps;
|
||||||
case Done:
|
|
||||||
break;
|
for (unsigned i = 0; i < numFrames; ++i)
|
||||||
}
|
{
|
||||||
|
isym = m_tuning ? 0 : m_ic / (4.0 * m_nsps); //Actual fsample=48000
|
||||||
Q_ASSERT (m_state == Done);
|
if (isym != m_isym0)
|
||||||
return 0;
|
{
|
||||||
}
|
double toneFrequency = m_frequency + itone[isym] * baud;
|
||||||
|
m_dphi = m_twoPi * toneFrequency / m_frameRate;
|
||||||
Modulator::frame_t Modulator::postProcessFrame (frame_t frame) const
|
m_isym0 = isym;
|
||||||
{
|
}
|
||||||
if (m_muted) // silent frame
|
m_phi += m_dphi;
|
||||||
{
|
if (m_phi > m_twoPi)
|
||||||
return 0;
|
{
|
||||||
}
|
m_phi -= m_twoPi;
|
||||||
|
}
|
||||||
if (m_addNoise)
|
if (m_ic > i0)
|
||||||
{
|
{
|
||||||
int i4 = m_fac * (gran () + frame * m_snr / 32768.0);
|
m_amp = 0.98 * m_amp;
|
||||||
if (i4 > std::numeric_limits<frame_t>::max ())
|
}
|
||||||
{
|
if (m_ic > i1)
|
||||||
i4 = std::numeric_limits<frame_t>::max ();
|
{
|
||||||
}
|
m_amp = 0.0;
|
||||||
if (i4 < std::numeric_limits<frame_t>::min ())
|
}
|
||||||
{
|
|
||||||
i4 = std::numeric_limits<frame_t>::min ();
|
frame_t frame;
|
||||||
}
|
for (unsigned c = 0; c < NUM_CHANNELS; ++c)
|
||||||
frame = i4;
|
{
|
||||||
}
|
frame.channel[c] = m_amp * qSin (m_phi);
|
||||||
return frame;
|
}
|
||||||
}
|
|
||||||
|
frame = postProcessFrame (frame);
|
||||||
|
|
||||||
|
*frames++ = frame;
|
||||||
|
|
||||||
|
++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
|
||||||
|
Q_EMIT stateChanged ((m_state = Idle));
|
||||||
|
m_framesSent += numFrames;
|
||||||
|
return numFrames * sizeof (frame_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_phi = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// done for this chunk - continue on next call
|
||||||
|
m_framesSent += numFrames;
|
||||||
|
return numFrames * sizeof (frame_t);
|
||||||
|
}
|
||||||
|
Q_EMIT stateChanged ((m_state = Idle));
|
||||||
|
// fall through
|
||||||
|
|
||||||
|
case Idle:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT (Idle == m_state);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Modulator::frame_t Modulator::postProcessFrame (frame_t frame) const
|
||||||
|
{
|
||||||
|
if (m_muted) // silent frame
|
||||||
|
{
|
||||||
|
for (unsigned c = 0; c < NUM_CHANNELS; ++c)
|
||||||
|
{
|
||||||
|
frame.channel[c] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m_addNoise)
|
||||||
|
{
|
||||||
|
qint32 f[NUM_CHANNELS];
|
||||||
|
for (unsigned c = 0; c < NUM_CHANNELS; ++c)
|
||||||
|
{
|
||||||
|
f[c] = m_fac * (gran () + frame.channel[c] * m_snr / 32768.0);
|
||||||
|
if (f[c] > std::numeric_limits<qint16>::max ())
|
||||||
|
{
|
||||||
|
f[c] = std::numeric_limits<qint16>::max ();
|
||||||
|
}
|
||||||
|
if (f[c] < std::numeric_limits<qint16>::min ())
|
||||||
|
{
|
||||||
|
f[c] = std::numeric_limits<qint16>::min ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned c = 0; c < NUM_CHANNELS; ++c)
|
||||||
|
{
|
||||||
|
frame.channel[c] = f[c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
#ifndef MODULATOR_HPP__
|
#ifndef MODULATOR_HPP__
|
||||||
#define MODULATOR_HPP__
|
#define MODULATOR_HPP__
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QScopedPointer>
|
|
||||||
|
#ifdef UNIX
|
||||||
|
# define NUM_CHANNELS 2
|
||||||
|
#else
|
||||||
|
# define NUM_CHANNELS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
// Input device that generates PCM audio frames that encode a message
|
// Input device that generates PCM audio frames that encode a message
|
||||||
@ -27,6 +30,12 @@ private:
|
|||||||
public:
|
public:
|
||||||
Modulator (unsigned frameRate, unsigned periodLengthInSeconds, QObject * parent = 0);
|
Modulator (unsigned frameRate, unsigned periodLengthInSeconds, QObject * parent = 0);
|
||||||
|
|
||||||
|
bool open () {return QIODevice::open (QIODevice::ReadOnly | QIODevice::Unbuffered);}
|
||||||
|
|
||||||
|
Q_SLOT void send (unsigned symbolsLength, double framesPerSymbol, unsigned frequency, bool synchronize = true, double dBSNR = 99.);
|
||||||
|
|
||||||
|
Q_SLOT void stop () {Q_EMIT stateChanged ((m_state = Idle));}
|
||||||
|
|
||||||
bool isTuning () const {return m_tuning;}
|
bool isTuning () const {return m_tuning;}
|
||||||
Q_SLOT void tune (bool newState = true) {m_tuning = newState;}
|
Q_SLOT void tune (bool newState = true) {m_tuning = newState;}
|
||||||
|
|
||||||
@ -36,12 +45,11 @@ public:
|
|||||||
unsigned frequency () const {return m_frequency;}
|
unsigned frequency () const {return m_frequency;}
|
||||||
Q_SLOT void setFrequency (unsigned newFrequency) {m_frequency = newFrequency;}
|
Q_SLOT void setFrequency (unsigned newFrequency) {m_frequency = newFrequency;}
|
||||||
|
|
||||||
bool open (std::vector<int> const * symbols, std::vector<int> const * cw, double framesPerSymbol, unsigned frequency, double dBSNR = 99.);
|
enum ModulatorState {Synchronizing, Active, Idle};
|
||||||
|
Q_SIGNAL void stateChanged (ModulatorState);
|
||||||
|
bool isActive () const {return m_state != Idle;}
|
||||||
|
|
||||||
bool isSequential () const
|
bool isSequential () const {return true;}
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
qint64 readData (char * data, qint64 maxSize);
|
qint64 readData (char * data, qint64 maxSize);
|
||||||
@ -51,12 +59,14 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef short frame_t;
|
typedef struct
|
||||||
|
{
|
||||||
|
qint16 channel[NUM_CHANNELS];
|
||||||
|
} frame_t;
|
||||||
|
|
||||||
frame_t postProcessFrame (frame_t frame) const;
|
frame_t postProcessFrame (frame_t frame) const;
|
||||||
|
|
||||||
QScopedPointer<std::vector<int> const> m_symbols;
|
unsigned m_symbolsLength;
|
||||||
QScopedPointer<std::vector<int> const> m_cw;
|
|
||||||
|
|
||||||
static double const m_twoPi;
|
static double const m_twoPi;
|
||||||
static unsigned const m_nspd; // CW ID WPM factor
|
static unsigned const m_nspd; // CW ID WPM factor
|
||||||
@ -64,11 +74,13 @@ private:
|
|||||||
int m_frameRate;
|
int m_frameRate;
|
||||||
int m_period;
|
int m_period;
|
||||||
double m_nsps;
|
double m_nsps;
|
||||||
double m_frequency;
|
double volatile m_frequency;
|
||||||
double m_snr;
|
double m_snr;
|
||||||
enum {Idle, Active, Done} m_state;
|
qint64 m_silentFrames;
|
||||||
bool m_tuning;
|
qint64 m_framesSent;
|
||||||
bool m_muted;
|
ModulatorState volatile m_state;
|
||||||
|
bool volatile m_tuning;
|
||||||
|
bool volatile m_muted;
|
||||||
bool m_addNoise;
|
bool m_addNoise;
|
||||||
double m_phi;
|
double m_phi;
|
||||||
double m_dphi;
|
double m_dphi;
|
||||||
|
81
devsetup.cpp
81
devsetup.cpp
@ -1,8 +1,10 @@
|
|||||||
#include "devsetup.h"
|
#include "devsetup.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QAudioDeviceInfo>
|
#include <QAudioDeviceInfo>
|
||||||
#include <QAudioInput>
|
#include <QAudioInput>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
#define MAXDEVICES 100
|
#define MAXDEVICES 100
|
||||||
|
|
||||||
@ -40,7 +42,7 @@ void DevSetup::initDlg()
|
|||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
|
||||||
//
|
//
|
||||||
// loaad combo boxes with setup choices
|
// load combo boxes with setup choices
|
||||||
//
|
//
|
||||||
{
|
{
|
||||||
int currentIndex = -1;
|
int currentIndex = -1;
|
||||||
@ -78,16 +80,7 @@ void DevSetup::initDlg()
|
|||||||
ui.comboBoxSndOut->setCurrentIndex (currentIndex != -1 ? currentIndex : defaultIndex);
|
ui.comboBoxSndOut->setCurrentIndex (currentIndex != -1 ? currentIndex : defaultIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(&p4, SIGNAL(readyReadStandardOutput()),
|
enumerateRigs ();
|
||||||
this, SLOT(p4ReadFromStdout()));
|
|
||||||
connect(&p4, SIGNAL(readyReadStandardError()),
|
|
||||||
this, SLOT(p4ReadFromStderr()));
|
|
||||||
connect(&p4, SIGNAL(error(QProcess::ProcessError)),
|
|
||||||
this, SLOT(p4Error()));
|
|
||||||
p4.start("rigctl -l");
|
|
||||||
p4.waitForFinished(1000);
|
|
||||||
ui.rigComboBox->addItem(" 9998 Commander");
|
|
||||||
ui.rigComboBox->addItem(" 9999 Ham Radio Deluxe");
|
|
||||||
|
|
||||||
QPalette pal(ui.myCallEntry->palette());
|
QPalette pal(ui.myCallEntry->palette());
|
||||||
if(m_myCall=="") {
|
if(m_myCall=="") {
|
||||||
@ -111,7 +104,6 @@ void DevSetup::initDlg()
|
|||||||
|
|
||||||
enableWidgets();
|
enableWidgets();
|
||||||
|
|
||||||
ui.rigComboBox->setCurrentIndex(m_rigIndex);
|
|
||||||
ui.catPortComboBox->setCurrentIndex(m_catPortIndex);
|
ui.catPortComboBox->setCurrentIndex(m_catPortIndex);
|
||||||
ui.serialRateComboBox->setCurrentIndex(m_serialRateIndex);
|
ui.serialRateComboBox->setCurrentIndex(m_serialRateIndex);
|
||||||
ui.dataBitsComboBox->setCurrentIndex(m_dataBitsIndex);
|
ui.dataBitsComboBox->setCurrentIndex(m_dataBitsIndex);
|
||||||
@ -338,34 +330,6 @@ void DevSetup::reject()
|
|||||||
QDialog::reject();
|
QDialog::reject();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DevSetup::p4ReadFromStdout() //p4readFromStdout
|
|
||||||
{
|
|
||||||
while(p4.canReadLine()) {
|
|
||||||
QString t(p4.readLine());
|
|
||||||
QString t1,t2,t3;
|
|
||||||
if(t.mid(0,6)!=" Rig #") {
|
|
||||||
t1=t.mid(0,6);
|
|
||||||
t2=t.mid(8,22).trimmed();
|
|
||||||
t3=t.mid(31,23).trimmed();
|
|
||||||
t=t1 + " " + t2 + " " + t3;
|
|
||||||
ui.rigComboBox->addItem(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DevSetup::p4ReadFromStderr() //p4readFromStderr
|
|
||||||
{
|
|
||||||
QByteArray t=p4.readAllStandardError();
|
|
||||||
if(t.length()>0) {
|
|
||||||
msgBox(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DevSetup::p4Error() //p4rror
|
|
||||||
{
|
|
||||||
msgBox("Error running 'rigctl -l'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void DevSetup::msgBox(QString t) //msgBox
|
void DevSetup::msgBox(QString t) //msgBox
|
||||||
{
|
{
|
||||||
msgBox0.setText(t);
|
msgBox0.setText(t);
|
||||||
@ -465,9 +429,7 @@ void DevSetup::on_stopBitsComboBox_activated(int index)
|
|||||||
|
|
||||||
void DevSetup::on_rigComboBox_activated(int index)
|
void DevSetup::on_rigComboBox_activated(int index)
|
||||||
{
|
{
|
||||||
m_rigIndex=index;
|
m_rig = ui.rigComboBox->itemData (index).toInt ();
|
||||||
QString t=ui.rigComboBox->itemText(index);
|
|
||||||
m_rig=t.mid(0,7).toInt();
|
|
||||||
enableWidgets();
|
enableWidgets();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -653,3 +615,36 @@ void DevSetup::on_cbXIT_toggled(bool checked)
|
|||||||
m_bXIT=checked;
|
m_bXIT=checked;
|
||||||
if(m_bSplit and m_bXIT) ui.cbSplit->setChecked(false);
|
if(m_bSplit and m_bXIT) ui.cbSplit->setChecked(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef QMap<QString, int> RigList;
|
||||||
|
|
||||||
|
int rigCallback (rig_caps const * caps, void * cbData)
|
||||||
|
{
|
||||||
|
RigList * rigs = reinterpret_cast<RigList *> (cbData);
|
||||||
|
|
||||||
|
QString key (QString::fromLatin1 (caps->mfg_name).trimmed ()
|
||||||
|
+ ' '+ QString::fromLatin1 (caps->model_name).trimmed ()
|
||||||
|
// + ' '+ QString::fromLatin1 (caps->version).trimmed ()
|
||||||
|
// + " (" + QString::fromLatin1 (rig_strstatus (caps->status)).trimmed () + ')'
|
||||||
|
);
|
||||||
|
|
||||||
|
(*rigs)[key] = caps->rig_model;
|
||||||
|
|
||||||
|
return 1; // keep them coming
|
||||||
|
}
|
||||||
|
|
||||||
|
void DevSetup::enumerateRigs ()
|
||||||
|
{
|
||||||
|
RigList rigs;
|
||||||
|
rig_load_all_backends ();
|
||||||
|
rig_list_foreach (rigCallback, &rigs);
|
||||||
|
|
||||||
|
for (RigList::const_iterator r = rigs.cbegin (); r != rigs.cend (); ++r)
|
||||||
|
{
|
||||||
|
ui.rigComboBox->addItem (r.key (), r.value ());
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.rigComboBox->addItem ("DX Lab Suite Commander", 9998);
|
||||||
|
ui.rigComboBox->addItem ("Ham Radio Deluxe", 9999);
|
||||||
|
ui.rigComboBox->setCurrentIndex (ui.rigComboBox->findData (m_rig));
|
||||||
|
}
|
||||||
|
11
devsetup.h
11
devsetup.h
@ -8,8 +8,12 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QAudioDeviceInfo>
|
#include <QAudioDeviceInfo>
|
||||||
|
|
||||||
|
#include <hamlib/rig.h>
|
||||||
|
|
||||||
#include "rigclass.h"
|
#include "rigclass.h"
|
||||||
|
|
||||||
|
int rigCallback (rig_caps const *, void *);
|
||||||
|
|
||||||
class DevSetup : public QDialog
|
class DevSetup : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -67,15 +71,11 @@ public:
|
|||||||
QStringList m_antDescription; // per band antenna description
|
QStringList m_antDescription; // per band antenna description
|
||||||
QStringList m_bandDescription; // per band description
|
QStringList m_bandDescription; // per band description
|
||||||
|
|
||||||
QProcess p4;
|
|
||||||
QMessageBox msgBox0;
|
QMessageBox msgBox0;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void accept();
|
void accept();
|
||||||
void reject();
|
void reject();
|
||||||
void p4ReadFromStdout();
|
|
||||||
void p4ReadFromStderr();
|
|
||||||
void p4Error();
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_myCallEntry_editingFinished();
|
void on_myCallEntry_editingFinished();
|
||||||
@ -105,12 +105,15 @@ private slots:
|
|||||||
void on_cbXIT_toggled(bool checked);
|
void on_cbXIT_toggled(bool checked);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void enumerateRigs ();
|
||||||
Rig* rig;
|
Rig* rig;
|
||||||
void msgBox(QString t);
|
void msgBox(QString t);
|
||||||
void setEnableAntennaDescriptions(bool enable);
|
void setEnableAntennaDescriptions(bool enable);
|
||||||
void enableWidgets();
|
void enableWidgets();
|
||||||
void openRig();
|
void openRig();
|
||||||
Ui::DialogSndCard ui;
|
Ui::DialogSndCard ui;
|
||||||
|
|
||||||
|
friend int rigCallback (rig_caps const *, void *);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int ptt(int nport, int ntx, int* iptt, int* nopen);
|
extern int ptt(int nport, int ntx, int* iptt, int* nopen);
|
||||||
|
55
devsetup.ui
55
devsetup.ui
@ -6,14 +6,14 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>570</width>
|
<width>571</width>
|
||||||
<height>465</height>
|
<height>440</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>588</width>
|
<width>588</width>
|
||||||
<height>557</height>
|
<height>522</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeIncrement">
|
<property name="sizeIncrement">
|
||||||
@ -1136,51 +1136,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_13">
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer_15">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeType">
|
|
||||||
<enum>QSizePolicy::Fixed</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>96</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_26">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>90</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>16777215</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Dev Ch API Name</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||||
<item>
|
<item>
|
||||||
@ -1852,8 +1807,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>308</width>
|
<width>510</width>
|
||||||
<height>505</height>
|
<height>449</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_14">
|
<layout class="QHBoxLayout" name="horizontalLayout_14">
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
# Set paths
|
# Set paths
|
||||||
EXE_DIR = ../../wsjtx_install
|
EXE_DIR = ../../wsjtx_install
|
||||||
INCPATH = -I'/usr/include/qt4' -I'/usr/include/qt4/QtCore'
|
INCPATH = -I/usr/include/qt5 -I/usr/include/qt5/QtCore
|
||||||
|
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CXX = g++
|
CXX = g++
|
||||||
FC = gfortran
|
FC = gfortran
|
||||||
|
|
||||||
FFLAGS = -O2 -fbounds-check -Wall -Wno-conversion -fno-second-underscore
|
FFLAGS = -O2 -fbounds-check -Wall -Wno-conversion -fno-second-underscore -fPIE
|
||||||
CFLAGS = -I. -fbounds-check -mno-stack-arg-probe
|
CFLAGS = -I. -fbounds-check -mno-stack-arg-probe -fPIE
|
||||||
|
|
||||||
# Default rules
|
# Default rules
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
@ -49,17 +49,17 @@ libjt9.a: $(OBJS1)
|
|||||||
OBJS2 = jt9.o jt9a.o jt9b.o jt9c.o
|
OBJS2 = jt9.o jt9a.o jt9b.o jt9c.o
|
||||||
|
|
||||||
jt9: $(OBJS2) libjt9.a
|
jt9: $(OBJS2) libjt9.a
|
||||||
$(CXX) -o jt9 $(OBJS2) libjt9.a -lfftw3f -lgfortran -lQtCore
|
$(CXX) -o jt9 $(OBJS2) -L. -ljt9 -lQt5Core -lfftw3f `$(FC) -print-file-name=libgfortran.so`
|
||||||
mkdir -p $(EXE_DIR)
|
mkdir -p $(EXE_DIR)
|
||||||
cp jt9 $(EXE_DIR)
|
cp jt9 $(EXE_DIR)
|
||||||
|
|
||||||
OBJS3 = jt9sim.o
|
OBJS3 = jt9sim.o
|
||||||
jt9sim: $(OBJS3) libjt9.a
|
jt9sim: $(OBJS3) libjt9.a
|
||||||
$(FC) -o jt9sim $(OBJS3) libjt9.a
|
$(FC) -o jt9sim $(OBJS3) -L. -ljt9
|
||||||
|
|
||||||
OBJS4 = jt9code.o
|
OBJS4 = jt9code.o
|
||||||
jt9code: $(OBJS4) libjt9.a
|
jt9code: $(OBJS4) libjt9.a
|
||||||
$(FC) -o jt9code $(OBJS4) libjt9.a
|
$(FC) -o jt9code $(OBJS4) -L. -ljt9
|
||||||
|
|
||||||
sync9.o: sync9.f90 jt9sync.f90
|
sync9.o: sync9.f90 jt9sync.f90
|
||||||
$(FC) $(FFLAGS) -c sync9.f90
|
$(FC) $(FFLAGS) -c sync9.f90
|
||||||
@ -80,7 +80,7 @@ redsync.o: redsync.f90 jt9sync.f90
|
|||||||
$(FC) $(FFLAGS) -c redsync.f90
|
$(FC) $(FFLAGS) -c redsync.f90
|
||||||
|
|
||||||
ipcomm.o: ipcomm.cpp
|
ipcomm.o: ipcomm.cpp
|
||||||
$(CXX) -c $(INCPATH) ipcomm.cpp
|
$(CXX) -c $(INCPATH) -fPIE ipcomm.cpp
|
||||||
|
|
||||||
sec_midn.o: sec_midn.f90
|
sec_midn.o: sec_midn.f90
|
||||||
$(FC) -c -fno-second-underscore sec_midn.f90
|
$(FC) -c -fno-second-underscore sec_midn.f90
|
||||||
|
3
main.cpp
3
main.cpp
@ -4,6 +4,7 @@
|
|||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
#endif
|
#endif
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
|
||||||
@ -52,5 +53,7 @@ int main(int argc, char *argv[])
|
|||||||
// Multiple instances: Call MainWindow() with the UUID key
|
// Multiple instances: Call MainWindow() with the UUID key
|
||||||
MainWindow w(&mem_jt9, &my_key, fontSize2, fontWeight2);
|
MainWindow w(&mem_jt9, &my_key, fontSize2, fontWeight2);
|
||||||
w.show();
|
w.show();
|
||||||
|
|
||||||
|
QObject::connect (&a, SIGNAL (lastWindowClosed()), &a, SLOT (quit()));
|
||||||
return a.exec();
|
return a.exec();
|
||||||
}
|
}
|
||||||
|
121
mainwindow.cpp
121
mainwindow.cpp
@ -2,9 +2,10 @@
|
|||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "ui_mainwindow.h"
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <QThread>
|
||||||
#include <QScopedPointer>
|
|
||||||
#include <QColorDialog>
|
#include <QColorDialog>
|
||||||
|
|
||||||
|
#include "soundout.h"
|
||||||
#include "devsetup.h"
|
#include "devsetup.h"
|
||||||
#include "plotter.h"
|
#include "plotter.h"
|
||||||
#include "about.h"
|
#include "about.h"
|
||||||
@ -18,11 +19,6 @@
|
|||||||
#include <QtConcurrent/QtConcurrentRun>
|
#include <QtConcurrent/QtConcurrentRun>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define NUM_JT65_SYMBOLS 126
|
|
||||||
#define NUM_JT9_SYMBOLS 85
|
|
||||||
#define NUM_CW_SYMBOLS 250
|
|
||||||
#define TX_SAMPLE_RATE 48000
|
|
||||||
|
|
||||||
int itone[NUM_JT65_SYMBOLS]; //Audio tones for all Tx symbols
|
int itone[NUM_JT65_SYMBOLS]; //Audio tones for all Tx symbols
|
||||||
int icw[NUM_CW_SYMBOLS]; //Dits for CW ID
|
int icw[NUM_CW_SYMBOLS]; //Dits for CW ID
|
||||||
|
|
||||||
@ -51,13 +47,44 @@ MainWindow::MainWindow(QSharedMemory *shdmem, QString *thekey, \
|
|||||||
QWidget *parent) :
|
QWidget *parent) :
|
||||||
QMainWindow(parent),
|
QMainWindow(parent),
|
||||||
ui(new Ui::MainWindow),
|
ui(new Ui::MainWindow),
|
||||||
m_audioInputDevice (QAudioDeviceInfo::defaultInputDevice ()), // start with default
|
|
||||||
m_detector (RX_SAMPLE_RATE, NTMAX / 2, 6912 / 2 * sizeof (jt9com_.d2[0]), this),
|
m_detector (RX_SAMPLE_RATE, NTMAX / 2, 6912 / 2 * sizeof (jt9com_.d2[0]), this),
|
||||||
|
m_audioInputDevice (QAudioDeviceInfo::defaultInputDevice ()), // start with default
|
||||||
|
m_modulator (TX_SAMPLE_RATE, NTMAX / 2),
|
||||||
m_audioOutputDevice (QAudioDeviceInfo::defaultOutputDevice ()), // start with default
|
m_audioOutputDevice (QAudioDeviceInfo::defaultOutputDevice ()), // start with default
|
||||||
m_modulator (TX_SAMPLE_RATE, NTMAX / 2, this)
|
m_soundOutput (&m_modulator)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
m_detector.open ();
|
m_detector.open ();
|
||||||
|
m_modulator.open ();
|
||||||
|
|
||||||
|
connect (this, &MainWindow::finished, this, &MainWindow::close);
|
||||||
|
|
||||||
|
// start sound out thread and hook up slots & signals for shutdown management
|
||||||
|
m_soundOutput.moveToThread (&m_soundOutputThread);
|
||||||
|
connect (this, &MainWindow::finished, &m_soundOutputThread, &QThread::quit); // quit thread event loop
|
||||||
|
connect (&m_soundOutputThread, &QThread::finished, &m_soundOutputThread, &QThread::deleteLater); // disposal
|
||||||
|
|
||||||
|
// hook up sound output stream slots & signals
|
||||||
|
connect (this, &MainWindow::startAudioOutputStream, &m_soundOutput, &SoundOutput::startStream);
|
||||||
|
connect (this, &MainWindow::stopAudioOutputStream, &m_soundOutput, &SoundOutput::stopStream);
|
||||||
|
connect (&m_soundOutput, &SoundOutput::error, this, &MainWindow::showSoundOutError);
|
||||||
|
// connect (&m_soundOutput, &SoundOutput::status, this, &MainWindow::showStatusMessage);
|
||||||
|
|
||||||
|
// hook up Modulator slots
|
||||||
|
connect (this, &MainWindow::muteAudioOutput, &m_modulator, &Modulator::mute);
|
||||||
|
connect (this, &MainWindow::transmitFrequency, &m_modulator, &Modulator::setFrequency);
|
||||||
|
connect (this, &MainWindow::endTransmitMessage, &m_modulator, &Modulator::stop);
|
||||||
|
connect (this, &MainWindow::tune, &m_modulator, &Modulator::tune);
|
||||||
|
connect (
|
||||||
|
this
|
||||||
|
, SIGNAL (sendMessage (unsigned, double, unsigned, bool, double))
|
||||||
|
, &m_modulator
|
||||||
|
, SLOT (send (unsigned, double, unsigned, bool, double))
|
||||||
|
);
|
||||||
|
|
||||||
|
// start the sound output thread
|
||||||
|
m_soundOutputThread.start (QThread::HighPriority);
|
||||||
|
|
||||||
on_EraseButton_clicked();
|
on_EraseButton_clicked();
|
||||||
|
|
||||||
QActionGroup* modeGroup = new QActionGroup(this);
|
QActionGroup* modeGroup = new QActionGroup(this);
|
||||||
@ -93,11 +120,7 @@ MainWindow::MainWindow(QSharedMemory *shdmem, QString *thekey, \
|
|||||||
connect(&m_detector, &Detector::bytesWritten, this, &MainWindow::dataSink);
|
connect(&m_detector, &Detector::bytesWritten, this, &MainWindow::dataSink);
|
||||||
connect(&m_soundInput, SIGNAL(error(QString)), this,
|
connect(&m_soundInput, SIGNAL(error(QString)), this,
|
||||||
SLOT(showSoundInError(QString)));
|
SLOT(showSoundInError(QString)));
|
||||||
connect(&m_soundOutput, SIGNAL(error(QString)), this,
|
// connect(&m_soundInput, SIGNAL(status(QString)), this,
|
||||||
SLOT(showSoundOutError(QString)));
|
|
||||||
connect(&m_soundInput, SIGNAL(status(QString)), this,
|
|
||||||
SLOT(showStatusMessage(QString)));
|
|
||||||
// connect(&m_soundOutput, SIGNAL(status(QString)), this,
|
|
||||||
// SLOT(showStatusMessage(QString)));
|
// SLOT(showStatusMessage(QString)));
|
||||||
createStatusBar();
|
createStatusBar();
|
||||||
|
|
||||||
@ -135,9 +158,9 @@ MainWindow::MainWindow(QSharedMemory *shdmem, QString *thekey, \
|
|||||||
font.setWeight(75);
|
font.setWeight(75);
|
||||||
ui->readFreq->setFont(font);
|
ui->readFreq->setFont(font);
|
||||||
|
|
||||||
QTimer *guiTimer = new QTimer(this);
|
connect(&m_guiTimer, SIGNAL(timeout()), this, SLOT(guiUpdate()));
|
||||||
connect(guiTimer, SIGNAL(timeout()), this, SLOT(guiUpdate()));
|
m_guiTimer.start(100); //Don't change the 100 ms!
|
||||||
guiTimer->start(100); //Don't change the 100 ms!
|
|
||||||
ptt0Timer = new QTimer(this);
|
ptt0Timer = new QTimer(this);
|
||||||
ptt0Timer->setSingleShot(true);
|
ptt0Timer->setSingleShot(true);
|
||||||
connect(ptt0Timer, SIGNAL(timeout()), this, SLOT(stopTx2()));
|
connect(ptt0Timer, SIGNAL(timeout()), this, SLOT(stopTx2()));
|
||||||
@ -161,7 +184,7 @@ MainWindow::MainWindow(QSharedMemory *shdmem, QString *thekey, \
|
|||||||
m_auto=false;
|
m_auto=false;
|
||||||
m_waterfallAvg = 1;
|
m_waterfallAvg = 1;
|
||||||
m_txFirst=false;
|
m_txFirst=false;
|
||||||
m_modulator.mute(false);
|
Q_EMIT muteAudioOutput (false);
|
||||||
m_btxMute=false;
|
m_btxMute=false;
|
||||||
m_btxok=false;
|
m_btxok=false;
|
||||||
m_restart=false;
|
m_restart=false;
|
||||||
@ -317,8 +340,8 @@ MainWindow::MainWindow(QSharedMemory *shdmem, QString *thekey, \
|
|||||||
connect(watcher2, SIGNAL(finished()),this,SLOT(diskWriteFinished()));
|
connect(watcher2, SIGNAL(finished()),this,SLOT(diskWriteFinished()));
|
||||||
|
|
||||||
m_soundInput.start(m_audioInputDevice, RX_SAMPLE_RATE / 10, &m_detector);
|
m_soundInput.start(m_audioInputDevice, RX_SAMPLE_RATE / 10, &m_detector);
|
||||||
m_modulator.setFrequency(m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0));
|
Q_EMIT transmitFrequency (m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0));
|
||||||
m_modulator.tune(false);
|
Q_EMIT muteAudioOutput (false);
|
||||||
m_monitoring=!m_monitorStartOFF; // Start with Monitoring ON/OFF
|
m_monitoring=!m_monitorStartOFF; // Start with Monitoring ON/OFF
|
||||||
m_detector.setMonitoring(m_monitoring);
|
m_detector.setMonitoring(m_monitoring);
|
||||||
m_diskData=false;
|
m_diskData=false;
|
||||||
@ -366,13 +389,10 @@ MainWindow::MainWindow(QSharedMemory *shdmem, QString *thekey, \
|
|||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
writeSettings();
|
writeSettings();
|
||||||
m_soundOutput.stop();
|
|
||||||
m_modulator.close();
|
|
||||||
if(!m_decoderBusy) {
|
if(!m_decoderBusy) {
|
||||||
QFile lockFile(m_appDir + "/.lock");
|
QFile lockFile(m_appDir + "/.lock");
|
||||||
lockFile.remove();
|
lockFile.remove();
|
||||||
}
|
}
|
||||||
m_detector.close ();
|
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,7 +517,7 @@ void MainWindow::readSettings()
|
|||||||
//
|
//
|
||||||
// retrieve audio input device
|
// retrieve audio input device
|
||||||
//
|
//
|
||||||
QString savedName = settings.value( "SoundInName", "default").toString();
|
QString savedName = settings.value( "SoundInName").toString();
|
||||||
QList<QAudioDeviceInfo> audioInputDevices (QAudioDeviceInfo::availableDevices (QAudio::AudioInput)); // available audio input devices
|
QList<QAudioDeviceInfo> audioInputDevices (QAudioDeviceInfo::availableDevices (QAudio::AudioInput)); // available audio input devices
|
||||||
for (QList<QAudioDeviceInfo>::const_iterator p = audioInputDevices.begin (); p != audioInputDevices.end (); ++p)
|
for (QList<QAudioDeviceInfo>::const_iterator p = audioInputDevices.begin (); p != audioInputDevices.end (); ++p)
|
||||||
{
|
{
|
||||||
@ -512,7 +532,7 @@ void MainWindow::readSettings()
|
|||||||
//
|
//
|
||||||
// retrieve audio output device
|
// retrieve audio output device
|
||||||
//
|
//
|
||||||
QString savedName = settings.value("SoundOutName", "default").toString();
|
QString savedName = settings.value("SoundOutName").toString();
|
||||||
QList<QAudioDeviceInfo> audioOutputDevices (QAudioDeviceInfo::availableDevices (QAudio::AudioOutput)); // available audio output devices
|
QList<QAudioDeviceInfo> audioOutputDevices (QAudioDeviceInfo::availableDevices (QAudio::AudioOutput)); // available audio output devices
|
||||||
for (QList<QAudioDeviceInfo>::const_iterator p = audioOutputDevices.begin (); p != audioOutputDevices.end (); ++p)
|
for (QList<QAudioDeviceInfo>::const_iterator p = audioOutputDevices.begin (); p != audioOutputDevices.end (); ++p)
|
||||||
{
|
{
|
||||||
@ -535,7 +555,7 @@ void MainWindow::readSettings()
|
|||||||
ui->RxFreqSpinBox->setValue(m_rxFreq);
|
ui->RxFreqSpinBox->setValue(m_rxFreq);
|
||||||
m_txFreq=settings.value("TxFreq",1500).toInt();
|
m_txFreq=settings.value("TxFreq",1500).toInt();
|
||||||
ui->TxFreqSpinBox->setValue(m_txFreq);
|
ui->TxFreqSpinBox->setValue(m_txFreq);
|
||||||
m_modulator.setFrequency(m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0));
|
Q_EMIT transmitFrequency (m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0));
|
||||||
m_saveDecoded=ui->actionSave_decoded->isChecked();
|
m_saveDecoded=ui->actionSave_decoded->isChecked();
|
||||||
m_saveAll=ui->actionSave_all->isChecked();
|
m_saveAll=ui->actionSave_all->isChecked();
|
||||||
m_ndepth=settings.value("NDepth",3).toInt();
|
m_ndepth=settings.value("NDepth",3).toInt();
|
||||||
@ -811,6 +831,7 @@ void MainWindow::on_monitorButton_clicked() //Monitor
|
|||||||
{
|
{
|
||||||
m_monitoring=true;
|
m_monitoring=true;
|
||||||
m_detector.setMonitoring(true);
|
m_detector.setMonitoring(true);
|
||||||
|
m_soundInput.start(m_audioInputDevice, RX_SAMPLE_RATE / 10, &m_detector);
|
||||||
m_diskData=false;
|
m_diskData=false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -827,7 +848,7 @@ void MainWindow::on_autoButton_clicked() //Auto
|
|||||||
ui->autoButton->setStyleSheet(m_pbAutoOn_style);
|
ui->autoButton->setStyleSheet(m_pbAutoOn_style);
|
||||||
} else {
|
} else {
|
||||||
m_btxok=false;
|
m_btxok=false;
|
||||||
m_modulator.mute();
|
Q_EMIT muteAudioOutput ();
|
||||||
ui->autoButton->setStyleSheet("");
|
ui->autoButton->setStyleSheet("");
|
||||||
on_monitorButton_clicked();
|
on_monitorButton_clicked();
|
||||||
m_repeatMsg=0;
|
m_repeatMsg=0;
|
||||||
@ -1031,6 +1052,7 @@ void MainWindow::closeEvent(QCloseEvent*)
|
|||||||
|
|
||||||
void MainWindow::OnExit()
|
void MainWindow::OnExit()
|
||||||
{
|
{
|
||||||
|
m_guiTimer.stop ();
|
||||||
g_pWideGraph->saveSettings();
|
g_pWideGraph->saveSettings();
|
||||||
if(m_fname != "") killFile();
|
if(m_fname != "") killFile();
|
||||||
m_killAll=true;
|
m_killAll=true;
|
||||||
@ -1042,13 +1064,16 @@ void MainWindow::OnExit()
|
|||||||
bool b=proc_jt9.waitForFinished(1000);
|
bool b=proc_jt9.waitForFinished(1000);
|
||||||
if(!b) proc_jt9.kill();
|
if(!b) proc_jt9.kill();
|
||||||
quitFile.remove();
|
quitFile.remove();
|
||||||
qApp->exit(0); // Exit the event loop
|
|
||||||
|
Q_EMIT finished ();
|
||||||
|
m_soundOutputThread.wait ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_stopButton_clicked() //stopButton
|
void MainWindow::on_stopButton_clicked() //stopButton
|
||||||
{
|
{
|
||||||
m_monitoring=false;
|
m_monitoring=false;
|
||||||
m_detector.setMonitoring(m_monitoring);
|
m_detector.setMonitoring(m_monitoring);
|
||||||
|
m_soundInput.stop ();
|
||||||
m_loopall=false;
|
m_loopall=false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1082,6 +1107,7 @@ void MainWindow::on_actionWide_Waterfall_triggered() //Display Waterfalls
|
|||||||
SLOT(setXIT(int)));
|
SLOT(setXIT(int)));
|
||||||
// connect(g_pWideGraph, SIGNAL(dialFreqChanged(double)),this,
|
// connect(g_pWideGraph, SIGNAL(dialFreqChanged(double)),this,
|
||||||
// SLOT(dialFreqChanged2(double)));
|
// SLOT(dialFreqChanged2(double)));
|
||||||
|
connect (this, &MainWindow::finished, g_pWideGraph, &WideGraph::close);
|
||||||
}
|
}
|
||||||
g_pWideGraph->show();
|
g_pWideGraph->show();
|
||||||
}
|
}
|
||||||
@ -1621,7 +1647,7 @@ void MainWindow::guiUpdate()
|
|||||||
}
|
}
|
||||||
if(!bTxTime || m_btxMute) {
|
if(!bTxTime || m_btxMute) {
|
||||||
m_btxok=false;
|
m_btxok=false;
|
||||||
m_modulator.mute();
|
Q_EMIT muteAudioOutput ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1719,8 +1745,9 @@ void MainWindow::guiUpdate()
|
|||||||
signalMeter->setValue(0);
|
signalMeter->setValue(0);
|
||||||
m_monitoring=false;
|
m_monitoring=false;
|
||||||
m_detector.setMonitoring(false);
|
m_detector.setMonitoring(false);
|
||||||
|
m_soundInput.stop ();
|
||||||
m_btxok=true;
|
m_btxok=true;
|
||||||
m_modulator.mute(false);
|
Q_EMIT muteAudioOutput (false);
|
||||||
m_transmitting=true;
|
m_transmitting=true;
|
||||||
ui->pbTxMode->setEnabled(false);
|
ui->pbTxMode->setEnabled(false);
|
||||||
if(!m_tune) {
|
if(!m_tune) {
|
||||||
@ -1839,7 +1866,7 @@ void MainWindow::displayTxMsg(QString t)
|
|||||||
|
|
||||||
void MainWindow::startTx2()
|
void MainWindow::startTx2()
|
||||||
{
|
{
|
||||||
if(!m_soundOutput.isRunning()) {
|
if (!m_modulator.isActive ()) {
|
||||||
QString t=ui->tx6->text();
|
QString t=ui->tx6->text();
|
||||||
double snr=t.mid(1,5).toDouble();
|
double snr=t.mid(1,5).toDouble();
|
||||||
if(snr>0.0 or snr < -50.0) snr=99.0;
|
if(snr>0.0 or snr < -50.0) snr=99.0;
|
||||||
@ -1847,8 +1874,9 @@ void MainWindow::startTx2()
|
|||||||
signalMeter->setValue(0);
|
signalMeter->setValue(0);
|
||||||
m_monitoring=false;
|
m_monitoring=false;
|
||||||
m_detector.setMonitoring(false);
|
m_detector.setMonitoring(false);
|
||||||
|
m_soundInput.stop ();
|
||||||
m_btxok=true;
|
m_btxok=true;
|
||||||
m_modulator.mute(false);
|
Q_EMIT muteAudioOutput (false);
|
||||||
m_transmitting=true;
|
m_transmitting=true;
|
||||||
ui->pbTxMode->setEnabled(false);
|
ui->pbTxMode->setEnabled(false);
|
||||||
}
|
}
|
||||||
@ -1856,8 +1884,8 @@ void MainWindow::startTx2()
|
|||||||
|
|
||||||
void MainWindow::stopTx()
|
void MainWindow::stopTx()
|
||||||
{
|
{
|
||||||
m_soundOutput.stop();
|
Q_EMIT endTransmitMessage ();
|
||||||
m_modulator.close ();
|
Q_EMIT stopAudioOutputStream ();
|
||||||
m_transmitting=false;
|
m_transmitting=false;
|
||||||
ui->pbTxMode->setEnabled(true);
|
ui->pbTxMode->setEnabled(true);
|
||||||
g_iptt=0;
|
g_iptt=0;
|
||||||
@ -1865,6 +1893,7 @@ void MainWindow::stopTx()
|
|||||||
lab1->setText("");
|
lab1->setText("");
|
||||||
ptt0Timer->start(200); //Sequencer delay
|
ptt0Timer->start(200); //Sequencer delay
|
||||||
m_monitoring=true;
|
m_monitoring=true;
|
||||||
|
m_soundInput.start(m_audioInputDevice, RX_SAMPLE_RATE / 10, &m_detector);
|
||||||
m_detector.setMonitoring(true);
|
m_detector.setMonitoring(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2381,7 +2410,7 @@ void MainWindow::on_tx6_editingFinished() //tx6 edited
|
|||||||
|
|
||||||
// double snr=t.mid(1,5).toDouble();
|
// double snr=t.mid(1,5).toDouble();
|
||||||
// if(snr>0.0 or snr < -50.0) snr=99.0;
|
// if(snr>0.0 or snr < -50.0) snr=99.0;
|
||||||
// m_soundOutput.setTxSNR(snr);
|
// m_modulator.setTxSNR(snr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_dxCallEntry_textChanged(const QString &t) //dxCall changed
|
void MainWindow::on_dxCallEntry_textChanged(const QString &t) //dxCall changed
|
||||||
@ -2525,7 +2554,7 @@ void MainWindow::on_TxFreqSpinBox_valueChanged(int n)
|
|||||||
m_txFreq=n;
|
m_txFreq=n;
|
||||||
if(g_pWideGraph!=NULL) g_pWideGraph->setTxFreq(n);
|
if(g_pWideGraph!=NULL) g_pWideGraph->setTxFreq(n);
|
||||||
if(m_lockTxFreq) ui->RxFreqSpinBox->setValue(n);
|
if(m_lockTxFreq) ui->RxFreqSpinBox->setValue(n);
|
||||||
m_modulator.setFrequency(m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0));
|
Q_EMIT transmitFrequency (m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_RxFreqSpinBox_valueChanged(int n)
|
void MainWindow::on_RxFreqSpinBox_valueChanged(int n)
|
||||||
@ -2848,7 +2877,7 @@ void MainWindow::on_tuneButton_clicked()
|
|||||||
} else {
|
} else {
|
||||||
m_tune=true;
|
m_tune=true;
|
||||||
m_sent73=false;
|
m_sent73=false;
|
||||||
m_modulator.tune();
|
Q_EMIT tune ();
|
||||||
m_repeatMsg=0;
|
m_repeatMsg=0;
|
||||||
ui->tuneButton->setStyleSheet(m_pbTune_style);
|
ui->tuneButton->setStyleSheet(m_pbTune_style);
|
||||||
}
|
}
|
||||||
@ -2858,11 +2887,11 @@ void MainWindow::on_stopTxButton_clicked() //Stop Tx
|
|||||||
{
|
{
|
||||||
if(m_tune) {
|
if(m_tune) {
|
||||||
m_tune=false;
|
m_tune=false;
|
||||||
m_modulator.tune(m_tune);
|
Q_EMIT tune (m_tune);
|
||||||
}
|
}
|
||||||
if(m_auto) on_autoButton_clicked();
|
if(m_auto) on_autoButton_clicked();
|
||||||
m_btxok=false;
|
m_btxok=false;
|
||||||
m_modulator.mute();
|
Q_EMIT muteAudioOutput ();
|
||||||
m_repeatMsg=0;
|
m_repeatMsg=0;
|
||||||
ui->tuneButton->setStyleSheet("");
|
ui->tuneButton->setStyleSheet("");
|
||||||
}
|
}
|
||||||
@ -2999,7 +3028,7 @@ void MainWindow::setXIT(int n)
|
|||||||
ret=rig->setSplitFreq(MHz(m_dialFreq)+m_XIT,RIG_VFO_B);
|
ret=rig->setSplitFreq(MHz(m_dialFreq)+m_XIT,RIG_VFO_B);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_modulator.setFrequency(m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0));
|
Q_EMIT transmitFrequency (m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::setFreq4(int rxFreq, int txFreq)
|
void MainWindow::setFreq4(int rxFreq, int txFreq)
|
||||||
@ -3055,19 +3084,13 @@ void MainWindow::pollRigFreq()
|
|||||||
|
|
||||||
void MainWindow::transmit (double snr)
|
void MainWindow::transmit (double snr)
|
||||||
{
|
{
|
||||||
QScopedPointer<std::vector<int> > cw (new std::vector<int> (NUM_CW_SYMBOLS));
|
|
||||||
cw->assign (icw, icw + NUM_CW_SYMBOLS); // load data
|
|
||||||
if (m_modeTx == "JT65")
|
if (m_modeTx == "JT65")
|
||||||
{
|
{
|
||||||
QScopedPointer<std::vector<int> > symbols (new std::vector<int> (NUM_JT65_SYMBOLS));
|
Q_EMIT sendMessage (NUM_JT65_SYMBOLS, 4096.0 * 12000.0 / 11025.0, m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0), true, snr);
|
||||||
symbols->assign (itone, itone + NUM_JT65_SYMBOLS); // load data
|
|
||||||
m_modulator.open (symbols.take (), cw.take (), 4096.0 * 12000.0 / 11025.0, m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0), snr);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QScopedPointer<std::vector<int> > symbols (new std::vector<int> (NUM_JT65_SYMBOLS));
|
Q_EMIT sendMessage (NUM_JT9_SYMBOLS, m_nsps, m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0), true, snr);
|
||||||
symbols->assign (itone, itone + NUM_JT9_SYMBOLS); // load data
|
|
||||||
m_modulator.open (symbols.take (), cw.take (), m_nsps, m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0), snr);
|
|
||||||
}
|
}
|
||||||
m_soundOutput.start(m_audioOutputDevice, &m_modulator);
|
Q_EMIT startAudioOutputStream (m_audioOutputDevice);
|
||||||
}
|
}
|
||||||
|
33
mainwindow.h
33
mainwindow.h
@ -5,6 +5,7 @@
|
|||||||
#else
|
#else
|
||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
#endif
|
#endif
|
||||||
|
#include <QThread>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
@ -24,6 +25,15 @@
|
|||||||
#include "PSKReporter.h"
|
#include "PSKReporter.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define NUM_JT65_SYMBOLS 126
|
||||||
|
#define NUM_JT9_SYMBOLS 85
|
||||||
|
#define NUM_CW_SYMBOLS 250
|
||||||
|
#define TX_SAMPLE_RATE 48000
|
||||||
|
|
||||||
|
extern int itone[NUM_JT65_SYMBOLS]; //Audio tones for all Tx symbols
|
||||||
|
extern int icw[NUM_CW_SYMBOLS]; //Dits for CW ID
|
||||||
|
|
||||||
|
|
||||||
//--------------------------------------------------------------- MainWindow
|
//--------------------------------------------------------------- MainWindow
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
@ -166,6 +176,16 @@ private slots:
|
|||||||
void on_actionTx2QSO_triggered(bool checked);
|
void on_actionTx2QSO_triggered(bool checked);
|
||||||
void on_cbPlus2kHz_toggled(bool checked);
|
void on_cbPlus2kHz_toggled(bool checked);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_SIGNAL void startAudioOutputStream (QAudioDeviceInfo);
|
||||||
|
Q_SIGNAL void stopAudioOutputStream ();
|
||||||
|
Q_SIGNAL void finished ();
|
||||||
|
Q_SIGNAL void muteAudioOutput (bool = true);
|
||||||
|
Q_SIGNAL void transmitFrequency (unsigned);
|
||||||
|
Q_SIGNAL void endTransmitMessage ();
|
||||||
|
Q_SIGNAL void tune (bool = true);
|
||||||
|
Q_SIGNAL void sendMessage (unsigned symbolsLength, double framesPerSymbol, unsigned frequency, bool synchronize = true, double dBSNR = 99.);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::MainWindow *ui;
|
Ui::MainWindow *ui;
|
||||||
|
|
||||||
@ -190,10 +210,16 @@ private:
|
|||||||
qint32 m_nutc0;
|
qint32 m_nutc0;
|
||||||
qint32 m_nrx;
|
qint32 m_nrx;
|
||||||
qint32 m_hsym;
|
qint32 m_hsym;
|
||||||
QAudioDeviceInfo m_audioInputDevice;
|
|
||||||
Detector m_detector;
|
Detector m_detector;
|
||||||
QAudioDeviceInfo m_audioOutputDevice;
|
QAudioDeviceInfo m_audioInputDevice;
|
||||||
|
SoundInput m_soundInput;
|
||||||
|
|
||||||
Modulator m_modulator;
|
Modulator m_modulator;
|
||||||
|
QAudioDeviceInfo m_audioOutputDevice;
|
||||||
|
SoundOutput m_soundOutput;
|
||||||
|
QThread m_soundOutputThread;
|
||||||
|
|
||||||
qint32 m_TRperiod;
|
qint32 m_TRperiod;
|
||||||
qint32 m_nsps;
|
qint32 m_nsps;
|
||||||
qint32 m_hsymStop;
|
qint32 m_hsymStop;
|
||||||
@ -299,6 +325,7 @@ private:
|
|||||||
|
|
||||||
QProcess proc_jt9;
|
QProcess proc_jt9;
|
||||||
|
|
||||||
|
QTimer m_guiTimer;
|
||||||
QTimer* ptt1Timer; //StartTx delay
|
QTimer* ptt1Timer; //StartTx delay
|
||||||
QTimer* ptt0Timer; //StopTx delay
|
QTimer* ptt0Timer; //StopTx delay
|
||||||
QTimer* logQSOTimer;
|
QTimer* logQSOTimer;
|
||||||
@ -344,8 +371,6 @@ private:
|
|||||||
|
|
||||||
QDateTime m_dateTimeQSO;
|
QDateTime m_dateTimeQSO;
|
||||||
|
|
||||||
SoundInput m_soundInput; //Instantiate the audio objects
|
|
||||||
SoundOutput m_soundOutput;
|
|
||||||
QSharedMemory *mem_jt9;
|
QSharedMemory *mem_jt9;
|
||||||
// Multiple instances:
|
// Multiple instances:
|
||||||
QString *mykey_jt9;
|
QString *mykey_jt9;
|
||||||
|
12
soundin.cpp
12
soundin.cpp
@ -74,7 +74,7 @@ bool SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, QIOD
|
|||||||
|
|
||||||
m_stream->start (sink);
|
m_stream->start (sink);
|
||||||
|
|
||||||
qDebug () << "audio input buffer size = " << m_stream->bufferSize () << " bytes\n";
|
qDebug () << "audio input buffer size = " << m_stream->bufferSize () << " bytes";
|
||||||
|
|
||||||
return audioError () ? false : true;
|
return audioError () ? false : true;
|
||||||
}
|
}
|
||||||
@ -84,29 +84,29 @@ void SoundInput::handleStateChanged (QAudio::State newState) const
|
|||||||
switch (newState)
|
switch (newState)
|
||||||
{
|
{
|
||||||
case QAudio::IdleState:
|
case QAudio::IdleState:
|
||||||
qDebug () << "SoundInput idle\n";
|
qDebug () << "SoundInput idle";
|
||||||
Q_EMIT status (tr ("Idle"));
|
Q_EMIT status (tr ("Idle"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case QAudio::ActiveState:
|
case QAudio::ActiveState:
|
||||||
qDebug () << "SoundInput active\n";
|
qDebug () << "SoundInput active";
|
||||||
Q_EMIT status (tr ("Receiving"));
|
Q_EMIT status (tr ("Receiving"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case QAudio::SuspendedState:
|
case QAudio::SuspendedState:
|
||||||
qDebug () << "SoundInput suspended\n";
|
qDebug () << "SoundInput suspended";
|
||||||
Q_EMIT status (tr ("Suspended"));
|
Q_EMIT status (tr ("Suspended"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case QAudio::StoppedState:
|
case QAudio::StoppedState:
|
||||||
if (audioError ())
|
if (audioError ())
|
||||||
{
|
{
|
||||||
qDebug () << "SoundInput error\n";
|
qDebug () << "SoundInput error";
|
||||||
Q_EMIT status (tr ("Error"));
|
Q_EMIT status (tr ("Error"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
qDebug () << "SoundInput stopped\n";
|
qDebug () << "SoundInput stopped";
|
||||||
Q_EMIT status (tr ("Stopped"));
|
Q_EMIT status (tr ("Stopped"));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
164
soundout.cpp
164
soundout.cpp
@ -5,6 +5,12 @@
|
|||||||
#include <QAudioOutput>
|
#include <QAudioOutput>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
|
#if defined (WIN32)
|
||||||
|
# define MS_BUFFERED 1000
|
||||||
|
#else
|
||||||
|
# define MS_BUFFERED 2000
|
||||||
|
#endif
|
||||||
|
|
||||||
bool SoundOutput::audioError () const
|
bool SoundOutput::audioError () const
|
||||||
{
|
{
|
||||||
bool result (true);
|
bool result (true);
|
||||||
@ -38,56 +44,119 @@ bool SoundOutput::audioError () const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SoundOutput::start(QAudioDeviceInfo const& device, QIODevice * source)
|
SoundOutput::SoundOutput (QIODevice * source)
|
||||||
|
: m_source (source)
|
||||||
|
, m_active (false)
|
||||||
|
, m_currentDevice (QAudioDeviceInfo::defaultOutputDevice ())
|
||||||
{
|
{
|
||||||
Q_ASSERT (source);
|
Q_ASSERT (source);
|
||||||
|
|
||||||
stop();
|
|
||||||
|
|
||||||
QAudioFormat format (device.preferredFormat());
|
|
||||||
format.setChannelCount (1);
|
|
||||||
format.setCodec ("audio/pcm");
|
|
||||||
format.setSampleRate (48000);
|
|
||||||
format.setSampleType (QAudioFormat::SignedInt);
|
|
||||||
format.setSampleSize (16);
|
|
||||||
if (!format.isValid ())
|
|
||||||
{
|
|
||||||
Q_EMIT error (tr ("Requested output audio format is not valid."));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!device.isFormatSupported (format))
|
|
||||||
{
|
|
||||||
Q_EMIT error (tr ("Requested output audio format is not supported on device."));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_stream.reset (new QAudioOutput (device, format, this));
|
|
||||||
if (audioError ())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
connect (m_stream.data(), &QAudioOutput::stateChanged, this, &SoundOutput::handleStateChanged);
|
|
||||||
|
|
||||||
m_stream->setBufferSize(48000);
|
|
||||||
m_stream->start (source);
|
|
||||||
if (audioError ()) // start the input stream
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_active = true;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundOutput::handleStateChanged (QAudio::State newState) const
|
void SoundOutput::startStream (QAudioDeviceInfo const& device)
|
||||||
|
{
|
||||||
|
if (!m_stream || device != m_currentDevice)
|
||||||
|
{
|
||||||
|
QAudioFormat format (device.preferredFormat ());
|
||||||
|
|
||||||
|
#ifdef UNIX
|
||||||
|
format.setChannelCount (2);
|
||||||
|
#else
|
||||||
|
format.setChannelCount (1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
format.setCodec ("audio/pcm");
|
||||||
|
format.setSampleRate (48000);
|
||||||
|
format.setSampleType (QAudioFormat::SignedInt);
|
||||||
|
format.setSampleSize (16);
|
||||||
|
if (!format.isValid ())
|
||||||
|
{
|
||||||
|
Q_EMIT error (tr ("Requested output audio format is not valid."));
|
||||||
|
}
|
||||||
|
if (!device.isFormatSupported (format))
|
||||||
|
{
|
||||||
|
Q_EMIT error (tr ("Requested output audio format is not supported on device."));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_stream.reset (new QAudioOutput (device, format, this));
|
||||||
|
audioError ();
|
||||||
|
|
||||||
|
connect (m_stream.data(), &QAudioOutput::stateChanged, this, &SoundOutput::handleStateChanged);
|
||||||
|
|
||||||
|
m_currentDevice = device;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// This buffer size is critical since we are running in the GUI
|
||||||
|
// thread. If it is too short; high activity levels on the GUI can
|
||||||
|
// starve the audio buffer. On the other hand the Windows
|
||||||
|
// implementation seems to take the length of the buffer in time to
|
||||||
|
// stop the audio stream even if reset() is used.
|
||||||
|
//
|
||||||
|
// 1 seconds seems a reasonable compromise except for Windows
|
||||||
|
// where things are probably broken.
|
||||||
|
//
|
||||||
|
// we have to set this before every start on the stream because the
|
||||||
|
// Windows implementation seems to forget the buffer size after a
|
||||||
|
// stop.
|
||||||
|
m_stream->setBufferSize (m_stream->format ().bytesForDuration (MS_BUFFERED * 1000));
|
||||||
|
m_stream->start (m_source);
|
||||||
|
audioError ();
|
||||||
|
|
||||||
|
qDebug () << "audio output buffer size = " << m_stream->bufferSize () << " bytes";
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundOutput::suspend ()
|
||||||
|
{
|
||||||
|
if (m_stream && QAudio::ActiveState == m_stream->state ())
|
||||||
|
{
|
||||||
|
m_stream->suspend ();
|
||||||
|
audioError ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundOutput::resume ()
|
||||||
|
{
|
||||||
|
if (m_stream && QAudio::SuspendedState == m_stream->state ())
|
||||||
|
{
|
||||||
|
m_stream->resume ();
|
||||||
|
audioError ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundOutput::stopStream ()
|
||||||
|
{
|
||||||
|
if (m_stream)
|
||||||
|
{
|
||||||
|
m_stream->stop ();
|
||||||
|
audioError ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundOutput::handleStateChanged (QAudio::State newState)
|
||||||
{
|
{
|
||||||
switch (newState)
|
switch (newState)
|
||||||
{
|
{
|
||||||
case QAudio::IdleState: Q_EMIT status (tr ("Idle")); break;
|
case QAudio::IdleState:
|
||||||
case QAudio::ActiveState: Q_EMIT status (tr ("Sending")); break;
|
qDebug () << "SoundOutput: entered Idle state";
|
||||||
case QAudio::SuspendedState: Q_EMIT status (tr ("Suspended")); break;
|
Q_EMIT status (tr ("Idle"));
|
||||||
|
m_active = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QAudio::ActiveState:
|
||||||
|
qDebug () << "SoundOutput: entered Active state";
|
||||||
|
m_active = true;
|
||||||
|
Q_EMIT status (tr ("Sending"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QAudio::SuspendedState:
|
||||||
|
qDebug () << "SoundOutput: entered Suspended state";
|
||||||
|
m_active = true;
|
||||||
|
Q_EMIT status (tr ("Suspended"));
|
||||||
|
break;
|
||||||
|
|
||||||
case QAudio::StoppedState:
|
case QAudio::StoppedState:
|
||||||
|
qDebug () << "SoundOutput: entered Stopped state";
|
||||||
|
m_active = false;
|
||||||
if (audioError ())
|
if (audioError ())
|
||||||
{
|
{
|
||||||
Q_EMIT status (tr ("Error"));
|
Q_EMIT status (tr ("Error"));
|
||||||
@ -100,13 +169,10 @@ void SoundOutput::handleStateChanged (QAudio::State newState) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundOutput::stop()
|
SoundOutput::~SoundOutput ()
|
||||||
{
|
{
|
||||||
m_stream.reset ();
|
if (m_stream)
|
||||||
m_active = false;
|
{
|
||||||
}
|
m_stream->stop ();
|
||||||
|
}
|
||||||
SoundOutput::~SoundOutput()
|
|
||||||
{
|
|
||||||
stop ();
|
|
||||||
}
|
}
|
||||||
|
26
soundout.h
26
soundout.h
@ -4,8 +4,9 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QAudioOutput>
|
#include <QAudioOutput>
|
||||||
|
#include <QAudioDeviceInfo>
|
||||||
|
|
||||||
#include "Modulator.hpp"
|
class QAudioDeviceInfo;
|
||||||
|
|
||||||
class QAudioDeviceInfo;
|
class QAudioDeviceInfo;
|
||||||
|
|
||||||
@ -21,32 +22,33 @@ class SoundOutput : public QObject
|
|||||||
Q_DISABLE_COPY (SoundOutput);
|
Q_DISABLE_COPY (SoundOutput);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SoundOutput ()
|
SoundOutput (QIODevice * source);
|
||||||
: m_active(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
~SoundOutput ();
|
~SoundOutput ();
|
||||||
|
|
||||||
bool isRunning() const {return m_active;}
|
bool isRunning() const {return m_active;}
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
bool start(QAudioDeviceInfo const& device, QIODevice * source);
|
void startStream (QAudioDeviceInfo const& device);
|
||||||
void stop();
|
void suspend ();
|
||||||
|
void resume ();
|
||||||
|
void stopStream ();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void error (QString message) const;
|
void error (QString message) const;
|
||||||
void status (QString message) const;
|
void status (QString message) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool audioError () const;
|
bool audioError () const;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void handleStateChanged (QAudio::State) const;
|
void handleStateChanged (QAudio::State);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QScopedPointer<QAudioOutput> m_stream;
|
QScopedPointer<QAudioOutput> m_stream;
|
||||||
|
|
||||||
bool m_active;
|
QIODevice * m_source;
|
||||||
|
bool volatile m_active;
|
||||||
|
QAudioDeviceInfo m_currentDevice;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -14,7 +14,6 @@ TARGET = wsjtx
|
|||||||
DESTDIR = ../wsjtx_install
|
DESTDIR = ../wsjtx_install
|
||||||
VERSION = 1.2
|
VERSION = 1.2
|
||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
#DEFINES = QT4
|
|
||||||
DEFINES = QT5
|
DEFINES = QT5
|
||||||
|
|
||||||
win32 {
|
win32 {
|
||||||
@ -82,9 +81,9 @@ FORMS += mainwindow.ui about.ui devsetup.ui widegraph.ui \
|
|||||||
RC_FILE = wsjtx.rc
|
RC_FILE = wsjtx.rc
|
||||||
|
|
||||||
unix {
|
unix {
|
||||||
LIBS += ../wsjtx/lib/libjt9.a
|
LIBS += -L lib -ljt9
|
||||||
LIBS += -lhamlib
|
LIBS += -lhamlib
|
||||||
LIBS += -lgfortran -lfftw3f
|
LIBS += -lfftw3f `$$F90 -print-file-name=libgfortran.so`
|
||||||
}
|
}
|
||||||
|
|
||||||
win32 {
|
win32 {
|
||||||
@ -99,3 +98,6 @@ LIBS += libwsock32
|
|||||||
LIBS += C:/MinGW/lib/libf95.a
|
LIBS += C:/MinGW/lib/libf95.a
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RESOURCES += \
|
||||||
|
wsjtx.qrc
|
||||||
|
Loading…
Reference in New Issue
Block a user