Merge pull request #6 from cjcliffe/threading_improvements
Threading improvements
This commit is contained in:
commit
c465a5687d
|
@ -85,7 +85,6 @@ endif (DEFINED WIN32)
|
||||||
SET (cubicsdr_sources
|
SET (cubicsdr_sources
|
||||||
src/CubicSDR.cpp
|
src/CubicSDR.cpp
|
||||||
src/SDRThread.cpp
|
src/SDRThread.cpp
|
||||||
src/IQBufferThread.cpp
|
|
||||||
src/PrimaryGLContext.cpp
|
src/PrimaryGLContext.cpp
|
||||||
src/AppFrame.cpp
|
src/AppFrame.cpp
|
||||||
src/SDRThreadQueue.cpp
|
src/SDRThreadQueue.cpp
|
||||||
|
@ -109,7 +108,6 @@ SET (cubicsdr_sources
|
||||||
SET (cubicsdr_headers
|
SET (cubicsdr_headers
|
||||||
src/CubicSDR.h
|
src/CubicSDR.h
|
||||||
src/SDRThread.h
|
src/SDRThread.h
|
||||||
src/IQBufferThread.h
|
|
||||||
src/PrimaryGLContext.h
|
src/PrimaryGLContext.h
|
||||||
src/AppFrame.h
|
src/AppFrame.h
|
||||||
src/CubicSDRDefs.h
|
src/CubicSDRDefs.h
|
||||||
|
|
116
src/AppFrame.cpp
116
src/AppFrame.cpp
|
@ -19,9 +19,7 @@
|
||||||
wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
|
wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
|
||||||
//EVT_MENU(wxID_NEW, AppFrame::OnNewWindow)
|
//EVT_MENU(wxID_NEW, AppFrame::OnNewWindow)
|
||||||
EVT_MENU(wxID_CLOSE, AppFrame::OnClose)
|
EVT_MENU(wxID_CLOSE, AppFrame::OnClose)
|
||||||
EVT_THREAD(EVENT_SDR_INPUT, AppFrame::OnEventInput)
|
EVT_COMMAND(wxID_ANY, wxEVT_THREAD, AppFrame::OnThread)
|
||||||
EVT_THREAD(EVENT_DEMOD_INPUT, AppFrame::OnDemodInput)
|
|
||||||
EVT_THREAD(EVENT_AUDIO_INPUT, AppFrame::OnAudioInput)
|
|
||||||
EVT_IDLE(AppFrame::OnIdle)
|
EVT_IDLE(AppFrame::OnIdle)
|
||||||
wxEND_EVENT_TABLE()
|
wxEND_EVENT_TABLE()
|
||||||
|
|
||||||
|
@ -85,14 +83,6 @@ AppFrame::AppFrame() :
|
||||||
t_Audio = NULL;
|
t_Audio = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// t_IQBuffer = new IQBufferThread(this);
|
|
||||||
// if (t_IQBuffer->Run() != wxTHREAD_NO_ERROR) {
|
|
||||||
// wxLogError
|
|
||||||
// ("Can't create the thread!");
|
|
||||||
// delete t_IQBuffer;
|
|
||||||
// t_IQBuffer = NULL;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 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");
|
||||||
// ShowFullScreen(true);
|
// ShowFullScreen(true);
|
||||||
|
@ -110,19 +100,7 @@ AppFrame::~AppFrame() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// {
|
|
||||||
// wxCriticalSectionLocker enter(m_pThreadCS);
|
|
||||||
// if (t_IQBuffer) {
|
|
||||||
// wxMessageOutputDebug().Printf("CubicSDR: deleting thread");
|
|
||||||
// if (t_IQBuffer->Delete() != wxTHREAD_NO_ERROR) {
|
|
||||||
// wxLogError
|
|
||||||
// ("Can't delete the thread!");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// delete t_SDR;
|
// delete t_SDR;
|
||||||
// delete t_IQBuffer;
|
|
||||||
delete threadQueueAudio;
|
delete threadQueueAudio;
|
||||||
delete threadQueueDemod;
|
delete threadQueueDemod;
|
||||||
delete threadQueueSDR;
|
delete threadQueueSDR;
|
||||||
|
@ -138,66 +116,64 @@ void AppFrame::OnNewWindow(wxCommandEvent& WXUNUSED(event)) {
|
||||||
new AppFrame();
|
new AppFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
// SDR IQ -> Demodulator
|
void AppFrame::OnThread(wxCommandEvent& event) {
|
||||||
void AppFrame::OnEventInput(wxThreadEvent& event) {
|
SDRThreadIQData *iqData;
|
||||||
std::vector<signed char> *new_buffer = event.GetPayload<std::vector<signed char> *>();
|
DemodulatorThreadAudioData *demodAudioData;
|
||||||
// std::cout << new_buffer->size() << std::endl;
|
|
||||||
if (new_buffer->size()) {
|
|
||||||
DemodulatorThreadTask task = DemodulatorThreadTask(DemodulatorThreadTask::DEMOD_THREAD_DATA);
|
|
||||||
task.setData(*new_buffer);
|
|
||||||
threadQueueDemod->addTask(task, DemodulatorThreadQueue::DEMOD_PRIORITY_HIGHEST);
|
|
||||||
|
|
||||||
// test_demod.writeBuffer(new_buffer);
|
std::vector<signed char> *new_uc_buffer;
|
||||||
// scopeCanvas->setWaveformPoints(test_demod.waveform_points);
|
std::vector<float> *new_float_buffer;
|
||||||
spectrumCanvas->setData(new_buffer);
|
|
||||||
waterfallCanvas->setData(new_buffer);
|
|
||||||
} else {
|
|
||||||
std::cout << "Incoming IQ data empty?" << std::endl;
|
|
||||||
}
|
|
||||||
delete new_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Demodulator -> Audio
|
switch (event.GetId()) {
|
||||||
void AppFrame::OnDemodInput(wxThreadEvent& event) {
|
|
||||||
std::vector<float> *new_buffer = event.GetPayload<std::vector<float> *>();
|
|
||||||
|
|
||||||
if (new_buffer->size()) {
|
// SDR IQ -> Demodulator
|
||||||
AudioThreadTask task = AudioThreadTask(AudioThreadTask::AUDIO_THREAD_DATA);
|
case SDRThreadTask::SDR_THREAD_DATA:
|
||||||
task.setData(*new_buffer);
|
iqData = (SDRThreadIQData *) event.GetClientData();
|
||||||
threadQueueAudio->addTask(task, AudioThreadQueue::AUDIO_PRIORITY_HIGHEST);
|
new_uc_buffer = &(iqData->data);
|
||||||
|
if (new_uc_buffer->size()) {
|
||||||
|
DemodulatorThreadTask task = DemodulatorThreadTask(DemodulatorThreadTask::DEMOD_THREAD_DATA);
|
||||||
|
task.data = new DemodulatorThreadIQData(iqData->bandwidth, iqData->frequency, iqData->data);
|
||||||
|
threadQueueDemod->addTask(task, DemodulatorThreadQueue::DEMOD_PRIORITY_HIGHEST);
|
||||||
|
|
||||||
if (scopeCanvas->waveform_points.size() != new_buffer->size() * 2) {
|
spectrumCanvas->setData(new_uc_buffer);
|
||||||
scopeCanvas->waveform_points.resize(new_buffer->size() * 2);
|
waterfallCanvas->setData(new_uc_buffer);
|
||||||
|
} else {
|
||||||
|
std::cout << "Incoming IQ data empty?" << std::endl;
|
||||||
}
|
}
|
||||||
|
delete iqData;
|
||||||
|
|
||||||
for (int i = 0, iMax = new_buffer->size(); i < iMax; i++) {
|
break; // thread wants to exit: disable controls and destroy main window
|
||||||
scopeCanvas->waveform_points[i * 2 + 1] = (*new_buffer)[i] * 0.5f;
|
|
||||||
scopeCanvas->waveform_points[i * 2] = ((double) i / (double) iMax);
|
// Demodulator -> Audio
|
||||||
|
case DemodulatorThreadTask::DEMOD_THREAD_AUDIO_DATA:
|
||||||
|
demodAudioData = (DemodulatorThreadAudioData *) event.GetClientData();
|
||||||
|
new_float_buffer = &(demodAudioData->data);
|
||||||
|
if (new_float_buffer->size()) {
|
||||||
|
AudioThreadTask task = AudioThreadTask(AudioThreadTask::AUDIO_THREAD_DATA);
|
||||||
|
task.data = new AudioThreadData(demodAudioData->frequency, demodAudioData->sampleRate, demodAudioData->data);
|
||||||
|
threadQueueAudio->addTask(task, AudioThreadQueue::AUDIO_PRIORITY_HIGHEST);
|
||||||
|
|
||||||
|
if (scopeCanvas->waveform_points.size() != new_float_buffer->size() * 2) {
|
||||||
|
scopeCanvas->waveform_points.resize(new_float_buffer->size() * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0, iMax = new_float_buffer->size(); i < iMax; i++) {
|
||||||
|
scopeCanvas->waveform_points[i * 2 + 1] = (*new_float_buffer)[i] * 0.5f;
|
||||||
|
scopeCanvas->waveform_points[i * 2] = ((double) i / (double) iMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
std::cout << "Incoming Demodulator data empty?" << std::endl;
|
||||||
}
|
}
|
||||||
|
delete demodAudioData;
|
||||||
|
|
||||||
} else {
|
break;
|
||||||
std::cout << "Incoming Demod data empty?" << std::endl;
|
default:
|
||||||
|
event.Skip();
|
||||||
}
|
}
|
||||||
delete new_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Audio -> Visual
|
|
||||||
void AppFrame::OnAudioInput(wxThreadEvent& event) {
|
|
||||||
// std::vector<float> *new_buffer = event.GetPayload<std::vector<float> *>();
|
|
||||||
//
|
|
||||||
// if (new_buffer->size()) {
|
|
||||||
// AudioThreadTask task = AudioThreadTask(AudioThreadTask::AUDIO_THREAD_DATA);
|
|
||||||
// task.setData(*new_buffer);
|
|
||||||
// threadQueueAudio->addTask(task, AudioThreadQueue::AUDIO_PRIORITY_HIGHEST);
|
|
||||||
// } else {
|
|
||||||
// std::cout << "Incoming Demod data empty?" << std::endl;
|
|
||||||
// }
|
|
||||||
// delete new_buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppFrame::OnIdle(wxIdleEvent& event) {
|
void AppFrame::OnIdle(wxIdleEvent& event) {
|
||||||
|
|
||||||
wxGetApp().Yield();
|
|
||||||
event.Skip();
|
event.Skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,8 @@ class AppFrame: public wxFrame {
|
||||||
public:
|
public:
|
||||||
AppFrame();
|
AppFrame();
|
||||||
~AppFrame();
|
~AppFrame();
|
||||||
|
void OnThread (wxCommandEvent& event);
|
||||||
void OnEventInput(wxThreadEvent& event);
|
void OnEventInput(wxThreadEvent& event);
|
||||||
void OnDemodInput(wxThreadEvent& event);
|
|
||||||
void OnAudioInput(wxThreadEvent& event);
|
|
||||||
|
|
||||||
|
|
||||||
void setFrequency(unsigned int freq);
|
void setFrequency(unsigned int freq);
|
||||||
|
@ -39,7 +38,6 @@ private:
|
||||||
DemodulatorThread *t_Demod;
|
DemodulatorThread *t_Demod;
|
||||||
DemodulatorThreadQueue* threadQueueDemod;
|
DemodulatorThreadQueue* threadQueueDemod;
|
||||||
|
|
||||||
// IQBufferThread *t_IQBuffer;
|
|
||||||
wxCriticalSection m_pThreadCS;
|
wxCriticalSection m_pThreadCS;
|
||||||
unsigned int frequency;
|
unsigned int frequency;
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
//wxDEFINE_EVENT(wxEVT_COMMAND_AudioThread_INPUT, wxThreadEvent);
|
//wxDEFINE_EVENT(wxEVT_COMMAND_AudioThread_INPUT, wxThreadEvent);
|
||||||
|
|
||||||
AudioThread::AudioThread(AudioThreadQueue* pQueue, int id) :
|
AudioThread::AudioThread(AudioThreadQueue* pQueue, int id) :
|
||||||
wxThread(wxTHREAD_DETACHED), m_pQueue(pQueue), m_ID(id), audio_queue_ptr(0) {
|
wxThread(wxTHREAD_DETACHED), m_pQueue(pQueue), m_ID(id), audio_queue_ptr(0), stream(NULL) {
|
||||||
|
|
||||||
}
|
}
|
||||||
AudioThread::~AudioThread() {
|
AudioThread::~AudioThread() {
|
||||||
|
@ -107,7 +107,10 @@ wxThread::ExitCode AudioThread::Entry() {
|
||||||
AudioThreadTask task = m_pQueue->pop(); // pop a task from the queue. this will block the worker thread if queue is empty
|
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) {
|
switch (task.m_cmd) {
|
||||||
case AudioThreadTask::AUDIO_THREAD_DATA:
|
case AudioThreadTask::AUDIO_THREAD_DATA:
|
||||||
audio_queue.push(task.getData());
|
if (!TestDestroy()) {
|
||||||
|
audio_queue.push(task.data->data);
|
||||||
|
}
|
||||||
|
delete task.data;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,2 @@
|
||||||
#include "AudioThreadTask.h"
|
#include "AudioThreadTask.h"
|
||||||
|
|
||||||
|
|
||||||
void AudioThreadTask::setData(std::vector<float> &data_in) {
|
|
||||||
data = data_in;
|
|
||||||
}
|
|
||||||
std::vector<float> &AudioThreadTask::getData() {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,25 +4,45 @@
|
||||||
|
|
||||||
#include "wx/defs.h"
|
#include "wx/defs.h"
|
||||||
#include "wx/string.h"
|
#include "wx/string.h"
|
||||||
|
#include "wx/object.h"
|
||||||
|
|
||||||
|
class AudioThreadData: public wxObject {
|
||||||
|
public:
|
||||||
|
unsigned int frequency;
|
||||||
|
unsigned int sampleRate;
|
||||||
|
unsigned short channels;
|
||||||
|
|
||||||
|
std::vector<float> data;
|
||||||
|
|
||||||
|
AudioThreadData(unsigned int frequency, unsigned int sampleRate, std::vector<float> data) :
|
||||||
|
data(data), sampleRate(sampleRate), frequency(frequency) {
|
||||||
|
channels = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
~AudioThreadData() {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class AudioThreadTask {
|
class AudioThreadTask {
|
||||||
public:
|
public:
|
||||||
enum AUDIO_THREAD_COMMAND {
|
enum AUDIO_THREAD_COMMAND {
|
||||||
AUDIO_THREAD_EXIT = wxID_EXIT, AUDIO_THREAD_NULL = wxID_HIGHEST + 1, AUDIO_THREAD_STARTED, AUDIO_THREAD_PROCESS, AUDIO_THREAD_ERROR, AUDIO_THREAD_DATA
|
AUDIO_THREAD_EXIT = wxID_EXIT,
|
||||||
|
AUDIO_THREAD_NULL = wxID_HIGHEST + 200,
|
||||||
|
AUDIO_THREAD_STARTED,
|
||||||
|
AUDIO_THREAD_PROCESS,
|
||||||
|
AUDIO_THREAD_ERROR,
|
||||||
|
AUDIO_THREAD_DATA
|
||||||
};
|
};
|
||||||
|
|
||||||
AudioThreadTask() :
|
AudioThreadTask() :
|
||||||
m_cmd(AUDIO_THREAD_NULL) {
|
m_cmd(AUDIO_THREAD_NULL), data(NULL) {
|
||||||
}
|
}
|
||||||
AudioThreadTask(AUDIO_THREAD_COMMAND cmd) :
|
AudioThreadTask(AUDIO_THREAD_COMMAND cmd) :
|
||||||
m_cmd(cmd) {
|
m_cmd(cmd), data(NULL) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setData(std::vector<float> &data_in);
|
AudioThreadData *data;
|
||||||
std::vector<float> &getData();
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<float> data;
|
|
||||||
|
|
||||||
AUDIO_THREAD_COMMAND m_cmd;
|
AUDIO_THREAD_COMMAND m_cmd;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#include "CubicSDRDefs.h"
|
#include "CubicSDRDefs.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
//wxDEFINE_EVENT(wxEVT_COMMAND_DemodulatorThread_INPUT, wxThreadEvent);
|
|
||||||
|
|
||||||
DemodulatorThread::DemodulatorThread(DemodulatorThreadQueue* pQueue, int id) :
|
DemodulatorThread::DemodulatorThread(DemodulatorThreadQueue* pQueue, int id) :
|
||||||
wxThread(wxTHREAD_DETACHED), m_pQueue(pQueue), m_ID(id) {
|
wxThread(wxTHREAD_DETACHED), m_pQueue(pQueue), m_ID(id) {
|
||||||
|
@ -61,7 +60,7 @@ wxThread::ExitCode DemodulatorThread::Entry() {
|
||||||
DemodulatorThreadTask task = m_pQueue->pop(); // pop a task from the queue. this will block the worker thread if queue is empty
|
DemodulatorThreadTask task = m_pQueue->pop(); // pop a task from the queue. this will block the worker thread if queue is empty
|
||||||
switch (task.m_cmd) {
|
switch (task.m_cmd) {
|
||||||
case DemodulatorThreadTask::DEMOD_THREAD_DATA:
|
case DemodulatorThreadTask::DEMOD_THREAD_DATA:
|
||||||
std::vector<signed char> *data = &task.getData();
|
std::vector<signed char> *data = &task.data->data;
|
||||||
if (data->size()) {
|
if (data->size()) {
|
||||||
liquid_float_complex filtered_input[BUF_SIZE / 2];
|
liquid_float_complex filtered_input[BUF_SIZE / 2];
|
||||||
|
|
||||||
|
@ -122,43 +121,24 @@ wxThread::ExitCode DemodulatorThread::Entry() {
|
||||||
unsigned int num_audio_written;
|
unsigned int num_audio_written;
|
||||||
msresamp_crcf_execute(audio_resampler, resampled_wbfm_output, num_wbfm_written, resampled_audio_output, &num_audio_written);
|
msresamp_crcf_execute(audio_resampler, resampled_wbfm_output, num_wbfm_written, resampled_audio_output, &num_audio_written);
|
||||||
|
|
||||||
std::vector<float> *newBuffer = new std::vector<float>;
|
std::vector<float> newBuffer;
|
||||||
newBuffer->resize(num_audio_written * 2);
|
newBuffer.resize(num_audio_written * 2);
|
||||||
for (int i = 0; i < num_audio_written; i++) {
|
for (int i = 0; i < num_audio_written; i++) {
|
||||||
liquid_float_complex y = resampled_audio_output[i];
|
liquid_float_complex y = resampled_audio_output[i];
|
||||||
|
|
||||||
(*newBuffer)[i * 2] = y.real;
|
newBuffer[i * 2] = y.real;
|
||||||
(*newBuffer)[i * 2 + 1] = y.real;
|
newBuffer[i * 2 + 1] = y.real;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!TestDestroy()) {
|
if (!TestDestroy()) {
|
||||||
wxThreadEvent event(wxEVT_THREAD, EVENT_DEMOD_INPUT);
|
DemodulatorThreadAudioData *audioOut = new DemodulatorThreadAudioData(task.data->frequency,audio_frequency,newBuffer);
|
||||||
event.SetPayload(newBuffer);
|
|
||||||
wxQueueEvent(m_pQueue->getHandler(), event.Clone());
|
m_pQueue->sendAudioData(DemodulatorThreadTask::DEMOD_THREAD_AUDIO_DATA,audioOut);
|
||||||
} else {
|
|
||||||
delete newBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::vector<float> *newBuffer = new std::vector<float>;
|
delete task.data;
|
||||||
// newBuffer->resize(num_audio_written * 2);
|
|
||||||
// for (int i = 0; i < num_audio_written; i++) {
|
|
||||||
// liquid_float_complex y = resampled_audio_output[i];
|
|
||||||
//
|
|
||||||
// (*newBuffer)[i * 2] = y.real;
|
|
||||||
// (*newBuffer)[i * 2 + 1] = y.real;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (waveform_points.size() != num_audio_written * 2) {
|
|
||||||
// waveform_points.resize(num_audio_written * 2);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for (int i = 0, iMax = waveform_points.size() / 2; i < iMax; i++) {
|
|
||||||
// waveform_points[i * 2 + 1] = resampled_audio_output[i].real * 0.5f;
|
|
||||||
// waveform_points[i * 2] = ((double) i / (double) iMax);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
// audio_queue.push(newBuffer);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,8 @@
|
||||||
#include "wx/thread.h"
|
#include "wx/thread.h"
|
||||||
|
|
||||||
#include "DemodulatorThreadQueue.h"
|
#include "DemodulatorThreadQueue.h"
|
||||||
|
|
||||||
#include "liquid/liquid.h"
|
#include "liquid/liquid.h"
|
||||||
|
|
||||||
// declare a new type of event, to be used by our DemodulatorThread class:
|
|
||||||
//wxDECLARE_EVENT(wxEVT_COMMAND_DemodulatorThread_COMPLETED, wxThreadEvent);
|
|
||||||
//wxDECLARE_EVENT(wxEVT_COMMAND_DemodulatorThread_UPDATE, wxThreadEvent);
|
|
||||||
//wxDECLARE_EVENT(wxEVT_COMMAND_DemodulatorThread_INPUT, wxThreadEvent);
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
EVENT_DEMOD_INPUT = wxID_HIGHEST + 2
|
EVENT_DEMOD_INPUT = wxID_HIGHEST + 2
|
||||||
|
@ -25,9 +20,6 @@ enum {
|
||||||
|
|
||||||
class DemodulatorThread: public wxThread {
|
class DemodulatorThread: public wxThread {
|
||||||
public:
|
public:
|
||||||
std::queue<std::vector<float> *> audio_queue;
|
|
||||||
unsigned int audio_queue_ptr;
|
|
||||||
|
|
||||||
DemodulatorThread(DemodulatorThreadQueue* pQueue, int id = 0);
|
DemodulatorThread(DemodulatorThreadQueue* pQueue, int id = 0);
|
||||||
~DemodulatorThread();
|
~DemodulatorThread();
|
||||||
|
|
||||||
|
@ -40,18 +32,16 @@ protected:
|
||||||
firfilt_crcf fir_audio_filter;
|
firfilt_crcf fir_audio_filter;
|
||||||
|
|
||||||
unsigned int bandwidth;
|
unsigned int bandwidth;
|
||||||
|
|
||||||
msresamp_crcf resampler;
|
msresamp_crcf resampler;
|
||||||
float resample_ratio;
|
float resample_ratio;
|
||||||
|
|
||||||
|
unsigned int wbfm_frequency;
|
||||||
msresamp_crcf wbfm_resampler;
|
msresamp_crcf wbfm_resampler;
|
||||||
float wbfm_resample_ratio;
|
float wbfm_resample_ratio;
|
||||||
unsigned int wbfm_frequency;
|
|
||||||
|
|
||||||
|
unsigned int audio_frequency;
|
||||||
msresamp_crcf audio_resampler;
|
msresamp_crcf audio_resampler;
|
||||||
float audio_resample_ratio;
|
float audio_resample_ratio;
|
||||||
|
|
||||||
unsigned int audio_frequency;
|
|
||||||
|
|
||||||
freqdem fdem;
|
freqdem fdem;
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,10 +26,11 @@ DemodulatorThreadTask DemodulatorThreadQueue::pop() {
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DemodulatorThreadQueue::report(const DemodulatorThreadTask::DEMOD_THREAD_COMMAND& cmd, const wxString& sArg, int iArg) {
|
void DemodulatorThreadQueue::sendAudioData(const DemodulatorThreadTask::DEMOD_THREAD_COMMAND& cmd, DemodulatorThreadAudioData *data) {
|
||||||
wxCommandEvent evt(wxEVT_THREAD, cmd);
|
wxCommandEvent evt(wxEVT_THREAD, cmd);
|
||||||
evt.SetString(sArg);
|
|
||||||
evt.SetInt(iArg);
|
evt.SetClientData(data);
|
||||||
|
|
||||||
m_pParent->AddPendingEvent(evt);
|
m_pParent->AddPendingEvent(evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ public:
|
||||||
DemodulatorThreadQueue(wxEvtHandler* pParent);
|
DemodulatorThreadQueue(wxEvtHandler* pParent);
|
||||||
|
|
||||||
void addTask(const DemodulatorThreadTask& task, const DEMOD_PRIORITY& priority = DEMOD_PRIORITY_NORMAL);
|
void addTask(const DemodulatorThreadTask& task, const DEMOD_PRIORITY& priority = DEMOD_PRIORITY_NORMAL);
|
||||||
void report(const DemodulatorThreadTask::DEMOD_THREAD_COMMAND& cmd, const wxString& sArg = wxEmptyString, int iArg = 0);
|
void sendAudioData(const DemodulatorThreadTask::DEMOD_THREAD_COMMAND& cmd, DemodulatorThreadAudioData *data);
|
||||||
|
|
||||||
DemodulatorThreadTask pop();
|
DemodulatorThreadTask pop();
|
||||||
size_t stackSize();
|
size_t stackSize();
|
||||||
|
|
|
@ -1,8 +1,2 @@
|
||||||
#include "DemodulatorThreadTask.h"
|
#include "DemodulatorThreadTask.h"
|
||||||
|
|
||||||
void DemodulatorThreadTask::setData(std::vector<signed char> &data_in) {
|
|
||||||
data = data_in;
|
|
||||||
}
|
|
||||||
std::vector<signed char> &DemodulatorThreadTask::getData() {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,24 +3,62 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "wx/defs.h"
|
#include "wx/defs.h"
|
||||||
#include "wx/string.h"
|
#include "wx/string.h"
|
||||||
|
#include "wx/object.h"
|
||||||
|
|
||||||
|
class DemodulatorThreadIQData: public wxObject {
|
||||||
|
public:
|
||||||
|
unsigned int frequency;
|
||||||
|
unsigned int bandwidth;
|
||||||
|
std::vector<signed char> data;
|
||||||
|
|
||||||
|
DemodulatorThreadIQData(unsigned int bandwidth, unsigned int frequency, std::vector<signed char> data) :
|
||||||
|
data(data), frequency(frequency), bandwidth(bandwidth) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~DemodulatorThreadIQData() {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DemodulatorThreadAudioData: public wxObject {
|
||||||
|
public:
|
||||||
|
unsigned int frequency;
|
||||||
|
unsigned int sampleRate;
|
||||||
|
unsigned char channels;
|
||||||
|
|
||||||
|
std::vector<float> data;
|
||||||
|
|
||||||
|
DemodulatorThreadAudioData(unsigned int frequency, unsigned int sampleRate, std::vector<float> data) :
|
||||||
|
data(data), sampleRate(sampleRate), frequency(frequency), channels(1) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~DemodulatorThreadAudioData() {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class DemodulatorThreadTask {
|
class DemodulatorThreadTask {
|
||||||
public:
|
public:
|
||||||
enum DEMOD_THREAD_COMMAND {
|
enum DEMOD_THREAD_COMMAND {
|
||||||
DEMOD_THREAD_EXIT = wxID_EXIT, DEMOD_THREAD_NULL = wxID_HIGHEST + 1, DEMOD_THREAD_STARTED, DEMOD_THREAD_PROCESS, DEMOD_THREAD_ERROR, DEMOD_THREAD_DATA
|
DEMOD_THREAD_EXIT = wxID_EXIT,
|
||||||
|
DEMOD_THREAD_NULL = wxID_HIGHEST + 100,
|
||||||
|
DEMOD_THREAD_STARTED,
|
||||||
|
DEMOD_THREAD_PROCESS,
|
||||||
|
DEMOD_THREAD_ERROR,
|
||||||
|
DEMOD_THREAD_DATA,
|
||||||
|
DEMOD_THREAD_AUDIO_DATA
|
||||||
};
|
};
|
||||||
|
|
||||||
DemodulatorThreadTask() :
|
DemodulatorThreadTask() :
|
||||||
m_cmd(DEMOD_THREAD_NULL) {
|
m_cmd(DEMOD_THREAD_NULL), data(NULL) {
|
||||||
}
|
}
|
||||||
DemodulatorThreadTask(DEMOD_THREAD_COMMAND cmd) :
|
DemodulatorThreadTask(DEMOD_THREAD_COMMAND cmd) :
|
||||||
m_cmd(cmd) {
|
m_cmd(cmd), data(NULL) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setData(std::vector<signed char> &data_in);
|
|
||||||
std::vector<signed char> &getData();
|
|
||||||
|
|
||||||
DEMOD_THREAD_COMMAND m_cmd;
|
DEMOD_THREAD_COMMAND m_cmd;
|
||||||
|
|
||||||
std::vector<signed char> data;
|
DemodulatorThreadIQData *data;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
#include "IQBufferThread.h"
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include "wx/wxprec.h"
|
|
||||||
|
|
||||||
#ifndef WX_PRECOMP
|
|
||||||
#include "wx/wx.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !wxUSE_GLCANVAS
|
|
||||||
#error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "CubicSDRDefs.h"
|
|
||||||
|
|
||||||
IQBufferThread::IQBufferThread(wxApp *app) :
|
|
||||||
wxThread(wxTHREAD_DETACHED) {
|
|
||||||
this->handler = handler;
|
|
||||||
}
|
|
||||||
IQBufferThread::~IQBufferThread() {
|
|
||||||
|
|
||||||
}
|
|
||||||
wxThread::ExitCode IQBufferThread::Entry() {
|
|
||||||
signed char *buf = (signed char*) malloc(BUF_SIZE);
|
|
||||||
|
|
||||||
int n_read;
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
// std::cout << "Sampling..";
|
|
||||||
while (!TestDestroy()) {
|
|
||||||
//
|
|
||||||
// iq_buffer.push(new_buffer);
|
|
||||||
//
|
|
||||||
// if (iq_buffer.size() > 100) {
|
|
||||||
// for (int i = 0; i < 50; i++) {
|
|
||||||
// std::vector<__int8> *old_buffer = iq_buffer.front();
|
|
||||||
// iq_buffer.pop();
|
|
||||||
// delete old_buffer;
|
|
||||||
// }
|
|
||||||
// std::cout << "#";
|
|
||||||
// }
|
|
||||||
this->Sleep(100);
|
|
||||||
}
|
|
||||||
std::cout << std::endl << "Done." << std::endl << std::endl;
|
|
||||||
|
|
||||||
free(buf);
|
|
||||||
|
|
||||||
return (wxThread::ExitCode) 0;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "wx/wxprec.h"
|
|
||||||
|
|
||||||
#ifndef WX_PRECOMP
|
|
||||||
#include "wx/wx.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "wx/thread.h"
|
|
||||||
#include "SDRThread.h"
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
class IQBufferThread: public wxThread {
|
|
||||||
public:
|
|
||||||
|
|
||||||
IQBufferThread(wxApp *app);
|
|
||||||
~IQBufferThread();
|
|
||||||
|
|
||||||
void OnEventInput(wxEvent& event) {
|
|
||||||
std::cout << "event !" << std::endl;
|
|
||||||
}
|
|
||||||
protected:
|
|
||||||
virtual ExitCode Entry();
|
|
||||||
wxApp *handler;
|
|
||||||
std::queue<std::vector<signed char> *> iq_buffer;
|
|
||||||
};
|
|
|
@ -103,9 +103,12 @@ wxThread::ExitCode SDRThread::Entry() {
|
||||||
|
|
||||||
signed char *buf = (signed char *) malloc(BUF_SIZE);
|
signed char *buf = (signed char *) malloc(BUF_SIZE);
|
||||||
|
|
||||||
|
unsigned int frequency = DEFAULT_FREQ;
|
||||||
|
unsigned int bandwidth = SRATE;
|
||||||
|
|
||||||
rtlsdr_open(&dev, first_available);
|
rtlsdr_open(&dev, first_available);
|
||||||
rtlsdr_set_sample_rate(dev, SRATE);
|
rtlsdr_set_sample_rate(dev, bandwidth);
|
||||||
rtlsdr_set_center_freq(dev, DEFAULT_FREQ);
|
rtlsdr_set_center_freq(dev, frequency);
|
||||||
rtlsdr_set_agc_mode(dev, 1);
|
rtlsdr_set_agc_mode(dev, 1);
|
||||||
rtlsdr_set_offset_tuning(dev, 1);
|
rtlsdr_set_offset_tuning(dev, 1);
|
||||||
rtlsdr_reset_buffer(dev);
|
rtlsdr_reset_buffer(dev);
|
||||||
|
@ -136,17 +139,18 @@ wxThread::ExitCode SDRThread::Entry() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (freq_changed) {
|
if (freq_changed) {
|
||||||
rtlsdr_set_center_freq(dev, new_freq);
|
frequency = new_freq;
|
||||||
|
rtlsdr_set_center_freq(dev, frequency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rtlsdr_read_sync(dev, buf, BUF_SIZE, &n_read);
|
rtlsdr_read_sync(dev, buf, BUF_SIZE, &n_read);
|
||||||
|
|
||||||
if (!TestDestroy()) {
|
if (!TestDestroy()) {
|
||||||
std::vector<signed char> *new_buffer = new std::vector<signed char>();
|
std::vector<signed char> new_buffer;
|
||||||
|
|
||||||
for (int i = 0; i < n_read; i++) {
|
for (int i = 0; i < n_read; i++) {
|
||||||
new_buffer->push_back(buf[i] - 127);
|
new_buffer.push_back(buf[i] - 127);
|
||||||
}
|
}
|
||||||
|
|
||||||
double time_slice = (double) n_read / (double) sample_rate;
|
double time_slice = (double) n_read / (double) sample_rate;
|
||||||
|
@ -154,11 +158,8 @@ wxThread::ExitCode SDRThread::Entry() {
|
||||||
|
|
||||||
// std::cout << "Time Slice: " << time_slice << std::endl;
|
// std::cout << "Time Slice: " << time_slice << std::endl;
|
||||||
if (!TestDestroy()) {
|
if (!TestDestroy()) {
|
||||||
wxThreadEvent event(wxEVT_THREAD, EVENT_SDR_INPUT);
|
SDRThreadIQData *iqData = new SDRThreadIQData(bandwidth,frequency,new_buffer);
|
||||||
event.SetPayload(new_buffer);
|
m_pQueue->sendIQData(SDRThreadTask::SDR_THREAD_DATA, iqData);
|
||||||
wxQueueEvent(m_pQueue->getHandler(), event.Clone());
|
|
||||||
} else {
|
|
||||||
delete new_buffer;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this->Yield();
|
this->Yield();
|
||||||
|
|
|
@ -9,18 +9,8 @@
|
||||||
|
|
||||||
#include "wx/thread.h"
|
#include "wx/thread.h"
|
||||||
|
|
||||||
#include "IQBufferThread.h"
|
|
||||||
#include "SDRThreadQueue.h"
|
#include "SDRThreadQueue.h"
|
||||||
|
|
||||||
// declare a new type of event, to be used by our SDRThread class:
|
|
||||||
//wxDECLARE_EVENT(wxEVT_COMMAND_SDRThread_COMPLETED, wxThreadEvent);
|
|
||||||
//wxDECLARE_EVENT(wxEVT_COMMAND_SDRThread_UPDATE, wxThreadEvent);
|
|
||||||
//wxDECLARE_EVENT(wxEVT_COMMAND_SDRThread_INPUT, wxThreadEvent);
|
|
||||||
|
|
||||||
enum {
|
|
||||||
EVENT_SDR_INPUT = wxID_HIGHEST + 1
|
|
||||||
};
|
|
||||||
|
|
||||||
class SDRThread: public wxThread {
|
class SDRThread: public wxThread {
|
||||||
public:
|
public:
|
||||||
rtlsdr_dev_t *dev;
|
rtlsdr_dev_t *dev;
|
||||||
|
|
|
@ -26,10 +26,11 @@ SDRThreadTask SDRThreadQueue::pop() {
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDRThreadQueue::report(const SDRThreadTask::SDR_COMMAND& cmd, const wxString& sArg, int iArg) {
|
void SDRThreadQueue::sendIQData(const SDRThreadTask::SDR_COMMAND& cmd, SDRThreadIQData *data) {
|
||||||
wxCommandEvent evt(wxEVT_THREAD, cmd);
|
wxCommandEvent evt(wxEVT_THREAD, cmd);
|
||||||
evt.SetString(sArg);
|
|
||||||
evt.SetInt(iArg);
|
evt.SetClientData(data);
|
||||||
|
|
||||||
m_pParent->AddPendingEvent(evt);
|
m_pParent->AddPendingEvent(evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
#include "SDRThreadTask.h"
|
#include "SDRThreadTask.h"
|
||||||
|
|
||||||
#include "wx/event.h"
|
#include "wx/event.h"
|
||||||
|
@ -13,7 +14,7 @@ public:
|
||||||
SDRThreadQueue(wxEvtHandler* pParent);
|
SDRThreadQueue(wxEvtHandler* pParent);
|
||||||
|
|
||||||
void addTask(const SDRThreadTask& task, const SDR_PRIORITY& priority = SDR_PRIORITY_NORMAL);
|
void addTask(const SDRThreadTask& task, const SDR_PRIORITY& priority = SDR_PRIORITY_NORMAL);
|
||||||
void report(const SDRThreadTask::SDR_COMMAND& cmd, const wxString& sArg = wxEmptyString, int iArg = 0);
|
void sendIQData(const SDRThreadTask::SDR_COMMAND& cmd, SDRThreadIQData *data);
|
||||||
|
|
||||||
SDRThreadTask pop();
|
SDRThreadTask pop();
|
||||||
size_t stackSize();
|
size_t stackSize();
|
||||||
|
|
|
@ -1,12 +1,36 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
#include "wx/defs.h"
|
#include "wx/defs.h"
|
||||||
#include "wx/string.h"
|
#include "wx/string.h"
|
||||||
|
#include "wx/object.h"
|
||||||
|
|
||||||
|
class SDRThreadIQData: public wxObject {
|
||||||
|
public:
|
||||||
|
unsigned int frequency;
|
||||||
|
unsigned int bandwidth;
|
||||||
|
std::vector<signed char> data;
|
||||||
|
|
||||||
|
SDRThreadIQData(unsigned int bandwidth, unsigned int frequency, std::vector<signed char> data) :
|
||||||
|
data(data), frequency(frequency), bandwidth(bandwidth) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~SDRThreadIQData() {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class SDRThreadTask {
|
class SDRThreadTask {
|
||||||
public:
|
public:
|
||||||
enum SDR_COMMAND {
|
enum SDR_COMMAND {
|
||||||
SDR_THREAD_EXIT = wxID_EXIT, SDR_THREAD_NULL = wxID_HIGHEST + 1, SDR_THREAD_STARTED, SDR_THREAD_PROCESS, SDR_THREAD_ERROR, SDR_THREAD_TUNING
|
SDR_THREAD_EXIT = wxID_EXIT,
|
||||||
|
SDR_THREAD_NULL = wxID_HIGHEST + 1,
|
||||||
|
SDR_THREAD_STARTED,
|
||||||
|
SDR_THREAD_PROCESS,
|
||||||
|
SDR_THREAD_ERROR,
|
||||||
|
SDR_THREAD_TUNING,
|
||||||
|
SDR_THREAD_DATA
|
||||||
};
|
};
|
||||||
|
|
||||||
SDRThreadTask() :
|
SDRThreadTask() :
|
||||||
|
|
|
@ -1,25 +1,3 @@
|
||||||
/*
|
|
||||||
This file is part of CubicVR.
|
|
||||||
|
|
||||||
CubicVR is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
CubicVR 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 Lesser General Public License
|
|
||||||
along with CubicVR; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
|
|
||||||
Source (c) 2003 by Charles J. Cliffe unless otherwise specified
|
|
||||||
To contact me, e-mail or MSN to cj@cubicproductions.com or by ICQ#7188044
|
|
||||||
http://www.cubicproductions.com
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Timer.h"
|
#include "Timer.h"
|
||||||
|
|
||||||
|
@ -27,7 +5,7 @@
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Timer::Timer(void) : time_elapsed(0), system_milliseconds(0), start_time(0), end_time(0), last_update(0), paused_time(0), offset(0), paused_state(false)
|
Timer::Timer(void) : time_elapsed(0), system_milliseconds(0), start_time(0), end_time(0), last_update(0), paused_time(0), offset(0), paused_state(false), num_updates(0), lock_state(0), lock_rate(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
22
src/Timer.h
22
src/Timer.h
|
@ -1,25 +1,3 @@
|
||||||
/*
|
|
||||||
This file is part of CubicVR.
|
|
||||||
|
|
||||||
CubicVR is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2.1 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
CubicVR 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 Lesser General Public License
|
|
||||||
along with CubicVR; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
|
|
||||||
Source (c) 2003 by Charles J. Cliffe unless otherwise specified
|
|
||||||
To contact me, e-mail or MSN to cj@cubicproductions.com or by ICQ#7188044
|
|
||||||
http://www.cubicproductions.com
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef TIMER_H
|
#ifndef TIMER_H
|
||||||
#define TIMER_H
|
#define TIMER_H
|
||||||
|
|
Loading…
Reference in New Issue