From cae1855fc5421f2ba60049cc005924f8d3ca086a Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Sun, 23 Nov 2014 19:39:27 -0500 Subject: [PATCH] Move code out of frame, proper thread termination --- src/AppFrame.cpp | 45 ++----------- src/AppFrame.h | 24 +------ src/CubicSDR.cpp | 62 +++++++++++++++--- src/CubicSDR.h | 34 ++++++++++ src/audio/AudioThread.cpp | 110 ++++---------------------------- src/audio/AudioThread.h | 4 +- src/demod/DemodulatorMgr.cpp | 33 +++++++--- src/demod/DemodulatorMgr.h | 3 + src/demod/DemodulatorThread.cpp | 10 ++- src/demod/DemodulatorThread.h | 4 ++ src/sdr/SDRThread.cpp | 7 +- src/sdr/SDRThread.h | 7 +- src/visual/ScopeCanvas.cpp | 8 +-- src/visual/SpectrumCanvas.cpp | 8 +-- src/visual/WaterfallCanvas.cpp | 8 +-- 15 files changed, 171 insertions(+), 196 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 22253c1..98166da 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -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; -} diff --git a/src/AppFrame.h b/src/AppFrame.h index 3fc8c75..26cc57b 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -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(); diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index 54dba5f..26b31e1 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -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; +} diff --git a/src/CubicSDR.h b/src/CubicSDR.h index 3ecbd5c..74cf702 100644 --- a/src/CubicSDR.h +++ b/src/CubicSDR.h @@ -4,9 +4,16 @@ //WX_GL_MAJOR_VERSION 3 //WX_GL_MINOR_VERSION 2 +#include + #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) diff --git a/src/audio/AudioThread.cpp b/src/audio/AudioThread.cpp index 6949c9e..4753dfc 100644 --- a/src/audio/AudioThread.cpp +++ b/src/audio/AudioThread.cpp @@ -3,7 +3,7 @@ #include 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 - #include - #include - #include - #include - - // 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(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(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); +} diff --git a/src/audio/AudioThread.h b/src/audio/AudioThread.h index de23239..57c3a86 100644 --- a/src/audio/AudioThread.h +++ b/src/audio/AudioThread.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "wx/wxprec.h" #ifndef WX_PRECOMP @@ -28,14 +29,15 @@ typedef ThreadQueue AudioThreadInputQueue; class AudioThread { public: AudioThread(AudioThreadInputQueue *inputQueue); - ~AudioThread(); void threadMain(); + void terminate(); private: AudioThreadInputQueue *inputQueue; PaStreamParameters outputParameters; PaStream *stream; + std::atomic terminated; }; diff --git a/src/demod/DemodulatorMgr.cpp b/src/demod/DemodulatorMgr.cpp index 9dd94c4..03a642c 100644 --- a/src/demod/DemodulatorMgr.cpp +++ b/src/demod/DemodulatorMgr.cpp @@ -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; + } +} diff --git a/src/demod/DemodulatorMgr.h b/src/demod/DemodulatorMgr.h index 39b02a8..5651ab6 100644 --- a/src/demod/DemodulatorMgr.h +++ b/src/demod/DemodulatorMgr.h @@ -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 demods; }; diff --git a/src/demod/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp index 26451b2..b6be1d2 100644 --- a/src/demod/DemodulatorThread.cpp +++ b/src/demod/DemodulatorThread.cpp @@ -3,7 +3,7 @@ #include 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); +} diff --git a/src/demod/DemodulatorThread.h b/src/demod/DemodulatorThread.h index 8f6bb39..3aed769 100644 --- a/src/demod/DemodulatorThread.h +++ b/src/demod/DemodulatorThread.h @@ -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 terminated; }; diff --git a/src/sdr/SDRThread.cpp b/src/sdr/SDRThread.cpp index ccafa0a..7c8f515 100644 --- a/src/sdr/SDRThread.cpp +++ b/src/sdr/SDRThread.cpp @@ -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; +} diff --git a/src/sdr/SDRThread.h b/src/sdr/SDRThread.h index 5327a25..4f1dd51 100644 --- a/src/sdr/SDRThread.h +++ b/src/sdr/SDRThread.h @@ -1,5 +1,7 @@ #pragma once +#include + #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 demodulators; + std::atomic terminated; }; diff --git a/src/visual/ScopeCanvas.cpp b/src/visual/ScopeCanvas.cpp index b1d6dba..ad877fe 100644 --- a/src/visual/ScopeCanvas.cpp +++ b/src/visual/ScopeCanvas.cpp @@ -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; diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index 7573574..81ccba7 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -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; diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 1d073ed..c5ce824 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -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;