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() wxEND_EVENT_TABLE()
AppFrame::AppFrame() : AppFrame::AppFrame() :
wxFrame(NULL, wxID_ANY, wxT("CubicSDR")), frequency(DEFAULT_FREQ) { wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) {
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
@ -58,26 +58,6 @@ AppFrame::AppFrame() :
Centre(); Centre();
Show(); 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 }; // static const int attribs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 };
// wxLogStatus("Double-buffered display %s supported", wxGLCanvas::IsDisplaySupported(attribs) ? "is" : "not"); // wxLogStatus("Double-buffered display %s supported", wxGLCanvas::IsDisplaySupported(attribs) ? "is" : "not");
@ -87,9 +67,7 @@ AppFrame::AppFrame() :
AppFrame::~AppFrame() { AppFrame::~AppFrame() {
// delete t_SDR; // delete t_SDR;
delete audioInputQueue;
delete audioThread;
delete threadCmdQueueSDR;
} }
void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) { void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) {
@ -109,9 +87,9 @@ void AppFrame::OnThread(wxCommandEvent& event) {
void AppFrame::OnIdle(wxIdleEvent& event) { void AppFrame::OnIdle(wxIdleEvent& event) {
bool work_done = false; bool work_done = false;
if (!iqVisualQueue->empty()) { if (!wxGetApp().getIQVisualQueue()->empty()) {
SDRThreadIQData iqData; SDRThreadIQData iqData;
iqVisualQueue->pop(iqData); wxGetApp().getIQVisualQueue()->pop(iqData);
if (iqData.data.size()) { if (iqData.data.size()) {
spectrumCanvas->setData(&iqData.data); spectrumCanvas->setData(&iqData.data);
@ -122,9 +100,9 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
work_done = true; work_done = true;
} }
if (!audioVisualQueue->empty()) { if (!wxGetApp().getAudioVisualQueue()->empty()) {
AudioThreadInput demodAudioData; AudioThreadInput demodAudioData;
audioVisualQueue->pop(demodAudioData); wxGetApp().getAudioVisualQueue()->pop(demodAudioData);
if (demodAudioData.data.size()) { if (demodAudioData.data.size()) {
if (scopeCanvas->waveform_points.size() != demodAudioData.data.size() * 2) { if (scopeCanvas->waveform_points.size() != demodAudioData.data.size() * 2) {
@ -146,14 +124,3 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
event.Skip(); 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 "wx/frame.h"
#include "PrimaryGLContext.h" #include "PrimaryGLContext.h"
#include "SDRThread.h"
#include "AudioThread.h"
#include "DemodulatorMgr.h"
#include "ScopeCanvas.h" #include "ScopeCanvas.h"
#include "SpectrumCanvas.h" #include "SpectrumCanvas.h"
#include "WaterfallCanvas.h" #include "WaterfallCanvas.h"
#include "ThreadQueue.h"
// Define a new frame type // Define a new frame type
class AppFrame: public wxFrame { class AppFrame: public wxFrame {
@ -19,8 +16,6 @@ public:
void OnThread(wxCommandEvent& event); void OnThread(wxCommandEvent& event);
void OnEventInput(wxThreadEvent& event); void OnEventInput(wxThreadEvent& event);
void setFrequency(unsigned int freq);
int getFrequency();
private: private:
void OnClose(wxCommandEvent& event); void OnClose(wxCommandEvent& event);
@ -31,23 +26,6 @@ private:
SpectrumCanvas *spectrumCanvas; SpectrumCanvas *spectrumCanvas;
WaterfallCanvas *waterfallCanvas; 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 // event table
wxDECLARE_EVENT_TABLE(); wxDECLARE_EVENT_TABLE();

View File

@ -19,22 +19,56 @@ bool CubicSDR::OnInit() {
if (!wxApp::OnInit()) if (!wxApp::OnInit())
return false; 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(); AppFrame *appframe = new AppFrame();
return true; return true;
} }
int CubicSDR::OnExit() { int CubicSDR::OnExit() {
delete m_glContext; std::cout << "Terminating SDR thread.." << std::endl;
sdrThread->terminate();
threadSDR->join();
// while (1) { delete sdrThread;
// { wxCriticalSectionLocker enter(m_pThreadCS); delete threadSDR;
// if (!m_pThread)
// break; demodMgr.terminateAll();
// }
// // wait for thread completion audioThread->terminate();
// wxThread::This()->Sleep(1); threadAudio->join();
// }
delete audioThread;
delete threadAudio;
delete audioInputQueue;
delete threadCmdQueueSDR;
delete iqVisualQueue;
delete audioVisualQueue;
delete m_glContext;
return wxApp::OnExit(); return wxApp::OnExit();
} }
@ -51,3 +85,13 @@ PrimaryGLContext& CubicSDR::GetContext(wxGLCanvas *canvas) {
return *glContext; 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_MAJOR_VERSION 3
//WX_GL_MINOR_VERSION 2 //WX_GL_MINOR_VERSION 2
#include <thread>
#include "wx/glcanvas.h" #include "wx/glcanvas.h"
#include "PrimaryGLContext.h" #include "PrimaryGLContext.h"
#include "ThreadQueue.h"
#include "SDRThread.h"
#include "AudioThread.h"
#include "DemodulatorMgr.h"
class CubicSDR: public wxApp { class CubicSDR: public wxApp {
public: public:
CubicSDR() { CubicSDR() {
@ -18,9 +25,36 @@ public:
virtual bool OnInit(); virtual bool OnInit();
virtual int OnExit(); virtual int OnExit();
void setFrequency(unsigned int freq);
int getFrequency();
DemodulatorThreadOutputQueue* getAudioVisualQueue() {
return audioVisualQueue;
}
SDRThreadIQDataQueue* getIQVisualQueue() {
return iqVisualQueue;
}
private: private:
PrimaryGLContext *m_glContext; 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) DECLARE_APP(CubicSDR)

View File

@ -3,7 +3,7 @@
#include <vector> #include <vector>
AudioThread::AudioThread(AudioThreadInputQueue *inputQueue) : AudioThread::AudioThread(AudioThreadInputQueue *inputQueue) :
inputQueue(inputQueue), stream(NULL) { inputQueue(inputQueue), stream(NULL), terminated(false) {
} }
@ -41,11 +41,6 @@ void AudioThread::threadMain() {
stream = NULL; stream = NULL;
// err = Pa_OpenStream(&stream, NULL, &outputParameters, AUDIO_FREQUENCY,
// paFramesPerBufferUnspecified,
// paPrimeOutputBuffersUsingStreamCallback | paClipOff, &audioCallback,
// this);
err = Pa_OpenStream(&stream, NULL, &outputParameters, AUDIO_FREQUENCY, err = Pa_OpenStream(&stream, NULL, &outputParameters, AUDIO_FREQUENCY,
paFramesPerBufferUnspecified, paClipOff, NULL, NULL); paFramesPerBufferUnspecified, paClipOff, NULL, NULL);
@ -56,101 +51,18 @@ void AudioThread::threadMain() {
std::cout << "\tPortAudio error: " << Pa_GetErrorText(err) << std::endl; std::cout << "\tPortAudio error: " << Pa_GetErrorText(err) << std::endl;
} }
while (1) { while (!terminated) {
AudioThreadInput inp; AudioThreadInput inp;
inputQueue->pop(inp); inputQueue->pop(inp);
if (inp.data.size()) {
Pa_WriteStream(stream, &inp.data[0], inp.data.size()/2); 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 void AudioThread::terminate() {
static inline std::wstring &wrtrim(std::wstring &s) { std::cout << "Terminating audio thread.." << std::endl;
s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end()); terminated = true;
return s; AudioThreadInput endCond; // push an empty input to bump the queue
inputQueue->push(endCond);
} }
// 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;
}
*/

View File

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

View File

@ -3,16 +3,23 @@
DemodulatorInstance::DemodulatorInstance() : DemodulatorInstance::DemodulatorInstance() :
t_Demod(NULL), threadQueueDemod(NULL), demodulatorThread(NULL) { t_Demod(NULL), threadQueueDemod(NULL), demodulatorThread(NULL) {
} }
DemodulatorInstance::~DemodulatorInstance() {
delete threadQueueDemod;
delete demodulatorThread;
delete t_Demod;
}
void DemodulatorInstance::setVisualOutputQueue(DemodulatorThreadOutputQueue *tQueue) { void DemodulatorInstance::setVisualOutputQueue(DemodulatorThreadOutputQueue *tQueue) {
demodulatorThread->setVisualOutputQueue(tQueue); demodulatorThread->setVisualOutputQueue(tQueue);
} }
void DemodulatorInstance::init() { void DemodulatorInstance::init() {
if (threadQueueDemod) {
delete threadQueueDemod;
}
if (demodulatorThread) { if (demodulatorThread) {
terminate();
delete threadQueueDemod;
delete demodulatorThread; delete demodulatorThread;
delete t_Demod;
} }
threadQueueDemod = new DemodulatorThreadInputQueue; threadQueueDemod = new DemodulatorThreadInputQueue;
@ -21,16 +28,17 @@ void DemodulatorInstance::init() {
t_Demod = new std::thread(&DemodulatorThread::threadMain, demodulatorThread); t_Demod = new std::thread(&DemodulatorThread::threadMain, demodulatorThread);
} }
void DemodulatorInstance::terminate() {
demodulatorThread->terminate();
t_Demod->join();
}
DemodulatorMgr::DemodulatorMgr() { DemodulatorMgr::DemodulatorMgr() {
} }
DemodulatorMgr::~DemodulatorMgr() { DemodulatorMgr::~DemodulatorMgr() {
while (demods.size()) { terminateAll();
DemodulatorInstance *d = demods.back();
demods.pop_back();
delete d;
}
} }
DemodulatorInstance *DemodulatorMgr::newThread() { DemodulatorInstance *DemodulatorMgr::newThread() {
@ -38,3 +46,12 @@ DemodulatorInstance *DemodulatorMgr::newThread() {
demods.push_back(newDemod); demods.push_back(newDemod);
return 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; DemodulatorThreadParameters params;
DemodulatorInstance(); DemodulatorInstance();
~DemodulatorInstance();
void setVisualOutputQueue(DemodulatorThreadOutputQueue *tQueue); void setVisualOutputQueue(DemodulatorThreadOutputQueue *tQueue);
void init(); void init();
void terminate();
}; };
class DemodulatorMgr { class DemodulatorMgr {
@ -25,6 +27,7 @@ public:
DemodulatorInstance *newThread(); DemodulatorInstance *newThread();
void terminateAll();
private: private:
std::vector<DemodulatorInstance *> demods; std::vector<DemodulatorInstance *> demods;
}; };

View File

@ -3,7 +3,7 @@
#include <vector> #include <vector>
DemodulatorThread::DemodulatorThread(DemodulatorThreadInputQueue* pQueue, DemodulatorThreadParameters *params_in) : DemodulatorThread::DemodulatorThread(DemodulatorThreadInputQueue* pQueue, DemodulatorThreadParameters *params_in) :
inputQueue(pQueue), visOutQueue(NULL) { inputQueue(pQueue), visOutQueue(NULL), terminated(false) {
DemodulatorThreadParameters defaultParams; DemodulatorThreadParameters defaultParams;
if (!params_in) { if (!params_in) {
@ -55,7 +55,7 @@ DemodulatorThread::~DemodulatorThread() {
void DemodulatorThread::threadMain() { void DemodulatorThread::threadMain() {
while (1) { while (!terminated) {
DemodulatorThreadIQData inp; DemodulatorThreadIQData inp;
inputQueue->pop(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); visOutQueue->set_max_num_items(1);
} }
void terminate();
protected: protected:
DemodulatorThreadInputQueue* inputQueue; DemodulatorThreadInputQueue* inputQueue;
DemodulatorThreadOutputQueue* visOutQueue; DemodulatorThreadOutputQueue* visOutQueue;
@ -119,4 +121,6 @@ protected:
DemodulatorThreadParameters params; DemodulatorThreadParameters params;
freqdem fdem; freqdem fdem;
std::atomic<bool> terminated;
}; };

View File

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

View File

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

View File

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

View File

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

View File

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