1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2025-06-16 05:22:54 -04:00

AM Modulator: implement generic volume control and volume level meter

This commit is contained in:
f4exb 2016-12-02 17:56:19 +01:00
parent c7b1d8a133
commit a1cd67745b
6 changed files with 161 additions and 98 deletions

View File

@ -33,6 +33,7 @@ MESSAGE_CLASS_DEFINITION(AMMod::MsgConfigureFileSourceStreamTiming, Message)
MESSAGE_CLASS_DEFINITION(AMMod::MsgReportFileSourceStreamData, Message) MESSAGE_CLASS_DEFINITION(AMMod::MsgReportFileSourceStreamData, Message)
MESSAGE_CLASS_DEFINITION(AMMod::MsgReportFileSourceStreamTiming, Message) MESSAGE_CLASS_DEFINITION(AMMod::MsgReportFileSourceStreamTiming, Message)
const int AMMod::m_levelNbSamples = 480; // every 10ms
AMMod::AMMod() : AMMod::AMMod() :
m_audioFifo(4, 48000), m_audioFifo(4, 48000),
@ -40,7 +41,10 @@ AMMod::AMMod() :
m_fileSize(0), m_fileSize(0),
m_recordLength(0), m_recordLength(0),
m_sampleRate(48000), m_sampleRate(48000),
m_afInput(AMModInputNone) m_afInput(AMModInputNone),
m_levelCalcCount(0),
m_peakLevel(0.0f),
m_levelSum(0.0f)
{ {
setObjectName("AMMod"); setObjectName("AMMod");
@ -73,11 +77,11 @@ void AMMod::configure(MessageQueue* messageQueue,
Real rfBandwidth, Real rfBandwidth,
float modFactor, float modFactor,
float toneFrequency, float toneFrequency,
int volumeTenths, float volumeFactor,
bool audioMute, bool audioMute,
bool playLoop) bool playLoop)
{ {
Message* cmd = MsgConfigureAMMod::create(rfBandwidth, modFactor, toneFrequency, volumeTenths, audioMute, playLoop); Message* cmd = MsgConfigureAMMod::create(rfBandwidth, modFactor, toneFrequency, volumeFactor, audioMute, playLoop);
messageQueue->push(cmd); messageQueue->push(cmd);
} }
@ -124,6 +128,7 @@ void AMMod::modulateSample()
Real t; Real t;
pullAF(t); pullAF(t);
calculateLevel(t);
m_modSample.real((t*m_running.m_modFactor + 1.0f) * 16384.0f); // modulate and scale zero frequency carrier m_modSample.real((t*m_running.m_modFactor + 1.0f) * 16384.0f); // modulate and scale zero frequency carrier
m_modSample.imag(0.0f); m_modSample.imag(0.0f);
@ -159,6 +164,7 @@ void AMMod::pullAF(Real& sample)
else else
{ {
m_ifstream.read(reinterpret_cast<char*>(&sample), sizeof(Real)); m_ifstream.read(reinterpret_cast<char*>(&sample), sizeof(Real));
sample *= m_running.m_volumeFactor;
} }
} }
else else
@ -168,7 +174,7 @@ void AMMod::pullAF(Real& sample)
break; break;
case AMModInputAudio: case AMModInputAudio:
m_audioFifo.read(reinterpret_cast<quint8*>(audioSample), 1, 10); m_audioFifo.read(reinterpret_cast<quint8*>(audioSample), 1, 10);
sample = ((audioSample[0] + audioSample[1]) * m_running.m_volumeFactor) / 6553600.0f; sample = ((audioSample[0] + audioSample[1]) / 65536.0f) * m_running.m_volumeFactor;
break; break;
case AMModInputNone: case AMModInputNone:
default: default:
@ -177,6 +183,25 @@ void AMMod::pullAF(Real& sample)
} }
} }
void AMMod::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 AMMod::start() void AMMod::start()
{ {
qDebug() << "AMMod::start: m_outputSampleRate: " << m_config.m_outputSampleRate qDebug() << "AMMod::start: m_outputSampleRate: " << m_config.m_outputSampleRate

View File

@ -177,7 +177,7 @@ public:
Real rfBandwidth, Real rfBandwidth,
float modFactor, float modFactor,
float toneFrequency, float toneFrequency,
int volumeFactor, float volumeFactor,
bool audioMute, bool audioMute,
bool playLoop); bool playLoop);
@ -188,6 +188,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 MsgConfigureAMMod : public Message class MsgConfigureAMMod : public Message
{ {
@ -197,11 +207,11 @@ private:
Real getRFBandwidth() const { return m_rfBandwidth; } Real getRFBandwidth() const { return m_rfBandwidth; }
float getModFactor() const { return m_modFactor; } float getModFactor() const { return m_modFactor; }
float getToneFrequency() const { return m_toneFrequency; } float getToneFrequency() const { return m_toneFrequency; }
int getVolumeFactor() const { return m_volumeFactor; } float getVolumeFactor() const { return m_volumeFactor; }
bool getAudioMute() const { return m_audioMute; } bool getAudioMute() const { return m_audioMute; }
bool getPlayLoop() const { return m_playLoop; } bool getPlayLoop() const { return m_playLoop; }
static MsgConfigureAMMod* create(Real rfBandwidth, float modFactor, float toneFreqeuncy, int volumeFactor, bool audioMute, bool playLoop) static MsgConfigureAMMod* create(Real rfBandwidth, float modFactor, float toneFreqeuncy, float volumeFactor, bool audioMute, bool playLoop)
{ {
return new MsgConfigureAMMod(rfBandwidth, modFactor, toneFreqeuncy, volumeFactor, audioMute, playLoop); return new MsgConfigureAMMod(rfBandwidth, modFactor, toneFreqeuncy, volumeFactor, audioMute, playLoop);
} }
@ -210,11 +220,11 @@ private:
Real m_rfBandwidth; Real m_rfBandwidth;
float m_modFactor; float m_modFactor;
float m_toneFrequency; float m_toneFrequency;
int m_volumeFactor; float m_volumeFactor;
bool m_audioMute; bool m_audioMute;
bool m_playLoop; bool m_playLoop;
MsgConfigureAMMod(Real rfBandwidth, float modFactor, float toneFrequency, int volumeFactor, bool audioMute, bool playLoop) : MsgConfigureAMMod(Real rfBandwidth, float modFactor, float toneFrequency, float volumeFactor, bool audioMute, bool playLoop) :
Message(), Message(),
m_rfBandwidth(rfBandwidth), m_rfBandwidth(rfBandwidth),
m_modFactor(modFactor), m_modFactor(modFactor),
@ -244,7 +254,7 @@ private:
Real m_rfBandwidth; Real m_rfBandwidth;
float m_modFactor; float m_modFactor;
float m_toneFrequency; float m_toneFrequency;
int m_volumeFactor; float m_volumeFactor;
quint32 m_audioSampleRate; quint32 m_audioSampleRate;
bool m_audioMute; bool m_audioMute;
bool m_playLoop; bool m_playLoop;
@ -255,7 +265,7 @@ private:
m_rfBandwidth(-1), m_rfBandwidth(-1),
m_modFactor(0.2f), m_modFactor(0.2f),
m_toneFrequency(100), m_toneFrequency(100),
m_volumeFactor(20), m_volumeFactor(1.0f),
m_audioSampleRate(0), m_audioSampleRate(0),
m_audioMute(false), m_audioMute(false),
m_playLoop(false) m_playLoop(false)
@ -293,9 +303,14 @@ private:
int m_sampleRate; int m_sampleRate;
AMModInputAF m_afInput; AMModInputAF 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);

View File

@ -72,7 +72,7 @@ void AMModGUI::resetToDefaults()
ui->rfBW->setValue(50); ui->rfBW->setValue(50);
ui->modPercent->setValue(20); ui->modPercent->setValue(20);
ui->micVolume->setValue(50); ui->volume->setValue(10);
ui->toneFrequency->setValue(100); ui->toneFrequency->setValue(100);
ui->deltaFrequency->setValue(0); ui->deltaFrequency->setValue(0);
@ -83,11 +83,14 @@ void AMModGUI::resetToDefaults()
QByteArray AMModGUI::serialize() const QByteArray AMModGUI::serialize() const
{ {
SimpleSerializer s(1); SimpleSerializer s(1);
s.writeS32(1, m_channelMarker.getCenterFrequency()); s.writeS32(1, m_channelMarker.getCenterFrequency());
s.writeS32(2, ui->rfBW->value()); s.writeS32(2, ui->rfBW->value());
s.writeS32(3, ui->toneFrequency->value()); s.writeS32(3, ui->toneFrequency->value());
s.writeS32(4, ui->modPercent->value()); s.writeS32(4, ui->modPercent->value());
s.writeU32(5, m_channelMarker.getColor().rgb()); s.writeU32(5, m_channelMarker.getColor().rgb());
s.writeS32(6, ui->volume->value());
return s.final(); return s.final();
} }
@ -124,6 +127,9 @@ bool AMModGUI::deserialize(const QByteArray& data)
m_channelMarker.setColor(u32tmp); m_channelMarker.setColor(u32tmp);
} }
d.readS32(6, &tmp, 10);
ui->volume->setValue(tmp);
blockApplySettings(false); blockApplySettings(false);
m_channelMarker.blockSignals(false); m_channelMarker.blockSignals(false);
@ -210,9 +216,9 @@ void AMModGUI::on_modPercent_valueChanged(int value)
applySettings(); applySettings();
} }
void AMModGUI::on_micVolume_valueChanged(int value) void AMModGUI::on_volume_valueChanged(int value)
{ {
ui->micVolumeText->setText(QString("%1").arg(value)); ui->volumeText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1));
applySettings(); applySettings();
} }
@ -360,6 +366,7 @@ AMModGUI::AMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pare
applySettings(); applySettings();
connect(m_amMod->getOutputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages())); connect(m_amMod->getOutputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages()));
connect(m_amMod, SIGNAL(levelChanged(qreal, qreal, int)), ui->volumeMeter, SLOT(levelChanged(qreal, qreal, int)));
} }
AMModGUI::~AMModGUI() AMModGUI::~AMModGUI()
@ -395,7 +402,7 @@ void AMModGUI::applySettings()
ui->rfBW->value() * 100.0, ui->rfBW->value() * 100.0,
ui->modPercent->value() / 100.0f, ui->modPercent->value() / 100.0f,
ui->toneFrequency->value() * 10.0f, ui->toneFrequency->value() * 10.0f,
ui->micVolume->value(), ui->volume->value() / 10.0f ,
ui->audioMute->isChecked(), ui->audioMute->isChecked(),
ui->playLoop->isChecked()); ui->playLoop->isChecked());
} }

View File

@ -62,7 +62,7 @@ private slots:
void on_deltaMinus_toggled(bool minus); void on_deltaMinus_toggled(bool minus);
void on_rfBW_valueChanged(int value); void on_rfBW_valueChanged(int value);
void on_modPercent_valueChanged(int value); void on_modPercent_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_toneFrequency_valueChanged(int value); void on_toneFrequency_valueChanged(int value);

View File

@ -50,7 +50,16 @@
<property name="spacing"> <property name="spacing">
<number>3</number> <number>3</number>
</property> </property>
<property name="margin"> <property name="leftMargin">
<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>
@ -287,6 +296,81 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="Line" name="line_4">
<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 gain</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 gain value</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>
<property name="toolTip">
<string>Volume meter. White line is 100% and shoul not be exceeded (red zone)</string>
</property>
</widget>
</item>
</layout>
</item>
<item> <item>
<layout class="QHBoxLayout" name="recordFileSelectLayout"> <layout class="QHBoxLayout" name="recordFileSelectLayout">
<item> <item>
@ -371,47 +455,6 @@
</property> </property>
</widget> </widget>
</item> </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>50</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="micVolumeText">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Audio input volume level</string>
</property>
<property name="text">
<string>50</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item> <item>
<spacer name="horizontalSpacer_3"> <spacer name="horizontalSpacer_3">
<property name="orientation"> <property name="orientation">
@ -427,6 +470,13 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item> <item>
<layout class="QHBoxLayout" name="fileNameLayout"> <layout class="QHBoxLayout" name="fileNameLayout">
<item> <item>
@ -611,29 +661,15 @@
<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"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
</resources> </resources>
<connections/> <connections/>
</ui> </ui>

View File

@ -694,26 +694,6 @@
</customwidgets> </customwidgets>
<resources> <resources>
<include location="../../../sdrbase/resources/res.qrc"/> <include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
<include location="../../../sdrbase/resources/res.qrc"/>
</resources> </resources>
<connections/> <connections/>
</ui> </ui>