Add functional Spectrum view to demodulator visuals

- Might need to do some renaming from Scope->AVDisplay or something for
ScopeCanvas to avoid confusion.
This commit is contained in:
Charles J. Cliffe 2015-08-24 01:31:37 -04:00
parent 13140ec28c
commit c30cce9114
11 changed files with 405 additions and 75 deletions

View File

@ -90,7 +90,9 @@ AppFrame::AppFrame() :
demodTray->AddSpacer(1); demodTray->AddSpacer(1);
scopeCanvas = new ScopeCanvas(this, attribList); scopeCanvas = new ScopeCanvas(this, attribList);
scopeCanvas->setHelpTip("Audio Visuals, drag left/right to toggle Scope or Spectrum.");
demodScopeTray->Add(scopeCanvas, 8, wxEXPAND | wxALL, 0); demodScopeTray->Add(scopeCanvas, 8, wxEXPAND | wxALL, 0);
wxGetApp().getScopeProcessor()->setup(2048);
wxGetApp().getScopeProcessor()->attachOutput(scopeCanvas->getInputQueue()); wxGetApp().getScopeProcessor()->attachOutput(scopeCanvas->getInputQueue());
demodScopeTray->AddSpacer(1); demodScopeTray->AddSpacer(1);
@ -790,6 +792,11 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
scopeCanvas->setPPMMode(demodTuner->isAltDown()); 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().getScopeProcessor()->run();
wxGetApp().getSpectrumDistributor()->run(); wxGetApp().getSpectrumDistributor()->run();

View File

@ -14,6 +14,7 @@
class AudioThreadInput: public ReferenceCounter { class AudioThreadInput: public ReferenceCounter {
public: public:
long long frequency; long long frequency;
int inputRate;
int sampleRate; int sampleRate;
int channels; int channels;
float peak; float peak;

View File

@ -310,6 +310,7 @@ void DemodulatorThread::run() {
ati = outputBuffers.getBuffer(); ati = outputBuffers.getBuffer();
ati->sampleRate = audioSampleRate; ati->sampleRate = audioSampleRate;
ati->inputRate = inp->sampleRate;
ati->setRefCount(1); ati->setRefCount(1);
if (demodulatorType == DEMOD_TYPE_RAW) { if (demodulatorType == DEMOD_TYPE_RAW) {
@ -359,7 +360,9 @@ void DemodulatorThread::run() {
if (ati && audioVisOutputQueue != NULL && audioVisOutputQueue->empty()) { if (ati && audioVisOutputQueue != NULL && audioVisOutputQueue->empty()) {
ati_vis->busy_update.lock(); ati_vis->busy_update.lock();
ati_vis->sampleRate = inp->sampleRate;
ati_vis->inputRate = inp->sampleRate;
int num_vis = DEMOD_VIS_SIZE; int num_vis = DEMOD_VIS_SIZE;
if (demodulatorType == DEMOD_TYPE_RAW || (stereo && inp->sampleRate >= 100000)) { if (demodulatorType == DEMOD_TYPE_RAW || (stereo && inp->sampleRate >= 100000)) {
ati_vis->channels = 2; ati_vis->channels = 2;
@ -377,6 +380,8 @@ void DemodulatorThread::run() {
} }
} else { } else {
for (int i = 0; i < stereoSize / 2; i++) { 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] = ati->data[i * 2];
ati_vis->data[i + stereoSize / 2] = ati->data[i * 2 + 1]; ati_vis->data[i + stereoSize / 2] = ati->data[i * 2 + 1];
} }
@ -384,7 +389,7 @@ void DemodulatorThread::run() {
} else { } else {
ati_vis->channels = 1; ati_vis->channels = 1;
if (numAudioWritten > bufSize) { if (numAudioWritten > bufSize) {
ati_vis->inputRate = audioSampleRate;
if (num_vis > numAudioWritten) { if (num_vis > numAudioWritten) {
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; // 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(); ati_vis->busy_update.unlock();
audioVisOutputQueue->push(ati_vis);
} }
if (ati != NULL) { if (ati != NULL) {

View File

@ -8,7 +8,7 @@
typedef ThreadQueue<AudioThreadInput *> DemodulatorThreadOutputQueue; typedef ThreadQueue<AudioThreadInput *> DemodulatorThreadOutputQueue;
#define DEMOD_VIS_SIZE 1024 #define DEMOD_VIS_SIZE 2048
class DemodulatorThread : public IOThread { class DemodulatorThread : public IOThread {
public: public:

View File

@ -5,7 +5,7 @@
#include <iomanip> #include <iomanip>
#include "ColorTheme.h" #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); setFill(GLPANEL_FILL_GRAD_Y);
setFillColor(ThemeMgr::mgr.currentTheme->fftBackground * 2.0, ThemeMgr::mgr.currentTheme->fftBackground); setFillColor(ThemeMgr::mgr.currentTheme->fftBackground * 2.0, ThemeMgr::mgr.currentTheme->fftBackground);
@ -51,6 +51,14 @@ long long SpectrumPanel::getBandwidth() {
return bandwidth; return bandwidth;
} }
void SpectrumPanel::setFFTSize(int fftSize_in) {
this->fftSize = fftSize_in;
}
int SpectrumPanel::getFFTSize() {
return fftSize;
}
void SpectrumPanel::setShowDb(bool showDb) { void SpectrumPanel::setShowDb(bool showDb) {
this->showDb = showDb; this->showDb = showDb;
if (showDb) { if (showDb) {
@ -133,7 +141,7 @@ void SpectrumPanel::drawPanelContents() {
long long leftFreq = (double) freq - ((double) bandwidth / 2.0); long long leftFreq = (double) freq - ((double) bandwidth / 2.0);
long long rightFreq = leftFreq + (double) bandwidth; 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; long double mhzStep = (100000.0 / (long double) (rightFreq - leftFreq)) * 2.0;
double mhzVisualStep = 0.1; double mhzVisualStep = 0.1;
@ -144,10 +152,37 @@ void SpectrumPanel::drawPanelContents() {
if (mhzStep * 0.5 * viewWidth < 40) { if (mhzStep * 0.5 * viewWidth < 40) {
mhzStep = (250000.0 / (long double) (rightFreq - leftFreq)) * 2.0; mhzStep = (250000.0 / (long double) (rightFreq - leftFreq)) * 2.0;
mhzVisualStep = 0.25; 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; mhzStep = (10000.0 / (long double) (rightFreq - leftFreq)) * 2.0;
mhzVisualStep = 0.01; mhzVisualStep = 0.01;
label.precision(2); label.precision(2);
@ -193,21 +228,20 @@ void SpectrumPanel::drawPanelContents() {
glLineWidth(1.0); glLineWidth(1.0);
if (showDb) { if (showDb) {
float dbPanelWidth = (1.0/viewWidth)*75.0; float dbPanelWidth = (1.0/viewWidth)*75.0;
float dbPanelHeight = (1.0/viewHeight)*14.0; float dbPanelHeight = (1.0/viewHeight)*14.0;
std::stringstream ssLabel; 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.setText(ssLabel.str(), GLFont::GLFONT_ALIGN_RIGHT);
dbPanelCeil.setSize(dbPanelWidth, dbPanelHeight); dbPanelCeil.setSize(dbPanelWidth, dbPanelHeight);
dbPanelCeil.setPosition(-1.0 + dbPanelWidth, 1.0 - dbPanelHeight); dbPanelCeil.setPosition(-1.0 + dbPanelWidth, 1.0 - dbPanelHeight);
ssLabel.str(""); 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.setText(ssLabel.str(), GLFont::GLFONT_ALIGN_RIGHT);
dbPanelFloor.setSize(dbPanelWidth, dbPanelHeight); dbPanelFloor.setSize(dbPanelWidth, dbPanelHeight);

View File

@ -19,7 +19,10 @@ public:
void setBandwidth(long long bandwidth); void setBandwidth(long long bandwidth);
long long getBandwidth(); long long getBandwidth();
void setFFTSize(int fftSize_in);
int getFFTSize();
void setShowDb(bool showDb); void setShowDb(bool showDb);
bool getShowDb(); bool getShowDb();
@ -28,6 +31,7 @@ protected:
private: private:
float floorValue, ceilValue; float floorValue, ceilValue;
int fftSize;
long long freq; long long freq;
long long bandwidth; long long bandwidth;
std::vector<float> points; std::vector<float> points;

View File

@ -1,4 +1,54 @@
#include "ScopeVisualProcessor.h" #include "ScopeVisualProcessor.h"
#include <cstring>
#include <string>
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() { void ScopeVisualProcessor::process() {
if (!isOutputEmpty()) { if (!isOutputEmpty()) {
@ -11,42 +61,144 @@ void ScopeVisualProcessor::process() {
if (!audioInputData) { if (!audioInputData) {
return; return;
} }
int iMax = audioInputData->data.size(); int i, iMax = audioInputData->data.size();
if (!iMax) { if (!iMax) {
audioInputData->decRefCount(); audioInputData->decRefCount();
return; return;
} }
audioInputData->busy_update.lock(); audioInputData->busy_update.lock();
ScopeRenderData *renderData = outputBuffers.getBuffer();
renderData->channels = audioInputData->channels;
if (renderData->waveform_points.size() != iMax * 2) { ScopeRenderData *renderData = NULL;
renderData->waveform_points.resize(iMax * 2);
} if (scopeEnabled) {
iMax = audioInputData->data.size();
if (iMax > maxScopeSamples) {
iMax = maxScopeSamples;
}
float peak = 1.0f; renderData = outputBuffers.getBuffer();
renderData->channels = audioInputData->channels;
for (int i = 0; i < iMax; i++) { renderData->inputRate = audioInputData->inputRate;
float p = fabs(audioInputData->data[i]); renderData->sampleRate = audioInputData->sampleRate;
if (p > peak) {
peak = p; 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(); audioInputData->busy_update.unlock();
} }
} }

View File

@ -2,17 +2,47 @@
#include "VisualProcessor.h" #include "VisualProcessor.h"
#include "AudioThread.h" #include "AudioThread.h"
#include "fftw3.h"
class ScopeRenderData: public ReferenceCounter { class ScopeRenderData: public ReferenceCounter {
public: public:
std::vector<float> waveform_points; std::vector<float> waveform_points;
int inputRate;
int sampleRate;
int channels; int channels;
bool spectrum;
int fft_size;
double fft_floor, fft_ceil;
}; };
typedef ThreadQueue<ScopeRenderData *> ScopeRenderDataQueue; typedef ThreadQueue<ScopeRenderData *> ScopeRenderDataQueue;
class ScopeVisualProcessor : public VisualProcessor<AudioThreadInput, ScopeRenderData> { class ScopeVisualProcessor : public VisualProcessor<AudioThreadInput, ScopeRenderData> {
public:
ScopeVisualProcessor();
~ScopeVisualProcessor();
void setup(int fftSize_in);
void setScopeEnabled(bool scopeEnable);
void setSpectrumEnabled(bool spectrumEnable);
protected: protected:
void process(); void process();
ReBuffer<ScopeRenderData> outputBuffers; ReBuffer<ScopeRenderData> 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<double> fft_result;
std::vector<double> fft_result_ma;
std::vector<double> fft_result_maa;
}; };

View File

@ -30,13 +30,17 @@ class ThreadQueue : public ThreadQueueBase {
public: public:
/*! Create safe queue. */ /*! Create safe queue. */
ThreadQueue() = default; ThreadQueue() {
m_max_num_items.store(0);
};
ThreadQueue(ThreadQueue&& sq) { ThreadQueue(ThreadQueue&& sq) {
m_queue = std::move(sq.m_queue); m_queue = std::move(sq.m_queue);
m_max_num_items.store(0);
} }
ThreadQueue(const ThreadQueue& sq) { ThreadQueue(const ThreadQueue& sq) {
std::lock_guard < std::mutex > lock(sq.m_mutex); std::lock_guard < std::mutex > lock(sq.m_mutex);
m_queue = sq.m_queue; m_queue = sq.m_queue;
m_max_num_items.store(0);
} }
/*! Destroy safe queue. */ /*! Destroy safe queue. */
@ -50,8 +54,9 @@ public:
*/ */
void set_max_num_items(unsigned int max_num_items) { void set_max_num_items(unsigned int max_num_items) {
std::lock_guard < std::mutex > lock(m_mutex); std::lock_guard < std::mutex > lock(m_mutex);
if (m_max_num_items.load() != max_num_items) {
m_max_num_items = max_num_items; m_max_num_items.store(max_num_items);
}
} }
/** /**
@ -62,7 +67,7 @@ public:
bool push(const value_type& item) { bool push(const value_type& item) {
std::lock_guard < std::mutex > lock(m_mutex); 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; return false;
m_queue.push(item); m_queue.push(item);
@ -78,7 +83,7 @@ public:
bool push(const value_type&& item) { bool push(const value_type&& item) {
std::lock_guard < std::mutex > lock(m_mutex); 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; return false;
m_queue.push(item); m_queue.push(item);
@ -219,7 +224,7 @@ public:
*/ */
bool full() const { bool full() const {
std::lock_guard < std::mutex > lock(m_mutex); 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<T, Container> m_queue; std::queue<T, Container> m_queue;
mutable std::mutex m_mutex; mutable std::mutex m_mutex;
std::condition_variable m_condition; 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. */ /*! Swaps the contents of two ThreadQueue objects. */

View File

@ -28,20 +28,51 @@ EVT_LEAVE_WINDOW(ScopeCanvas::OnMouseLeftWindow)
EVT_ENTER_WINDOW(ScopeCanvas::OnMouseEnterWindow) EVT_ENTER_WINDOW(ScopeCanvas::OnMouseEnterWindow)
wxEND_EVENT_TABLE() 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)); 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.setFill(GLPanel::GLPANEL_FILL_GRAD_Y);
bgPanel.setSize(1.0, 0.5); bgPanel.setSize(1.0, 0.5);
bgPanel.setPosition(0.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() { 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) { void ScopeCanvas::setStereo(bool state) {
stereo = state; stereo = state;
} }
@ -59,20 +90,40 @@ bool ScopeCanvas::getPPMMode() {
return ppmMode; return ppmMode;
} }
void ScopeCanvas::setShowDb(bool showDb) {
this->showDb = showDb;
}
bool ScopeCanvas::getShowDb() {
return showDb;
}
void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
wxPaintDC dc(this); wxPaintDC dc(this);
const wxSize ClientSize = GetClientSize(); const wxSize ClientSize = GetClientSize();
if (!inputData.empty()) { while (!inputData.empty()) {
ScopeRenderData *avData; ScopeRenderData *avData;
inputData.pop(avData); inputData.pop(avData);
if (avData) { if (!avData->spectrum) {
if (avData->waveform_points.size()) { if (avData->waveform_points.size()) {
scopePanel.setPoints(avData->waveform_points); scopePanel.setPoints(avData->waveform_points);
setStereo(avData->channels == 2); 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(); avData->decRefCount();
} }
} }
@ -96,53 +147,74 @@ void ScopeCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); 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 panelWidth = 1.0;
float panelInterval = (panelWidth * 2.0 + panelSpacing); float panelInterval = (panelWidth * 2.0 + panelSpacing);
if (!mouseTracker.mouseDown()) { 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 (!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) { if (ctr != ctrTarget) {
ctr += (ctrTarget-ctr)*0.2; ctr += (ctrTarget-ctr)*0.2;
} }
if (abs(ctr - ctrTarget) < 0.001) {
ctr=ctrTarget;
}
} else { } else {
dragAccel -= dragAccel * 0.01; dragAccel -= dragAccel * 0.1;
if (abs(dragAccel) < 0.1 || ctr < ctrTarget-panelInterval/2.0 || ctr > ctrTarget+panelInterval/2.0 ) { if ((abs(dragAccel) < 0.2) || (ctr < (ctrTarget-panelInterval/2.0)) || (ctr > (ctrTarget+panelInterval/2.0)) ) {
dragAccel = 0; dragAccel = 0;
} else { } else {
ctr += dragAccel; ctr += dragAccel;
} }
} }
} }
float roty = 0;
scopePanel.setPosition(ctr, 0); scopePanel.setPosition(ctr, 0);
float roty = atan2(scopePanel.pos[0],1.2); if (scopeVisible()) {
scopePanel.rot[1] = -(roty * (180.0 / M_PI)); scopePanel.contentsVisible = true;
scopePanel.calcTransform(modelView); roty = atan2(scopePanel.pos[0],1.2);
scopePanel.draw(); 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); parentPanel.calcTransform(modelView);
roty = atan2(scopePanel.pos[0],1.2); parentPanel.draw();
scopePanel.rot[1] = -(roty * (180.0 / M_PI));
scopePanel.calcTransform(modelView); if (spectrumVisible()) {
scopePanel.draw(); spectrumPanel.drawChildren();
}
glLoadMatrixf(scopePanel.transform);
if (!deviceName.empty()) {
glContext->DrawDeviceName(deviceName);
}
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
glLoadIdentity(); glLoadIdentity();
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); glLoadIdentity();
glContext->DrawTunerTitles(ppmMode); glContext->DrawTunerTitles(ppmMode);
if (!deviceName.empty()) {
glContext->DrawDeviceName(deviceName);
}
glContext->DrawEnd(); glContext->DrawEnd();
SwapBuffers(); SwapBuffers();
@ -182,7 +254,10 @@ void ScopeCanvas::OnMouseReleased(wxMouseEvent& event) {
void ScopeCanvas::OnMouseEnterWindow(wxMouseEvent& event) { void ScopeCanvas::OnMouseEnterWindow(wxMouseEvent& event) {
InteractiveCanvas::OnMouseEnterWindow(event); InteractiveCanvas::OnMouseEnterWindow(event);
if (!helpTip.empty()) {
setStatusText(helpTip);
}
SetCursor(wxCURSOR_SIZEWE);
} }
void ScopeCanvas::OnMouseLeftWindow(wxMouseEvent& event) { void ScopeCanvas::OnMouseLeftWindow(wxMouseEvent& event) {
@ -190,3 +265,8 @@ void ScopeCanvas::OnMouseLeftWindow(wxMouseEvent& event) {
} }
void ScopeCanvas::setHelpTip(std::string tip) {
helpTip = tip;
}

View File

@ -9,6 +9,7 @@
#include "ScopeContext.h" #include "ScopeContext.h"
#include "ScopeVisualProcessor.h" #include "ScopeVisualProcessor.h"
#include "ScopePanel.h" #include "ScopePanel.h"
#include "SpectrumPanel.h"
#include "fftw3.h" #include "fftw3.h"
#include "InteractiveCanvas.h" #include "InteractiveCanvas.h"
@ -22,6 +23,14 @@ public:
void setPPMMode(bool ppmMode); void setPPMMode(bool ppmMode);
bool getPPMMode(); bool getPPMMode();
void setShowDb(bool showDb);
bool getShowDb();
bool scopeVisible();
bool spectrumVisible();
void setHelpTip(std::string tip);
ScopeRenderDataQueue *getInputQueue(); ScopeRenderDataQueue *getInputQueue();
private: private:
@ -36,15 +45,19 @@ private:
ScopeRenderDataQueue inputData; ScopeRenderDataQueue inputData;
ScopePanel scopePanel; ScopePanel scopePanel;
GLPanel parentPanel;
SpectrumPanel spectrumPanel;
GLPanel bgPanel; GLPanel bgPanel;
ScopeContext *glContext; ScopeContext *glContext;
std::string deviceName; std::string deviceName;
bool stereo; bool stereo;
bool ppmMode; bool ppmMode;
bool showDb;
float panelSpacing; float panelSpacing;
float ctr; float ctr;
float ctrTarget; float ctrTarget;
float dragAccel; float dragAccel;
std::string helpTip;
// event table // event table
wxDECLARE_EVENT_TABLE(); wxDECLARE_EVENT_TABLE();
}; };