Move code out of frame, proper thread termination

This commit is contained in:
Charles J. Cliffe 2014-11-23 19:39:27 -05:00
parent ac20bc1e84
commit cae1855fc5
15 changed files with 171 additions and 196 deletions

View File

@ -26,7 +26,7 @@ EVT_IDLE(AppFrame::OnIdle)
wxEND_EVENT_TABLE()
AppFrame::AppFrame() :
wxFrame(NULL, wxID_ANY, wxT("CubicSDR")), frequency(DEFAULT_FREQ) {
wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) {
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
@ -58,26 +58,6 @@ AppFrame::AppFrame() :
Centre();
Show();
audioInputQueue = new AudioThreadInputQueue;
audioThread = new AudioThread(audioInputQueue);
threadAudio = new std::thread(&AudioThread::threadMain, audioThread);
demodulatorTest = demodMgr.newThread();
demodulatorTest->params.audioInputQueue = audioInputQueue;
demodulatorTest->init();
audioVisualQueue = new DemodulatorThreadOutputQueue();
demodulatorTest->setVisualOutputQueue(audioVisualQueue);
threadCmdQueueSDR = new SDRThreadCommandQueue;
sdrThread = new SDRThread(threadCmdQueueSDR);
sdrThread->bindDemodulator(demodulatorTest);
iqVisualQueue = new SDRThreadIQDataQueue;
sdrThread->setIQVisualQueue(iqVisualQueue);
threadSDR = new std::thread(&SDRThread::threadMain, sdrThread);
// static const int attribs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 };
// wxLogStatus("Double-buffered display %s supported", wxGLCanvas::IsDisplaySupported(attribs) ? "is" : "not");
@ -87,9 +67,7 @@ AppFrame::AppFrame() :
AppFrame::~AppFrame() {
// delete t_SDR;
delete audioInputQueue;
delete audioThread;
delete threadCmdQueueSDR;
}
void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) {
@ -109,9 +87,9 @@ void AppFrame::OnThread(wxCommandEvent& event) {
void AppFrame::OnIdle(wxIdleEvent& event) {
bool work_done = false;
if (!iqVisualQueue->empty()) {
if (!wxGetApp().getIQVisualQueue()->empty()) {
SDRThreadIQData iqData;
iqVisualQueue->pop(iqData);
wxGetApp().getIQVisualQueue()->pop(iqData);
if (iqData.data.size()) {
spectrumCanvas->setData(&iqData.data);
@ -122,9 +100,9 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
work_done = true;
}
if (!audioVisualQueue->empty()) {
if (!wxGetApp().getAudioVisualQueue()->empty()) {
AudioThreadInput demodAudioData;
audioVisualQueue->pop(demodAudioData);
wxGetApp().getAudioVisualQueue()->pop(demodAudioData);
if (demodAudioData.data.size()) {
if (scopeCanvas->waveform_points.size() != demodAudioData.data.size() * 2) {
@ -146,14 +124,3 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
event.Skip();
}
}
void AppFrame::setFrequency(unsigned int freq) {
frequency = freq;
SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_TUNE);
command.int_value = freq;
threadCmdQueueSDR->push(command);
}
int AppFrame::getFrequency() {
return frequency;
}

View File

@ -2,14 +2,11 @@
#include "wx/frame.h"
#include "PrimaryGLContext.h"
#include "SDRThread.h"
#include "AudioThread.h"
#include "DemodulatorMgr.h"
#include "ScopeCanvas.h"
#include "SpectrumCanvas.h"
#include "WaterfallCanvas.h"
#include "ThreadQueue.h"
// Define a new frame type
class AppFrame: public wxFrame {
@ -19,8 +16,6 @@ public:
void OnThread(wxCommandEvent& event);
void OnEventInput(wxThreadEvent& event);
void setFrequency(unsigned int freq);
int getFrequency();
private:
void OnClose(wxCommandEvent& event);
@ -31,23 +26,6 @@ private:
SpectrumCanvas *spectrumCanvas;
WaterfallCanvas *waterfallCanvas;
DemodulatorMgr demodMgr;
wxCriticalSection m_pThreadCS;
unsigned int frequency;
DemodulatorInstance *demodulatorTest;
AudioThreadInputQueue *audioInputQueue;
AudioThread *audioThread;
SDRThread *sdrThread;
SDRThreadCommandQueue* threadCmdQueueSDR;
SDRThreadIQDataQueue* iqVisualQueue;
DemodulatorThreadOutputQueue* audioVisualQueue;
std::thread *threadAudio;
std::thread *threadSDR;
// event table
wxDECLARE_EVENT_TABLE();

View File

@ -19,22 +19,56 @@ bool CubicSDR::OnInit() {
if (!wxApp::OnInit())
return false;
frequency = DEFAULT_FREQ;
audioInputQueue = new AudioThreadInputQueue;
audioThread = new AudioThread(audioInputQueue);
threadAudio = new std::thread(&AudioThread::threadMain, audioThread);
demodulatorTest = demodMgr.newThread();
demodulatorTest->params.audioInputQueue = audioInputQueue;
demodulatorTest->init();
audioVisualQueue = new DemodulatorThreadOutputQueue();
demodulatorTest->setVisualOutputQueue(audioVisualQueue);
threadCmdQueueSDR = new SDRThreadCommandQueue;
sdrThread = new SDRThread(threadCmdQueueSDR);
sdrThread->bindDemodulator(demodulatorTest);
iqVisualQueue = new SDRThreadIQDataQueue;
sdrThread->setIQVisualQueue(iqVisualQueue);
threadSDR = new std::thread(&SDRThread::threadMain, sdrThread);
AppFrame *appframe = new AppFrame();
return true;
}
int CubicSDR::OnExit() {
delete m_glContext;
std::cout << "Terminating SDR thread.." << std::endl;
sdrThread->terminate();
threadSDR->join();
// while (1) {
// { wxCriticalSectionLocker enter(m_pThreadCS);
// if (!m_pThread)
// break;
// }
// // wait for thread completion
// wxThread::This()->Sleep(1);
// }
delete sdrThread;
delete threadSDR;
demodMgr.terminateAll();
audioThread->terminate();
threadAudio->join();
delete audioThread;
delete threadAudio;
delete audioInputQueue;
delete threadCmdQueueSDR;
delete iqVisualQueue;
delete audioVisualQueue;
delete m_glContext;
return wxApp::OnExit();
}
@ -51,3 +85,13 @@ PrimaryGLContext& CubicSDR::GetContext(wxGLCanvas *canvas) {
return *glContext;
}
void CubicSDR::setFrequency(unsigned int freq) {
frequency = freq;
SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_TUNE);
command.int_value = freq;
threadCmdQueueSDR->push(command);
}
int CubicSDR::getFrequency() {
return frequency;
}

View File

@ -4,9 +4,16 @@
//WX_GL_MAJOR_VERSION 3
//WX_GL_MINOR_VERSION 2
#include <thread>
#include "wx/glcanvas.h"
#include "PrimaryGLContext.h"
#include "ThreadQueue.h"
#include "SDRThread.h"
#include "AudioThread.h"
#include "DemodulatorMgr.h"
class CubicSDR: public wxApp {
public:
CubicSDR() {
@ -18,9 +25,36 @@ public:
virtual bool OnInit();
virtual int OnExit();
void setFrequency(unsigned int freq);
int getFrequency();
DemodulatorThreadOutputQueue* getAudioVisualQueue() {
return audioVisualQueue;
}
SDRThreadIQDataQueue* getIQVisualQueue() {
return iqVisualQueue;
}
private:
PrimaryGLContext *m_glContext;
DemodulatorMgr demodMgr;
unsigned int frequency;
DemodulatorInstance *demodulatorTest;
AudioThreadInputQueue *audioInputQueue;
AudioThread *audioThread;
SDRThread *sdrThread;
SDRThreadCommandQueue* threadCmdQueueSDR;
SDRThreadIQDataQueue* iqVisualQueue;
DemodulatorThreadOutputQueue* audioVisualQueue;
std::thread *threadAudio;
std::thread *threadSDR;
};
DECLARE_APP(CubicSDR)

View File

@ -3,7 +3,7 @@
#include <vector>
AudioThread::AudioThread(AudioThreadInputQueue *inputQueue) :
inputQueue(inputQueue), stream(NULL) {
inputQueue(inputQueue), stream(NULL), terminated(false) {
}
@ -41,11 +41,6 @@ void AudioThread::threadMain() {
stream = NULL;
// err = Pa_OpenStream(&stream, NULL, &outputParameters, AUDIO_FREQUENCY,
// paFramesPerBufferUnspecified,
// paPrimeOutputBuffersUsingStreamCallback | paClipOff, &audioCallback,
// this);
err = Pa_OpenStream(&stream, NULL, &outputParameters, AUDIO_FREQUENCY,
paFramesPerBufferUnspecified, paClipOff, NULL, NULL);
@ -56,101 +51,18 @@ void AudioThread::threadMain() {
std::cout << "\tPortAudio error: " << Pa_GetErrorText(err) << std::endl;
}
while (1) {
while (!terminated) {
AudioThreadInput inp;
inputQueue->pop(inp);
Pa_WriteStream(stream, &inp.data[0], inp.data.size()/2);
if (inp.data.size()) {
Pa_WriteStream(stream, &inp.data[0], inp.data.size()/2);
}
}
}
/*
#ifdef WIN32
#include <algorithm>
#include <functional>
#include <cctype>
#include <locale>
#include <sstream>
// trim from start
static inline std::wstring &wltrim(std::wstring &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
return s;
}
// trim from end
static inline std::wstring &wrtrim(std::wstring &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
return s;
}
// trim from both ends
static inline std::wstring &wtrim(std::wstring &s) {
return wltrim(wrtrim(s));
}
#endif
//wxDEFINE_EVENT(wxEVT_COMMAND_AudioThread_INPUT, wxThreadEvent);
AudioThread::AudioThread(AudioThreadQueue* pQueue, int id) :
wxThread(wxTHREAD_DETACHED), m_pQueue(pQueue), m_ID(id), audio_queue_ptr(0), stream(NULL) {
}
AudioThread::~AudioThread() {
}
wxThread::ExitCode AudioThread::Entry() {
//#ifdef WIN32
// wchar_t dev_str[255];
// memset(dev_str, 0, sizeof(wchar_t) * 255);
// std::wstring env_name(L"PA_RECOMMENDED_OUTPUT_DEVICE");
// GetEnvironmentVariable(wtrim(env_name).c_str(), dev_str, 255);
// std::wistringstream env_result(dev_str);
//
// if (!env_result.eof()) {
// int env_dev = -1;
// env_result >> env_dev;
//
// if (env_result.eof()) { // read everything, was all a number
// if (env_dev >= 0) {
// std::cout << "Using preferred PortAudio device PA_RECOMMENDED_OUTPUT_DEVICE=" << env_dev << std::endl;
// preferred_device = env_dev;
// } else {
// std::cout << "Environment variable PA_RECOMMENDED_OUTPUT_DEVICE not set, using PortAudio defaults." << std::endl;
// }
// } else {
// std::cout << "Environment variable PA_RECOMMENDED_OUTPUT_DEVICE didn't evaluate to a number, using PortAudio defaults." << std::endl;
// }
// }
//#endif
while (!TestDestroy()) {
if (m_pQueue->stackSize()) {
while (m_pQueue->stackSize()) {
AudioThreadTask task = m_pQueue->pop(); // pop a task from the queue. this will block the worker thread if queue is empty
switch (task.m_cmd) {
case AudioThreadTask::AUDIO_THREAD_DATA:
if (!TestDestroy()) {
audio_queue.push(task.data->data);
}
delete task.data;
break;
}
}
} else {
this->Yield();
this->Sleep(1);
}
}
std::cout << std::endl << "Audio Thread Done." << std::endl << std::endl;
return (wxThread::ExitCode) 0;
}
*/
void AudioThread::terminate() {
std::cout << "Terminating audio thread.." << std::endl;
terminated = true;
AudioThreadInput endCond; // push an empty input to bump the queue
inputQueue->push(endCond);
}

View File

@ -3,6 +3,7 @@
#include <queue>
#include <vector>
#include <string>
#include <atomic>
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
@ -28,14 +29,15 @@ typedef ThreadQueue<AudioThreadInput> AudioThreadInputQueue;
class AudioThread {
public:
AudioThread(AudioThreadInputQueue *inputQueue);
~AudioThread();
void threadMain();
void terminate();
private:
AudioThreadInputQueue *inputQueue;
PaStreamParameters outputParameters;
PaStream *stream;
std::atomic<bool> terminated;
};

View File

@ -3,16 +3,23 @@
DemodulatorInstance::DemodulatorInstance() :
t_Demod(NULL), threadQueueDemod(NULL), demodulatorThread(NULL) {
}
DemodulatorInstance::~DemodulatorInstance() {
delete threadQueueDemod;
delete demodulatorThread;
delete t_Demod;
}
void DemodulatorInstance::setVisualOutputQueue(DemodulatorThreadOutputQueue *tQueue) {
demodulatorThread->setVisualOutputQueue(tQueue);
}
void DemodulatorInstance::init() {
if (threadQueueDemod) {
delete threadQueueDemod;
}
if (demodulatorThread) {
terminate();
delete threadQueueDemod;
delete demodulatorThread;
delete t_Demod;
}
threadQueueDemod = new DemodulatorThreadInputQueue;
@ -21,16 +28,17 @@ void DemodulatorInstance::init() {
t_Demod = new std::thread(&DemodulatorThread::threadMain, demodulatorThread);
}
void DemodulatorInstance::terminate() {
demodulatorThread->terminate();
t_Demod->join();
}
DemodulatorMgr::DemodulatorMgr() {
}
DemodulatorMgr::~DemodulatorMgr() {
while (demods.size()) {
DemodulatorInstance *d = demods.back();
demods.pop_back();
delete d;
}
terminateAll();
}
DemodulatorInstance *DemodulatorMgr::newThread() {
@ -38,3 +46,12 @@ DemodulatorInstance *DemodulatorMgr::newThread() {
demods.push_back(newDemod);
return newDemod;
}
void DemodulatorMgr::terminateAll() {
while (demods.size()) {
DemodulatorInstance *d = demods.back();
demods.pop_back();
d->terminate();
delete d;
}
}

View File

@ -14,8 +14,10 @@ public:
DemodulatorThreadParameters params;
DemodulatorInstance();
~DemodulatorInstance();
void setVisualOutputQueue(DemodulatorThreadOutputQueue *tQueue);
void init();
void terminate();
};
class DemodulatorMgr {
@ -25,6 +27,7 @@ public:
DemodulatorInstance *newThread();
void terminateAll();
private:
std::vector<DemodulatorInstance *> demods;
};

View File

@ -3,7 +3,7 @@
#include <vector>
DemodulatorThread::DemodulatorThread(DemodulatorThreadInputQueue* pQueue, DemodulatorThreadParameters *params_in) :
inputQueue(pQueue), visOutQueue(NULL) {
inputQueue(pQueue), visOutQueue(NULL), terminated(false) {
DemodulatorThreadParameters defaultParams;
if (!params_in) {
@ -55,7 +55,7 @@ DemodulatorThread::~DemodulatorThread() {
void DemodulatorThread::threadMain() {
while (1) {
while (!terminated) {
DemodulatorThreadIQData inp;
inputQueue->pop(inp);
@ -143,3 +143,9 @@ void DemodulatorThread::threadMain() {
}
}
void DemodulatorThread::terminate() {
std::cout << "Terminating demodulator thread.." << std::endl;
terminated = true;
DemodulatorThreadIQData inp; // push dummy to nudge queue
inputQueue->push(inp);
}

View File

@ -101,6 +101,8 @@ public:
visOutQueue->set_max_num_items(1);
}
void terminate();
protected:
DemodulatorThreadInputQueue* inputQueue;
DemodulatorThreadOutputQueue* visOutQueue;
@ -119,4 +121,6 @@ protected:
DemodulatorThreadParameters params;
freqdem fdem;
std::atomic<bool> terminated;
};

View File

@ -4,7 +4,7 @@
#include "CubicSDR.h"
SDRThread::SDRThread(SDRThreadCommandQueue* pQueue) :
m_pQueue(pQueue), iqDataOutQueue(NULL), iqVisualQueue(NULL) {
m_pQueue(pQueue), iqDataOutQueue(NULL), iqVisualQueue(NULL), terminated(false) {
dev = NULL;
sample_rate = SRATE;
}
@ -121,7 +121,7 @@ void SDRThread::threadMain() {
double seconds = 0.0;
std::cout << "Sampling..";
while (1) {
while (!terminated) {
if (!m_pQueue->empty()) {
bool freq_changed = false;
float new_freq;
@ -186,3 +186,6 @@ void SDRThread::threadMain() {
}
void SDRThread::terminate() {
terminated = true;
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <atomic>
#include "wx/wxprec.h"
#include "rtl-sdr.h"
@ -19,7 +21,7 @@ public:
SDR_THREAD_CMD_TUNE
};
SDRThreadCommand() : cmd(cmd), int_value(SDR_THREAD_CMD_NULL){
SDRThreadCommand() : cmd(cmd), int_value(SDR_THREAD_CMD_NULL) {
}
@ -77,6 +79,8 @@ public:
iqVisualQueue = iqVisQueue;
iqVisualQueue->set_max_num_items(1);
}
void terminate();
protected:
uint32_t sample_rate;
@ -85,4 +89,5 @@ protected:
SDRThreadIQDataQueue* iqVisualQueue;
std::vector<DemodulatorInstance *> demodulators;
std::atomic<bool> terminated;
};

View File

@ -54,14 +54,14 @@ void ScopeCanvas::OnKeyDown(wxKeyEvent& event) {
unsigned int freq;
switch (event.GetKeyCode()) {
case WXK_RIGHT:
freq = ((AppFrame*) parent)->getFrequency();
freq = wxGetApp().getFrequency();
freq += 100000;
((AppFrame*) parent)->setFrequency(freq);
wxGetApp().setFrequency(freq);
break;
case WXK_LEFT:
freq = ((AppFrame*) parent)->getFrequency();
wxGetApp().getFrequency();
freq -= 100000;
((AppFrame*) parent)->setFrequency(freq);
wxGetApp().setFrequency(freq);
break;
case WXK_DOWN:
break;

View File

@ -60,14 +60,14 @@ void SpectrumCanvas::OnKeyDown(wxKeyEvent& event) {
unsigned int freq;
switch (event.GetKeyCode()) {
case WXK_RIGHT:
freq = ((AppFrame*) parent)->getFrequency();
freq = wxGetApp().getFrequency();
freq += 100000;
((AppFrame*) parent)->setFrequency(freq);
wxGetApp().setFrequency(freq);
break;
case WXK_LEFT:
freq = ((AppFrame*) parent)->getFrequency();
freq = wxGetApp().getFrequency();
freq -= 100000;
((AppFrame*) parent)->setFrequency(freq);
wxGetApp().setFrequency(freq);
break;
case WXK_DOWN:
break;

View File

@ -60,14 +60,14 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
unsigned int freq;
switch (event.GetKeyCode()) {
case WXK_RIGHT:
freq = ((AppFrame*) parent)->getFrequency();
freq = wxGetApp().getFrequency();
freq += 100000;
((AppFrame*) parent)->setFrequency(freq);
wxGetApp().setFrequency(freq);
break;
case WXK_LEFT:
freq = ((AppFrame*) parent)->getFrequency();
wxGetApp().getFrequency();
freq -= 100000;
((AppFrame*) parent)->setFrequency(freq);
wxGetApp().setFrequency(freq);
break;
case WXK_DOWN:
break;