diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index f85d2b2..0a20f77 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -90,7 +90,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); @@ -790,6 +792,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(); 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/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp index 485fd54..2ed31b0 100644 --- a/src/demod/DemodulatorThread.cpp +++ b/src/demod/DemodulatorThread.cpp @@ -310,6 +310,7 @@ void DemodulatorThread::run() { ati = outputBuffers.getBuffer(); ati->sampleRate = audioSampleRate; + ati->inputRate = inp->sampleRate; ati->setRefCount(1); if (demodulatorType == DEMOD_TYPE_RAW) { @@ -359,7 +360,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; @@ -377,6 +380,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]; } @@ -384,7 +389,7 @@ void DemodulatorThread::run() { } else { ati_vis->channels = 1; if (numAudioWritten > bufSize) { - + ati_vis->inputRate = audioSampleRate; if (num_vis > numAudioWritten) { num_vis = numAudioWritten; } @@ -399,9 +404,8 @@ 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) { diff --git a/src/demod/DemodulatorThread.h b/src/demod/DemodulatorThread.h index 7f7e417..91d6b0c 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: diff --git a/src/panel/SpectrumPanel.cpp b/src/panel/SpectrumPanel.cpp index 2b6f65e..142e1a4 100644 --- a/src/panel/SpectrumPanel.cpp +++ b/src/panel/SpectrumPanel.cpp @@ -5,7 +5,7 @@ #include #include "ColorTheme.h" -SpectrumPanel::SpectrumPanel() : floorValue(0), ceilValue(1), showDb(false) { +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); @@ -51,6 +51,14 @@ 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) { @@ -133,7 +141,7 @@ void SpectrumPanel::drawPanelContents() { long long leftFreq = (double) freq - ((double) bandwidth / 2.0); long long rightFreq = leftFreq + (double) bandwidth; - long long hzStep = 1000000; + long long hzStep = 100000; long double mhzStep = (100000.0 / (long double) (rightFreq - leftFreq)) * 2.0; double mhzVisualStep = 0.1; @@ -144,10 +152,37 @@ void SpectrumPanel::drawPanelContents() { if (mhzStep * 0.5 * viewWidth < 40) { mhzStep = (250000.0 / (long double) (rightFreq - leftFreq)) * 2.0; mhzVisualStep = 0.25; - label.precision(2); - } - if (mhzStep * 0.5 * viewWidth > 350) { + 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.01; label.precision(2); @@ -193,21 +228,20 @@ 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())/2048.0)) << "dB"; + 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())/2048.0)) << "dB"; + ssLabel << (20.0 * log10(2.0*(getFloorValue())/(double)fftSize)) << "dB"; dbPanelFloor.setText(ssLabel.str(), GLFont::GLFONT_ALIGN_RIGHT); dbPanelFloor.setSize(dbPanelWidth, dbPanelHeight); diff --git a/src/panel/SpectrumPanel.h b/src/panel/SpectrumPanel.h index 6ffa3f8..3ccd747 100644 --- a/src/panel/SpectrumPanel.h +++ b/src/panel/SpectrumPanel.h @@ -19,7 +19,10 @@ public: void setBandwidth(long long bandwidth); long long getBandwidth(); - + + void setFFTSize(int fftSize_in); + int getFFTSize(); + void setShowDb(bool showDb); bool getShowDb(); @@ -28,6 +31,7 @@ protected: private: float floorValue, ceilValue; + int fftSize; long long freq; long long bandwidth; std::vector points; diff --git a/src/process/ScopeVisualProcessor.cpp b/src/process/ScopeVisualProcessor.cpp index 15669da..1cc4d24 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/util/ThreadQueue.h b/src/util/ThreadQueue.h index a2d693e..2d88b59 100644 --- a/src/util/ThreadQueue.h +++ b/src/util/ThreadQueue.h @@ -30,13 +30,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. */ @@ -50,8 +54,9 @@ public: */ void set_max_num_items(unsigned int max_num_items) { std::lock_guard < std::mutex > lock(m_mutex); - - m_max_num_items = max_num_items; + if (m_max_num_items.load() != max_num_items) { + m_max_num_items.store(max_num_items); + } } /** @@ -62,7 +67,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); @@ -78,7 +83,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); @@ -219,7 +224,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()); } /** @@ -280,7 +285,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/ScopeCanvas.cpp b/src/visual/ScopeCanvas.cpp index 220997f..9dfaced 100644 --- a/src/visual/ScopeCanvas.cpp +++ b/src/visual/ScopeCanvas.cpp @@ -28,20 +28,51 @@ EVT_LEAVE_WINDOW(ScopeCanvas::OnMouseLeftWindow) EVT_ENTER_WINDOW(ScopeCanvas::OnMouseEnterWindow) wxEND_EVENT_TABLE() -ScopeCanvas::ScopeCanvas(wxWindow *parent, int *attribList) : InteractiveCanvas(parent, attribList), stereo(false), ppmMode(false), ctr(0), ctrTarget(0), dragAccel(0) { +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.2; + 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; } @@ -59,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(); } } @@ -96,53 +147,74 @@ void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - CubicVR::mat4 modelView = CubicVR::mat4::lookat(0, 0, -1.2, 0, 0, 0, 0, -1, 0); + 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) { - ctrTarget = round(ctr / panelInterval); - if (ctrTarget < -1.0) { - ctrTarget = -1.0; - } else if (ctrTarget > 0.0) { - ctrTarget = 0.0; - } - ctrTarget *= panelInterval; if (ctr != ctrTarget) { ctr += (ctrTarget-ctr)*0.2; } + if (abs(ctr - ctrTarget) < 0.001) { + ctr=ctrTarget; + } } else { - dragAccel -= dragAccel * 0.01; - if (abs(dragAccel) < 0.1 || ctr < ctrTarget-panelInterval/2.0 || ctr > ctrTarget+panelInterval/2.0 ) { + 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); - float roty = atan2(scopePanel.pos[0],1.2); - scopePanel.rot[1] = -(roty * (180.0 / M_PI)); - scopePanel.calcTransform(modelView); - scopePanel.draw(); + 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; + } - scopePanel.setPosition(panelInterval+ctr, 0); - roty = atan2(scopePanel.pos[0],1.2); - scopePanel.rot[1] = -(roty * (180.0 / M_PI)); - scopePanel.calcTransform(modelView); - scopePanel.draw(); + parentPanel.calcTransform(modelView); + parentPanel.draw(); + + if (spectrumVisible()) { + spectrumPanel.drawChildren(); + } + + glLoadMatrixf(scopePanel.transform); + if (!deviceName.empty()) { + glContext->DrawDeviceName(deviceName); + } glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glContext->DrawTunerTitles(ppmMode); - if (!deviceName.empty()) { - glContext->DrawDeviceName(deviceName); - } glContext->DrawEnd(); SwapBuffers(); @@ -182,7 +254,10 @@ void ScopeCanvas::OnMouseReleased(wxMouseEvent& event) { void ScopeCanvas::OnMouseEnterWindow(wxMouseEvent& event) { InteractiveCanvas::OnMouseEnterWindow(event); - + if (!helpTip.empty()) { + setStatusText(helpTip); + } + SetCursor(wxCURSOR_SIZEWE); } void ScopeCanvas::OnMouseLeftWindow(wxMouseEvent& event) { @@ -190,3 +265,8 @@ void ScopeCanvas::OnMouseLeftWindow(wxMouseEvent& event) { } + +void ScopeCanvas::setHelpTip(std::string tip) { + helpTip = tip; +} + diff --git a/src/visual/ScopeCanvas.h b/src/visual/ScopeCanvas.h index d5bfba5..d868b2e 100644 --- a/src/visual/ScopeCanvas.h +++ b/src/visual/ScopeCanvas.h @@ -9,6 +9,7 @@ #include "ScopeContext.h" #include "ScopeVisualProcessor.h" #include "ScopePanel.h" +#include "SpectrumPanel.h" #include "fftw3.h" #include "InteractiveCanvas.h" @@ -22,6 +23,14 @@ 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: @@ -36,15 +45,19 @@ private: 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(); };