mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2024-09-27 15:56:48 -04:00
New threads partly implemented
Mid transition from 1 to 3 threads: SDR IQ, Demod and Audio separated, AppFrame routes.
This commit is contained in:
parent
9705d293f9
commit
16a9add9ef
@ -91,6 +91,12 @@ SET (cubicsdr_sources
|
|||||||
src/SDRThreadQueue.cpp
|
src/SDRThreadQueue.cpp
|
||||||
src/SDRThreadTask.cpp
|
src/SDRThreadTask.cpp
|
||||||
src/Demodulator.cpp
|
src/Demodulator.cpp
|
||||||
|
src/DemodulatorThread.cpp
|
||||||
|
src/DemodulatorThreadQueue.cpp
|
||||||
|
src/DemodulatorThreadTask.cpp
|
||||||
|
src/AudioThread.cpp
|
||||||
|
src/AudioThreadQueue.cpp
|
||||||
|
src/AudioThreadTask.cpp
|
||||||
src/Gradient.cpp
|
src/Gradient.cpp
|
||||||
src/visual/ScopeCanvas.cpp
|
src/visual/ScopeCanvas.cpp
|
||||||
src/visual/ScopeContext.cpp
|
src/visual/ScopeContext.cpp
|
||||||
@ -110,6 +116,12 @@ SET (cubicsdr_headers
|
|||||||
src/SDRThreadQueue.h
|
src/SDRThreadQueue.h
|
||||||
src/SDRThreadTask.h
|
src/SDRThreadTask.h
|
||||||
src/Demodulator.h
|
src/Demodulator.h
|
||||||
|
src/DemodulatorThread.h
|
||||||
|
src/DemodulatorThreadQueue.h
|
||||||
|
src/DemodulatorThreadTask.h
|
||||||
|
src/AudioThread.h
|
||||||
|
src/AudioThreadQueue.h
|
||||||
|
src/AudioThreadTask.h
|
||||||
src/Gradient.h
|
src/Gradient.h
|
||||||
src/visual/ScopeCanvas.h
|
src/visual/ScopeCanvas.h
|
||||||
src/visual/ScopeContext.h
|
src/visual/ScopeContext.h
|
||||||
|
124
src/AudioThread.cpp
Normal file
124
src/AudioThread.cpp
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
#include "AudioThread.h"
|
||||||
|
#include "CubicSDRDefs.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
//wxDEFINE_EVENT(wxEVT_COMMAND_AudioThread_INPUT, wxThreadEvent);
|
||||||
|
|
||||||
|
AudioThread::AudioThread(AudioThreadQueue* pQueue, int id) :
|
||||||
|
wxThread(wxTHREAD_DETACHED), m_pQueue(pQueue), m_ID(id) {
|
||||||
|
|
||||||
|
}
|
||||||
|
AudioThread::~AudioThread() {
|
||||||
|
PaError err;
|
||||||
|
err = Pa_StopStream(stream);
|
||||||
|
err = Pa_CloseStream(stream);
|
||||||
|
Pa_Terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int patestCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo,
|
||||||
|
PaStreamCallbackFlags statusFlags, void *userData) {
|
||||||
|
|
||||||
|
AudioThread *src = (AudioThread *) userData;
|
||||||
|
|
||||||
|
float *out = (float*) outputBuffer;
|
||||||
|
|
||||||
|
if (!src->audio_queue.size()) {
|
||||||
|
for (int i = 0; i < framesPerBuffer * 2; i++) {
|
||||||
|
out[i] = 0;
|
||||||
|
}
|
||||||
|
return paContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<float> *nextBuffer = src->audio_queue.front();
|
||||||
|
|
||||||
|
for (int i = 0; i < framesPerBuffer * 2; i++) {
|
||||||
|
out[i] = (*nextBuffer)[src->audio_queue_ptr];
|
||||||
|
|
||||||
|
src->audio_queue_ptr++;
|
||||||
|
|
||||||
|
if (src->audio_queue_ptr == nextBuffer->size()) {
|
||||||
|
src->audio_queue.pop();
|
||||||
|
delete nextBuffer;
|
||||||
|
src->audio_queue_ptr = 0;
|
||||||
|
if (!src->audio_queue.size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nextBuffer = src->audio_queue.front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
wxThread::ExitCode AudioThread::Entry() {
|
||||||
|
|
||||||
|
PaError err;
|
||||||
|
err = Pa_Initialize();
|
||||||
|
if (err != paNoError) {
|
||||||
|
std::cout << "Error starting :(\n";
|
||||||
|
return (wxThread::ExitCode) 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int preferred_device = -1;
|
||||||
|
|
||||||
|
#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(env_name.c_str(), dev_str, 255);
|
||||||
|
std::wstring env_result(dev_str);
|
||||||
|
|
||||||
|
int env_dev = _wtoi(env_result.c_str());
|
||||||
|
|
||||||
|
if (env_dev || env_result.length()) {
|
||||||
|
std::cout << "Using preferred PortAudio device PA_RECOMMENDED_OUTPUT_DEVICE=" << env_result.c_str() << std::endl;
|
||||||
|
preferred_device = env_dev;
|
||||||
|
} else {
|
||||||
|
std::cout << "Environment variable PA_RECOMMENDED_OUTPUT_DEVICE not set, using PortAudio defaults." << std::endl;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
outputParameters.device = (preferred_device != -1) ? preferred_device : Pa_GetDefaultOutputDevice();
|
||||||
|
if (outputParameters.device == paNoDevice) {
|
||||||
|
std::cout << "Error: No default output device.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
outputParameters.channelCount = 2; /* Stereo output, most likely supported. */
|
||||||
|
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output. */
|
||||||
|
outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
|
||||||
|
outputParameters.hostApiSpecificStreamInfo = NULL;
|
||||||
|
|
||||||
|
stream = NULL;
|
||||||
|
|
||||||
|
err = Pa_OpenStream(&stream, NULL, &outputParameters, AUDIO_FREQUENCY, 1024, paClipOff, &patestCallback, this);
|
||||||
|
|
||||||
|
err = Pa_StartStream(stream);
|
||||||
|
if (err != paNoError) {
|
||||||
|
std::cout << "Error starting stream: " << Pa_GetErrorText(err) << std::endl;
|
||||||
|
std::cout << "\tPortAudio error: " << Pa_GetErrorText(err) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_TUNING:
|
||||||
|
//
|
||||||
|
// audio_queue.push(newBuffer);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Sleep(1000);
|
||||||
|
}
|
||||||
|
std::cout << std::endl << "Audio Thread Done." << std::endl << std::endl;
|
||||||
|
|
||||||
|
return (wxThread::ExitCode) 0;
|
||||||
|
}
|
||||||
|
|
47
src/AudioThread.h
Normal file
47
src/AudioThread.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <vector>
|
||||||
|
#include "wx/wxprec.h"
|
||||||
|
|
||||||
|
#ifndef WX_PRECOMP
|
||||||
|
#include "wx/wx.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "wx/thread.h"
|
||||||
|
|
||||||
|
#include "AudioThreadQueue.h"
|
||||||
|
#include "portaudio.h"
|
||||||
|
#ifdef WIN32
|
||||||
|
#include "pa_stream.h"
|
||||||
|
#include "pa_debugprint.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int patestCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo,
|
||||||
|
PaStreamCallbackFlags statusFlags, void *userData);
|
||||||
|
|
||||||
|
// declare a new type of event, to be used by our AudioThread class:
|
||||||
|
//wxDECLARE_EVENT(wxEVT_COMMAND_AudioThread_COMPLETED, wxThreadEvent);
|
||||||
|
//wxDECLARE_EVENT(wxEVT_COMMAND_AudioThread_UPDATE, wxThreadEvent);
|
||||||
|
//wxDECLARE_EVENT(wxEVT_COMMAND_AudioThread_INPUT, wxThreadEvent);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
EVENT_AUDIO_INPUT = wxID_HIGHEST + 1
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioThread: public wxThread {
|
||||||
|
public:
|
||||||
|
std::queue<std::vector<float> *> audio_queue;
|
||||||
|
unsigned int audio_queue_ptr;
|
||||||
|
|
||||||
|
AudioThread(AudioThreadQueue* pQueue, int id = 0);
|
||||||
|
~AudioThread();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ExitCode Entry();
|
||||||
|
AudioThreadQueue* m_pQueue;
|
||||||
|
int m_ID;
|
||||||
|
|
||||||
|
PaStreamParameters outputParameters;
|
||||||
|
PaStream *stream;
|
||||||
|
};
|
43
src/AudioThreadQueue.cpp
Normal file
43
src/AudioThreadQueue.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#include "AudioThreadQueue.h"
|
||||||
|
|
||||||
|
#include "wx/wxprec.h"
|
||||||
|
|
||||||
|
#ifndef WX_PRECOMP
|
||||||
|
#include "wx/wx.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AudioThreadQueue::AudioThreadQueue(wxEvtHandler* pParent) :
|
||||||
|
m_pParent(pParent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioThreadQueue::addTask(const AudioThreadTask& task, const AUDIO_PRIORITY& priority) {
|
||||||
|
wxMutexLocker lock(m_MutexQueue);
|
||||||
|
m_Tasks.insert(std::make_pair(priority, task));
|
||||||
|
m_QueueCount.Post();
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioThreadTask AudioThreadQueue::pop() {
|
||||||
|
AudioThreadTask element;
|
||||||
|
m_QueueCount.Wait();
|
||||||
|
m_MutexQueue.Lock();
|
||||||
|
element = (m_Tasks.begin())->second;
|
||||||
|
m_Tasks.erase(m_Tasks.begin());
|
||||||
|
m_MutexQueue.Unlock();
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioThreadQueue::report(const AudioThreadTask::AUDIO_THREAD_COMMAND& cmd, const wxString& sArg, int iArg) {
|
||||||
|
wxCommandEvent evt(wxEVT_THREAD, cmd);
|
||||||
|
evt.SetString(sArg);
|
||||||
|
evt.SetInt(iArg);
|
||||||
|
m_pParent->AddPendingEvent(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AudioThreadQueue::stackSize() {
|
||||||
|
wxMutexLocker lock(m_MutexQueue);
|
||||||
|
return m_Tasks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
wxEvtHandler* AudioThreadQueue::getHandler() {
|
||||||
|
return m_pParent;
|
||||||
|
}
|
28
src/AudioThreadQueue.h
Normal file
28
src/AudioThreadQueue.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include "AudioThreadTask.h"
|
||||||
|
|
||||||
|
#include "wx/event.h"
|
||||||
|
|
||||||
|
class AudioThreadQueue {
|
||||||
|
public:
|
||||||
|
enum AUDIO_PRIORITY {
|
||||||
|
AUDIO_PRIORITY_HIGHEST, AUDIO_PRIORITY_HIGHER, AUDIO_PRIORITY_NORMAL, AUDIO_PRIORITY_BELOW_NORMAL, AUDIO_PRIORITY_LOW, AUDIO_PRIORITY_IDLE
|
||||||
|
};
|
||||||
|
AudioThreadQueue(wxEvtHandler* pParent);
|
||||||
|
|
||||||
|
void addTask(const AudioThreadTask& task, const AUDIO_PRIORITY& priority = AUDIO_PRIORITY_NORMAL);
|
||||||
|
void report(const AudioThreadTask::AUDIO_THREAD_COMMAND& cmd, const wxString& sArg = wxEmptyString, int iArg = 0);
|
||||||
|
|
||||||
|
AudioThreadTask pop();
|
||||||
|
size_t stackSize();
|
||||||
|
|
||||||
|
wxEvtHandler* getHandler();
|
||||||
|
|
||||||
|
private:
|
||||||
|
wxEvtHandler* m_pParent;
|
||||||
|
std::multimap<AUDIO_PRIORITY, AudioThreadTask> m_Tasks;
|
||||||
|
wxMutex m_MutexQueue;
|
||||||
|
wxSemaphore m_QueueCount;
|
||||||
|
};
|
9
src/AudioThreadTask.cpp
Normal file
9
src/AudioThreadTask.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "AudioThreadTask.h"
|
||||||
|
|
||||||
|
|
||||||
|
void AudioThreadTask::setData(std::vector<float> &data_in) {
|
||||||
|
data = data_in;
|
||||||
|
}
|
||||||
|
std::vector<float> &AudioThreadTask::getData() {
|
||||||
|
return data;
|
||||||
|
}
|
28
src/AudioThreadTask.h
Normal file
28
src/AudioThreadTask.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "wx/defs.h"
|
||||||
|
#include "wx/string.h"
|
||||||
|
|
||||||
|
class AudioThreadTask {
|
||||||
|
public:
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
AudioThreadTask() :
|
||||||
|
m_cmd(AUDIO_THREAD_NULL) {
|
||||||
|
}
|
||||||
|
AudioThreadTask(AUDIO_THREAD_COMMAND cmd) :
|
||||||
|
m_cmd(cmd) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void setData(std::vector<float> &data_in);
|
||||||
|
std::vector<float> &getData();
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<float> data;
|
||||||
|
|
||||||
|
AUDIO_THREAD_COMMAND m_cmd;
|
||||||
|
};
|
@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
#include "wx/thread.h"
|
#include "wx/thread.h"
|
||||||
|
|
||||||
#include "SDRThread.h"
|
|
||||||
#include "IQBufferThread.h"
|
#include "IQBufferThread.h"
|
||||||
#include "SDRThreadQueue.h"
|
#include "SDRThreadQueue.h"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user