diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 1334782..9308687 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -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()) { diff --git a/src/AppFrame.h b/src/AppFrame.h index 912b871..be1b2ea 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -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 input_devices; std::map output_devices; + std::map output_device_menuitems; wxDECLARE_EVENT_TABLE(); }; diff --git a/src/audio/AudioThread.cpp b/src/audio/AudioThread.cpp index 4bb46a7..5a2d012 100644 --- a/src/audio/AudioThread.cpp +++ b/src/audio/AudioThread.cpp @@ -10,7 +10,7 @@ std::map 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; @@ -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 &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(¶meters, 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; +} diff --git a/src/audio/AudioThread.h b/src/audio/AudioThread.h index de0c806..1e9ed8e 100644 --- a/src/audio/AudioThread.h +++ b/src/audio/AudioThread.h @@ -61,6 +61,7 @@ public: std::atomic underflow_count; std::atomic terminated; std::atomic active; + std::atomic output_device; float gain; AudioThread(AudioThreadInputQueue *inputQueue, DemodulatorThreadCommandQueue* threadQueueNotify); @@ -68,12 +69,16 @@ public: static void enumerateDevices(std::vector &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; diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index 6618a1a..fefe5c4 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -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(); +} diff --git a/src/demod/DemodulatorInstance.h b/src/demod/DemodulatorInstance.h index 7432e0b..a88b8a6 100644 --- a/src/demod/DemodulatorInstance.h +++ b/src/demod/DemodulatorInstance.h @@ -60,6 +60,9 @@ public: void setSquelchLevel(float signal_level_in); float getSquelchLevel(); + void setOutputDevice(int device_id); + int getOutputDevice(); + private: std::atomic label; // bool terminated; // diff --git a/src/util/GLFont.cpp b/src/util/GLFont.cpp index 4fa2bed..88c7b64 100644 --- a/src/util/GLFont.cpp +++ b/src/util/GLFont.cpp @@ -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); } diff --git a/src/visual/ScopeCanvas.cpp b/src/visual/ScopeCanvas.cpp index 80ad241..ef04240 100644 --- a/src/visual/ScopeCanvas.cpp +++ b/src/visual/ScopeCanvas.cpp @@ -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; -// } } diff --git a/src/visual/ScopeCanvas.h b/src/visual/ScopeCanvas.h index 29d1cd4..94f3328 100644 --- a/src/visual/ScopeCanvas.h +++ b/src/visual/ScopeCanvas.h @@ -7,9 +7,7 @@ #include #include "ScopeContext.h" - #include "fftw3.h" -#include "Timer.h" class ScopeCanvas: public wxGLCanvas { public: @@ -20,6 +18,7 @@ public: void setWaveformPoints(std::vector &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(); diff --git a/src/visual/ScopeContext.cpp b/src/visual/ScopeContext.cpp index 1a12298..15d43ef 100644 --- a/src/visual/ScopeContext.cpp +++ b/src/visual/ScopeContext.cpp @@ -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 &points, bool stereo) { @@ -26,27 +26,27 @@ void ScopeContext::Plot(std::vector &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 &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(); } diff --git a/src/visual/ScopeContext.h b/src/visual/ScopeContext.h index 321f14e..d4fa1a0 100644 --- a/src/visual/ScopeContext.h +++ b/src/visual/ScopeContext.h @@ -13,6 +13,7 @@ public: void DrawBegin(); void Plot(std::vector &points, bool stereo=false); + void DrawDeviceName(std::string deviceName); void DrawDivider(); void DrawEnd(); diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index 1c73431..6b9708e 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -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) { diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h index dea6b74..1d68c6c 100644 --- a/src/visual/SpectrumCanvas.h +++ b/src/visual/SpectrumCanvas.h @@ -9,7 +9,6 @@ #include "SpectrumContext.h" #include "fftw3.h" -#include "Timer.h" #include "MouseTracker.h" class SpectrumCanvas: public wxGLCanvas { diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 0c9a4a1..4f69b82 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -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) { diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index fb8196c..9656e32 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -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 fft_result_maa; WaterfallContext *glContext; - Timer timer; - float frameTimer; MouseTracker mTracker; int activeDemodulatorBandwidth;