Can now choose demodulator output devices

This commit is contained in:
Charles J. Cliffe 2014-12-31 21:31:37 -05:00
parent b7793ef905
commit 6679b20fbb
15 changed files with 136 additions and 71 deletions

View File

@ -22,13 +22,15 @@
wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
//EVT_MENU(wxID_NEW, AppFrame::OnNewWindow)
EVT_MENU(wxID_CLOSE, AppFrame::OnClose)
//EVT_MENU(wxID_CLOSE, AppFrame::OnClose)
EVT_MENU(wxID_ANY, AppFrame::OnMenu)
EVT_COMMAND(wxID_ANY, wxEVT_THREAD, AppFrame::OnThread)
EVT_IDLE(AppFrame::OnIdle)
wxEND_EVENT_TABLE()
AppFrame::AppFrame() :
wxFrame(NULL, wxID_ANY, wxT("CubicSDR")) {
wxFrame(NULL, wxID_ANY, wxT("CubicSDR")), activeDemodulator(NULL) {
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *demodOpts = new wxBoxSizer(wxVERTICAL);
@ -137,12 +139,14 @@ AppFrame::AppFrame() :
i++;
}
i = 0;
for (mdevices_i = output_devices.begin(); mdevices_i != output_devices.end(); mdevices_i++) {
wxMenuItem *itm = menu->AppendRadioItem(wxID_RT_AUDIO_DEVICE+i,mdevices_i->second.name,wxT("Description?"));
wxMenuItem *itm = menu->AppendRadioItem(wxID_RT_AUDIO_DEVICE+mdevices_i->first,mdevices_i->second.name,wxT("Description?"));
if (mdevices_i->second.isDefaultOutput) {
itm->Check(true);
}
output_device_menuitems[mdevices_i->first] = itm;
}
wxMenuBar *menuBar = new wxMenuBar;
@ -166,10 +170,13 @@ AppFrame::~AppFrame() {
}
void AppFrame::OnClose(wxCommandEvent& WXUNUSED(event)) {
// true is to force the frame to close
Close(true);
void AppFrame::OnMenu(wxCommandEvent& event) {
if (event.GetId() >= wxID_RT_AUDIO_DEVICE && event.GetId() < wxID_RT_AUDIO_DEVICE+output_devices.size()) {
if (activeDemodulator) {
activeDemodulator->setOutputDevice(event.GetId()-wxID_RT_AUDIO_DEVICE);
activeDemodulator = NULL;
}
}
}
void AppFrame::OnNewWindow(wxCommandEvent& WXUNUSED(event)) {
@ -193,6 +200,9 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
if (demod) {
if (demod != activeDemodulator) {
demodSignalMeter->setInputValue(demod->getSquelchLevel());
int outputDevice = demod->getOutputDevice();
scopeCanvas->setDeviceName(output_devices[outputDevice].name);
output_device_menuitems[outputDevice]->Check(true);
}
if (demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) {
if (demod->getParams().frequency != demodWaterfallCanvas->GetCenterFrequency()) {

View File

@ -21,7 +21,7 @@ public:
void OnEventInput(wxThreadEvent& event);
private:
void OnClose(wxCommandEvent& event);
void OnMenu(wxCommandEvent& event);
void OnNewWindow(wxCommandEvent& event);
void OnIdle(wxIdleEvent& event);
@ -37,6 +37,7 @@ private:
std::map<int,RtAudio::DeviceInfo> input_devices;
std::map<int,RtAudio::DeviceInfo> output_devices;
std::map<int,wxMenuItem *> output_device_menuitems;
wxDECLARE_EVENT_TABLE();
};

View File

@ -10,7 +10,7 @@ std::map<int, std::thread *> AudioThread::deviceThread;
#endif
AudioThread::AudioThread(AudioThreadInputQueue *inputQueue, DemodulatorThreadCommandQueue* threadQueueNotify) :
currentInput(NULL), inputQueue(inputQueue), audio_queue_ptr(0), underflow_count(0), terminated(false), active(false), gain(1.0), threadQueueNotify(
currentInput(NULL), inputQueue(inputQueue), audio_queue_ptr(0), underflow_count(0), terminated(false), active(false), output_device(-1), gain(1.0), threadQueueNotify(
threadQueueNotify) {
#ifdef __APPLE__
boundThreads = new std::vector<AudioThread *>;
@ -25,7 +25,9 @@ AudioThread::~AudioThread() {
#ifdef __APPLE__
void AudioThread::bindThread(AudioThread *other) {
boundThreads.load()->push_back(other);
if (boundThreads.find(other) == boundThreads.end()) {
boundThreads.load()->push_back(other);
}
}
void AudioThread::removeThread(AudioThread *other) {
@ -263,22 +265,8 @@ void AudioThread::enumerateDevices(std::vector<RtAudio::DeviceInfo> &devs) {
}
}
void AudioThread::threadMain() {
#ifdef __APPLE__
pthread_t tID = pthread_self(); // ID of this thread
int priority = sched_get_priority_max( SCHED_RR) - 1;
sched_param prio = {priority}; // scheduling priority of thread
pthread_setschedparam(tID, SCHED_RR, &prio);
#endif
std::cout << "Audio thread initializing.." << std::endl;
if (dac.getDeviceCount() < 1) {
std::cout << "No audio devices found!" << std::endl;
return;
}
parameters.deviceId = dac.getDefaultOutputDevice();
void AudioThread::setupDevice(int deviceId) {
parameters.deviceId = deviceId;
parameters.nChannels = 2;
parameters.firstChannel = 0;
unsigned int sampleRate = AUDIO_FREQUENCY;
@ -287,9 +275,15 @@ void AudioThread::threadMain() {
RtAudio::StreamOptions opts;
opts.streamName = "CubicSDR Audio Output";
output_device = deviceId;
try {
#ifdef __APPLE__
if (active && deviceController.find(parameters.deviceId) != deviceController.end()) {
deviceController[parameters.deviceId]->removeThread(this);
}
opts.priority = sched_get_priority_max(SCHED_FIFO);
// opts.flags = RTAUDIO_MINIMIZE_LATENCY;
opts.flags = RTAUDIO_SCHEDULE_REALTIME;
@ -306,6 +300,13 @@ void AudioThread::threadMain() {
}
active = true;
#else
if (dac.isStreamOpen()) {
if (dac.isStreamRunning()) {
dac.stopStream();
}
dac.closeStream();
}
dac.openStream(&parameters, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &audioCallback, (void *) this, &opts);
dac.startStream();
@ -314,10 +315,39 @@ void AudioThread::threadMain() {
e.printMessage();
return;
}
}
int AudioThread::getOutputDevice() {
if (output_device == -1) {
return dac.getDefaultOutputDevice();
}
return output_device;
}
void AudioThread::threadMain() {
#ifdef __APPLE__
pthread_t tID = pthread_self(); // ID of this thread
int priority = sched_get_priority_max( SCHED_RR) - 1;
sched_param prio = {priority}; // scheduling priority of thread
pthread_setschedparam(tID, SCHED_RR, &prio);
#endif
std::cout << "Audio thread initializing.." << std::endl;
if (dac.getDeviceCount() < 1) {
std::cout << "No audio devices found!" << std::endl;
return;
}
setupDevice(dac.getDefaultOutputDevice());
while (!terminated) {
AudioThreadCommand command;
cmdQueue.pop(command);
if (command.cmd == AudioThreadCommand::AUDIO_THREAD_CMD_SET_DEVICE) {
setupDevice(command.int_value);
}
}
#ifdef __APPLE__
@ -387,3 +417,8 @@ void AudioThread::setActive(bool state) {
#endif
active = state;
}
AudioThreadCommandQueue *AudioThread::getCommandQueue() {
return &cmdQueue;
}

View File

@ -61,6 +61,7 @@ public:
std::atomic<unsigned int> underflow_count;
std::atomic<bool> terminated;
std::atomic<bool> active;
std::atomic<int> output_device;
float gain;
AudioThread(AudioThreadInputQueue *inputQueue, DemodulatorThreadCommandQueue* threadQueueNotify);
@ -68,12 +69,16 @@ public:
static void enumerateDevices(std::vector<RtAudio::DeviceInfo> &devs);
void setupDevice(int deviceId);
int getOutputDevice();
void threadMain();
void terminate();
bool isActive();
void setActive(bool state);
AudioThreadCommandQueue *getCommandQueue();
private:
RtAudio dac;
RtAudio::StreamParameters parameters;

View File

@ -194,3 +194,16 @@ float DemodulatorInstance::getSquelchLevel() {
return demodulatorThread->getSquelchLevel();
}
void DemodulatorInstance::setOutputDevice(int device_id) {
if (audioThread) {
AudioThreadCommand command;
command.cmd = AudioThreadCommand::AUDIO_THREAD_CMD_SET_DEVICE;
command.int_value = device_id;
audioThread->getCommandQueue()->push(command);
}
}
int DemodulatorInstance::getOutputDevice() {
return audioThread->getOutputDevice();
}

View File

@ -60,6 +60,9 @@ public:
void setSquelchLevel(float signal_level_in);
float getSquelchLevel();
void setOutputDevice(int device_id);
int getOutputDevice();
private:
std::atomic<std::string *> label; //
bool terminated; //

View File

@ -382,7 +382,7 @@ void GLFont::drawString(std::string str, float xpos, float ypos, int pxHeight, A
glPushMatrix();
glTranslatef(xpos, ypos, 0.0f);
switch (hAlign) {
switch (vAlign) {
case GLFONT_ALIGN_TOP:
glTranslatef(0.0, -size, 0.0);
break;
@ -393,7 +393,7 @@ void GLFont::drawString(std::string str, float xpos, float ypos, int pxHeight, A
break;
}
switch (vAlign) {
switch (hAlign) {
case GLFONT_ALIGN_RIGHT:
glTranslatef(-msgWidth, 0.0, 0.0);
break;
@ -445,5 +445,6 @@ void GLFont::drawString(std::string str, float xpos, float ypos, int pxHeight, A
glPopMatrix();
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
}

View File

@ -21,10 +21,9 @@ wxEND_EVENT_TABLE()
ScopeCanvas::ScopeCanvas(wxWindow *parent, int *attribList) :
wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), stereo(false) {
wxFULL_REPAINT_ON_RESIZE), parent(parent), stereo(false) {
glContext = new ScopeContext(this, &wxGetApp().GetContext(this));
timer.start();
}
ScopeCanvas::~ScopeCanvas() {
@ -39,6 +38,11 @@ void ScopeCanvas::setStereo(bool state) {
stereo = state;
}
void ScopeCanvas::setDeviceName(std::string device_name) {
deviceName = device_name;
deviceName.append(" ");
}
void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
wxPaintDC dc(this);
const wxSize ClientSize = GetClientSize();
@ -47,17 +51,16 @@ void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
glViewport(0, 0, ClientSize.x, ClientSize.y);
glContext->DrawBegin();
if (!deviceName.empty()) {
glContext->DrawDeviceName(deviceName);
}
glContext->Plot(waveform_points, stereo);
glContext->DrawEnd();
SwapBuffers();
}
void ScopeCanvas::OnIdle(wxIdleEvent &event) {
// timer.update();
// frameTimer += timer.lastUpdateSeconds();
// if (frameTimer > 1.0/30.0) {
Refresh(false);
// frameTimer = 0;
// }
}

View File

@ -7,9 +7,7 @@
#include <queue>
#include "ScopeContext.h"
#include "fftw3.h"
#include "Timer.h"
class ScopeCanvas: public wxGLCanvas {
public:
@ -20,6 +18,7 @@ public:
void setWaveformPoints(std::vector<float> &waveform_points_in);
void setStereo(bool state);
void setDeviceName(std::string device_name);
private:
void OnPaint(wxPaintEvent& event);
@ -28,8 +27,7 @@ private:
wxWindow *parent;
ScopeContext *glContext;
Timer timer;
float frameTimer;
std::string deviceName;
bool stereo;
// event table
wxDECLARE_EVENT_TABLE();

View File

@ -4,20 +4,20 @@
ScopeContext::ScopeContext(ScopeCanvas *canvas, wxGLContext *sharedContext) :
PrimaryGLContext(canvas, sharedContext) {
glDisable (GL_CULL_FACE);
glDisable (GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glMatrixMode (GL_PROJECTION);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
}
void ScopeContext::DrawBegin() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode (GL_MODELVIEW);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable (GL_TEXTURE_2D);
glDisable(GL_TEXTURE_2D);
}
void ScopeContext::Plot(std::vector<float> &points, bool stereo) {
@ -26,27 +26,27 @@ void ScopeContext::Plot(std::vector<float> &points, bool stereo) {
if (stereo) {
glColor3f(0.7, 0.7, 0.7);
glBegin(GL_LINES);
glVertex2f(-1.0,0.0);
glVertex2f(1.0,0.0);
glVertex2f(-1.0, 0.0);
glVertex2f(1.0, 0.0);
glEnd();
glColor3f(0.3, 0.3, 0.3);
glBegin(GL_LINES);
glVertex2f(-1.0,0.5);
glVertex2f(1.0,0.5);
glVertex2f(-1.0,-0.5);
glVertex2f(1.0,-0.5);
glVertex2f(-1.0, 0.5);
glVertex2f(1.0, 0.5);
glVertex2f(-1.0, -0.5);
glVertex2f(1.0, -0.5);
glEnd();
} else {
glColor3f(0.3, 0.3, 0.3);
glBegin(GL_LINES);
glVertex2f(-1.0,0.0);
glVertex2f(1.0,0.0);
glVertex2f(-1.0, 0.0);
glVertex2f(1.0, 0.0);
glEnd();
}
glColor3f(0.9, 0.9, 0.9);
if (points.size()) {
glEnableClientState (GL_VERTEX_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, &points[0]);
if (stereo) {
glPushMatrix();
@ -74,6 +74,16 @@ void ScopeContext::Plot(std::vector<float> &points, bool stereo) {
}
}
void ScopeContext::DrawDeviceName(std::string deviceName) {
GLint vp[4];
glGetIntegerv( GL_VIEWPORT, vp);
float viewHeight = (float) vp[3];
float hPos = (float) (viewHeight - 20) / viewHeight;
glColor3f(0.65,0.65,0.65);
getFont(PrimaryGLContext::GLFONT_SIZE12).drawString(deviceName.c_str(), 1.0, hPos, 12, GLFont::GLFONT_ALIGN_RIGHT, GLFont::GLFONT_ALIGN_CENTER);
}
void ScopeContext::DrawEnd() {
glFlush();
@ -83,7 +93,7 @@ void ScopeContext::DrawEnd() {
void ScopeContext::DrawDivider() {
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_LINES);
glVertex2f(0.0,-1.0);
glVertex2f(0.0,1.0);
glVertex2f(0.0, -1.0);
glVertex2f(0.0, 1.0);
glEnd();
}

View File

@ -13,6 +13,7 @@ public:
void DrawBegin();
void Plot(std::vector<float> &points, bool stereo=false);
void DrawDeviceName(std::string deviceName);
void DrawDivider();
void DrawEnd();

View File

@ -207,12 +207,7 @@ unsigned int SpectrumCanvas::GetBandwidth() {
}
void SpectrumCanvas::OnIdle(wxIdleEvent &event) {
// timer.update();
// frameTimer += timer.lastUpdateSeconds();
// if (frameTimer > 1.0/30.0) {
Refresh(false);
// frameTimer = 0;
// }
}
void SpectrumCanvas::mouseMoved(wxMouseEvent& event) {

View File

@ -9,7 +9,6 @@
#include "SpectrumContext.h"
#include "fftw3.h"
#include "Timer.h"
#include "MouseTracker.h"
class SpectrumCanvas: public wxGLCanvas {

View File

@ -32,7 +32,7 @@ wxEND_EVENT_TABLE()
WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) :
wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
wxFULL_REPAINT_ON_RESIZE), parent(parent), spectrumCanvas(NULL), frameTimer(0), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState(
wxFULL_REPAINT_ON_RESIZE), parent(parent), spectrumCanvas(NULL), activeDemodulatorBandwidth(0), activeDemodulatorFrequency(0), dragState(
WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), shiftDown(false), altDown(false), ctrlDown(false), fft_size(0), waterfall_lines(0), plan(
NULL), in(NULL), out(NULL), center_freq(0), bandwidth(0), isView(false), resampler(NULL), resample_ratio(0), last_bandwidth(0), last_input_bandwidth(
0) {
@ -85,7 +85,6 @@ void WaterfallCanvas::Setup(int fft_size_in, int waterfall_lines_in) {
plan = fftw_plan_dft_1d(fft_size, in, out, FFTW_FORWARD, FFTW_MEASURE);
glContext->Setup(fft_size, waterfall_lines);
timer.start();
}
int WaterfallCanvas::GetFrequencyAt(float x) {
@ -447,12 +446,7 @@ void WaterfallCanvas::setData(DemodulatorThreadIQData *input) {
}
void WaterfallCanvas::OnIdle(wxIdleEvent &event) {
// timer.update();
// frameTimer += timer.lastUpdateSeconds();
// if (frameTimer > 1.0/30.0) {
Refresh(false);
// frameTimer = 0;
// }
}
void WaterfallCanvas::mouseMoved(wxMouseEvent& event) {

View File

@ -11,7 +11,6 @@
#include "SpectrumCanvas.h"
#include "fftw3.h"
#include "Timer.h"
class WaterfallCanvas: public wxGLCanvas {
public:
@ -69,8 +68,6 @@ private:
std::vector<float> fft_result_maa;
WaterfallContext *glContext;
Timer timer;
float frameTimer;
MouseTracker mTracker;
int activeDemodulatorBandwidth;