diff --git a/CMakeLists.txt b/CMakeLists.txt index d2b37bd..f98307e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,6 +91,12 @@ SET (cubicsdr_sources src/SDRThreadQueue.cpp src/SDRThreadTask.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/visual/ScopeCanvas.cpp src/visual/ScopeContext.cpp @@ -110,6 +116,12 @@ SET (cubicsdr_headers src/SDRThreadQueue.h src/SDRThreadTask.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/visual/ScopeCanvas.h src/visual/ScopeContext.h diff --git a/src/AudioThread.cpp b/src/AudioThread.cpp new file mode 100644 index 0000000..7a414bd --- /dev/null +++ b/src/AudioThread.cpp @@ -0,0 +1,124 @@ +#include "AudioThread.h" +#include "CubicSDRDefs.h" +#include + +//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 *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; +} + diff --git a/src/AudioThread.h b/src/AudioThread.h new file mode 100644 index 0000000..8d99c10 --- /dev/null +++ b/src/AudioThread.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#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 *> 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; +}; diff --git a/src/AudioThreadQueue.cpp b/src/AudioThreadQueue.cpp new file mode 100644 index 0000000..a1843c1 --- /dev/null +++ b/src/AudioThreadQueue.cpp @@ -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; +} diff --git a/src/AudioThreadQueue.h b/src/AudioThreadQueue.h new file mode 100644 index 0000000..b506046 --- /dev/null +++ b/src/AudioThreadQueue.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#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 m_Tasks; + wxMutex m_MutexQueue; + wxSemaphore m_QueueCount; +}; diff --git a/src/AudioThreadTask.cpp b/src/AudioThreadTask.cpp new file mode 100644 index 0000000..173bee3 --- /dev/null +++ b/src/AudioThreadTask.cpp @@ -0,0 +1,9 @@ +#include "AudioThreadTask.h" + + +void AudioThreadTask::setData(std::vector &data_in) { + data = data_in; +} +std::vector &AudioThreadTask::getData() { + return data; +} diff --git a/src/AudioThreadTask.h b/src/AudioThreadTask.h new file mode 100644 index 0000000..563f67a --- /dev/null +++ b/src/AudioThreadTask.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#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 &data_in); + std::vector &getData(); + + + std::vector data; + + AUDIO_THREAD_COMMAND m_cmd; +}; diff --git a/src/SDRThread.h b/src/SDRThread.h index 795e9c2..f3bc521 100644 --- a/src/SDRThread.h +++ b/src/SDRThread.h @@ -9,7 +9,6 @@ #include "wx/thread.h" -#include "SDRThread.h" #include "IQBufferThread.h" #include "SDRThreadQueue.h"