mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-22 16:08:39 -05:00
NFM Modulator: basic input volume gauge
This commit is contained in:
parent
ab60cac358
commit
7a07b56b01
@ -144,6 +144,7 @@ set(sdrbase_SOURCES
|
|||||||
sdrbase/gui/glspectrum.cpp
|
sdrbase/gui/glspectrum.cpp
|
||||||
sdrbase/gui/glspectrumgui.cpp
|
sdrbase/gui/glspectrumgui.cpp
|
||||||
sdrbase/gui/indicator.cpp
|
sdrbase/gui/indicator.cpp
|
||||||
|
sdrbase/gui/levelmeter.cpp
|
||||||
sdrbase/gui/mypositiondialog.cpp
|
sdrbase/gui/mypositiondialog.cpp
|
||||||
sdrbase/gui/pluginsdialog.cpp
|
sdrbase/gui/pluginsdialog.cpp
|
||||||
sdrbase/gui/audiodialog.cpp
|
sdrbase/gui/audiodialog.cpp
|
||||||
@ -249,6 +250,7 @@ set(sdrbase_HEADERS
|
|||||||
sdrbase/gui/glspectrum.h
|
sdrbase/gui/glspectrum.h
|
||||||
sdrbase/gui/glspectrumgui.h
|
sdrbase/gui/glspectrumgui.h
|
||||||
sdrbase/gui/indicator.h
|
sdrbase/gui/indicator.h
|
||||||
|
sdrbase/gui/levelmeter.h
|
||||||
sdrbase/gui/mypositiondialog.h
|
sdrbase/gui/mypositiondialog.h
|
||||||
sdrbase/gui/physicalunit.h
|
sdrbase/gui/physicalunit.h
|
||||||
sdrbase/gui/pluginsdialog.h
|
sdrbase/gui/pluginsdialog.h
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <QMutexLocker>
|
#include <QMutexLocker>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <complex.h>
|
#include <complex.h>
|
||||||
|
#include <algorithm>
|
||||||
#include <dsp/upchannelizer.h>
|
#include <dsp/upchannelizer.h>
|
||||||
#include "dsp/dspengine.h"
|
#include "dsp/dspengine.h"
|
||||||
#include "dsp/pidcontroller.h"
|
#include "dsp/pidcontroller.h"
|
||||||
@ -32,6 +33,7 @@ MESSAGE_CLASS_DEFINITION(NFMMod::MsgConfigureFileSourceStreamTiming, Message)
|
|||||||
MESSAGE_CLASS_DEFINITION(NFMMod::MsgReportFileSourceStreamData, Message)
|
MESSAGE_CLASS_DEFINITION(NFMMod::MsgReportFileSourceStreamData, Message)
|
||||||
MESSAGE_CLASS_DEFINITION(NFMMod::MsgReportFileSourceStreamTiming, Message)
|
MESSAGE_CLASS_DEFINITION(NFMMod::MsgReportFileSourceStreamTiming, Message)
|
||||||
|
|
||||||
|
const int NFMMod::m_levelNbSamples = 480; // every 10ms
|
||||||
|
|
||||||
NFMMod::NFMMod() :
|
NFMMod::NFMMod() :
|
||||||
m_modPhasor(0.0f),
|
m_modPhasor(0.0f),
|
||||||
@ -40,7 +42,10 @@ NFMMod::NFMMod() :
|
|||||||
m_fileSize(0),
|
m_fileSize(0),
|
||||||
m_recordLength(0),
|
m_recordLength(0),
|
||||||
m_sampleRate(48000),
|
m_sampleRate(48000),
|
||||||
m_afInput(NFMModInputNone)
|
m_afInput(NFMModInputNone),
|
||||||
|
m_levelCalcCount(0),
|
||||||
|
m_peakLevel(0.0f),
|
||||||
|
m_levelSum(0.0f)
|
||||||
{
|
{
|
||||||
setObjectName("NFMod");
|
setObjectName("NFMod");
|
||||||
|
|
||||||
@ -127,6 +132,7 @@ void NFMMod::modulateSample()
|
|||||||
Real t;
|
Real t;
|
||||||
|
|
||||||
pullAF(t);
|
pullAF(t);
|
||||||
|
calculateLevel(t);
|
||||||
|
|
||||||
m_modPhasor += (m_running.m_fmDeviation / (float) m_running.m_audioSampleRate) * m_bandpass.filter(t) * (M_PI / 1208.0f);
|
m_modPhasor += (m_running.m_fmDeviation / (float) m_running.m_audioSampleRate) * m_bandpass.filter(t) * (M_PI / 1208.0f);
|
||||||
m_modSample.real(cos(m_modPhasor) * 32678.0f);
|
m_modSample.real(cos(m_modPhasor) * 32678.0f);
|
||||||
@ -173,7 +179,7 @@ void NFMMod::pullAF(Real& sample)
|
|||||||
break;
|
break;
|
||||||
case NFMModInputAudio:
|
case NFMModInputAudio:
|
||||||
m_audioFifo.read(reinterpret_cast<quint8*>(audioSample), 1, 10);
|
m_audioFifo.read(reinterpret_cast<quint8*>(audioSample), 1, 10);
|
||||||
sample = ((audioSample[0] + audioSample[1]) / 131072.0f) * m_running.m_volumeFactor;
|
sample = ((audioSample[0] + audioSample[1]) / 65536.0f) * m_running.m_volumeFactor;
|
||||||
break;
|
break;
|
||||||
case NFMModInputNone:
|
case NFMModInputNone:
|
||||||
default:
|
default:
|
||||||
@ -182,6 +188,25 @@ void NFMMod::pullAF(Real& sample)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NFMMod::calculateLevel(Real& sample)
|
||||||
|
{
|
||||||
|
if (m_levelCalcCount < m_levelNbSamples)
|
||||||
|
{
|
||||||
|
m_peakLevel = std::max(std::fabs(m_peakLevel), sample);
|
||||||
|
m_levelSum += sample * sample;
|
||||||
|
m_levelCalcCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qreal rmsLevel = sqrt(m_levelSum / m_levelNbSamples);
|
||||||
|
//qDebug("NFMMod::calculateLevel: %f %f", rmsLevel, m_peakLevel);
|
||||||
|
emit levelChanged(rmsLevel, m_peakLevel, m_levelNbSamples);
|
||||||
|
m_peakLevel = 0.0f;
|
||||||
|
m_levelSum = 0.0f;
|
||||||
|
m_levelCalcCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NFMMod::start()
|
void NFMMod::start()
|
||||||
{
|
{
|
||||||
qDebug() << "NFMMod::start: m_outputSampleRate: " << m_config.m_outputSampleRate
|
qDebug() << "NFMMod::start: m_outputSampleRate: " << m_config.m_outputSampleRate
|
||||||
|
@ -191,6 +191,16 @@ public:
|
|||||||
|
|
||||||
Real getMagSq() const { return m_magsq; }
|
Real getMagSq() const { return m_magsq; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/**
|
||||||
|
* Level changed
|
||||||
|
* \param rmsLevel RMS level in range 0.0 - 1.0
|
||||||
|
* \param peakLevel Peak level in range 0.0 - 1.0
|
||||||
|
* \param numSamples Number of audio samples analyzed
|
||||||
|
*/
|
||||||
|
void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class MsgConfigureNFMMod : public Message
|
class MsgConfigureNFMMod : public Message
|
||||||
{
|
{
|
||||||
@ -304,9 +314,14 @@ private:
|
|||||||
int m_sampleRate;
|
int m_sampleRate;
|
||||||
|
|
||||||
NFMModInputAF m_afInput;
|
NFMModInputAF m_afInput;
|
||||||
|
quint32 m_levelCalcCount;
|
||||||
|
Real m_peakLevel;
|
||||||
|
Real m_levelSum;
|
||||||
|
static const int m_levelNbSamples;
|
||||||
|
|
||||||
void apply();
|
void apply();
|
||||||
void pullAF(Real& sample);
|
void pullAF(Real& sample);
|
||||||
|
void calculateLevel(Real& sample);
|
||||||
void modulateSample();
|
void modulateSample();
|
||||||
void openFileStream();
|
void openFileStream();
|
||||||
void seekFileStream(int seekPercentage);
|
void seekFileStream(int seekPercentage);
|
||||||
|
@ -78,7 +78,7 @@ void NFMModGUI::resetToDefaults()
|
|||||||
ui->afBW->setValue(3);
|
ui->afBW->setValue(3);
|
||||||
ui->fmDev->setValue(50);
|
ui->fmDev->setValue(50);
|
||||||
ui->toneFrequency->setValue(100);
|
ui->toneFrequency->setValue(100);
|
||||||
ui->micVolume->setValue(10);
|
ui->volume->setValue(10);
|
||||||
ui->deltaFrequency->setValue(0);
|
ui->deltaFrequency->setValue(0);
|
||||||
|
|
||||||
blockApplySettings(false);
|
blockApplySettings(false);
|
||||||
@ -94,7 +94,7 @@ QByteArray NFMModGUI::serialize() const
|
|||||||
s.writeS32(4, ui->fmDev->value());
|
s.writeS32(4, ui->fmDev->value());
|
||||||
s.writeU32(5, m_channelMarker.getColor().rgb());
|
s.writeU32(5, m_channelMarker.getColor().rgb());
|
||||||
s.writeS32(6, ui->toneFrequency->value());
|
s.writeS32(6, ui->toneFrequency->value());
|
||||||
s.writeS32(7, ui->micVolume->value());
|
s.writeS32(7, ui->volume->value());
|
||||||
return s.final();
|
return s.final();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ bool NFMModGUI::deserialize(const QByteArray& data)
|
|||||||
d.readS32(6, &tmp, 100);
|
d.readS32(6, &tmp, 100);
|
||||||
ui->toneFrequency->setValue(tmp);
|
ui->toneFrequency->setValue(tmp);
|
||||||
d.readS32(7, &tmp, 10);
|
d.readS32(7, &tmp, 10);
|
||||||
ui->micVolume->setValue(tmp);
|
ui->volume->setValue(tmp);
|
||||||
|
|
||||||
blockApplySettings(false);
|
blockApplySettings(false);
|
||||||
m_channelMarker.blockSignals(false);
|
m_channelMarker.blockSignals(false);
|
||||||
@ -227,9 +227,9 @@ void NFMModGUI::on_fmDev_valueChanged(int value)
|
|||||||
applySettings();
|
applySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NFMModGUI::on_micVolume_valueChanged(int value)
|
void NFMModGUI::on_volume_valueChanged(int value)
|
||||||
{
|
{
|
||||||
ui->micVolumeText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1));
|
ui->volumeText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1));
|
||||||
applySettings();
|
applySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,6 +386,7 @@ NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pa
|
|||||||
applySettings();
|
applySettings();
|
||||||
|
|
||||||
connect(m_nfmMod->getOutputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages()));
|
connect(m_nfmMod->getOutputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages()));
|
||||||
|
connect(m_nfmMod, SIGNAL(levelChanged(qreal, qreal, int)), ui->volumeMeter, SLOT(levelChanged(qreal, qreal, int)));
|
||||||
}
|
}
|
||||||
|
|
||||||
NFMModGUI::~NFMModGUI()
|
NFMModGUI::~NFMModGUI()
|
||||||
@ -422,7 +423,7 @@ void NFMModGUI::applySettings()
|
|||||||
ui->afBW->value() * 1000.0,
|
ui->afBW->value() * 1000.0,
|
||||||
ui->fmDev->value() * 100.0f, // value is in '100 Hz
|
ui->fmDev->value() * 100.0f, // value is in '100 Hz
|
||||||
ui->toneFrequency->value() * 10.0f,
|
ui->toneFrequency->value() * 10.0f,
|
||||||
ui->micVolume->value() / 10.0f,
|
ui->volume->value() / 10.0f,
|
||||||
ui->audioMute->isChecked(),
|
ui->audioMute->isChecked(),
|
||||||
ui->playLoop->isChecked());
|
ui->playLoop->isChecked());
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ private slots:
|
|||||||
void on_afBW_valueChanged(int value);
|
void on_afBW_valueChanged(int value);
|
||||||
void on_fmDev_valueChanged(int value);
|
void on_fmDev_valueChanged(int value);
|
||||||
void on_toneFrequency_valueChanged(int value);
|
void on_toneFrequency_valueChanged(int value);
|
||||||
void on_micVolume_valueChanged(int value);
|
void on_volume_valueChanged(int value);
|
||||||
void on_audioMute_toggled(bool checked);
|
void on_audioMute_toggled(bool checked);
|
||||||
void on_tone_toggled(bool checked);
|
void on_tone_toggled(bool checked);
|
||||||
void on_mic_toggled(bool checked);
|
void on_mic_toggled(bool checked);
|
||||||
|
@ -50,16 +50,7 @@
|
|||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>3</number>
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="leftMargin">
|
<property name="margin">
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>2</number>
|
<number>2</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
@ -329,6 +320,78 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line_5">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="volumeLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="volLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Vol</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDial" name="volume">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>24</width>
|
||||||
|
<height>24</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Audio input volume</string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
<property name="pageStep">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="volumeText">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>25</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Audio input volume level</string>
|
||||||
|
</property>
|
||||||
|
<property name="statusTip">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>1.0</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="LevelMeter" name="volumeMeter" native="true">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="recordFileSelectLayout">
|
<layout class="QHBoxLayout" name="recordFileSelectLayout">
|
||||||
<item>
|
<item>
|
||||||
@ -426,66 +489,15 @@
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="Line" name="line">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="volLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Vol</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QDial" name="micVolume">
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>24</width>
|
|
||||||
<height>24</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Audio input volume</string>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<number>100</number>
|
|
||||||
</property>
|
|
||||||
<property name="pageStep">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<number>10</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="micVolumeText">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>25</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Audio input volume level</string>
|
|
||||||
</property>
|
|
||||||
<property name="statusTip">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>1.0</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line_4">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="fileNameLayout">
|
<layout class="QHBoxLayout" name="fileNameLayout">
|
||||||
<item>
|
<item>
|
||||||
@ -670,6 +682,12 @@
|
|||||||
<extends>QToolButton</extends>
|
<extends>QToolButton</extends>
|
||||||
<header>gui/buttonswitch.h</header>
|
<header>gui/buttonswitch.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>LevelMeter</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>gui/levelmeter.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../sdrbase/resources/res.qrc"/>
|
<include location="../../../sdrbase/resources/res.qrc"/>
|
||||||
|
174
sdrbase/gui/levelmeter.cpp
Normal file
174
sdrbase/gui/levelmeter.cpp
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* Copyright (C) 2016 Edouard Griffiths, F4EXB
|
||||||
|
* Modifications made to:
|
||||||
|
* - use the widget horizontally
|
||||||
|
* - differentiate each area with a different color
|
||||||
|
**
|
||||||
|
** Copyright (C) 2015 The Qt Company Ltd.
|
||||||
|
** Contact: http://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the examples of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:BSD$
|
||||||
|
** You may use this file under the terms of the BSD license as follows:
|
||||||
|
**
|
||||||
|
** "Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions are
|
||||||
|
** met:
|
||||||
|
** * Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** * Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in
|
||||||
|
** the documentation and/or other materials provided with the
|
||||||
|
** distribution.
|
||||||
|
** * Neither the name of The Qt Company Ltd nor the names of its
|
||||||
|
** contributors may be used to endorse or promote products derived
|
||||||
|
** from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "gui/levelmeter.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
const int RedrawInterval = 100; // ms
|
||||||
|
const qreal PeakDecayRate = 0.001;
|
||||||
|
const int PeakHoldLevelDuration = 2000; // ms
|
||||||
|
|
||||||
|
LevelMeter::LevelMeter(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, m_rmsLevel(0.0)
|
||||||
|
, m_peakLevel(0.0)
|
||||||
|
, m_decayedPeakLevel(0.0)
|
||||||
|
, m_peakDecayRate(PeakDecayRate)
|
||||||
|
, m_peakHoldLevel(0.0)
|
||||||
|
, m_redrawTimer(new QTimer(this))
|
||||||
|
, m_rmsColor(Qt::green) // m_rmsColor(Qt::red)
|
||||||
|
, m_decayedPeakColor(Qt::yellow)
|
||||||
|
, m_peakColor(255, 0, 0, 255) // m_peakColor(255, 200, 200, 255)
|
||||||
|
{
|
||||||
|
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
|
||||||
|
setMinimumWidth(30);
|
||||||
|
|
||||||
|
connect(m_redrawTimer, SIGNAL(timeout()), this, SLOT(redrawTimerExpired()));
|
||||||
|
m_redrawTimer->start(RedrawInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
LevelMeter::~LevelMeter()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void LevelMeter::reset()
|
||||||
|
{
|
||||||
|
m_rmsLevel = 0.0;
|
||||||
|
m_peakLevel = 0.0;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LevelMeter::levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples)
|
||||||
|
{
|
||||||
|
// Smooth the RMS signal
|
||||||
|
const qreal smooth = pow(qreal(0.9), static_cast<qreal>(numSamples) / 256); // TODO: remove this magic number
|
||||||
|
m_rmsLevel = (m_rmsLevel * smooth) + (rmsLevel * (1.0 - smooth));
|
||||||
|
|
||||||
|
if (peakLevel > m_decayedPeakLevel) {
|
||||||
|
m_peakLevel = peakLevel;
|
||||||
|
m_decayedPeakLevel = peakLevel;
|
||||||
|
m_peakLevelChanged.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peakLevel > m_peakHoldLevel) {
|
||||||
|
m_peakHoldLevel = peakLevel;
|
||||||
|
m_peakHoldLevelChanged.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LevelMeter::redrawTimerExpired()
|
||||||
|
{
|
||||||
|
// Decay the peak signal
|
||||||
|
const int elapsedMs = m_peakLevelChanged.elapsed();
|
||||||
|
const qreal decayAmount = m_peakDecayRate * elapsedMs;
|
||||||
|
if (decayAmount < m_peakLevel)
|
||||||
|
m_decayedPeakLevel = m_peakLevel - decayAmount;
|
||||||
|
else
|
||||||
|
m_decayedPeakLevel = 0.0;
|
||||||
|
|
||||||
|
// Check whether to clear the peak hold level
|
||||||
|
if (m_peakHoldLevelChanged.elapsed() > PeakHoldLevelDuration)
|
||||||
|
m_peakHoldLevel = 0.0;
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LevelMeter::paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
Q_UNUSED(event)
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
painter.fillRect(rect(), Qt::black);
|
||||||
|
|
||||||
|
QRect bar = rect();
|
||||||
|
|
||||||
|
// old
|
||||||
|
|
||||||
|
// bar.setTop(rect().top() + (1.0 - m_peakHoldLevel) * rect().height());
|
||||||
|
// bar.setBottom(bar.top() + 5);
|
||||||
|
// painter.fillRect(bar, m_rmsColor);
|
||||||
|
// bar.setBottom(rect().bottom());
|
||||||
|
//
|
||||||
|
// bar.setTop(rect().top() + (1.0 - m_decayedPeakLevel) * rect().height());
|
||||||
|
// painter.fillRect(bar, m_peakColor);
|
||||||
|
//
|
||||||
|
// bar.setTop(rect().top() + (1.0 - m_rmsLevel) * rect().height());
|
||||||
|
// painter.fillRect(bar, m_rmsColor);
|
||||||
|
|
||||||
|
// old v.2
|
||||||
|
|
||||||
|
// bar.setTop(rect().top() + (1.0 - m_peakHoldLevel) * rect().height());
|
||||||
|
// bar.setBottom(bar.top() + 5);
|
||||||
|
// painter.fillRect(bar, m_peakColor);
|
||||||
|
// bar.setBottom(rect().bottom());
|
||||||
|
//
|
||||||
|
// bar.setTop(rect().top() + (1.0 - m_decayedPeakLevel) * rect().height());
|
||||||
|
// painter.fillRect(bar, m_decayedPeakColor);
|
||||||
|
//
|
||||||
|
// bar.setTop(rect().top() + (1.0 - m_rmsLevel) * rect().height());
|
||||||
|
// painter.fillRect(bar, m_rmsColor);
|
||||||
|
|
||||||
|
// new
|
||||||
|
|
||||||
|
bar.setRight(rect().right() - (1.0 - m_peakHoldLevel) * rect().width());
|
||||||
|
bar.setLeft(bar.right() - 5);
|
||||||
|
painter.fillRect(bar, m_peakColor);
|
||||||
|
bar.setLeft(rect().left());
|
||||||
|
|
||||||
|
bar.setRight(rect().right() - (1.0 - m_decayedPeakLevel) * rect().width());
|
||||||
|
painter.fillRect(bar, m_decayedPeakColor);
|
||||||
|
|
||||||
|
bar.setRight(rect().right() - (1.0 - m_rmsLevel) * rect().width());
|
||||||
|
painter.fillRect(bar, m_rmsColor);
|
||||||
|
|
||||||
|
}
|
124
sdrbase/gui/levelmeter.h
Normal file
124
sdrbase/gui/levelmeter.h
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* Copyright (C) 2016 Edouard Griffiths, F4EXB
|
||||||
|
* Modifications made to:
|
||||||
|
* - use the widget horizontally
|
||||||
|
* - differentiate each area with a different color
|
||||||
|
**
|
||||||
|
** Copyright (C) 2015 The Qt Company Ltd.
|
||||||
|
** Contact: http://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the examples of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:BSD$
|
||||||
|
** You may use this file under the terms of the BSD license as follows:
|
||||||
|
**
|
||||||
|
** "Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions are
|
||||||
|
** met:
|
||||||
|
** * Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** * Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in
|
||||||
|
** the documentation and/or other materials provided with the
|
||||||
|
** distribution.
|
||||||
|
** * Neither the name of The Qt Company Ltd nor the names of its
|
||||||
|
** contributors may be used to endorse or promote products derived
|
||||||
|
** from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef SDRBASE_GUI_LEVELMETER_H_
|
||||||
|
#define SDRBASE_GUI_LEVELMETER_H_
|
||||||
|
|
||||||
|
#include <QTime>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <dsp/dsptypes.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Widget which displays a vertical audio level meter, indicating the
|
||||||
|
* RMS and peak levels of the window of audio samples most recently analyzed
|
||||||
|
* by the Engine.
|
||||||
|
*/
|
||||||
|
class LevelMeter : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit LevelMeter(QWidget *parent = 0);
|
||||||
|
~LevelMeter();
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent *event);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void reset();
|
||||||
|
void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void redrawTimerExpired();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Height of RMS level bar.
|
||||||
|
* Range 0.0 - 1.0.
|
||||||
|
*/
|
||||||
|
qreal m_rmsLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Most recent peak level.
|
||||||
|
* Range 0.0 - 1.0.
|
||||||
|
*/
|
||||||
|
qreal m_peakLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Height of peak level bar.
|
||||||
|
* This is calculated by decaying m_peakLevel depending on the
|
||||||
|
* elapsed time since m_peakLevelChanged, and the value of m_decayRate.
|
||||||
|
*/
|
||||||
|
qreal m_decayedPeakLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time at which m_peakLevel was last changed.
|
||||||
|
*/
|
||||||
|
QTime m_peakLevelChanged;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rate at which peak level bar decays.
|
||||||
|
* Expressed in level units / millisecond.
|
||||||
|
*/
|
||||||
|
qreal m_peakDecayRate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* High watermark of peak level.
|
||||||
|
* Range 0.0 - 1.0.
|
||||||
|
*/
|
||||||
|
qreal m_peakHoldLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time at which m_peakHoldLevel was last changed.
|
||||||
|
*/
|
||||||
|
QTime m_peakHoldLevelChanged;
|
||||||
|
|
||||||
|
QTimer *m_redrawTimer;
|
||||||
|
|
||||||
|
QColor m_rmsColor;
|
||||||
|
QColor m_peakColor;
|
||||||
|
QColor m_decayedPeakColor;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* SDRBASE_GUI_LEVELMETER_H_ */
|
Loading…
Reference in New Issue
Block a user