diff --git a/external/cubicvr2/math/mat4.h b/external/cubicvr2/math/mat4.h index 0059cd8..b847d26 100644 --- a/external/cubicvr2/math/mat4.h +++ b/external/cubicvr2/math/mat4.h @@ -74,13 +74,24 @@ namespace CubicVR { return mOut; } - - static mat4 perspective(__float fovy, __float aspect, __float znear, __float zfar) { + static mat4 frustum(__float left, __float right, __float bottom, __float top, __float zNear, __float zFar) { + __float A = (right + left) / (right - left); + __float B = (top + bottom) / (top - bottom); + __float C = - (zFar + zNear) / (zFar - zNear); + __float D = - (-2.0 * zFar * zNear) / (zFar - zNear); + + + return mat4((2.0 * zNear) / (right - left), 0, A, 0, + 0, (2.0 * zNear) / (top - bottom), B, 0, + 0, 0, C, D, + 0, 0, -1, 0); + }; + static mat4 perspective(__float fovy, __float aspect, __float zNear, __float zFar) { __float yFac = tan(fovy * (float)M_PI / 360.0f); __float xFac = yFac * aspect; - return mat4( - 1.0f / xFac, 0, 0, 0, 0, 1.0f / yFac, 0, 0, 0, 0, -(zfar + znear) / (zfar - znear), -1, 0, 0, -(2.0f * zfar * znear) / (zfar - znear), 0); + return mat4::frustum(-xFac, xFac, -yFac, yFac, zNear, zFar); + }; static mat4 ortho(__float left,__float right,__float bottom,__float top,__float znear,__float zfar) { return mat4(2.0f / (right - left), 0, 0, 0, 0, 2.0f / (top - bottom), 0, 0, 0, 0, -2.0f / (zfar - znear), 0, -(left + right) / (right - left), -(top + bottom) / (top - bottom), -(zfar + znear) / (zfar - znear), 1); @@ -300,8 +311,18 @@ namespace CubicVR { return mat4::translate(-eyex,-eyey,-eyez) * mat4( side[0], up[0], -forward[0], 0, side[1], up[1], -forward[1], 0, side[2], up[2], -forward[2], 0, 0, 0, 0, 1); }; + + static vec3 unProject(mat4 pMatrix, mat4 mvMatrix, float width, float height, float winx, float winy, float winz) { + vec4 p(((winx / width) * 2.0) - 1.0, -(((winy / height) * 2.0) - 1.0), 1.0, 1.0); + + vec4 invp = mat4::vec4_multiply(mat4::vec4_multiply(p, mat4::inverse(pMatrix)), mat4::inverse(mvMatrix)); + + vec3 result(invp[0] / invp[3], invp[1] / invp[3], invp[2] / invp[3]); + + return result; + }; }; - -} + } + #endif /* defined(__CubicVR2__mat4__) */ diff --git a/external/fftw-3.3.4/64/libfftw3f-3.lib b/external/fftw-3.3.4/64/libfftw3f-3.lib deleted file mode 100644 index 4f2f89d..0000000 Binary files a/external/fftw-3.3.4/64/libfftw3f-3.lib and /dev/null differ diff --git a/src/AppConfig.cpp b/src/AppConfig.cpp index 7510cc6..f1a716d 100644 --- a/src/AppConfig.cpp +++ b/src/AppConfig.cpp @@ -2,6 +2,7 @@ #include "CubicSDR.h" DeviceConfig::DeviceConfig() : deviceId("") { + iqSwap.store(0); ppm.store(0); directSampling.store(false); offset.store(0); diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 12cd972..a9d162a 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -117,7 +117,9 @@ AppFrame::AppFrame() : demodTray->AddSpacer(1); scopeCanvas = new ScopeCanvas(this, attribList); + scopeCanvas->setHelpTip("Audio Visuals, drag left/right to toggle Scope or Spectrum."); demodScopeTray->Add(scopeCanvas, 8, wxEXPAND | wxALL, 0); + wxGetApp().getScopeProcessor()->setup(2048); wxGetApp().getScopeProcessor()->attachOutput(scopeCanvas->getInputQueue()); demodScopeTray->AddSpacer(1); @@ -130,18 +132,34 @@ AppFrame::AppFrame() : demodTray->AddSpacer(1); + wxBoxSizer *demodGainTray = new wxBoxSizer(wxVERTICAL); + demodGainMeter = new MeterCanvas(this, attribList); demodGainMeter->setMax(2.0); demodGainMeter->setHelpTip("Current Demodulator Gain Level. Click / Drag to set Gain level."); demodGainMeter->setShowUserInput(false); - demodTray->Add(demodGainMeter, 1, wxEXPAND | wxALL, 0); + demodGainTray->Add(demodGainMeter, 8, wxEXPAND | wxALL, 0); + demodGainTray->AddSpacer(1); + + demodMuteButton = new ModeSelectorCanvas(this, attribList); + demodMuteButton->addChoice(1, "M"); + demodMuteButton->setPadding(-1,-1); + demodMuteButton->setHighlightColor(RGBA4f(0.8,0.2,0.2)); + demodMuteButton->setHelpTip("Demodulator Mute Toggle"); + demodMuteButton->setToggleMode(true); + + demodGainTray->Add(demodMuteButton, 1, wxEXPAND | wxALL, 0); + + demodTray->Add(demodGainTray, 1, wxEXPAND | wxALL, 0); + vbox->Add(demodTray, 12, wxEXPAND | wxALL, 0); vbox->AddSpacer(1); wxBoxSizer *spectrumSizer = new wxBoxSizer(wxHORIZONTAL); wxGetApp().getSpectrumProcessor()->setup(2048); spectrumCanvas = new SpectrumCanvas(this, attribList); + spectrumCanvas->setShowDb(true); wxGetApp().getSpectrumProcessor()->attachOutput(spectrumCanvas->getVisualDataQueue()); spectrumAvgMeter = new MeterCanvas(this, attribList); @@ -164,11 +182,12 @@ AppFrame::AppFrame() : waterfallCanvas->setup(2048, 512); waterfallDataThread = new FFTVisualDataThread(); - t_FFTData = new std::thread(&FFTVisualDataThread::threadMain, waterfallDataThread); waterfallDataThread->setInputQueue("IQDataInput", wxGetApp().getWaterfallVisualQueue()); waterfallDataThread->setOutputQueue("FFTDataOutput", waterfallCanvas->getVisualDataQueue()); - + + t_FFTData = new std::thread(&FFTVisualDataThread::threadMain, waterfallDataThread); + waterfallSpeedMeter = new MeterCanvas(this, attribList); waterfallSpeedMeter->setHelpTip("Waterfall speed, click or drag to adjust (max 1024 lines per second)"); waterfallSpeedMeter->setMax(sqrt(1024)); @@ -505,6 +524,7 @@ void AppFrame::OnMenu(wxCommandEvent& event) { wxGetApp().setFrequency(100000000); wxGetApp().getDemodMgr().setLastDemodulatorType(DEMOD_TYPE_FM); demodModeSelector->setSelection(1); + wxGetApp().getDemodMgr().setLastMuted(false); wxGetApp().getDemodMgr().setLastStereo(false); wxGetApp().getDemodMgr().setLastBandwidth(DEFAULT_DEMOD_BW); wxGetApp().getDemodMgr().setLastGain(1.0); @@ -693,6 +713,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) { demodModeSelector->setSelection(dType); demodModeSelectorAdv->setSelection(dType); demodModeSelectorCons->setSelection(dCons); + demodMuteButton->setSelection(demod->isMuted()?1:-1); } if (demodWaterfallCanvas->getDragState() == WaterfallCanvas::WF_DRAG_NONE) { long long centerFreq = demod->getFrequency(); @@ -739,6 +760,25 @@ void AppFrame::OnIdle(wxIdleEvent& event) { demod->setDemodulatorCons(dSelectionCons); } + int muteMode = demodMuteButton->getSelection(); + if (demodMuteButton->modeChanged()) { + if (demod->isMuted() && muteMode == -1) { + demod->setMuted(false); + } else if (!demod->isMuted() && muteMode == 1) { + demod->setMuted(true); + } + wxGetApp().getDemodMgr().setLastMuted(demod->isMuted()); + demodMuteButton->clearModeChanged(); + } else { + if (demod->isMuted() && muteMode == -1) { + demodMuteButton->setSelection(1); + wxGetApp().getDemodMgr().setLastMuted(demod->isMuted()); + } else if (!demod->isMuted() && muteMode == 1) { + demodMuteButton->setSelection(-1); + wxGetApp().getDemodMgr().setLastMuted(demod->isMuted()); + } + } + demodWaterfallCanvas->setBandwidth(demodBw); demodSpectrumCanvas->setBandwidth(demodBw); } @@ -792,6 +832,15 @@ void AppFrame::OnIdle(wxIdleEvent& event) { spectrumCanvas->setCenterFrequency(wxGetApp().getFrequency()); waterfallCanvas->setCenterFrequency(wxGetApp().getFrequency()); } + if (demodMuteButton->modeChanged()) { + int muteMode = demodMuteButton->getSelection(); + if (muteMode == -1) { + wxGetApp().getDemodMgr().setLastMuted(false); + } else if (muteMode == 1) { + wxGetApp().getDemodMgr().setLastMuted(true); + } + demodMuteButton->clearModeChanged(); + } } if (demodTuner->getMouseTracker()->mouseInView()) { @@ -804,6 +853,11 @@ void AppFrame::OnIdle(wxIdleEvent& event) { scopeCanvas->setPPMMode(demodTuner->isAltDown()); + scopeCanvas->setShowDb(spectrumCanvas->getShowDb()); + wxGetApp().getScopeProcessor()->setScopeEnabled(scopeCanvas->scopeVisible()); + wxGetApp().getScopeProcessor()->setSpectrumEnabled(scopeCanvas->spectrumVisible()); + wxGetApp().getAudioVisualQueue()->set_max_num_items((scopeCanvas->scopeVisible()?1:0) + (scopeCanvas->spectrumVisible()?1:0)); + wxGetApp().getScopeProcessor()->run(); wxGetApp().getSpectrumDistributor()->run(); @@ -814,6 +868,9 @@ void AppFrame::OnIdle(wxIdleEvent& event) { if (val < 0.01) { val = 0.01; } + if (val > 0.99) { + val = 0.99; + } spectrumAvgMeter->setLevel(val); proc->setFFTAverageRate(val); @@ -873,6 +930,7 @@ void AppFrame::saveSession(std::string fileName) { *demod->newChild("stereo") = (*instance_i)->isStereo() ? 1 : 0; *demod->newChild("output_device") = outputDevices[(*instance_i)->getOutputDevice()].name; *demod->newChild("gain") = (*instance_i)->getGain(); + *demod->newChild("muted") = (*instance_i)->isMuted() ? 1 : 0; } s.SaveToFileXML(fileName); @@ -904,6 +962,9 @@ bool AppFrame::loadSession(std::string fileName) { DataNode *demodulators = l.rootNode()->getNext("demodulators"); + int numDemodulators = 0; + DemodulatorInstance *loadedDemod = NULL; + while (demodulators->hasAnother("demodulator")) { DataNode *demod = demodulators->getNext("demodulator"); @@ -917,15 +978,19 @@ bool AppFrame::loadSession(std::string fileName) { float squelch_level = demod->hasAnother("squelch_level") ? (float) *demod->getNext("squelch_level") : 0; int squelch_enabled = demod->hasAnother("squelch_enabled") ? (int) *demod->getNext("squelch_enabled") : 0; int stereo = demod->hasAnother("stereo") ? (int) *demod->getNext("stereo") : 0; + int muted = demod->hasAnother("muted") ? (int) *demod->getNext("muted") : 0; std::string output_device = demod->hasAnother("output_device") ? string(*(demod->getNext("output_device"))) : ""; float gain = demod->hasAnother("gain") ? (float) *demod->getNext("gain") : 1.0; DemodulatorInstance *newDemod = wxGetApp().getDemodMgr().newThread(); + loadedDemod = newDemod; + numDemodulators++; newDemod->setDemodulatorType(type); newDemod->setBandwidth(bandwidth); newDemod->setFrequency(freq); newDemod->setGain(gain); newDemod->updateLabel(freq); + newDemod->setMuted(muted?true:false); if (squelch_enabled) { newDemod->setSquelchEnabled(true); newDemod->setSquelchLevel(squelch_level); @@ -958,6 +1023,13 @@ bool AppFrame::loadSession(std::string fileName) { std::cout << "\t\tStereo: " << (stereo ? "true" : "false") << std::endl; std::cout << "\t\tOutput Device: " << output_device << std::endl; } + + if ((numDemodulators == 1) && loadedDemod) { + loadedDemod->setActive(true); + loadedDemod->setFollow(true); + loadedDemod->setTracking(true); + wxGetApp().getDemodMgr().setActiveDemodulator(loadedDemod); + } } catch (DataInvalidChildException &e) { std::cout << e.what() << std::endl; return false; diff --git a/src/AppFrame.h b/src/AppFrame.h index 9d68071..1147b94 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -84,6 +84,7 @@ private: // UITestCanvas *testCanvas; MeterCanvas *spectrumAvgMeter; MeterCanvas *waterfallSpeedMeter; + ModeSelectorCanvas *demodMuteButton; DemodulatorInstance *activeDemodulator; diff --git a/src/audio/AudioThread.h b/src/audio/AudioThread.h index d5dcb61..01ebda5 100644 --- a/src/audio/AudioThread.h +++ b/src/audio/AudioThread.h @@ -14,6 +14,7 @@ class AudioThreadInput: public ReferenceCounter { public: long long frequency; + int inputRate; int sampleRate; int channels; float peak; diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index e887200..a8ce1c7 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -10,6 +10,7 @@ DemodulatorInstance::DemodulatorInstance() : active.store(false); squelch.store(false); stereo.store(false); + muted.store(false); tracking.store(false); follow.store(false); currentAudioSampleRate.store(0); @@ -430,6 +431,15 @@ void DemodulatorInstance::setTracking(bool tracking) { this->tracking = tracking; } +bool DemodulatorInstance::isMuted() { + return demodulatorThread->isMuted(); +} + +void DemodulatorInstance::setMuted(bool muted) { + this->muted = muted; + demodulatorThread->setMuted(muted); +} + DemodulatorThreadInputQueue *DemodulatorInstance::getIQInputDataPipe() { return pipeIQInputData; } diff --git a/src/demod/DemodulatorInstance.h b/src/demod/DemodulatorInstance.h index 208d93e..19d3fb6 100644 --- a/src/demod/DemodulatorInstance.h +++ b/src/demod/DemodulatorInstance.h @@ -79,6 +79,9 @@ public: bool isTracking(); void setTracking(bool tracking); + + bool isMuted(); + void setMuted(bool muted); DemodulatorThreadInputQueue *getIQInputDataPipe(); @@ -104,6 +107,7 @@ private: std::atomic_bool active; std::atomic_bool squelch; std::atomic_bool stereo; + std::atomic_bool muted; std::atomic_llong currentFrequency; std::atomic_int currentBandwidth; diff --git a/src/demod/DemodulatorMgr.cpp b/src/demod/DemodulatorMgr.cpp index 9606d25..60b65cf 100644 --- a/src/demod/DemodulatorMgr.cpp +++ b/src/demod/DemodulatorMgr.cpp @@ -232,3 +232,12 @@ void DemodulatorMgr::setLastStereo(bool lastStereo) { this->lastStereo = lastStereo; } + +bool DemodulatorMgr::isLastMuted() const { + return lastMuted; +} + +void DemodulatorMgr::setLastMuted(bool lastMuted) { + this->lastMuted = lastMuted; +} + diff --git a/src/demod/DemodulatorMgr.h b/src/demod/DemodulatorMgr.h index 0dcd3f0..0cfb4ed 100644 --- a/src/demod/DemodulatorMgr.h +++ b/src/demod/DemodulatorMgr.h @@ -43,6 +43,9 @@ public: bool isLastStereo() const; void setLastStereo(bool lastStereo); + bool isLastMuted() const; + void setLastMuted(bool lastMuted); + private: void garbageCollect(); void updateLastState(); @@ -60,5 +63,5 @@ private: bool lastSquelchEnabled; float lastSquelch; float lastGain; - bool lastStereo; + bool lastStereo, lastMuted; }; diff --git a/src/demod/DemodulatorPreThread.cpp b/src/demod/DemodulatorPreThread.cpp index c93df6f..5c3820f 100644 --- a/src/demod/DemodulatorPreThread.cpp +++ b/src/demod/DemodulatorPreThread.cpp @@ -72,9 +72,6 @@ void DemodulatorPreThread::initialize() { } DemodulatorPreThread::~DemodulatorPreThread() { - delete workerThread; - delete workerQueue; - delete workerResults; } void DemodulatorPreThread::run() { @@ -260,7 +257,7 @@ void DemodulatorPreThread::run() { inp->decRefCount(); - if (!workerResults->empty()) { + if (!terminated && !workerResults->empty()) { while (!workerResults->empty()) { DemodulatorWorkerThreadResult result; workerResults->pop(result); @@ -323,7 +320,12 @@ void DemodulatorPreThread::terminate() { terminated = true; DemodulatorThreadIQData *inp = new DemodulatorThreadIQData; // push dummy to nudge queue iqInputQueue->push(inp); + DemodulatorWorkerThreadCommand command(DemodulatorWorkerThreadCommand::DEMOD_WORKER_THREAD_CMD_NULL); + workerQueue->push(command); workerThread->terminate(); - t_Worker->detach(); + t_Worker->join(); delete t_Worker; + delete workerThread; + delete workerResults; + delete workerQueue; } diff --git a/src/demod/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp index 269e5e4..228ad3f 100644 --- a/src/demod/DemodulatorThread.cpp +++ b/src/demod/DemodulatorThread.cpp @@ -14,6 +14,7 @@ DemodulatorThread::DemodulatorThread() : IOThread(), iqAutoGain(NULL), amOutputCeil(1), amOutputCeilMA(1), amOutputCeilMAA(1), audioSampleRate(0), squelchLevel(0), signalLevel(0), squelchEnabled(false), iqInputQueue(NULL), audioOutputQueue(NULL), audioVisOutputQueue(NULL), threadQueueControl(NULL), threadQueueNotify(NULL) { stereo.store(false); + muted.store(false); agcEnabled.store(false); demodulatorType.store(DEMOD_TYPE_FM); @@ -685,6 +686,7 @@ void DemodulatorThread::run() { ati = outputBuffers.getBuffer(); ati->sampleRate = audioSampleRate; + ati->inputRate = inp->sampleRate; ati->setRefCount(1); if (demodulatorType == DEMOD_TYPE_RAW) { @@ -734,7 +736,9 @@ void DemodulatorThread::run() { if (ati && audioVisOutputQueue != NULL && audioVisOutputQueue->empty()) { ati_vis->busy_update.lock(); - + ati_vis->sampleRate = inp->sampleRate; + ati_vis->inputRate = inp->sampleRate; + int num_vis = DEMOD_VIS_SIZE; if (demodulatorType == DEMOD_TYPE_RAW || (stereo && inp->sampleRate >= 100000)) { ati_vis->channels = 2; @@ -752,6 +756,8 @@ void DemodulatorThread::run() { } } else { for (int i = 0; i < stereoSize / 2; i++) { + ati_vis->inputRate = audioSampleRate; + ati_vis->sampleRate = 36000; ati_vis->data[i] = ati->data[i * 2]; ati_vis->data[i + stereoSize / 2] = ati->data[i * 2 + 1]; } @@ -759,7 +765,7 @@ void DemodulatorThread::run() { } else { ati_vis->channels = 1; if (numAudioWritten > bufSize) { - + ati_vis->inputRate = audioSampleRate; if (num_vis > numAudioWritten) { num_vis = numAudioWritten; } @@ -774,13 +780,16 @@ void DemodulatorThread::run() { // std::cout << "Signal: " << agc_crcf_get_signal_level(agc) << " -- " << agc_crcf_get_rssi(agc) << "dB " << std::endl; } - audioVisOutputQueue->push(ati_vis); - ati_vis->busy_update.unlock(); + audioVisOutputQueue->push(ati_vis); } if (ati != NULL) { - audioOutputQueue->push(ati); + if (!muted.load()) { + audioOutputQueue->push(ati); + } else { + ati->setRefCount(0); + } } if (!threadQueueControl->empty()) { @@ -922,6 +931,14 @@ bool DemodulatorThread::isStereo() { return stereo.load(); } +bool DemodulatorThread::isMuted() { + return muted.load(); +} + +void DemodulatorThread::setMuted(bool muted) { + this->muted.store(muted); +} + void DemodulatorThread::setAGC(bool state) { agcEnabled.store(state); } diff --git a/src/demod/DemodulatorThread.h b/src/demod/DemodulatorThread.h index e7400af..891849f 100644 --- a/src/demod/DemodulatorThread.h +++ b/src/demod/DemodulatorThread.h @@ -8,7 +8,7 @@ typedef ThreadQueue DemodulatorThreadOutputQueue; -#define DEMOD_VIS_SIZE 1024 +#define DEMOD_VIS_SIZE 2048 class DemodulatorThread : public IOThread { public: @@ -27,6 +27,9 @@ public: void setAGC(bool state); bool getAGC(); + void setMuted(bool state); + bool isMuted(); + float getSignalLevel(); void setSquelchLevel(float signal_level_in); float getSquelchLevel(); @@ -135,6 +138,7 @@ protected: float amOutputCeilMAA; std::atomic_bool stereo; + std::atomic_bool muted; std::atomic_bool agcEnabled; std::atomic_int demodulatorType; std::atomic_int demodulatorCons; diff --git a/src/panel/ScopePanel.cpp b/src/panel/ScopePanel.cpp index c50fa08..f72a14c 100644 --- a/src/panel/ScopePanel.cpp +++ b/src/panel/ScopePanel.cpp @@ -2,6 +2,7 @@ #include "ColorTheme.h" ScopePanel::ScopePanel() : GLPanel(), scopeMode(SCOPE_MODE_Y) { + setFill(GLPanelFillType::GLPANEL_FILL_NONE); bgPanel.setFill(GLPanelFillType::GLPANEL_FILL_GRAD_BAR_Y); bgPanelStereo[0].setFill(GLPanelFillType::GLPANEL_FILL_GRAD_BAR_Y); bgPanelStereo[0].setPosition(0, 0.5); @@ -21,14 +22,13 @@ void ScopePanel::setPoints(std::vector &points) { } void ScopePanel::drawPanelContents() { - - glLineWidth(1.0); if (scopeMode == SCOPE_MODE_Y) { bgPanel.setFillColor(ThemeMgr::mgr.currentTheme->scopeBackground, ThemeMgr::mgr.currentTheme->scopeBackground * 2.0); bgPanel.calcTransform(transform); bgPanel.draw(); - + glLineWidth(1.0); + glEnable(GL_LINE_SMOOTH); glLoadMatrixf(transform); glColor3f(ThemeMgr::mgr.currentTheme->scopeLine.r * 0.35, ThemeMgr::mgr.currentTheme->scopeLine.g * 0.35, ThemeMgr::mgr.currentTheme->scopeLine.b * 0.35); @@ -45,8 +45,10 @@ void ScopePanel::drawPanelContents() { bgPanelStereo[1].calcTransform(transform); bgPanelStereo[1].draw(); + glLineWidth(1.0); glLoadMatrixf(transform); glColor3f(ThemeMgr::mgr.currentTheme->scopeLine.r, ThemeMgr::mgr.currentTheme->scopeLine.g, ThemeMgr::mgr.currentTheme->scopeLine.b); + glEnable(GL_LINE_SMOOTH); glBegin (GL_LINES); glVertex2f(-1.0, 0.0); glVertex2f(1.0, 0.0); diff --git a/src/panel/SpectrumPanel.cpp b/src/panel/SpectrumPanel.cpp index 81f62f6..034d3c5 100644 --- a/src/panel/SpectrumPanel.cpp +++ b/src/panel/SpectrumPanel.cpp @@ -2,15 +2,24 @@ #include #include +#include #include "ColorTheme.h" -SpectrumPanel::SpectrumPanel() : floorValue(0), ceilValue(1) { +SpectrumPanel::SpectrumPanel() : floorValue(0), ceilValue(1), showDb(false), fftSize(2048) { setFill(GLPANEL_FILL_GRAD_Y); setFillColor(ThemeMgr::mgr.currentTheme->fftBackground * 2.0, ThemeMgr::mgr.currentTheme->fftBackground); + + dbPanelCeil.setMarginPx(0); + dbPanelCeil.setFill(GLPanel::GLPANEL_FILL_GRAD_X); + dbPanelCeil.setFillColor(RGBA4f(0.2,0.2,0.2,5.0), RGBA4f(0.2,0.2,0.2,0.0)); + + dbPanelFloor.setMarginPx(0); + dbPanelFloor.setFill(GLPanel::GLPANEL_FILL_GRAD_X); + dbPanelFloor.setFillColor(RGBA4f(0.2,0.2,0.2,5.), RGBA4f(0.2,0.2,0.2,0.0)); } -float SpectrumPanel::getFloorValue() const { +float SpectrumPanel::getFloorValue() { return floorValue; } @@ -18,7 +27,7 @@ void SpectrumPanel::setFloorValue(float floorValue) { this->floorValue = floorValue; } -float SpectrumPanel::getCeilValue() const { +float SpectrumPanel::getCeilValue() { return ceilValue; } @@ -42,6 +51,31 @@ long long SpectrumPanel::getBandwidth() { return bandwidth; } +void SpectrumPanel::setFFTSize(int fftSize_in) { + this->fftSize = fftSize_in; +} + +int SpectrumPanel::getFFTSize() { + return fftSize; +} + +void SpectrumPanel::setShowDb(bool showDb) { + this->showDb = showDb; + if (showDb) { + addChild(&dbPanelCeil); + addChild(&dbPanelFloor); + } else { + removeChild(&dbPanelCeil); + removeChild(&dbPanelFloor); + } + +} + +bool SpectrumPanel::getShowDb() { + return showDb; +} + + void SpectrumPanel::setPoints(std::vector &points) { this->points.assign(points.begin(), points.end()); } @@ -95,38 +129,69 @@ void SpectrumPanel::drawPanelContents() { glDrawArrays(GL_LINE_STRIP, 0, points.size() / 2); glDisableClientState(GL_VERTEX_ARRAY); } - - glLoadMatrixf(transform); - + GLint vp[4]; glGetIntegerv( GL_VIEWPORT, vp); float viewHeight = (float) vp[3]; float viewWidth = (float) vp[2]; + glLoadMatrixf(transform); + long long leftFreq = (double) freq - ((double) bandwidth / 2.0); long long rightFreq = leftFreq + (double) bandwidth; - - long long firstMhz = (leftFreq / 1000000) * 1000000; - long double mhzStart = ((long double) (firstMhz - leftFreq) / (long double) (rightFreq - leftFreq)) * 2.0; + + long long hzStep = 1000000; long double mhzStep = (100000.0 / (long double) (rightFreq - leftFreq)) * 2.0; - double mhzVisualStep = 0.1f; - + double mhzVisualStep = 0.1; + + std::stringstream label; + label.precision(1); + if (mhzStep * 0.5 * viewWidth < 40) { mhzStep = (250000.0 / (long double) (rightFreq - leftFreq)) * 2.0; - mhzVisualStep = 0.25f; - } + mhzVisualStep = 0.25; - if (mhzStep * 0.5 * viewWidth > 400) { + if (mhzStep * 0.5 * viewWidth < 40) { + mhzStep = (500000.0 / (long double) (rightFreq - leftFreq)) * 2.0; + mhzVisualStep = 0.5; + } + + if (mhzStep * 0.5 * viewWidth < 40) { + mhzStep = (1000000.0 / (long double) (rightFreq - leftFreq)) * 2.0; + mhzVisualStep = 1.0; + } + + if (mhzStep * 0.5 * viewWidth < 40) { + mhzStep = (2500000.0 / (long double) (rightFreq - leftFreq)) * 2.0; + mhzVisualStep = 2.5; + } + + if (mhzStep * 0.5 * viewWidth < 40) { + mhzStep = (5000000.0 / (long double) (rightFreq - leftFreq)) * 2.0; + mhzVisualStep = 5.0; + } + + if (mhzStep * 0.5 * viewWidth < 40) { + mhzStep = (10000000.0 / (long double) (rightFreq - leftFreq)) * 2.0; + mhzVisualStep = 10.0; + } + + if (mhzStep * 0.5 * viewWidth < 40) { + mhzStep = (50000000.0 / (long double) (rightFreq - leftFreq)) * 2.0; + mhzVisualStep = 50.0; + } + } else if (mhzStep * 0.5 * viewWidth > 350) { mhzStep = (10000.0 / (long double) (rightFreq - leftFreq)) * 2.0; - mhzVisualStep = 0.01f; + mhzVisualStep = 0.01; + label.precision(2); } - - long double currentMhz = trunc(floor(firstMhz / 1000000.0)); - std::stringstream label; - label.precision(2); + long long firstMhz = (leftFreq / hzStep) * hzStep; + long double mhzStart = ((long double) (firstMhz - leftFreq) / (long double) (rightFreq - leftFreq)) * 2.0; + long double currentMhz = trunc(floor(firstMhz / (long double)1000000.0)); + double hPos = 1.0 - (16.0 / viewHeight); double lMhzPos = 1.0 - (5.0 / viewHeight); @@ -162,4 +227,24 @@ void SpectrumPanel::drawPanelContents() { } glLineWidth(1.0); + + if (showDb) { + float dbPanelWidth = (1.0/viewWidth)*75.0; + float dbPanelHeight = (1.0/viewHeight)*14.0; + + + std::stringstream ssLabel; + ssLabel << std::fixed << std::setprecision(1) << (20.0 * log10(2.0*(getCeilValue())/(double)fftSize)) << "dB"; + + dbPanelCeil.setText(ssLabel.str(), GLFont::GLFONT_ALIGN_RIGHT); + dbPanelCeil.setSize(dbPanelWidth, dbPanelHeight); + dbPanelCeil.setPosition(-1.0 + dbPanelWidth, 1.0 - dbPanelHeight); + + ssLabel.str(""); + ssLabel << (20.0 * log10(2.0*(getFloorValue())/(double)fftSize)) << "dB"; + + dbPanelFloor.setText(ssLabel.str(), GLFont::GLFONT_ALIGN_RIGHT); + dbPanelFloor.setSize(dbPanelWidth, dbPanelHeight); + dbPanelFloor.setPosition(-1.0 + dbPanelWidth, - 1.0 + dbPanelHeight); + } } diff --git a/src/panel/SpectrumPanel.h b/src/panel/SpectrumPanel.h index af2d2f7..3ccd747 100644 --- a/src/panel/SpectrumPanel.h +++ b/src/panel/SpectrumPanel.h @@ -8,10 +8,10 @@ public: void setPoints(std::vector &points); - float getFloorValue() const; + float getFloorValue(); void setFloorValue(float floorValue); - float getCeilValue() const; + float getCeilValue(); void setCeilValue(float ceilValue); void setFreq(long long freq); @@ -19,13 +19,24 @@ public: void setBandwidth(long long bandwidth); long long getBandwidth(); + + void setFFTSize(int fftSize_in); + int getFFTSize(); + + void setShowDb(bool showDb); + bool getShowDb(); protected: void drawPanelContents(); private: float floorValue, ceilValue; + int fftSize; long long freq; long long bandwidth; std::vector points; + + GLTextPanel dbPanelCeil; + GLTextPanel dbPanelFloor; + bool showDb; }; \ No newline at end of file diff --git a/src/panel/WaterfallPanel.cpp b/src/panel/WaterfallPanel.cpp index d3b0224..5de721a 100644 --- a/src/panel/WaterfallPanel.cpp +++ b/src/panel/WaterfallPanel.cpp @@ -1,7 +1,7 @@ #include "WaterfallPanel.h" WaterfallPanel::WaterfallPanel() : GLPanel(), fft_size(0), waterfall_lines(0), waterfall_slice(NULL), activeTheme(NULL) { - setFillColor(RGB3f(0,0,0)); + setFillColor(RGBA4f(0,0,0)); for (int i = 0; i < 2; i++) { waterfall[i] = 0; } diff --git a/src/process/ScopeVisualProcessor.cpp b/src/process/ScopeVisualProcessor.cpp index 15669da..7282ec9 100644 --- a/src/process/ScopeVisualProcessor.cpp +++ b/src/process/ScopeVisualProcessor.cpp @@ -1,4 +1,54 @@ #include "ScopeVisualProcessor.h" +#include +#include + +ScopeVisualProcessor::ScopeVisualProcessor(): fftInData(NULL), fftwOutput(NULL), fftw_plan(NULL), maxScopeSamples(1024) { + scopeEnabled.store(true); + spectrumEnabled.store(true); + fft_average_rate = 0.65; +} + +ScopeVisualProcessor::~ScopeVisualProcessor() { + /*if (fftInData) { + free(fftInData); + } + if (fftwOutput) { + free(fftwOutput); + }*/ + if (fftw_plan) { + fftwf_destroy_plan(fftw_plan); + } +} + + +void ScopeVisualProcessor::setup(int fftSize_in) { + fftSize = fftSize_in; + desiredInputSize = fftSize; + + if (fftInData) { + free(fftInData); + } + fftInData = (float*) fftwf_malloc(sizeof(float) * fftSize); + if (fftwOutput) { + free(fftwOutput); + } + fftwOutput = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftSize); + if (fftw_plan) { + fftwf_destroy_plan(fftw_plan); + } + fftw_plan = fftwf_plan_dft_r2c_1d(fftSize, fftInData, fftwOutput, FFTW_ESTIMATE); + //(fftSize, fftInData, fftwOutput, 0); + //(fftSize, fftwInput, fftwOutput, FFTW_R2HC, FFTW_ESTIMATE); + +} + +void ScopeVisualProcessor::setScopeEnabled(bool scopeEnable) { + scopeEnabled.store(scopeEnable); +} + +void ScopeVisualProcessor::setSpectrumEnabled(bool spectrumEnable) { + spectrumEnabled.store(spectrumEnable); +} void ScopeVisualProcessor::process() { if (!isOutputEmpty()) { @@ -11,42 +61,144 @@ void ScopeVisualProcessor::process() { if (!audioInputData) { return; } - int iMax = audioInputData->data.size(); + int i, iMax = audioInputData->data.size(); if (!iMax) { audioInputData->decRefCount(); return; } audioInputData->busy_update.lock(); - ScopeRenderData *renderData = outputBuffers.getBuffer(); - renderData->channels = audioInputData->channels; - if (renderData->waveform_points.size() != iMax * 2) { - renderData->waveform_points.resize(iMax * 2); - } + ScopeRenderData *renderData = NULL; + + if (scopeEnabled) { + iMax = audioInputData->data.size(); + if (iMax > maxScopeSamples) { + iMax = maxScopeSamples; + } - float peak = 1.0f; - - for (int i = 0; i < iMax; i++) { - float p = fabs(audioInputData->data[i]); - if (p > peak) { - peak = p; + renderData = outputBuffers.getBuffer(); + renderData->channels = audioInputData->channels; + renderData->inputRate = audioInputData->inputRate; + renderData->sampleRate = audioInputData->sampleRate; + + if (renderData->waveform_points.size() != iMax * 2) { + renderData->waveform_points.resize(iMax * 2); } - } - - if (audioInputData->channels == 2) { - for (int i = 0; i < iMax; i++) { - renderData->waveform_points[i * 2] = (((double) (i % (iMax/2)) / (double) iMax) * 2.0 - 0.5) * 2.0; - renderData->waveform_points[i * 2 + 1] = audioInputData->data[i] / peak; - } - } else { - for (int i = 0; i < iMax; i++) { - renderData->waveform_points[i * 2] = (((double) i / (double) iMax) - 0.5) * 2.0; - renderData->waveform_points[i * 2 + 1] = audioInputData->data[i] / peak; - } - } - distribute(renderData); + float peak = 1.0f; + + for (i = 0; i < iMax; i++) { + float p = fabs(audioInputData->data[i]); + if (p > peak) { + peak = p; + } + } + + if (audioInputData->channels == 2) { + iMax = audioInputData->data.size(); + if (renderData->waveform_points.size() != iMax * 2) { + renderData->waveform_points.resize(iMax * 2); + } + for (i = 0; i < iMax; i++) { + renderData->waveform_points[i * 2] = (((double) (i % (iMax/2)) / (double) iMax) * 2.0 - 0.5) * 2.0; + renderData->waveform_points[i * 2 + 1] = audioInputData->data[i] / peak; + } + } else { + for (i = 0; i < iMax; i++) { + renderData->waveform_points[i * 2] = (((double) i / (double) iMax) - 0.5) * 2.0; + renderData->waveform_points[i * 2 + 1] = audioInputData->data[i] / peak; + } + } + + renderData->spectrum = false; + + distribute(renderData); + } + + if (spectrumEnabled) { + renderData = outputBuffers.getBuffer(); + iMax = audioInputData->data.size(); + + if (audioInputData->channels==1) { + for (i = 0; i < fftSize; i++) { + if (i < iMax) { + fftInData[i] = audioInputData->data[i]; + } else { + fftInData[i] = 0; + } + } + } else if (audioInputData->channels==2) { + iMax = iMax/2; + for (i = 0; i < fftSize; i++) { + if (i < iMax) { + fftInData[i] = audioInputData->data[i] + audioInputData->data[iMax+i]; + } else { + fftInData[i] = 0; + } + } + } + + + fftwf_execute(fftw_plan); + + float fft_ceil = 0, fft_floor = 1; + + if (fft_result.size() < (fftSize/2)) { + fft_result.resize((fftSize/2)); + fft_result_ma.resize((fftSize/2)); + fft_result_maa.resize((fftSize/2)); + } + + for (i = 0; i < (fftSize/2); i++) { + float a = fftwOutput[i][0]; + float b = fftwOutput[i][1]; + fft_result[i] = sqrt( a * a + b * b); + } + + for (i = 0; i < (fftSize/2); i++) { + fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * fft_average_rate; + fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * fft_average_rate; + + if (fft_result_maa[i] > fft_ceil) { + fft_ceil = fft_result_maa[i]; + } + if (fft_result_maa[i] < fft_floor) { + fft_floor = fft_result_maa[i]; + } + } + + fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05; + fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05; + + fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05; + fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05; + + int outSize = fftSize/2; + + if (audioInputData->sampleRate != audioInputData->inputRate) { + outSize = (int)floor((float)outSize * ((float)audioInputData->sampleRate/(float)audioInputData->inputRate)); + } + + if (renderData->waveform_points.size() != outSize*2) { + renderData->waveform_points.resize(outSize*2); + } + + for (i = 0; i < outSize; i++) { + float v = (log10(fft_result_maa[i]+0.25 - (fft_floor_maa-0.75)) / log10((fft_ceil_maa+0.25) - (fft_floor_maa-0.75))); + renderData->waveform_points[i * 2] = ((double) i / (double) (outSize)); + renderData->waveform_points[i * 2 + 1] = v; + } + + renderData->fft_floor = fft_floor_maa; + renderData->fft_ceil = fft_ceil_maa; + renderData->fft_size = fftSize/2; + renderData->inputRate = audioInputData->inputRate; + renderData->sampleRate = audioInputData->sampleRate; + renderData->spectrum = true; + distribute(renderData); + } + audioInputData->busy_update.unlock(); } } diff --git a/src/process/ScopeVisualProcessor.h b/src/process/ScopeVisualProcessor.h index 08ce3bb..98a2398 100644 --- a/src/process/ScopeVisualProcessor.h +++ b/src/process/ScopeVisualProcessor.h @@ -2,17 +2,47 @@ #include "VisualProcessor.h" #include "AudioThread.h" +#include "fftw3.h" class ScopeRenderData: public ReferenceCounter { public: std::vector waveform_points; + int inputRate; + int sampleRate; int channels; + bool spectrum; + int fft_size; + double fft_floor, fft_ceil; }; typedef ThreadQueue ScopeRenderDataQueue; class ScopeVisualProcessor : public VisualProcessor { +public: + ScopeVisualProcessor(); + ~ScopeVisualProcessor(); + void setup(int fftSize_in); + void setScopeEnabled(bool scopeEnable); + void setSpectrumEnabled(bool spectrumEnable); protected: void process(); ReBuffer outputBuffers; + + std::atomic_bool scopeEnabled; + std::atomic_bool spectrumEnabled; + + float *fftInData; + fftwf_complex *fftwOutput; + fftwf_plan fftw_plan; + int fftSize; + int desiredInputSize; + int maxScopeSamples; + + double fft_ceil_ma, fft_ceil_maa; + double fft_floor_ma, fft_floor_maa; + float fft_average_rate; + + std::vector fft_result; + std::vector fft_result_ma; + std::vector fft_result_maa; }; diff --git a/src/process/SpectrumVisualProcessor.cpp b/src/process/SpectrumVisualProcessor.cpp index 06c65c2..8dc890d 100644 --- a/src/process/SpectrumVisualProcessor.cpp +++ b/src/process/SpectrumVisualProcessor.cpp @@ -14,7 +14,7 @@ SpectrumVisualProcessor::SpectrumVisualProcessor() : lastInputBandwidth(0), last fft_ceil_ma = fft_ceil_maa = 100.0; fft_floor_ma = fft_floor_maa = 0.0; - desiredInputSize = 0; + desiredInputSize.store(0); fft_average_rate = 0.65; } @@ -27,19 +27,25 @@ bool SpectrumVisualProcessor::isView() { } void SpectrumVisualProcessor::setView(bool bView) { + busy_run.lock(); is_view.store(bView); + busy_run.unlock(); } void SpectrumVisualProcessor::setFFTAverageRate(float fftAverageRate) { - this->fft_average_rate = fftAverageRate; + busy_run.lock(); + this->fft_average_rate.store(fftAverageRate); + busy_run.unlock(); } float SpectrumVisualProcessor::getFFTAverageRate() { - return this->fft_average_rate; + return this->fft_average_rate.load(); } void SpectrumVisualProcessor::setCenterFrequency(long long centerFreq_in) { + busy_run.lock(); centerFreq.store(centerFreq_in); + busy_run.unlock(); } long long SpectrumVisualProcessor::getCenterFrequency() { @@ -47,7 +53,9 @@ long long SpectrumVisualProcessor::getCenterFrequency() { } void SpectrumVisualProcessor::setBandwidth(long bandwidth_in) { + busy_run.lock(); bandwidth.store(bandwidth_in); + busy_run.unlock(); } long SpectrumVisualProcessor::getBandwidth() { @@ -55,12 +63,14 @@ long SpectrumVisualProcessor::getBandwidth() { } int SpectrumVisualProcessor::getDesiredInputSize() { - return desiredInputSize; + return desiredInputSize.load(); } void SpectrumVisualProcessor::setup(int fftSize_in) { + busy_run.lock(); + fftSize = fftSize_in; - desiredInputSize = fftSize; + desiredInputSize.store(fftSize); if (fftwInput) { free(fftwInput); @@ -82,7 +92,7 @@ void SpectrumVisualProcessor::setup(int fftSize_in) { fftwf_destroy_plan(fftw_plan); } fftw_plan = fftwf_plan_dft_1d(fftSize, fftwInput, fftwOutput, FFTW_FORWARD, FFTW_ESTIMATE); - + busy_run.unlock(); } void SpectrumVisualProcessor::process() { @@ -102,6 +112,7 @@ void SpectrumVisualProcessor::process() { } iqData->busy_rw.lock(); + busy_run.lock(); std::vector *data = &iqData->data; @@ -118,6 +129,7 @@ void SpectrumVisualProcessor::process() { if (!iqData->frequency || !iqData->sampleRate) { iqData->decRefCount(); iqData->busy_rw.unlock(); + busy_run.unlock(); return; } @@ -125,7 +137,7 @@ void SpectrumVisualProcessor::process() { int desired_input_size = fftSize / resamplerRatio; - this->desiredInputSize = desired_input_size; + this->desiredInputSize.store(desired_input_size); if (iqData->data.size() < desired_input_size) { // std::cout << "fft underflow, desired: " << desired_input_size << " actual:" << input->data.size() << std::endl; @@ -283,9 +295,6 @@ void SpectrumVisualProcessor::process() { } } - fft_ceil += 0.25; - fft_floor -= 1; - fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05; fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05; @@ -293,7 +302,7 @@ void SpectrumVisualProcessor::process() { fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05; for (int i = 0, iMax = fftSize; i < iMax; i++) { - float v = (log10(fft_result_maa[i] - fft_floor_maa) / log10(fft_ceil_maa - fft_floor_maa)); + float v = (log10(fft_result_maa[i]+0.25 - (fft_floor_maa-0.75)) / log10((fft_ceil_maa+0.25) - (fft_floor_maa-0.75))); output->spectrum_points[i * 2] = ((float) i / (float) iMax); output->spectrum_points[i * 2 + 1] = v; } @@ -307,5 +316,6 @@ void SpectrumVisualProcessor::process() { iqData->decRefCount(); iqData->busy_rw.unlock(); + busy_run.unlock(); } diff --git a/src/process/SpectrumVisualProcessor.h b/src/process/SpectrumVisualProcessor.h index 8e1e694..149c170 100644 --- a/src/process/SpectrumVisualProcessor.h +++ b/src/process/SpectrumVisualProcessor.h @@ -53,7 +53,7 @@ private: double fft_ceil_ma, fft_ceil_maa; double fft_floor_ma, fft_floor_maa; - float fft_average_rate; + std::atomic fft_average_rate; std::vector fft_result; std::vector fft_result_ma; @@ -66,7 +66,8 @@ private: std::vector shiftBuffer; std::vector resampleBuffer; - int desiredInputSize; + std::atomic_int desiredInputSize; + std::mutex busy_run; }; diff --git a/src/ui/GLPanel.cpp b/src/ui/GLPanel.cpp index c7e2bca..7d06c81 100644 --- a/src/ui/GLPanel.cpp +++ b/src/ui/GLPanel.cpp @@ -1,17 +1,21 @@ #include "GLPanel.h" #include "cubic_math.h" +#include using namespace CubicVR; GLPanel::GLPanel() : fillType(GLPANEL_FILL_SOLID), contentsVisible(true), transform(mat4::identity()) { pos[0] = 0.0f; pos[1] = 0.0f; + rot[0] = 0.0f; + rot[1] = 0.0f; + rot[2] = 0.0f; size[0] = 1.0f; size[1] = 1.0f; - fill[0] = RGB3f(0.5,0.5,0.5); - fill[1] = RGB3f(0.1,0.1,0.1); - borderColor = RGB3f(0.8, 0.8, 0.8); + fill[0] = RGBA4f(0.5,0.5,0.5); + fill[1] = RGBA4f(0.1,0.1,0.1); + borderColor = RGBA4f(0.8, 0.8, 0.8); setCoordinateSystem(GLPANEL_Y_UP); setMarginPx(0); setBorderPx(0); @@ -23,8 +27,8 @@ void GLPanel::genArrays() { if (fillType == GLPANEL_FILL_SOLID || fillType == GLPANEL_FILL_GRAD_X || fillType == GLPANEL_FILL_GRAD_Y) { glPoints.reserve(2 * 4); glPoints.resize(2 * 4); - glColors.reserve(3 * 4); - glColors.resize(3 * 4); + glColors.reserve(4 * 4); + glColors.resize(4 * 4); float pts[2 * 4] = { min, min, @@ -33,7 +37,7 @@ void GLPanel::genArrays() { max, min }; - RGB3f c[4]; + RGBA4f c[4]; if (fillType == GLPANEL_FILL_SOLID) { c[0] = c[1] = c[2] = c[3] = fill[0]; @@ -45,22 +49,22 @@ void GLPanel::genArrays() { c[1] = c[2] = fill[1]; } - float clr[3 * 4] = { - c[0].r, c[0].g, c[0].b, - c[1].r, c[1].g, c[1].b, - c[2].r, c[2].g, c[2].b, - c[3].r, c[3].g, c[3].b - }; + float clr[4 * 4] = { + c[0].r, c[0].g, c[0].b, c[0].a, + c[1].r, c[1].g, c[1].b, c[1].a, + c[2].r, c[2].g, c[2].b, c[2].a, + c[3].r, c[3].g, c[3].b, c[3].a + }; glPoints.assign(pts, pts + (2 * 4)); - glColors.assign(clr, clr + (3 * 4)); + glColors.assign(clr, clr + (4 * 4)); } else { glPoints.reserve(2 * 8); glPoints.resize(2 * 8); - glColors.reserve(3 * 8); - glColors.resize(3 * 8); + glColors.reserve(4 * 8); + glColors.resize(4 * 8); - RGB3f c[8]; + RGBA4f c[8]; if (fillType == GLPANEL_FILL_GRAD_BAR_X) { float pts[2 * 8] = { @@ -103,18 +107,18 @@ void GLPanel::genArrays() { c[5] = c[6] = fill[0]; } - float clr[3 * 8] = { - c[0].r, c[0].g, c[0].b, - c[1].r, c[1].g, c[1].b, - c[2].r, c[2].g, c[2].b, - c[3].r, c[3].g, c[3].b, - c[4].r, c[4].g, c[4].b, - c[5].r, c[5].g, c[5].b, - c[6].r, c[6].g, c[6].b, - c[7].r, c[7].g, c[7].b + float clr[4 * 8] = { + c[0].r, c[0].g, c[0].b, c[0].a, + c[1].r, c[1].g, c[1].b, c[1].a, + c[2].r, c[2].g, c[2].b, c[2].a, + c[3].r, c[3].g, c[3].b, c[3].a, + c[4].r, c[4].g, c[4].b, c[4].a, + c[5].r, c[5].g, c[5].b, c[5].a, + c[6].r, c[6].g, c[6].b, c[6].a, + c[7].r, c[7].g, c[7].b, c[7].a }; - glColors.assign(clr, clr + (3 * 8)); + glColors.assign(clr, clr + (4 * 8)); } } @@ -175,12 +179,12 @@ void GLPanel::setFill(GLPanelFillType fill_mode) { genArrays(); } -void GLPanel::setFillColor(RGB3f color1) { +void GLPanel::setFillColor(RGBA4f color1) { fill[0] = color1; genArrays(); } -void GLPanel::setFillColor(RGB3f color1, RGB3f color2) { +void GLPanel::setFillColor(RGBA4f color1, RGBA4f color2) { fill[0] = color1; fill[1] = color2; genArrays(); @@ -191,7 +195,7 @@ void GLPanel::setMarginPx(float marg) { } -void GLPanel::setBorderColor(RGB3f clr) { +void GLPanel::setBorderColor(RGBA4f clr) { borderColor = clr; } @@ -207,7 +211,19 @@ void GLPanel::setBorderPx(float bordl, float bordr, float bordt, float bordb) { } void GLPanel::addChild(GLPanel *childPanel) { - children.push_back(childPanel); + std::vector::iterator i = std::find(children.begin(), children.end(), childPanel); + + if (i == children.end()) { + children.push_back(childPanel); + } +} + +void GLPanel::removeChild(GLPanel *childPanel) { + std::vector::iterator i = std::find(children.begin(), children.end(), childPanel); + + if (i != children.end()) { + children.erase(i); + } } void GLPanel::drawChildren() { @@ -228,6 +244,11 @@ void GLPanel::drawPanelContents() { void GLPanel::calcTransform(mat4 transform_in) { // compute local transform localTransform = mat4::translate(pos[0], pos[1], 0) * mat4::scale(size[0], size[1], 1); + + if (rot[0] || rot[1] || rot[2]) { + localTransform *= mat4::rotate(rot[0], rot[1], rot[2]); + } + // compute global transform transform = transform_in * localTransform; @@ -265,10 +286,12 @@ void GLPanel::draw() { glLoadMatrixf(transform); if (fillType != GLPANEL_FILL_NONE) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glVertexPointer(2, GL_FLOAT, 0, &glPoints[0]); - glColorPointer(3, GL_FLOAT, 0, &glColors[0]); + glColorPointer(4, GL_FLOAT, 0, &glColors[0]); glDrawArrays(GL_QUADS, 0, glPoints.size() / 2); @@ -277,7 +300,7 @@ void GLPanel::draw() { if (borderPx.left || borderPx.right || borderPx.top || borderPx.bottom) { glEnable(GL_LINE_SMOOTH); - glColor3f(borderColor.r, borderColor.g, borderColor.b); + glColor4f(borderColor.r, borderColor.g, borderColor.b, borderColor.a); if (borderPx.left) { glLineWidth(borderPx.left); @@ -313,6 +336,7 @@ void GLPanel::draw() { glDisable(GL_LINE_SMOOTH); } + glDisable(GL_BLEND); } if (contentsVisible) { @@ -337,6 +361,8 @@ void GLPanel::draw() { GLTextPanel::GLTextPanel() : GLPanel() { coord = GLPANEL_Y_UP; + horizAlign = GLFont::GLFONT_ALIGN_CENTER; + vertAlign = GLFont::GLFONT_ALIGN_CENTER; } void GLTextPanel::drawPanelContents() { @@ -346,19 +372,19 @@ void GLTextPanel::drawPanelContents() { float size; - if (pdim.y < 16) { + if (pdim.y <= 16) { sz = GLFont::GLFONT_SIZE12; size = 12; - } else if (pdim.y < 18) { + } else if (pdim.y <= 18) { sz = GLFont::GLFONT_SIZE16; size = 16; - } else if(pdim.y < 24) { + } else if(pdim.y <= 24) { sz = GLFont::GLFONT_SIZE18; size = 18; - } else if(pdim.y < 32) { + } else if(pdim.y <= 32) { sz = GLFont::GLFONT_SIZE24; size = 24; - } else if(pdim.y < 48) { + } else if(pdim.y <= 48) { sz = GLFont::GLFONT_SIZE32; size = 32; } else { @@ -367,11 +393,13 @@ void GLTextPanel::drawPanelContents() { } - GLFont::getFont(sz).drawString(textVal, mid, mid, size, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER, (int)pdim.x, (int)pdim.y); + GLFont::getFont(sz).drawString(textVal, mid, mid, size, horizAlign, vertAlign, (int)pdim.x, (int)pdim.y); } -void GLTextPanel::setText(std::string text) { +void GLTextPanel::setText(std::string text, GLFont::Align hAlign, GLFont::Align vAlign) { textVal = text; + horizAlign = hAlign; + vertAlign = vAlign; } std::string GLTextPanel::getText() { diff --git a/src/ui/GLPanel.h b/src/ui/GLPanel.h index db16fe2..0f3e3b6 100644 --- a/src/ui/GLPanel.h +++ b/src/ui/GLPanel.h @@ -36,14 +36,15 @@ public: typedef enum GLPanelFillType { GLPANEL_FILL_NONE, GLPANEL_FILL_SOLID, GLPANEL_FILL_GRAD_X, GLPANEL_FILL_GRAD_Y, GLPANEL_FILL_GRAD_BAR_X, GLPANEL_FILL_GRAD_BAR_Y } GLPanelFillType; typedef enum GLPanelCoordinateSystem { GLPANEL_Y_DOWN_ZERO_ONE, GLPANEL_Y_UP_ZERO_ONE, GLPANEL_Y_UP, GLPANEL_Y_DOWN } GLPanelCoordinateSystem; float pos[2]; + float rot[3]; float size[2]; float view[2]; GLPanelFillType fillType; GLPanelCoordinateSystem coord; float marginPx; GLPanelEdges borderPx; - RGB3f fill[2]; - RGB3f borderColor; + RGBA4f fill[2]; + RGBA4f borderColor; bool contentsVisible; CubicVR::mat4 transform; CubicVR::mat4 localTransform; @@ -68,15 +69,16 @@ public: void setCoordinateSystem(GLPanelCoordinateSystem coord); void setFill(GLPanelFillType fill_mode); - void setFillColor(RGB3f color1); - void setFillColor(RGB3f color1, RGB3f color2); + void setFillColor(RGBA4f color1); + void setFillColor(RGBA4f color1, RGBA4f color2); void setMarginPx(float marg); - void setBorderColor(RGB3f clr); + void setBorderColor(RGBA4f clr); void setBorderPx(float bord); void setBorderPx(float bordl, float bordr, float bordt, float bordb); void addChild(GLPanel *childPanel); + void removeChild(GLPanel *childPanel); void drawChildren(); virtual void drawPanelContents(); @@ -88,11 +90,13 @@ public: class GLTextPanel : public GLPanel { private: std::string textVal; + GLFont::Align horizAlign; + GLFont::Align vertAlign; public: GLTextPanel(); void drawPanelContents(); - void setText(std::string text); + void setText(std::string text, GLFont::Align hAlign = GLFont::GLFONT_ALIGN_CENTER, GLFont::Align vAlign = GLFont::GLFONT_ALIGN_CENTER); std::string getText(); }; diff --git a/src/ui/UITestContext.cpp b/src/ui/UITestContext.cpp index ec2722d..bb91cdc 100644 --- a/src/ui/UITestContext.cpp +++ b/src/ui/UITestContext.cpp @@ -9,30 +9,30 @@ PrimaryGLContext(canvas, sharedContext) { testPanel.setSize(1.0, 1.0); testPanel.setMarginPx(10); testPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_BAR_Y); - testPanel.setFillColor(RGB3f(0.0,0.0,1.0), RGB3f(0.0,1.0,0.0)); + testPanel.setFillColor(RGBA4f(0.0,0.0,1.0), RGBA4f(0.0,1.0,0.0)); testChildPanel.setPosition(0.0, 0.0); testChildPanel.setMarginPx(5); testChildPanel.setSize(1.0, 0.33); testChildPanel.setCoordinateSystem(GLPanel::GLPANEL_Y_DOWN_ZERO_ONE); testChildPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_BAR_X); - testChildPanel.setFillColor(RGB3f(0.0,0.0,1.0), RGB3f(0.0,1.0,0.0)); + testChildPanel.setFillColor(RGBA4f(0.0,0.0,1.0), RGBA4f(0.0,1.0,0.0)); testChildPanel.setBorderPx(1); testChildPanel2.setPosition(0.0, -0.66); testChildPanel2.setSize(1.0, 0.33); testChildPanel2.setMarginPx(5); testChildPanel2.setFill(GLPanel::GLPANEL_FILL_GRAD_X); - testChildPanel2.setFillColor(RGB3f(0.0,0.0,1.0), RGB3f(0.0,1.0,0.0)); - testChildPanel2.setBorderColor(RGB3f(1.0,0.0,0.0)); + testChildPanel2.setFillColor(RGBA4f(0.0,0.0,1.0), RGBA4f(0.0,1.0,0.0)); + testChildPanel2.setBorderColor(RGBA4f(1.0,0.0,0.0)); testChildPanel2.setBorderPx(1); testChildPanel3.setPosition(0.0, 0.66); testChildPanel3.setSize(1.0, 0.33); testChildPanel3.setMarginPx(5); testChildPanel3.setFill(GLPanel::GLPANEL_FILL_GRAD_X); - testChildPanel3.setFillColor(RGB3f(0.0,0.0,1.0), RGB3f(0.0,1.0,0.0)); - testChildPanel3.setBorderColor(RGB3f(1.0,0.0,0.0)); + testChildPanel3.setFillColor(RGBA4f(0.0,0.0,1.0), RGBA4f(0.0,1.0,0.0)); + testChildPanel3.setBorderColor(RGBA4f(1.0,0.0,0.0)); testChildPanel3.setBorderPx(1); testText1.setText("Testing 123.."); diff --git a/src/util/ThreadQueue.h b/src/util/ThreadQueue.h index 8ba5e0a..e69265c 100644 --- a/src/util/ThreadQueue.h +++ b/src/util/ThreadQueue.h @@ -14,6 +14,7 @@ #include #include #include +#include class ThreadQueueBase { @@ -30,13 +31,17 @@ class ThreadQueue : public ThreadQueueBase { public: /*! Create safe queue. */ - ThreadQueue() = default; + ThreadQueue() { + m_max_num_items.store(0); + }; ThreadQueue(ThreadQueue&& sq) { m_queue = std::move(sq.m_queue); + m_max_num_items.store(0); } ThreadQueue(const ThreadQueue& sq) { std::lock_guard < std::mutex > lock(sq.m_mutex); m_queue = sq.m_queue; + m_max_num_items.store(0); } /*! Destroy safe queue. */ @@ -49,7 +54,10 @@ public: * \param[in] item An item. */ void set_max_num_items(unsigned int max_num_items) { - m_max_num_items = max_num_items; + std::lock_guard < std::mutex > lock(m_mutex); + if (m_max_num_items.load() != max_num_items) { + m_max_num_items.store(max_num_items); + } } /** @@ -60,7 +68,7 @@ public: bool push(const value_type& item) { std::lock_guard < std::mutex > lock(m_mutex); - if (m_max_num_items > 0 && m_queue.size() > m_max_num_items) + if (m_max_num_items.load() > 0 && m_queue.size() > m_max_num_items.load()) return false; m_queue.push(item); @@ -76,7 +84,7 @@ public: bool push(const value_type&& item) { std::lock_guard < std::mutex > lock(m_mutex); - if (m_max_num_items > 0 && m_queue.size() > m_max_num_items) + if (m_max_num_items.load() > 0 && m_queue.size() > m_max_num_items.load()) return false; m_queue.push(item); @@ -217,7 +225,7 @@ public: */ bool full() const { std::lock_guard < std::mutex > lock(m_mutex); - return (m_max_num_items != 0) && (m_queue.size() >= m_max_num_items); + return (m_max_num_items.load() != 0) && (m_queue.size() >= m_max_num_items.load()); } /** @@ -278,7 +286,7 @@ private: std::queue m_queue; mutable std::mutex m_mutex; std::condition_variable m_condition; - unsigned int m_max_num_items = 0; + std::atomic_uint m_max_num_items; }; /*! Swaps the contents of two ThreadQueue objects. */ diff --git a/src/visual/ColorTheme.cpp b/src/visual/ColorTheme.cpp index 7c0b5ef..3590c83 100644 --- a/src/visual/ColorTheme.cpp +++ b/src/visual/ColorTheme.cpp @@ -41,27 +41,27 @@ DefaultColorTheme::DefaultColorTheme() { waterfallGradient.addColor(GradientColor(1.0, 1.0, 0)); waterfallGradient.addColor(GradientColor(1.0, 0.2, 0.0)); waterfallGradient.generate(256); - waterfallHighlight = RGB3f(1, 1, 1); - waterfallNew = RGB3f(0, 1, 0); - waterfallHover = RGB3f(1, 1, 0); - waterfallDestroy = RGB3f(1, 0, 0); - fftLine = RGB3f(0.9, 0.9, 0.9); - fftHighlight = RGB3f(1, 1, 1); - scopeLine = RGB3f(0.9, 0.9, 0.9); - tuningBarLight = RGB3f(0.2, 0.2, 0.9); - tuningBarDark = RGB3f(0.0, 0.0, 0.35); - tuningBarUp = RGB3f(1.0, 139.0/255.0, 96.0/255.0); - tuningBarDown = RGB3f(148.0/255.0, 148.0/255.0, 1.0); - meterLevel = RGB3f(0.1, 0.75, 0.1); - meterValue = RGB3f(0.75, 0.1, 0.1); - text = RGB3f(1, 1, 1); - freqLine = RGB3f(1, 1, 1); - button = RGB3f(0.65, 0.65, 0.65); - buttonHighlight = RGB3f(1, 1, 0); + waterfallHighlight = RGBA4f(1, 1, 1); + waterfallNew = RGBA4f(0, 1, 0); + waterfallHover = RGBA4f(1, 1, 0); + waterfallDestroy = RGBA4f(1, 0, 0); + fftLine = RGBA4f(0.9, 0.9, 0.9); + fftHighlight = RGBA4f(1, 1, 1); + scopeLine = RGBA4f(0.9, 0.9, 0.9); + tuningBarLight = RGBA4f(0.2, 0.2, 0.9); + tuningBarDark = RGBA4f(0.0, 0.0, 0.35); + tuningBarUp = RGBA4f(1.0, 139.0/255.0, 96.0/255.0); + tuningBarDown = RGBA4f(148.0/255.0, 148.0/255.0, 1.0); + meterLevel = RGBA4f(0.1, 0.75, 0.1); + meterValue = RGBA4f(0.75, 0.1, 0.1); + text = RGBA4f(1, 1, 1); + freqLine = RGBA4f(1, 1, 1); + button = RGBA4f(0.65, 0.65, 0.65); + buttonHighlight = RGBA4f(1, 1, 0); - scopeBackground = RGB3f(0.1, 0.1, 0.1); - fftBackground = RGB3f(0.1, 0.1, 0.1); - generalBackground = RGB3f(0.1, 0.1, 0.1); + scopeBackground = RGBA4f(0.1, 0.1, 0.1); + fftBackground = RGBA4f(0.1, 0.1, 0.1); + generalBackground = RGBA4f(0.1, 0.1, 0.1); } @@ -72,27 +72,27 @@ RadarColorTheme::RadarColorTheme() { waterfallGradient.addColor(GradientColor(40.0 / 255.0, 240.0 / 255.0, 60.0 / 255.0)); waterfallGradient.addColor(GradientColor(250.0 / 255.0, 250.0 / 255.0, 250.0 / 255.0)); waterfallGradient.generate(256); - waterfallHighlight = RGB3f(1, 1, 1); - waterfallNew = RGB3f(0, 1, 0); - waterfallHover = RGB3f(1, 1, 0); - waterfallDestroy = RGB3f(1, 0, 0); - fftLine = RGB3f(0.8, 1.0, 0.8); - fftHighlight = RGB3f(1, 1, 1); - scopeLine = RGB3f(0.8, 1.0, 0.8); - tuningBarLight = RGB3f(0.0, 0.45, 0.0); - tuningBarDark = RGB3f(0.0, 0.1, 0.0); - tuningBarUp = RGB3f(1.0, 139.0/255.0, 96.0/255.0); - tuningBarDown = RGB3f(148.0/255.0, 0.0, 0.0); - meterLevel = RGB3f(0, 0.5, 0); - meterValue = RGB3f(0, 0.5, 0); - text = RGB3f(0.8, 1.0, 0.8); - freqLine = RGB3f(1, 1, 1); - button = RGB3f(0.65, 0.75, 0.65); - buttonHighlight = RGB3f(0.65, 1.0, 0.65); + waterfallHighlight = RGBA4f(1, 1, 1); + waterfallNew = RGBA4f(0, 1, 0); + waterfallHover = RGBA4f(1, 1, 0); + waterfallDestroy = RGBA4f(1, 0, 0); + fftLine = RGBA4f(0.8, 1.0, 0.8); + fftHighlight = RGBA4f(1, 1, 1); + scopeLine = RGBA4f(0.8, 1.0, 0.8); + tuningBarLight = RGBA4f(0.0, 0.45, 0.0); + tuningBarDark = RGBA4f(0.0, 0.1, 0.0); + tuningBarUp = RGBA4f(1.0, 139.0/255.0, 96.0/255.0); + tuningBarDown = RGBA4f(148.0/255.0, 0.0, 0.0); + meterLevel = RGBA4f(0, 0.5, 0); + meterValue = RGBA4f(0, 0.5, 0); + text = RGBA4f(0.8, 1.0, 0.8); + freqLine = RGBA4f(1, 1, 1); + button = RGBA4f(0.65, 0.75, 0.65); + buttonHighlight = RGBA4f(0.65, 1.0, 0.65); - scopeBackground = RGB3f(0.05, 0.1, 0.05); - fftBackground = RGB3f(0.05, 0.1, 0.05); - generalBackground = RGB3f(0.05, 0.1, 0.05); + scopeBackground = RGBA4f(0.05, 0.1, 0.05); + fftBackground = RGBA4f(0.05, 0.1, 0.05); + generalBackground = RGBA4f(0.05, 0.1, 0.05); } BlackAndWhiteColorTheme::BlackAndWhiteColorTheme() { @@ -101,27 +101,27 @@ BlackAndWhiteColorTheme::BlackAndWhiteColorTheme() { waterfallGradient.addColor(GradientColor(0.75, 0.75, 0.75)); waterfallGradient.addColor(GradientColor(1.0, 1.0, 1.0)); waterfallGradient.generate(256); - waterfallHighlight = RGB3f(1, 1, 0.9); - waterfallNew = RGB3f(0, 1, 0); - waterfallHover = RGB3f(1, 1, 0); - waterfallDestroy = RGB3f(1, 0, 0); - fftLine = RGB3f(0.9, 0.9, 0.9); - fftHighlight = RGB3f(1, 1, 0.9); - scopeLine = RGB3f(0.9, 0.9, 0.9); - tuningBarLight = RGB3f(0.4, 0.4, 0.4); - tuningBarDark = RGB3f(0.1, 0.1, 0.1); - tuningBarUp = RGB3f(0.8, 0.8, 0.8); - tuningBarDown = RGB3f(0.4, 0.4, 0.4); - meterLevel = RGB3f(0.5, 0.5, 0.5); - meterValue = RGB3f(0.5, 0.5, 0.5); - text = RGB3f(1, 1, 1); - freqLine = RGB3f(1, 1, 1); - button = RGB3f(0.65, 0.65, 0.65); - buttonHighlight = RGB3f(1, 1, 1); + waterfallHighlight = RGBA4f(1, 1, 0.9); + waterfallNew = RGBA4f(0, 1, 0); + waterfallHover = RGBA4f(1, 1, 0); + waterfallDestroy = RGBA4f(1, 0, 0); + fftLine = RGBA4f(0.9, 0.9, 0.9); + fftHighlight = RGBA4f(1, 1, 0.9); + scopeLine = RGBA4f(0.9, 0.9, 0.9); + tuningBarLight = RGBA4f(0.4, 0.4, 0.4); + tuningBarDark = RGBA4f(0.1, 0.1, 0.1); + tuningBarUp = RGBA4f(0.8, 0.8, 0.8); + tuningBarDown = RGBA4f(0.4, 0.4, 0.4); + meterLevel = RGBA4f(0.5, 0.5, 0.5); + meterValue = RGBA4f(0.5, 0.5, 0.5); + text = RGBA4f(1, 1, 1); + freqLine = RGBA4f(1, 1, 1); + button = RGBA4f(0.65, 0.65, 0.65); + buttonHighlight = RGBA4f(1, 1, 1); - scopeBackground = RGB3f(0.1, 0.1, 0.1); - fftBackground = RGB3f(0.1, 0.1, 0.1); - generalBackground = RGB3f(0.1, 0.1, 0.1); + scopeBackground = RGBA4f(0.1, 0.1, 0.1); + fftBackground = RGBA4f(0.1, 0.1, 0.1); + generalBackground = RGBA4f(0.1, 0.1, 0.1); } @@ -139,27 +139,27 @@ SharpColorTheme::SharpColorTheme() { waterfallGradient.addColor(GradientColor(1.0, 0.25, 0.0)); waterfallGradient.addColor(GradientColor(0.5, 0.1, 0.0)); waterfallGradient.generate(256); - waterfallHighlight = RGB3f(0.9, 0.9, 1.0); - waterfallNew = RGB3f(0, 1, 0); - waterfallHover = RGB3f(1, 1, 0); - waterfallDestroy = RGB3f(1, 0, 0); - fftLine = RGB3f(0.9, 0.9, 1.0); - fftHighlight = RGB3f(0.9, 0.9, 1.0); - scopeLine = RGB3f(0.85, 0.85, 1.0); - tuningBarLight = RGB3f(28.0 / 255.0, 106.0 / 255.0, 179.0 / 255.0); - tuningBarDark = RGB3f(14.0 / 255.0, 53.0 / 255.0, 89.5 / 255.0); - tuningBarUp = RGB3f(0.7, 0.7, 0.7); - tuningBarDown = RGB3f(1.0, 0.0, 0.0); - meterLevel = RGB3f(28.0 / 255.0, 106.0 / 255.0, 179.0 / 255.0); - meterValue = RGB3f(190.0 / 255.0, 190.0 / 255.0, 60.0 / 255.0); - text = RGB3f(0.9, 0.9, 1); - freqLine = RGB3f(0.85, 0.85, 1.0); - button = RGB3f(217.0 / 255.0, 218.0 / 255.0, 228.0 / 255.0); - buttonHighlight = RGB3f(208.0 / 255.0, 249.0 / 255.0, 255.0 / 255.0); + waterfallHighlight = RGBA4f(0.9, 0.9, 1.0); + waterfallNew = RGBA4f(0, 1, 0); + waterfallHover = RGBA4f(1, 1, 0); + waterfallDestroy = RGBA4f(1, 0, 0); + fftLine = RGBA4f(0.9, 0.9, 1.0); + fftHighlight = RGBA4f(0.9, 0.9, 1.0); + scopeLine = RGBA4f(0.85, 0.85, 1.0); + tuningBarLight = RGBA4f(28.0 / 255.0, 106.0 / 255.0, 179.0 / 255.0); + tuningBarDark = RGBA4f(14.0 / 255.0, 53.0 / 255.0, 89.5 / 255.0); + tuningBarUp = RGBA4f(0.7, 0.7, 0.7); + tuningBarDown = RGBA4f(1.0, 0.0, 0.0); + meterLevel = RGBA4f(28.0 / 255.0, 106.0 / 255.0, 179.0 / 255.0); + meterValue = RGBA4f(190.0 / 255.0, 190.0 / 255.0, 60.0 / 255.0); + text = RGBA4f(0.9, 0.9, 1); + freqLine = RGBA4f(0.85, 0.85, 1.0); + button = RGBA4f(217.0 / 255.0, 218.0 / 255.0, 228.0 / 255.0); + buttonHighlight = RGBA4f(208.0 / 255.0, 249.0 / 255.0, 255.0 / 255.0); - scopeBackground = RGB3f(0.05, 0.05, 0.15); - fftBackground = RGB3f(0.05, 0.05, 0.15); - generalBackground = RGB3f(0.05, 0.05, 0.15); + scopeBackground = RGBA4f(0.05, 0.05, 0.15); + fftBackground = RGBA4f(0.05, 0.05, 0.15); + generalBackground = RGBA4f(0.05, 0.05, 0.15); } RadColorTheme::RadColorTheme() { @@ -170,27 +170,27 @@ RadColorTheme::RadColorTheme() { waterfallGradient.addColor(GradientColor(1.0, 40.0 / 255.0, 40.0 / 255.0)); waterfallGradient.addColor(GradientColor(1.0, 1.0, 1.0)); waterfallGradient.generate(256); - waterfallHighlight = RGB3f(1, 1, 1); - waterfallNew = RGB3f(0, 1, 0); - waterfallHover = RGB3f(1, 1, 0); - waterfallDestroy = RGB3f(1, 0, 0); - fftLine = RGB3f(1.0, 0.9, 0.9); - fftHighlight = RGB3f(1, 1, 1); - scopeLine = RGB3f(1.0, 0.9, 0.9); - tuningBarLight = RGB3f(0.0, 0.45, 0.0); - tuningBarDark = RGB3f(0.0, 0.1, 0.0); - tuningBarUp = RGB3f(1.0, 0.0, 0.0); - tuningBarDown = RGB3f(0.0, 0.5, 1.0); - meterLevel = RGB3f(0, 0.5, 0); - meterValue = RGB3f(0.5, 0, 0); - text = RGB3f(1, 1, 1); - freqLine = RGB3f(1, 1, 1); - button = RGB3f(0.65, 0.65, 0.65); - buttonHighlight = RGB3f(0.76, 0.65, 0); + waterfallHighlight = RGBA4f(1, 1, 1); + waterfallNew = RGBA4f(0, 1, 0); + waterfallHover = RGBA4f(1, 1, 0); + waterfallDestroy = RGBA4f(1, 0, 0); + fftLine = RGBA4f(1.0, 0.9, 0.9); + fftHighlight = RGBA4f(1, 1, 1); + scopeLine = RGBA4f(1.0, 0.9, 0.9); + tuningBarLight = RGBA4f(0.0, 0.45, 0.0); + tuningBarDark = RGBA4f(0.0, 0.1, 0.0); + tuningBarUp = RGBA4f(1.0, 0.0, 0.0); + tuningBarDown = RGBA4f(0.0, 0.5, 1.0); + meterLevel = RGBA4f(0, 0.5, 0); + meterValue = RGBA4f(0.5, 0, 0); + text = RGBA4f(1, 1, 1); + freqLine = RGBA4f(1, 1, 1); + button = RGBA4f(0.65, 0.65, 0.65); + buttonHighlight = RGBA4f(0.76, 0.65, 0); - scopeBackground = RGB3f(13.0 / 255.0, 47.0 / 255.0, 9.0 / 255.0); - fftBackground = RGB3f(0, 0, 50.0 / 255.0); - generalBackground = RGB3f(13.0 / 255.0, 47.0 / 255.0, 9.0 / 255.0); + scopeBackground = RGBA4f(13.0 / 255.0, 47.0 / 255.0, 9.0 / 255.0); + fftBackground = RGBA4f(0, 0, 50.0 / 255.0); + generalBackground = RGBA4f(13.0 / 255.0, 47.0 / 255.0, 9.0 / 255.0); } TouchColorTheme::TouchColorTheme() { @@ -204,27 +204,27 @@ TouchColorTheme::TouchColorTheme() { waterfallGradient.addColor(GradientColor(255.0 / 255.0, 0.0 / 255.0, 0.0 / 255.0)); waterfallGradient.addColor(GradientColor(255.0 / 255.0, 255.0 / 255.0, 255.0 / 255.0)); waterfallGradient.generate(256); - waterfallHighlight = RGB3f(1, 1, 1); - waterfallNew = RGB3f(0, 1, 0); - waterfallHover = RGB3f(1, 1, 0); - waterfallDestroy = RGB3f(1, 0, 0); - fftLine = RGB3f(234.0 / 255.0, 232.0 / 255.0, 247.0 / 255.0); - fftHighlight = RGB3f(1.0, 1.0, 1.0); - scopeLine = RGB3f(234.0 / 255.0, 232.0 / 255.0, 247.0 / 255.0); - tuningBarLight = RGB3f(0.2, 0.2, 0.7); - tuningBarDark = RGB3f(0.1, 0.1, 0.45); - tuningBarUp = RGB3f(0.5, 139.0/255.0, 96.0/255.0); - tuningBarDown = RGB3f(0.6, 108.0/255.0, 1.0); - meterLevel = RGB3f(61.0 / 255.0, 57.0 / 255.0, 88.0 / 255.0); - meterValue = RGB3f(61.0 / 255.0, 57.0 / 255.0, 88.0 / 255.0); - text = RGB3f(1, 1, 1); - freqLine = RGB3f(1, 1, 1); - button = RGB3f(1.0, 1.0, 1.0); - buttonHighlight = RGB3f(208.0 / 255.0, 202.0 / 255.0, 247.0 / 255.0); + waterfallHighlight = RGBA4f(1, 1, 1); + waterfallNew = RGBA4f(0, 1, 0); + waterfallHover = RGBA4f(1, 1, 0); + waterfallDestroy = RGBA4f(1, 0, 0); + fftLine = RGBA4f(234.0 / 255.0, 232.0 / 255.0, 247.0 / 255.0); + fftHighlight = RGBA4f(1.0, 1.0, 1.0); + scopeLine = RGBA4f(234.0 / 255.0, 232.0 / 255.0, 247.0 / 255.0); + tuningBarLight = RGBA4f(0.2, 0.2, 0.7); + tuningBarDark = RGBA4f(0.1, 0.1, 0.45); + tuningBarUp = RGBA4f(0.5, 139.0/255.0, 96.0/255.0); + tuningBarDown = RGBA4f(0.6, 108.0/255.0, 1.0); + meterLevel = RGBA4f(61.0 / 255.0, 57.0 / 255.0, 88.0 / 255.0); + meterValue = RGBA4f(61.0 / 255.0, 57.0 / 255.0, 88.0 / 255.0); + text = RGBA4f(1, 1, 1); + freqLine = RGBA4f(1, 1, 1); + button = RGBA4f(1.0, 1.0, 1.0); + buttonHighlight = RGBA4f(208.0 / 255.0, 202.0 / 255.0, 247.0 / 255.0); - scopeBackground = RGB3f(39.0 / 255.0, 36.0 / 255.0, 56.0 / 255.0); - fftBackground = RGB3f(39.0 / 255.0, 36.0 / 255.0, 56.0 / 255.0); - generalBackground = RGB3f(61.0 / 255.0, 57.0 / 255.0, 88.0 / 255.0); + scopeBackground = RGBA4f(39.0 / 255.0, 36.0 / 255.0, 56.0 / 255.0); + fftBackground = RGBA4f(39.0 / 255.0, 36.0 / 255.0, 56.0 / 255.0); + generalBackground = RGBA4f(61.0 / 255.0, 57.0 / 255.0, 88.0 / 255.0); } @@ -239,27 +239,27 @@ HDColorTheme::HDColorTheme() { waterfallGradient.addColor(GradientColor(255.0 / 255.0, 235.0 / 255.0, 100.0 / 255.0)); waterfallGradient.addColor(GradientColor(250.0 / 255.0, 250.0 / 255.0, 250.0 / 255.0)); waterfallGradient.generate(256); - waterfallHighlight = RGB3f(1, 1, 1); - waterfallNew = RGB3f(0, 1, 0); - waterfallHover = RGB3f(1, 1, 0); - waterfallDestroy = RGB3f(1, 0, 0); - fftLine = RGB3f(0.9, 0.9, 0.9); - fftHighlight = RGB3f(1, 1, 1); - scopeLine = RGB3f(0.9, 0.9, 0.9); - tuningBarLight = RGB3f(0.4, 0.4, 1.0); - tuningBarDark = RGB3f(0.1, 0.1, 0.45); - tuningBarUp = RGB3f(1.0, 139.0/255.0, 96.0/255.0); - tuningBarDown = RGB3f(148.0/255.0, 148.0/255.0, 1.0); - meterLevel = RGB3f(0, 0.5, 0); - meterValue = RGB3f(0.0, 0.0, 1.0); - text = RGB3f(1, 1, 1); - freqLine = RGB3f(1, 1, 1); - button = RGB3f(0, 0.7, 0.7); - buttonHighlight = RGB3f(1, 1, 1); + waterfallHighlight = RGBA4f(1, 1, 1); + waterfallNew = RGBA4f(0, 1, 0); + waterfallHover = RGBA4f(1, 1, 0); + waterfallDestroy = RGBA4f(1, 0, 0); + fftLine = RGBA4f(0.9, 0.9, 0.9); + fftHighlight = RGBA4f(1, 1, 1); + scopeLine = RGBA4f(0.9, 0.9, 0.9); + tuningBarLight = RGBA4f(0.4, 0.4, 1.0); + tuningBarDark = RGBA4f(0.1, 0.1, 0.45); + tuningBarUp = RGBA4f(1.0, 139.0/255.0, 96.0/255.0); + tuningBarDown = RGBA4f(148.0/255.0, 148.0/255.0, 1.0); + meterLevel = RGBA4f(0, 0.5, 0); + meterValue = RGBA4f(0.0, 0.0, 1.0); + text = RGBA4f(1, 1, 1); + freqLine = RGBA4f(1, 1, 1); + button = RGBA4f(0, 0.7, 0.7); + buttonHighlight = RGBA4f(1, 1, 1); - scopeBackground = RGB3f(0.0, 0.0, 48.0 / 255.0); - fftBackground = RGB3f(0.0, 0.0, 48.0 / 255.0); - generalBackground = RGB3f(0.0, 0.0, 0.0); + scopeBackground = RGBA4f(0.0, 0.0, 48.0 / 255.0); + fftBackground = RGBA4f(0.0, 0.0, 48.0 / 255.0); + generalBackground = RGBA4f(0.0, 0.0, 0.0); } diff --git a/src/visual/ColorTheme.h b/src/visual/ColorTheme.h index 52b431a..6f35324 100644 --- a/src/visual/ColorTheme.h +++ b/src/visual/ColorTheme.h @@ -15,55 +15,56 @@ #define COLOR_THEME_RADAR 6 #define COLOR_THEME_MAX 7 -class RGB3f { +class RGBA4f { public: - float r, g, b; - RGB3f(float r, float g, float b) : - r(r), g(g), b(b) { + float r, g, b, a; + RGBA4f(float r, float g, float b, float a = 1.0) : + r(r), g(g), b(b), a(a) { } - RGB3f() : - RGB3f(0, 0, 0) { + RGBA4f() : + RGBA4f(0, 0, 0) { } - ~RGB3f() { + ~RGBA4f() { } - RGB3f & operator=(const RGB3f &other) { + RGBA4f & operator=(const RGBA4f &other) { r = other.r; g = other.g; b = other.b; + a = other.a; return *this; } - RGB3f operator*(float v) { return RGB3f(r*v, g*v, b*v); } + RGBA4f operator*(float v) { return RGBA4f(r*v, g*v, b*v); } }; class ColorTheme { public: - RGB3f waterfallHighlight; - RGB3f waterfallNew; - RGB3f wfHighlight; - RGB3f waterfallHover; - RGB3f waterfallDestroy; - RGB3f fftLine; - RGB3f fftHighlight; - RGB3f scopeLine; - RGB3f tuningBarLight; - RGB3f tuningBarDark; - RGB3f tuningBarUp; - RGB3f tuningBarDown; - RGB3f meterLevel; - RGB3f meterValue; - RGB3f text; - RGB3f freqLine; - RGB3f button; - RGB3f buttonHighlight; + RGBA4f waterfallHighlight; + RGBA4f waterfallNew; + RGBA4f wfHighlight; + RGBA4f waterfallHover; + RGBA4f waterfallDestroy; + RGBA4f fftLine; + RGBA4f fftHighlight; + RGBA4f scopeLine; + RGBA4f tuningBarLight; + RGBA4f tuningBarDark; + RGBA4f tuningBarUp; + RGBA4f tuningBarDown; + RGBA4f meterLevel; + RGBA4f meterValue; + RGBA4f text; + RGBA4f freqLine; + RGBA4f button; + RGBA4f buttonHighlight; - RGB3f scopeBackground; - RGB3f fftBackground; - RGB3f generalBackground; + RGBA4f scopeBackground; + RGBA4f fftBackground; + RGBA4f generalBackground; Gradient waterfallGradient; diff --git a/src/visual/ModeSelectorCanvas.cpp b/src/visual/ModeSelectorCanvas.cpp index 5058291..425fad6 100644 --- a/src/visual/ModeSelectorCanvas.cpp +++ b/src/visual/ModeSelectorCanvas.cpp @@ -25,9 +25,11 @@ EVT_ENTER_WINDOW(ModeSelectorCanvas::OnMouseEnterWindow) wxEND_EVENT_TABLE() ModeSelectorCanvas::ModeSelectorCanvas(wxWindow *parent, int *attribList) : -InteractiveCanvas(parent, attribList), numChoices(0), currentSelection(-1) { +InteractiveCanvas(parent, attribList), numChoices(0), currentSelection(-1), toggleMode(false), inputChanged(false), padX(4.0), padY(4.0), highlightOverride(false) { glContext = new ModeSelectorContext(this, &wxGetApp().GetContext(this)); + + highlightColor = RGBA4f(1.0,1.0,1.0,1.0); } ModeSelectorCanvas::~ModeSelectorCanvas() { @@ -59,10 +61,15 @@ void ModeSelectorCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { int yval = getHoveredSelection(); for (int i = 0; i < numChoices; i++) { - if (yval == i) { - glContext->DrawSelector(selections[i].label, i, numChoices, true, ThemeMgr::mgr.currentTheme->buttonHighlight.r, ThemeMgr::mgr.currentTheme->buttonHighlight.g, ThemeMgr::mgr.currentTheme->buttonHighlight.b, 1.0); + if (yval == i && !highlightOverride) { + RGBA4f hc = ThemeMgr::mgr.currentTheme->buttonHighlight; + glContext->DrawSelector(selections[i].label, i, numChoices, true, hc.r, hc.g, hc.b, 1.0, padX, padY); } else { - glContext->DrawSelector(selections[i].label, i, numChoices, i == currentSelection, ThemeMgr::mgr.currentTheme->button.r, ThemeMgr::mgr.currentTheme->button.g, ThemeMgr::mgr.currentTheme->button.b, 1.0); + RGBA4f hc = ThemeMgr::mgr.currentTheme->button; + if (highlightOverride) { + hc = highlightColor; + } + glContext->DrawSelector(selections[i].label, i, numChoices, i == currentSelection, hc.r, hc.g, hc.b, 1.0, padX, padY); } } @@ -97,11 +104,22 @@ void ModeSelectorCanvas::OnMouseReleased(wxMouseEvent& event) { const wxSize ClientSize = GetClientSize(); + int selectedButton = currentSelection; if (mouseTracker.getOriginDeltaMouseX() < 2.0 / ClientSize.y) { - currentSelection = getHoveredSelection(); + selectedButton = getHoveredSelection(); } - SetCursor (wxCURSOR_ARROW); + if (toggleMode && (currentSelection == selectedButton)) { + selectedButton = -1; + } + + if (currentSelection != selectedButton) { + inputChanged = true; + } + + currentSelection = selectedButton; + + SetCursor (wxCURSOR_HAND); } void ModeSelectorCanvas::OnMouseLeftWindow(wxMouseEvent& event) { @@ -112,7 +130,7 @@ void ModeSelectorCanvas::OnMouseLeftWindow(wxMouseEvent& event) { void ModeSelectorCanvas::OnMouseEnterWindow(wxMouseEvent& event) { InteractiveCanvas::mouseTracker.OnMouseEnterWindow(event); - SetCursor (wxCURSOR_ARROW); + SetCursor (wxCURSOR_HAND); if (!helpTip.empty()) { setStatusText(helpTip); } @@ -148,4 +166,24 @@ int ModeSelectorCanvas::getSelection() { return selections[currentSelection].value; } +void ModeSelectorCanvas::setToggleMode(bool toggleMode) { + this->toggleMode = toggleMode; +} +bool ModeSelectorCanvas::modeChanged() { + return inputChanged; +} + +void ModeSelectorCanvas::clearModeChanged() { + inputChanged = false; +} + +void ModeSelectorCanvas::setPadding(float padX, float padY) { + this->padX = padX; + this->padY = padY; +} + +void ModeSelectorCanvas::setHighlightColor(RGBA4f hc) { + this->highlightColor = hc; + this->highlightOverride = true; +} diff --git a/src/visual/ModeSelectorCanvas.h b/src/visual/ModeSelectorCanvas.h index 7dcf12b..2c76294 100644 --- a/src/visual/ModeSelectorCanvas.h +++ b/src/visual/ModeSelectorCanvas.h @@ -35,6 +35,14 @@ public: void setSelection(int value); int getSelection(); + void setToggleMode(bool toggleMode); + + bool modeChanged(); + void clearModeChanged(); + + void setPadding(float padX, float padY); + void setHighlightColor(RGBA4f hc); + private: void setNumChoices(int numChoices_in); @@ -53,7 +61,12 @@ private: std::string helpTip; int numChoices; int currentSelection; + bool toggleMode; + bool inputChanged; std::vector selections; + float padX, padY; + RGBA4f highlightColor; + bool highlightOverride; // wxDECLARE_EVENT_TABLE(); }; diff --git a/src/visual/ModeSelectorContext.cpp b/src/visual/ModeSelectorContext.cpp index 2e0f2eb..d92b06b 100644 --- a/src/visual/ModeSelectorContext.cpp +++ b/src/visual/ModeSelectorContext.cpp @@ -22,7 +22,7 @@ void ModeSelectorContext::DrawBegin() { glDisable(GL_TEXTURE_2D); } -void ModeSelectorContext::DrawSelector(std::string label, int c, int cMax, bool on, float r, float g, float b, float a) { +void ModeSelectorContext::DrawSelector(std::string label, int c, int cMax, bool on, float r, float g, float b, float a, float px, float py) { GLint vp[4]; glGetIntegerv( GL_VIEWPORT, vp); @@ -42,15 +42,21 @@ void ModeSelectorContext::DrawSelector(std::string label, int c, int cMax, bool float y = 1.0 - ((float) (c+1) / (float) cMax * 2.0); float height = (2.0 / (float) cMax); - float padX = (4.0 / viewWidth); - float padY = (4.0 / viewHeight); + float padX = (px / viewWidth); + float padY = (py / viewHeight); + if (a < 1.0) { + glEnable(GL_BLEND); + } glBegin(on?GL_QUADS:GL_LINE_LOOP); glVertex2f(-1.0 + padX, y + padY); glVertex2f(1.0 - padX, y + padY); glVertex2f(1.0 - padX, y + height - padY); glVertex2f(-1.0 + padX, y + height - padY); glEnd(); + if (a < 1.0) { + glDisable(GL_BLEND); + } if (on) { glColor4f(0, 0, 0, a); diff --git a/src/visual/ModeSelectorContext.h b/src/visual/ModeSelectorContext.h index 0e66e44..2d84fa3 100644 --- a/src/visual/ModeSelectorContext.h +++ b/src/visual/ModeSelectorContext.h @@ -12,6 +12,6 @@ public: ModeSelectorContext(ModeSelectorCanvas *canvas, wxGLContext *sharedContext); void DrawBegin(); - void DrawSelector(std::string label, int c, int cMax, bool on, float r, float g, float b, float a); + void DrawSelector(std::string label, int c, int cMax, bool on, float r, float g, float b, float a, float padx, float pady); void DrawEnd(); }; diff --git a/src/visual/PrimaryGLContext.cpp b/src/visual/PrimaryGLContext.cpp index af1ae95..f11fdac 100644 --- a/src/visual/PrimaryGLContext.cpp +++ b/src/visual/PrimaryGLContext.cpp @@ -59,7 +59,7 @@ PrimaryGLContext::PrimaryGLContext(wxGLCanvas *canvas, wxGLContext *sharedContex //#endif } -void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, RGB3f color, long long center_freq, long long srate) { +void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, RGBA4f color, long long center_freq, long long srate) { if (!demod) { return; } @@ -94,7 +94,12 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, RGB3f color, lo glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4f(0, 0, 0, 0.35); + if (demod->isMuted()) { + glColor4f(0.8, 0, 0, 0.35); + } else { + glColor4f(0, 0, 0, 0.35); + } + glBegin(GL_QUADS); glVertex3f(uxPos - ofsLeft, hPos + labelHeight, 0.0); glVertex3f(uxPos - ofsLeft, -1.0, 0.0); @@ -128,19 +133,25 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, RGB3f color, lo glColor4f(1.0, 1.0, 1.0, 0.8); + std::string demodLabel = demod->getLabel(); + + if (demod->isMuted()) { + demodLabel = std::string("[M] ") + demodLabel; + } + if (demod->getDemodulatorType() == DEMOD_TYPE_USB) { - GLFont::getFont(GLFont::GLFONT_SIZE16).drawString(demod->getLabel(), uxPos, hPos, 16, GLFont::GLFONT_ALIGN_LEFT, GLFont::GLFONT_ALIGN_CENTER); + GLFont::getFont(GLFont::GLFONT_SIZE16).drawString(demodLabel, uxPos, hPos, 16, GLFont::GLFONT_ALIGN_LEFT, GLFont::GLFONT_ALIGN_CENTER); } else if (demod->getDemodulatorType() == DEMOD_TYPE_LSB) { - GLFont::getFont(GLFont::GLFONT_SIZE16).drawString(demod->getLabel(), uxPos, hPos, 16, GLFont::GLFONT_ALIGN_RIGHT, GLFont::GLFONT_ALIGN_CENTER); + GLFont::getFont(GLFont::GLFONT_SIZE16).drawString(demodLabel, uxPos, hPos, 16, GLFont::GLFONT_ALIGN_RIGHT, GLFont::GLFONT_ALIGN_CENTER); } else { - GLFont::getFont(GLFont::GLFONT_SIZE16).drawString(demod->getLabel(), uxPos, hPos, 16, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER); + GLFont::getFont(GLFont::GLFONT_SIZE16).drawString(demodLabel, uxPos, hPos, 16, GLFont::GLFONT_ALIGN_CENTER, GLFont::GLFONT_ALIGN_CENTER); } glDisable(GL_BLEND); } -void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, RGB3f color, long long center_freq, long long srate) { +void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, RGBA4f color, long long center_freq, long long srate) { if (!demod) { return; } @@ -283,14 +294,14 @@ void PrimaryGLContext::DrawDemod(DemodulatorInstance *demod, RGB3f color, long l glColor3f(0, 0, 0); GLFont::getFont(GLFont::GLFONT_SIZE16).drawString(demodStr, 2.0 * (uxPos - 0.5) + xOfs, -1.0 + hPos - yOfs, 16, demodAlign, GLFont::GLFONT_ALIGN_CENTER); - glColor3f(0.8, 0.8, 0.8); + glColor3f(1, 1, 1); GLFont::getFont(GLFont::GLFONT_SIZE16).drawString(demodStr, 2.0 * (uxPos - 0.5), -1.0 + hPos, 16, demodAlign, GLFont::GLFONT_ALIGN_CENTER); glDisable(GL_BLEND); } -void PrimaryGLContext::DrawFreqSelector(float uxPos, RGB3f color, float w, long long center_freq, long long srate) { +void PrimaryGLContext::DrawFreqSelector(float uxPos, RGBA4f color, float w, long long center_freq, long long srate) { DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); long long bw = 0; @@ -341,7 +352,7 @@ void PrimaryGLContext::DrawFreqSelector(float uxPos, RGB3f color, float w, long } -void PrimaryGLContext::DrawRangeSelector(float uxPos1, float uxPos2, RGB3f color) { +void PrimaryGLContext::DrawRangeSelector(float uxPos1, float uxPos2, RGBA4f color) { if (uxPos2 < uxPos1) { float temp = uxPos2; uxPos2=uxPos1; diff --git a/src/visual/PrimaryGLContext.h b/src/visual/PrimaryGLContext.h index 4f87e4d..254d2e7 100644 --- a/src/visual/PrimaryGLContext.h +++ b/src/visual/PrimaryGLContext.h @@ -21,10 +21,10 @@ public: void BeginDraw(float r, float g, float b); void EndDraw(); - void DrawFreqSelector(float uxPos, RGB3f color, float w = 0, long long center_freq = -1, long long srate = 0); - void DrawRangeSelector(float uxPos1, float uxPos2, RGB3f color); - void DrawDemod(DemodulatorInstance *demod, RGB3f color, long long center_freq = -1, long long srate = 0); - void DrawDemodInfo(DemodulatorInstance *demod, RGB3f color, long long center_freq = -1, long long srate = 0); + void DrawFreqSelector(float uxPos, RGBA4f color, float w = 0, long long center_freq = -1, long long srate = 0); + void DrawRangeSelector(float uxPos1, float uxPos2, RGBA4f color); + void DrawDemod(DemodulatorInstance *demod, RGBA4f color, long long center_freq = -1, long long srate = 0); + void DrawDemodInfo(DemodulatorInstance *demod, RGBA4f color, long long center_freq = -1, long long srate = 0); void setHoverAlpha(float hoverAlpha); diff --git a/src/visual/ScopeCanvas.cpp b/src/visual/ScopeCanvas.cpp index a4cd4be..9dfaced 100644 --- a/src/visual/ScopeCanvas.cpp +++ b/src/visual/ScopeCanvas.cpp @@ -14,24 +14,65 @@ #include "CubicSDRDefs.h" #include "AppFrame.h" #include +#include wxBEGIN_EVENT_TABLE(ScopeCanvas, wxGLCanvas) EVT_PAINT(ScopeCanvas::OnPaint) EVT_IDLE(ScopeCanvas::OnIdle) +EVT_MOTION(ScopeCanvas::OnMouseMoved) +EVT_LEFT_DOWN(ScopeCanvas::OnMouseDown) +EVT_LEFT_UP(ScopeCanvas::OnMouseReleased) +EVT_RIGHT_DOWN(ScopeCanvas::OnMouseRightDown) +EVT_RIGHT_UP(ScopeCanvas::OnMouseRightReleased) +EVT_LEAVE_WINDOW(ScopeCanvas::OnMouseLeftWindow) +EVT_ENTER_WINDOW(ScopeCanvas::OnMouseEnterWindow) wxEND_EVENT_TABLE() -ScopeCanvas::ScopeCanvas(wxWindow *parent, int *attribList) : - wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), stereo(false), ppmMode(false) { +ScopeCanvas::ScopeCanvas(wxWindow *parent, int *attribList) : InteractiveCanvas(parent, attribList), stereo(false), ppmMode(false), ctr(0), ctrTarget(0), dragAccel(0), helpTip("") { glContext = new ScopeContext(this, &wxGetApp().GetContext(this)); - inputData.set_max_num_items(1); + inputData.set_max_num_items(2); + bgPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_Y); + bgPanel.setSize(1.0, 0.5); + bgPanel.setPosition(0.0, -0.5); + panelSpacing = 0.4; + + parentPanel.addChild(&scopePanel); + parentPanel.addChild(&spectrumPanel); + parentPanel.setFill(GLPanel::GLPANEL_FILL_NONE); + scopePanel.setSize(1.0,-1.0); + spectrumPanel.setSize(1.0,-1.0); + spectrumPanel.setShowDb(true); } ScopeCanvas::~ScopeCanvas() { } +bool ScopeCanvas::scopeVisible() { + float panelInterval = (2.0 + panelSpacing); + + ctrTarget = abs(round(ctr / panelInterval)); + + if (ctrTarget == 0 || dragAccel || (ctr != ctrTarget)) { + return true; + } + + return false; +} + +bool ScopeCanvas::spectrumVisible() { + float panelInterval = (2.0 + panelSpacing); + + ctrTarget = abs(round(ctr / panelInterval)); + + if (ctrTarget == 1 || dragAccel || (ctr != ctrTarget)) { + return true; + } + + return false; +} + void ScopeCanvas::setStereo(bool state) { stereo = state; } @@ -49,20 +90,40 @@ bool ScopeCanvas::getPPMMode() { return ppmMode; } +void ScopeCanvas::setShowDb(bool showDb) { + this->showDb = showDb; +} + +bool ScopeCanvas::getShowDb() { + return showDb; +} + void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { wxPaintDC dc(this); const wxSize ClientSize = GetClientSize(); - if (!inputData.empty()) { + while (!inputData.empty()) { ScopeRenderData *avData; inputData.pop(avData); - if (avData) { + if (!avData->spectrum) { if (avData->waveform_points.size()) { scopePanel.setPoints(avData->waveform_points); setStereo(avData->channels == 2); } + avData->decRefCount(); + } else { + if (avData->waveform_points.size()) { + spectrumPanel.setPoints(avData->waveform_points); + spectrumPanel.setFloorValue(avData->fft_floor); + spectrumPanel.setCeilValue(avData->fft_ceil); + spectrumPanel.setBandwidth((avData->sampleRate/2)*1000); + spectrumPanel.setFreq((avData->sampleRate/4)*1000); + spectrumPanel.setFFTSize(avData->fft_size); + spectrumPanel.setShowDb(showDb); + } + avData->decRefCount(); } } @@ -71,21 +132,95 @@ void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { initGLExtensions(); glViewport(0, 0, ClientSize.x, ClientSize.y); - + glContext->DrawBegin(); + + bgPanel.setFillColor(ThemeMgr::mgr.currentTheme->scopeBackground * 3.0, RGBA4f(0,0,0,0)); + bgPanel.calcTransform(CubicVR::mat4::identity()); + bgPanel.draw(); + scopePanel.setMode(stereo?ScopePanel::SCOPE_MODE_2Y:ScopePanel::SCOPE_MODE_Y); - scopePanel.calcTransform(CubicVR::mat4::identity()); - scopePanel.draw(); - glContext->DrawTunerTitles(ppmMode); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glLoadMatrixf(CubicVR::mat4::perspective(45.0, 1.0, 1.0, 1000.0)); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + CubicVR::mat4 modelView = CubicVR::mat4::lookat(0, 0, -1.205, 0, 0, 0, 0, -1, 0); + + float panelWidth = 1.0; + float panelInterval = (panelWidth * 2.0 + panelSpacing); + + if (!mouseTracker.mouseDown()) { + ctrTarget = round(ctr / panelInterval); + if (ctrTarget < -1.0) { + ctrTarget = -1.0; + } else if (ctrTarget > 0.0) { + ctrTarget = 0.0; + } + ctrTarget *= panelInterval; + if (!dragAccel) { + if (ctr != ctrTarget) { + ctr += (ctrTarget-ctr)*0.2; + } + if (abs(ctr - ctrTarget) < 0.001) { + ctr=ctrTarget; + } + } else { + dragAccel -= dragAccel * 0.1; + if ((abs(dragAccel) < 0.2) || (ctr < (ctrTarget-panelInterval/2.0)) || (ctr > (ctrTarget+panelInterval/2.0)) ) { + dragAccel = 0; + } else { + ctr += dragAccel; + } + } + } + + float roty = 0; + + scopePanel.setPosition(ctr, 0); + if (scopeVisible()) { + scopePanel.contentsVisible = true; + roty = atan2(scopePanel.pos[0],1.2); + scopePanel.rot[1] = -(roty * (180.0 / M_PI)); + } else { + scopePanel.contentsVisible = false; + } + + spectrumPanel.setPosition(panelInterval+ctr, 0); + if (spectrumVisible()) { + spectrumPanel.setFillColor(ThemeMgr::mgr.currentTheme->scopeBackground * 2.0, RGBA4f(0,0,0,0)); + spectrumPanel.contentsVisible = true; + roty = atan2(spectrumPanel.pos[0],1.2); + spectrumPanel.rot[1] = -(roty * (180.0 / M_PI)); + } else { + spectrumPanel.contentsVisible = false; + } + + parentPanel.calcTransform(modelView); + parentPanel.draw(); + + if (spectrumVisible()) { + spectrumPanel.drawChildren(); + } + + glLoadMatrixf(scopePanel.transform); if (!deviceName.empty()) { glContext->DrawDeviceName(deviceName); } - glContext->DrawEnd(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glContext->DrawTunerTitles(ppmMode); + glContext->DrawEnd(); SwapBuffers(); } + void ScopeCanvas::OnIdle(wxIdleEvent &event) { Refresh(); event.RequestMore(); @@ -94,3 +229,44 @@ void ScopeCanvas::OnIdle(wxIdleEvent &event) { ScopeRenderDataQueue *ScopeCanvas::getInputQueue() { return &inputData; } + +void ScopeCanvas::OnMouseMoved(wxMouseEvent& event) { + InteractiveCanvas::OnMouseMoved(event); + if (mouseTracker.mouseDown()) { + dragAccel = 4.0*mouseTracker.getDeltaMouseX(); + ctr += dragAccel; + } +} + +void ScopeCanvas::OnMouseWheelMoved(wxMouseEvent& event) { + +} + +void ScopeCanvas::OnMouseDown(wxMouseEvent& event) { + InteractiveCanvas::OnMouseDown(event); + +} + +void ScopeCanvas::OnMouseReleased(wxMouseEvent& event) { + InteractiveCanvas::OnMouseReleased(event); + +} + +void ScopeCanvas::OnMouseEnterWindow(wxMouseEvent& event) { + InteractiveCanvas::OnMouseEnterWindow(event); + if (!helpTip.empty()) { + setStatusText(helpTip); + } + SetCursor(wxCURSOR_SIZEWE); +} + +void ScopeCanvas::OnMouseLeftWindow(wxMouseEvent& event) { + InteractiveCanvas::OnMouseLeftWindow(event); + +} + + +void ScopeCanvas::setHelpTip(std::string tip) { + helpTip = tip; +} + diff --git a/src/visual/ScopeCanvas.h b/src/visual/ScopeCanvas.h index 6cdff03..d868b2e 100644 --- a/src/visual/ScopeCanvas.h +++ b/src/visual/ScopeCanvas.h @@ -9,9 +9,11 @@ #include "ScopeContext.h" #include "ScopeVisualProcessor.h" #include "ScopePanel.h" +#include "SpectrumPanel.h" #include "fftw3.h" +#include "InteractiveCanvas.h" -class ScopeCanvas: public wxGLCanvas { +class ScopeCanvas: public InteractiveCanvas { public: ScopeCanvas(wxWindow *parent, int *attribList = NULL); ~ScopeCanvas(); @@ -21,18 +23,41 @@ public: void setPPMMode(bool ppmMode); bool getPPMMode(); + void setShowDb(bool showDb); + bool getShowDb(); + + bool scopeVisible(); + bool spectrumVisible(); + + void setHelpTip(std::string tip); + ScopeRenderDataQueue *getInputQueue(); private: void OnPaint(wxPaintEvent& event); void OnIdle(wxIdleEvent &event); + void OnMouseMoved(wxMouseEvent& event); + void OnMouseWheelMoved(wxMouseEvent& event); + void OnMouseDown(wxMouseEvent& event); + void OnMouseReleased(wxMouseEvent& event); + void OnMouseEnterWindow(wxMouseEvent& event); + void OnMouseLeftWindow(wxMouseEvent& event); ScopeRenderDataQueue inputData; ScopePanel scopePanel; + GLPanel parentPanel; + SpectrumPanel spectrumPanel; + GLPanel bgPanel; ScopeContext *glContext; std::string deviceName; bool stereo; bool ppmMode; + bool showDb; + float panelSpacing; + float ctr; + float ctrTarget; + float dragAccel; + std::string helpTip; // event table wxDECLARE_EVENT_TABLE(); }; diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index a35aca5..adcc96c 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -73,6 +73,8 @@ void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { spectrumPanel.calcTransform(CubicVR::mat4::identity()); spectrumPanel.draw(); + glLoadIdentity(); + std::vector &demods = wxGetApp().getDemodMgr().getDemodulators(); for (int i = 0, iMax = demods.size(); i < iMax; i++) { @@ -81,6 +83,8 @@ void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { glContext->EndDraw(); + spectrumPanel.drawChildren(); + SwapBuffers(); } @@ -122,10 +126,18 @@ void SpectrumCanvas::moveCenterFrequency(long long freqChange) { freq -= freqChange; } wxGetApp().setFrequency(freq); - setStatusText("Set center frequency: %s", freq); } } +void SpectrumCanvas::setShowDb(bool showDb) { + spectrumPanel.setShowDb(showDb); +} + +bool SpectrumCanvas::getShowDb() { + return spectrumPanel.getShowDb(); +} + + void SpectrumCanvas::OnMouseMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseMoved(event); if (mouseTracker.mouseDown()) { @@ -135,7 +147,7 @@ void SpectrumCanvas::OnMouseMoved(wxMouseEvent& event) { moveCenterFrequency(freqChange); } } else { - setStatusText("Click and drag to adjust center frequency."); + setStatusText("Click and drag to adjust center frequency. 'B' to toggle decibels display."); } } diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h index af07f6c..238c48f 100644 --- a/src/visual/SpectrumCanvas.h +++ b/src/visual/SpectrumCanvas.h @@ -19,6 +19,9 @@ public: void attachWaterfallCanvas(WaterfallCanvas *canvas_in); void moveCenterFrequency(long long freqChange); + void setShowDb(bool showDb); + bool getShowDb(); + SpectrumVisualDataQueue *getVisualDataQueue(); private: diff --git a/src/visual/TuningCanvas.cpp b/src/visual/TuningCanvas.cpp index b6fa267..a9fe015 100644 --- a/src/visual/TuningCanvas.cpp +++ b/src/visual/TuningCanvas.cpp @@ -98,10 +98,10 @@ void TuningCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { 0.75, mouseTracker.getOriginMouseX(), mouseTracker.getMouseX()); } - RGB3f clr = top ? ThemeMgr::mgr.currentTheme->tuningBarUp : ThemeMgr::mgr.currentTheme->tuningBarDown; + RGBA4f clr = top ? ThemeMgr::mgr.currentTheme->tuningBarUp : ThemeMgr::mgr.currentTheme->tuningBarDown; - RGB3f clrDark = ThemeMgr::mgr.currentTheme->tuningBarDark; - RGB3f clrMid = ThemeMgr::mgr.currentTheme->tuningBarLight; + RGBA4f clrDark = ThemeMgr::mgr.currentTheme->tuningBarDark; + RGBA4f clrMid = ThemeMgr::mgr.currentTheme->tuningBarLight; glContext->DrawTunerBarIndexed(1, 3, 11, freqDP, freqW, clrMid, 0.25, true, true); // freq glContext->DrawTunerBarIndexed(4, 6, 11, freqDP, freqW, clrDark, 0.25, true, true); @@ -143,7 +143,7 @@ void TuningCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { glContext->DrawTuner(freq, 11, freqDP, freqW); int snap = wxGetApp().getFrequencySnap(); if (snap != 1) { - glContext->DrawTunerDigitBox((int)log10(snap), 11, freqDP, freqW, RGB3f(1.0,0.0,0.0)); + glContext->DrawTunerDigitBox((int)log10(snap), 11, freqDP, freqW, RGBA4f(1.0,0.0,0.0)); } } glContext->DrawTuner(bw, 7, bwDP, bwW); diff --git a/src/visual/TuningContext.cpp b/src/visual/TuningContext.cpp index bd9ca4a..a499da0 100644 --- a/src/visual/TuningContext.cpp +++ b/src/visual/TuningContext.cpp @@ -112,7 +112,7 @@ void TuningContext::DrawTuner(long long freq, int count, float displayPos, float } -void TuningContext::DrawTunerDigitBox(int index, int count, float displayPos, float displayWidth, RGB3f c) { +void TuningContext::DrawTunerDigitBox(int index, int count, float displayPos, float displayWidth, RGBA4f c) { GLint vp[4]; glGetIntegerv( GL_VIEWPORT, vp); @@ -152,7 +152,7 @@ int TuningContext::GetTunerDigitIndex(float mPos, int count, float displayPos, f return count - index; } -void TuningContext::DrawTunerBarIndexed(int start, int end, int count, float displayPos, float displayWidth, RGB3f color, float alpha, bool top, +void TuningContext::DrawTunerBarIndexed(int start, int end, int count, float displayPos, float displayWidth, RGBA4f color, float alpha, bool top, bool bottom) { float ofs = (displayWidth / (float) count); float p2 = displayPos + ofs * (float) (count - start + 1); diff --git a/src/visual/TuningContext.h b/src/visual/TuningContext.h index 61dc9f3..3bbec2f 100644 --- a/src/visual/TuningContext.h +++ b/src/visual/TuningContext.h @@ -14,9 +14,9 @@ public: void DrawBegin(); void Draw(float r, float g, float b, float a, float p1, float p2); void DrawTuner(long long freq, int count, float displayPos, float displayWidth); - void DrawTunerDigitBox(int index, int count, float displayPos, float displayWidth, RGB3f c); + void DrawTunerDigitBox(int index, int count, float displayPos, float displayWidth, RGBA4f c); int GetTunerDigitIndex(float mPos, int count, float displayPos, float displayWidth); - void DrawTunerBarIndexed(int start, int end, int count, float displayPos, float displayWidth, RGB3f color, float alpha, bool top, bool bottom); + void DrawTunerBarIndexed(int start, int end, int count, float displayPos, float displayWidth, RGBA4f color, float alpha, bool top, bool bottom); void DrawDemodFreqBw(long long freq, unsigned int bw, long long center); void DrawEnd(); diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index f2d2578..108a1e2 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -36,7 +36,7 @@ wxEND_EVENT_TABLE() WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : InteractiveCanvas(parent, attribList), dragState(WF_DRAG_NONE), nextDragState(WF_DRAG_NONE), fft_size(0), waterfall_lines(0), - dragOfs(0), mouseZoom(1), zoom(1), hoverAlpha(1.0) { + dragOfs(0), mouseZoom(1), zoom(1), freqMove(0.0), freqMoving(false), hoverAlpha(1.0) { glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this)); @@ -102,6 +102,19 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { } } + if (freqMove != 0.0) { + long long newFreq = getCenterFrequency() + (long long)((long double)getBandwidth()*freqMove) * 0.01; + + updateCenterFrequency(newFreq); + + if (!freqMoving) { + freqMove -= (freqMove * 0.2); + if (fabs(freqMove) < 0.01) { + freqMove = 0.0; + } + } + } + long long bw; if (currentZoom != 1) { long long freq = wxGetApp().getFrequency(); @@ -161,7 +174,13 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { if (spectrumCanvas) { if ((spectrumCanvas->getCenterFrequency() != centerFreq) || (spectrumCanvas->getBandwidth() != bw)) { - spectrumCanvas->setView(centerFreq,bw); + if (getViewState()) { + spectrumCanvas->setView(centerFreq,bw); + } else { + spectrumCanvas->disableView(); + spectrumCanvas->setCenterFrequency(centerFreq); + spectrumCanvas->setBandwidth(bw); + } } } } @@ -274,6 +293,12 @@ void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) { zoom = 1.0; mouseZoom = 1.05; break; + case WXK_LEFT: + case WXK_NUMPAD_LEFT: + case WXK_RIGHT: + case WXK_NUMPAD_RIGHT: + freqMoving = false; + break; } } @@ -300,18 +325,20 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { break; case WXK_RIGHT: case WXK_NUMPAD_RIGHT: - if (shiftDown) { - freq += getBandwidth() * 10; + if (isView) { + freqMove = shiftDown?5.0:1.0; + freqMoving = true; } else { - freq += getBandwidth() / 2; + freq += shiftDown?(getBandwidth() * 10):(getBandwidth() / 2); } break; case WXK_LEFT: case WXK_NUMPAD_LEFT: - if (shiftDown) { - freq -= getBandwidth() * 10; + if (isView) { + freqMove = shiftDown?-5.0:-1.0; + freqMoving = true; } else { - freq -= getBandwidth() / 2; + freq -= shiftDown?(getBandwidth() * 10):(getBandwidth() / 2); } break; case 'D': @@ -322,6 +349,12 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { wxGetApp().removeDemodulator(activeDemod); wxGetApp().getDemodMgr().deleteThread(activeDemod); break; + case 'M': + if (!activeDemod) { + break; + } + activeDemod->setMuted(!activeDemod->isMuted()); + break; case 'S': if (!activeDemod) { break; @@ -332,6 +365,11 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { activeDemod->setStereo(true); } break; + case 'B': + if (spectrumCanvas) { + spectrumCanvas->setShowDb(!spectrumCanvas->getShowDb()); + } + break; case WXK_SPACE: wxGetApp().showFrequencyInput(); break; @@ -346,30 +384,7 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { } if (freq != originalFreq) { - if (isView) { - setView(freq, getBandwidth()); - if (spectrumCanvas) { - spectrumCanvas->setView(freq, getBandwidth()); - } - - long long minFreq = wxGetApp().getFrequency()-(wxGetApp().getSampleRate()/2); - long long maxFreq = wxGetApp().getFrequency()+(wxGetApp().getSampleRate()/2); - - if (freq < minFreq) { - wxGetApp().setFrequency(freq+(wxGetApp().getSampleRate()/2)); - setStatusText("Set center frequency: %s", freq); - } - if (freq > maxFreq) { - wxGetApp().setFrequency(freq-(wxGetApp().getSampleRate()/2)); - setStatusText("Set center frequency: %s", freq); - } - } else { - if (spectrumCanvas) { - spectrumCanvas->setCenterFrequency(freq); - } - wxGetApp().setFrequency(freq); - setStatusText("Set center frequency: %s", freq); - } + updateCenterFrequency(freq); } } @@ -406,7 +421,6 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { } demod->setBandwidth(currentBW); - setStatusText("Set demodulator bandwidth: %s", demod->getBandwidth()); } if (dragState == WF_DRAG_FREQUENCY) { @@ -424,8 +438,6 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { currentFreq = demod->getFrequency(); demod->updateLabel(currentFreq); } - - setStatusText("Set demodulator frequency: %s", demod->getFrequency()); } } else if (mouseTracker.mouseRightDown()) { mouseZoom = mouseZoom + ((1.0 - (mouseTracker.getDeltaMouseY() * 4.0)) - mouseZoom) * 0.1; @@ -505,14 +517,14 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { mouseTracker.setVertDragLock(true); mouseTracker.setHorizDragLock(false); - setStatusText("Click and drag to change demodulator bandwidth. SPACE for direct frequency input. D to delete, S for stereo."); + setStatusText("Click and drag to change demodulator bandwidth. SPACE for direct frequency input. M for mute, D to delete, S for stereo."); } else { SetCursor(wxCURSOR_SIZING); nextDragState = WF_DRAG_FREQUENCY; mouseTracker.setVertDragLock(true); mouseTracker.setHorizDragLock(false); - setStatusText("Click and drag to change demodulator frequency; SPACE for direct input. D to delete, S for stereo."); + setStatusText("Click and drag to change demodulator frequency; SPACE for direct input. M for mute, D to delete, S for stereo."); } } else { SetCursor(wxCURSOR_CROSS); @@ -521,7 +533,7 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { setStatusText("Click to create a new demodulator or hold ALT to drag range, SPACE for direct center frequency input."); } else { setStatusText( - "Click to move active demodulator frequency or hold ALT to drag range; hold SHIFT to create new. Right drag or A / Z to Zoom. Arrow keys (+SHIFT) to move center frequency; SPACE for direct input."); + "Click to move active demodulator frequency or hold ALT to drag range; hold SHIFT to create new. Right drag or wheel to Zoom. Arrow keys to navigate/zoom."); } } @@ -600,6 +612,7 @@ void WaterfallCanvas::OnMouseReleased(wxMouseEvent& event) { demod->setSquelchEnabled(mgr->isLastSquelchEnabled()); demod->setStereo(mgr->isLastStereo()); demod->setGain(mgr->getLastGain()); + demod->setMuted(mgr->isLastMuted()); demod->run(); @@ -740,3 +753,29 @@ void WaterfallCanvas::OnMouseRightReleased(wxMouseEvent& event) { SpectrumVisualDataQueue *WaterfallCanvas::getVisualDataQueue() { return &visualDataQueue; } + +void WaterfallCanvas::updateCenterFrequency(long long freq) { + if (isView) { + setView(freq, getBandwidth()); + if (spectrumCanvas) { + spectrumCanvas->setView(freq, getBandwidth()); + } + + long long minFreq = wxGetApp().getFrequency()-(wxGetApp().getSampleRate()/2); + long long maxFreq = wxGetApp().getFrequency()+(wxGetApp().getSampleRate()/2); + + if (freq < minFreq) { + wxGetApp().setFrequency(freq+(wxGetApp().getSampleRate()/2)); + } + if (freq > maxFreq) { + wxGetApp().setFrequency(freq-(wxGetApp().getSampleRate()/2)); + } + } else { + if (spectrumCanvas) { + spectrumCanvas->setCenterFrequency(freq); + } + wxGetApp().setFrequency(freq); + } + +} + diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index 2f51ec3..28d0070 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -45,6 +45,8 @@ private: void OnMouseEnterWindow(wxMouseEvent& event); void OnMouseLeftWindow(wxMouseEvent& event); + void updateCenterFrequency(long long freq); + std::vector spectrum_points; SpectrumCanvas *spectrumCanvas; @@ -59,6 +61,8 @@ private: int dragOfs; float mouseZoom, zoom; + bool freqMoving; + long double freqMove; float hoverAlpha; SpectrumVisualDataQueue visualDataQueue;