diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2aa5ab9..06f9796 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -253,6 +253,8 @@ set (wsjtx_CXXSRCS
WaveFile.cpp
AudioDecoder.cpp
NotificationAudio.cpp
+ ProcessThread.cpp
+ Decoder.cpp
)
set (wsjt_CXXSRCS
diff --git a/Decoder.cpp b/Decoder.cpp
new file mode 100644
index 0000000..891501a
--- /dev/null
+++ b/Decoder.cpp
@@ -0,0 +1,163 @@
+/**
+ * This file is part of JS8Call.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * (C) 2019 Jordan Sherer - All Rights Reserved
+ *
+ **/
+
+#include "Decoder.h"
+
+#include "commons.h"
+
+#include
+
+
+Decoder::Decoder(QObject *parent):
+ QObject(parent)
+{
+}
+
+Decoder::~Decoder(){
+}
+
+//
+void Decoder::lock(){
+ // NOOP
+}
+
+//
+void Decoder::unlock(){
+ // NOOP
+}
+
+//
+Worker* Decoder::createWorker(){
+ auto worker = new Worker();
+ worker->moveToThread(&m_thread);
+ connect(&m_thread, &QThread::finished, worker, &QObject::deleteLater);
+ connect(this, &Decoder::startWorker, worker, &Worker::start);
+ connect(this, &Decoder::quitWorker, worker, &Worker::quit);
+ connect(worker, &Worker::ready, this, &Decoder::ready);
+ return worker;
+}
+
+//
+void Decoder::start(QThread::Priority priority){
+ m_thread.start(priority);
+}
+
+//
+void Decoder::quit(){
+ m_thread.quit();
+}
+
+//
+bool Decoder::wait(){
+ return m_thread.wait();
+}
+
+//
+void Decoder::processStart(QString path, QStringList args){
+ if(m_worker.isNull()){
+ m_worker = createWorker();
+ }
+
+ emit startWorker(path, args);
+}
+
+//
+void Decoder::processReady(QByteArray t){
+ emit ready(t);
+}
+
+//
+void Decoder::processQuit(){
+ emit quitWorker();
+}
+
+////////////////////////////////////////
+//////////////// WORKER ////////////////
+////////////////////////////////////////
+
+//
+Worker::~Worker(){
+}
+
+//
+void Worker::setProcess(QProcess *proc, int msecs){
+ if(!m_proc.isNull()){
+ bool b = m_proc->waitForFinished(msecs);
+ if(!b) m_proc->close();
+ m_proc.reset();
+ }
+
+ if(proc){
+ m_proc.reset(proc);
+ }
+}
+
+//
+void Worker::start(QString path, QStringList args){
+ if(JS8_DEBUG_DECODE) qDebug() << "decoder process starting...";
+
+ auto proc = new QProcess(this);
+
+ connect(proc, &QProcess::readyReadStandardOutput,
+ [this, proc](){
+ while(proc->canReadLine()){
+ emit ready(proc->readLine());
+ }
+ });
+
+ connect(proc, static_cast (&QProcess::error),
+ [this, proc] (QProcess::ProcessError e) {
+ emit error();
+ });
+
+ connect(proc, static_cast (&QProcess::finished),
+ [this, proc] (int exitCode, QProcess::ExitStatus status) {
+ emit finished();
+ });
+
+
+ auto watcher = new QTimer(this);
+
+ connect(proc, static_cast (&QProcess::finished),
+ [this, watcher] (int /*exitCode*/, QProcess::ExitStatus /*status*/) {
+ watcher->stop();
+ });
+
+ connect(watcher, &QTimer::timeout,
+ [this, proc](){
+ if(JS8_DEBUG_DECODE) qDebug() << "decode process" << proc->state() << "can readline?" << proc->canReadLine();
+ });
+
+ watcher->setInterval(500);
+ watcher->start();
+
+
+ QProcessEnvironment env {QProcessEnvironment::systemEnvironment ()};
+ env.insert ("OMP_STACKSIZE", "4M");
+ proc->setProcessEnvironment (env);
+ proc->start(path, args, QIODevice::ReadWrite | QIODevice::Unbuffered);
+
+ setProcess(proc);
+}
+
+//
+void Worker::quit(){
+ setProcess(nullptr);
+}
diff --git a/Decoder.h b/Decoder.h
new file mode 100644
index 0000000..6dd2756
--- /dev/null
+++ b/Decoder.h
@@ -0,0 +1,71 @@
+#ifndef DECODER_H
+#define DECODER_H
+
+/**
+ * (C) 2019 Jordan Sherer - All Rights Reserved
+ **/
+
+#include "ProcessThread.h"
+
+#include
+#include
+#include
+#include
+
+
+class Worker : public QObject{
+ Q_OBJECT
+public:
+ ~Worker();
+public slots:
+ void start(QString path, QStringList args);
+ void quit();
+
+private:
+ void setProcess(QProcess *proc, int msecs=1000);
+
+signals:
+ void ready(QByteArray t);
+ void error();
+ void finished();
+
+private:
+ QScopedPointer m_proc;
+};
+
+
+class Decoder: public QObject
+{
+ Q_OBJECT
+public:
+ Decoder(QObject *parent=nullptr);
+ ~Decoder();
+
+ void lock();
+ void unlock();
+
+private:
+ Worker* createWorker();
+
+public slots:
+ void start(QThread::Priority priority);
+ void quit();
+ bool wait();
+
+ void processStart(QString path, QStringList args);
+ void processReady(QByteArray t);
+ void processQuit();
+
+signals:
+ void startWorker(QString path, QStringList args);
+ void quitWorker();
+
+ void ready(QByteArray t);
+
+private:
+ QPointer m_worker;
+ QThread m_thread;
+};
+
+
+#endif // DECODER_H
diff --git a/ProcessThread.cpp b/ProcessThread.cpp
new file mode 100644
index 0000000..3050c7f
--- /dev/null
+++ b/ProcessThread.cpp
@@ -0,0 +1,46 @@
+/**
+ * This file is part of JS8Call.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * (C) 2019 Jordan Sherer - All Rights Reserved
+ *
+ **/
+#include "ProcessThread.h"
+
+ProcessThread::ProcessThread(QObject *parent):
+ QThread(parent)
+{
+}
+
+ProcessThread::~ProcessThread(){
+ setProcess(nullptr);
+}
+
+/**
+ * @brief ProcessThread::setProcess
+ * @param proc - process to move to this thread and take ownership
+ */
+void ProcessThread::setProcess(QProcess *proc, int msecs){
+ if(!m_proc.isNull()){
+ bool b = m_proc->waitForFinished(msecs);
+ if(!b) m_proc->close();
+ m_proc.reset();
+ }
+
+ if(proc){
+ proc->moveToThread(this);
+ m_proc.reset(proc);
+ }
+}
diff --git a/ProcessThread.h b/ProcessThread.h
new file mode 100644
index 0000000..e0c578c
--- /dev/null
+++ b/ProcessThread.h
@@ -0,0 +1,29 @@
+#ifndef PROCESSTHREAD_H
+#define PROCESSTHREAD_H
+
+/**
+ * (C) 2019 Jordan Sherer - All Rights Reserved
+ **/
+
+#include
+#include
+#include
+
+
+class ProcessThread : public QThread
+{
+ Q_OBJECT
+public:
+ ProcessThread(QObject *parent=nullptr);
+ ~ProcessThread();
+
+ void setProcess(QProcess *proc, int msecs=1000);
+ QProcess * process(){
+ return m_proc.data();
+ }
+
+protected:
+ QScopedPointer m_proc;
+};
+
+#endif // PROCESSTHREAD_H
diff --git a/js8call.pro b/js8call.pro
index 924b2aa..722e710 100644
--- a/js8call.pro
+++ b/js8call.pro
@@ -88,7 +88,10 @@ SOURCES += \
CallsignValidator.cpp \
AudioDecoder.cpp \
WaveFile.cpp \
- WaveUtils.cpp
+ WaveUtils.cpp \
+ ProcessThread.cpp \
+ DecoderThread.cpp \
+ Decoder.cpp
HEADERS += qt_helpers.hpp \
pimpl_h.hpp pimpl_impl.hpp \
@@ -127,7 +130,10 @@ HEADERS += qt_helpers.hpp \
NotificationAudio.h \
AudioDecoder.h \
WaveFile.h \
- WaveUtils.h
+ WaveUtils.h \
+ ProcessThread.h \
+ DecoderThread.h \
+ Decoder.h
INCLUDEPATH += qmake_only
diff --git a/mainwindow.cpp b/mainwindow.cpp
index c8d370a..9cbde48 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -419,6 +419,8 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
m_downSampleFactor (downSampleFactor),
m_audioThreadPriority (QThread::HighPriority),
m_notificationAudioThreadPriority (QThread::LowPriority),
+ m_decoderThreadPriority (QThread::HighPriority),
+ m_decoder {this},
m_bandEdited {false},
m_splitMode {false},
m_monitoring {false},
@@ -537,7 +539,9 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
#endif
// decoder queue handler
- connect(this, &MainWindow::decodedLineReady, this, &MainWindow::processDecodedLine);
+ //connect (&m_decodeThread, &QThread::finished, m_notification, &QObject::deleteLater);
+ //connect(this, &MainWindow::decodedLineReady, this, &MainWindow::processDecodedLine);
+ connect(&m_decoder, &Decoder::ready, this, &MainWindow::processDecodedLine);
on_EraseButton_clicked ();
@@ -807,6 +811,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
m_audioThread.start (m_audioThreadPriority);
m_notificationAudioThread.start(m_notificationAudioThreadPriority);
+ m_decoder.start(m_decoderThreadPriority);
#ifdef WIN32
if (!m_multiple)
@@ -1561,17 +1566,17 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
}
void MainWindow::initDecoderSubprocess(){
+ //delete any .quit file that might have been left lying around
+ //since its presence will cause jt9 to exit a soon as we start it
+ //and decodes will hang
{
- //delete any .quit file that might have been left lying around
- //since its presence will cause jt9 to exit a soon as we start it
- //and decodes will hang
- QFile quitFile {m_config.temp_dir ().absoluteFilePath (".quit")};
- while (quitFile.exists ())
+ QFile quitFile {m_config.temp_dir ().absoluteFilePath (".quit")};
+ while (quitFile.exists ())
{
- if (!quitFile.remove ())
+ if (!quitFile.remove ())
{
- MessageBox::query_message (this, tr ("Error removing \"%1\"").arg (quitFile.fileName ())
- , tr ("Click OK to retry"));
+ MessageBox::query_message (this, tr ("Error removing \"%1\"").arg (quitFile.fileName ())
+ , tr ("Click OK to retry"));
}
}
}
@@ -1580,7 +1585,11 @@ void MainWindow::initDecoderSubprocess(){
if(JS8_DEBUG_DECODE) qDebug() << "decoder lock create";
QFile {m_config.temp_dir ().absoluteFilePath (".lock")}.open(QIODevice::ReadWrite);
- QStringList js8_args {
+ // create path
+ QString path = QDir::toNativeSeparators(m_appDir) + QDir::separator() + "js8";
+
+ // create args
+ QStringList args {
"-s", QApplication::applicationName () // shared memory key,
// includes rig
#ifdef NDEBUG
@@ -1599,79 +1608,10 @@ void MainWindow::initDecoderSubprocess(){
, "-e", QDir::toNativeSeparators (m_appDir)
, "-a", QDir::toNativeSeparators (m_config.writeable_data_dir ().absolutePath ())
, "-t", QDir::toNativeSeparators (m_config.temp_dir ().absolutePath ())
- };
+ };
- QProcessEnvironment env {QProcessEnvironment::systemEnvironment ()};
- env.insert ("OMP_STACKSIZE", "4M");
-
- if(JS8_DEBUG_DECODE) qDebug() << "decoder subprocess starting...";
-
-#if JS8_DECODE_THREAD
- auto thread = new QThread(nullptr);
-#endif
-
- auto proc = new QProcess(nullptr);
-
- connect(proc, &QProcess::readyReadStandardOutput, this,
- [this, proc](){
-#if JS8_DECODE_THREAD
- while(proc->canReadLine()){
- emit decodedLineReady(proc->readLine());
- }
-#else
- readFromStdout(proc);
-#endif
- });
-
- connect(proc, static_cast (&QProcess::error),
- [this, proc] (QProcess::ProcessError error) {
- subProcessError (proc, error);
- });
-
- connect(proc, static_cast (&QProcess::finished),
- [this, proc] (int exitCode, QProcess::ExitStatus status) {
-#if JS8_DECODE_THREAD
- proc->deleteLater();
- proc->thread()->quit();
-#endif
- subProcessFailed (proc, exitCode, status);
- });
-
- proc->setProcessEnvironment (env);
- proc->start(QDir::toNativeSeparators (m_appDir) + QDir::separator () +
- "js8", js8_args, QIODevice::ReadWrite | QIODevice::Unbuffered);
-
-#if JS8_DECODE_THREAD
- if(JS8_DEBUG_DECODE) qDebug() << "decoder subprocess moving to new thread...";
- // move process handling into its own thread
- proc->moveToThread(thread);
- connect(thread, &QThread::finished, thread, &QObject::deleteLater);
- thread->moveToThread(qApp->thread());
- thread->start(QThread::HighPriority);
-#endif
-
- // create a process watcher looking for stdout read...
- // seems like we're starving the event loop or something?
- // auto watcher = new QTimer(proc);
- // watcher->setInterval(500);
- // connect(watcher, &QTimer::timeout, this,
- // [this, proc](){
- // if(proc->canReadLine()){
- // if(JS8_DEBUG_DECODE) qDebug() << "decode process watcher intercepted readline";
- // readFromStdout(proc);
- // }
- // });
- // watcher->start();
-
- // kill the previous proc and set the new one
- m_valid = false;
- {
- if(!proc_js8.isNull()){
- proc_js8->kill();
- }
- proc_js8.reset(proc);
- }
- m_valid = true;
+ // initialize
+ m_decoder.processStart(path, args);
// reset decode busy
if(m_decoderBusy){
@@ -2194,6 +2134,9 @@ MainWindow::~MainWindow()
m_notificationAudioThread.quit();
m_notificationAudioThread.wait();
+ m_decoder.quit();
+ m_decoder.wait();
+
remove_child_from_event_filter (this);
}
@@ -2468,6 +2411,8 @@ void MainWindow::readSettings()
m_msAudioOutputBuffered = m_settings->value ("Audio/OutputBufferMs").toInt ();
m_framesAudioInputBuffered = m_settings->value ("Audio/InputBufferFrames", RX_SAMPLE_RATE / 10).toInt ();
m_audioThreadPriority = static_cast (m_settings->value ("Audio/ThreadPriority", QThread::HighPriority).toInt () % 8);
+ m_notificationAudioThreadPriority = static_cast (m_settings->value ("Audio/NotificationThreadPriority", QThread::LowPriority).toInt () % 8);
+ m_decoderThreadPriority = static_cast (m_settings->value ("Audio/DecoderThreadPriority", QThread::HighPriority).toInt () % 8);
m_settings->endGroup ();
if(m_config.reset_activity()){
@@ -3600,14 +3545,12 @@ void MainWindow::closeEvent(QCloseEvent * e)
mem_js8->detach();
QFile quitFile {m_config.temp_dir ().absoluteFilePath (".quit")};
quitFile.open(QIODevice::ReadWrite);
- if(JS8_DEBUG_DECODE) qDebug() << "decoder lock remove";
- QFile {m_config.temp_dir ().absoluteFilePath (".lock")}.remove(); // Allow jt9 to terminate
- if(!proc_js8.isNull()){
- bool b=proc_js8->waitForFinished(1000);
- if(!b) proc_js8->close();
+ {
+ if(JS8_DEBUG_DECODE) qDebug() << "decoder lock remove";
+ QFile {m_config.temp_dir ().absoluteFilePath (".lock")}.remove(); // Allow jt9 to terminate
+ m_decoder.processQuit();
}
quitFile.remove();
-
Q_EMIT finished ();
QMainWindow::closeEvent (e);
diff --git a/mainwindow.h b/mainwindow.h
index 43612bb..63f0abf 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -48,6 +48,8 @@
#include "SpotClient.h"
#include "keyeater.h"
#include "NotificationAudio.h"
+#include "ProcessThread.h"
+#include "Decoder.h"
#define NUM_JT4_SYMBOLS 206 //(72+31)*2, embedded sync
#define NUM_JT65_SYMBOLS 126 //63 data + 63 sync
@@ -500,8 +502,10 @@ private:
Modulator * m_modulator;
SoundOutput * m_soundOutput;
NotificationAudio * m_notification;
+
QThread m_audioThread;
QThread m_notificationAudioThread;
+ Decoder m_decoder;
qint64 m_msErase;
qint64 m_secBandChanged;
@@ -665,7 +669,7 @@ private:
QFutureWatcher watcher3;
QFutureWatcher m_saveWAVWatcher;
- QScopedPointer proc_js8;
+ //QPointer proc_js8;
QTimer m_guiTimer;
QTimer ptt1Timer; //StartTx delay
@@ -897,6 +901,7 @@ private:
unsigned m_downSampleFactor;
QThread::Priority m_audioThreadPriority;
QThread::Priority m_notificationAudioThreadPriority;
+ QThread::Priority m_decoderThreadPriority;
bool m_bandEdited;
bool m_splitMode;
bool m_monitoring;