mirror of
https://github.com/f4exb/sdrangel.git
synced 2026-06-05 15:34:57 -04:00
FreeDV modulator: implemented audio input with possible resampling
This commit is contained in:
@@ -182,7 +182,7 @@ void FreeDVMod::pull(Sample& sample)
|
||||
|
||||
void FreeDVMod::pullAudio(int nbSamples)
|
||||
{
|
||||
unsigned int nbSamplesAudio = nbSamples * ((Real) m_audioSampleRate / (Real) m_basebandSampleRate);
|
||||
unsigned int nbSamplesAudio = nbSamples * ((Real) m_audioSampleRate / (Real) m_modemSampleRate);
|
||||
|
||||
if (nbSamplesAudio > m_audioBuffer.size())
|
||||
{
|
||||
@@ -196,7 +196,9 @@ void FreeDVMod::pullAudio(int nbSamples)
|
||||
void FreeDVMod::modulateSample()
|
||||
{
|
||||
pullAF(m_modSample);
|
||||
calculateLevel(m_modSample);
|
||||
if (!m_settings.m_gaugeInputElseModem) {
|
||||
calculateLevel(m_modSample);
|
||||
}
|
||||
m_audioBufferFill++;
|
||||
}
|
||||
|
||||
@@ -221,8 +223,12 @@ void FreeDVMod::pullAF(Complex& sample)
|
||||
switch (m_settings.m_modAFInput)
|
||||
{
|
||||
case FreeDVModSettings::FreeDVModInputTone:
|
||||
for (int i = 0; i < m_nSpeechSamples; i++) {
|
||||
for (int i = 0; i < m_nSpeechSamples; i++)
|
||||
{
|
||||
m_speechIn[i] = m_toneNco.next() * 32768.0f * m_settings.m_volumeFactor;
|
||||
if (m_settings.m_gaugeInputElseModem) {
|
||||
calculateLevel(m_speechIn[i]);
|
||||
}
|
||||
}
|
||||
freedv_tx(m_freeDV, m_modOut, m_speechIn);
|
||||
break;
|
||||
@@ -251,10 +257,16 @@ void FreeDVMod::pullAF(Complex& sample)
|
||||
|
||||
m_ifstream.read(reinterpret_cast<char*>(m_speechIn), sizeof(int16_t) * m_nSpeechSamples);
|
||||
|
||||
if (m_settings.m_volumeFactor != 1.0)
|
||||
if ((m_settings.m_volumeFactor != 1.0) || m_settings.m_gaugeInputElseModem)
|
||||
{
|
||||
for (int i = 0; i < m_nSpeechSamples; i++) {
|
||||
m_speechIn[i] *= m_settings.m_volumeFactor;
|
||||
for (int i = 0; i < m_nSpeechSamples; i++)
|
||||
{
|
||||
if (m_settings.m_volumeFactor != 1.0) {
|
||||
m_speechIn[i] *= m_settings.m_volumeFactor;
|
||||
}
|
||||
if (m_settings.m_gaugeInputElseModem) {
|
||||
calculateLevel(m_speechIn[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,8 +280,20 @@ void FreeDVMod::pullAF(Complex& sample)
|
||||
}
|
||||
break;
|
||||
case FreeDVModSettings::FreeDVModInputAudio:
|
||||
for (int i = 0; i < m_nSpeechSamples; i++) {
|
||||
m_speechIn[i] = (m_audioBuffer[m_audioBufferFill].l + m_audioBuffer[m_audioBufferFill].r) * (m_settings.m_volumeFactor / 2);
|
||||
for (int i = 0; i < m_nSpeechSamples; i++)
|
||||
{
|
||||
qint16 audioSample = (m_audioBuffer[m_audioBufferFill].l + m_audioBuffer[m_audioBufferFill].r) * (m_settings.m_volumeFactor / 2.0f);
|
||||
m_audioBufferFill++;
|
||||
|
||||
while (!m_audioResampler.downSample(audioSample, m_speechIn[i]))
|
||||
{
|
||||
audioSample = (m_audioBuffer[m_audioBufferFill].l + m_audioBuffer[m_audioBufferFill].r) * (m_settings.m_volumeFactor / 2.0f);
|
||||
m_audioBufferFill++;
|
||||
}
|
||||
|
||||
if (m_settings.m_gaugeInputElseModem) {
|
||||
calculateLevel(m_speechIn[i]);
|
||||
}
|
||||
}
|
||||
freedv_tx(m_freeDV, m_modOut, m_speechIn);
|
||||
break;
|
||||
@@ -295,6 +319,10 @@ void FreeDVMod::pullAF(Complex& sample)
|
||||
m_toneNco.setPhase(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_settings.m_gaugeInputElseModem) {
|
||||
calculateLevel(m_speechIn[i]);
|
||||
}
|
||||
}
|
||||
freedv_tx(m_freeDV, m_modOut, m_speechIn);
|
||||
break;
|
||||
@@ -367,6 +395,27 @@ void FreeDVMod::calculateLevel(Complex& sample)
|
||||
}
|
||||
}
|
||||
|
||||
void FreeDVMod::calculateLevel(qint16& sample)
|
||||
{
|
||||
Real t = sample / SDR_TX_SCALEF;
|
||||
|
||||
if (m_levelCalcCount < m_levelNbSamples)
|
||||
{
|
||||
m_peakLevel = std::max(std::fabs(m_peakLevel), t);
|
||||
m_levelSum += t * t;
|
||||
m_levelCalcCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
qreal rmsLevel = sqrt(m_levelSum / m_levelNbSamples);
|
||||
//qDebug("FreeDVMod::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 FreeDVMod::start()
|
||||
{
|
||||
qDebug() << "FreeDVMod::start: m_outputSampleRate: " << m_outputSampleRate
|
||||
@@ -522,6 +571,12 @@ void FreeDVMod::applyAudioSampleRate(int sampleRate)
|
||||
{
|
||||
qDebug("FreeDVMod::applyAudioSampleRate: %d", sampleRate);
|
||||
// TODO: put up simple IIR interpolator when sampleRate < m_modemSampleRate
|
||||
|
||||
m_settingsMutex.lock();
|
||||
m_audioResampler.setDecimation(sampleRate / m_inputSampleRate);
|
||||
m_audioResampler.setAudioFilters(sampleRate, m_inputSampleRate, 250, 3300);
|
||||
m_settingsMutex.unlock();
|
||||
|
||||
m_audioSampleRate = sampleRate;
|
||||
}
|
||||
|
||||
@@ -686,6 +741,9 @@ void FreeDVMod::applySettings(const FreeDVModSettings& settings, bool force)
|
||||
if ((settings.m_playLoop != m_settings.m_playLoop) || force) {
|
||||
reverseAPIKeys.append("playLoop");
|
||||
}
|
||||
if ((settings.m_playLoop != m_settings.m_gaugeInputElseModem) || force) {
|
||||
reverseAPIKeys.append("gaugeInputElseModem");
|
||||
}
|
||||
if ((settings.m_rgbColor != m_settings.m_rgbColor) || force) {
|
||||
reverseAPIKeys.append("rgbColor");
|
||||
}
|
||||
@@ -801,6 +859,9 @@ int FreeDVMod::webapiSettingsPutPatch(
|
||||
if (channelSettingsKeys.contains("playLoop")) {
|
||||
settings.m_playLoop = response.getFreeDvModSettings()->getPlayLoop() != 0;
|
||||
}
|
||||
if (channelSettingsKeys.contains("gaugeInputElseModem")) {
|
||||
settings.m_gaugeInputElseModem = response.getFreeDvModSettings()->getGaugeInputElseModem() != 0;
|
||||
}
|
||||
if (channelSettingsKeys.contains("rgbColor")) {
|
||||
settings.m_rgbColor = response.getFreeDvModSettings()->getRgbColor();
|
||||
}
|
||||
@@ -907,6 +968,7 @@ void FreeDVMod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& res
|
||||
response.getFreeDvModSettings()->setAudioMute(settings.m_audioMute ? 1 : 0);
|
||||
response.getFreeDvModSettings()->setPlayLoop(settings.m_playLoop ? 1 : 0);
|
||||
response.getFreeDvModSettings()->setRgbColor(settings.m_rgbColor);
|
||||
response.getFreeDvModSettings()->setGaugeInputElseModem(settings.m_gaugeInputElseModem ? 1 : 0);
|
||||
|
||||
if (response.getFreeDvModSettings()->getTitle()) {
|
||||
*response.getFreeDvModSettings()->getTitle() = settings.m_title;
|
||||
@@ -940,17 +1002,17 @@ void FreeDVMod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& res
|
||||
|
||||
apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm);
|
||||
|
||||
response.getAmModSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
|
||||
response.getFreeDvModSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
|
||||
|
||||
if (response.getAmModSettings()->getReverseApiAddress()) {
|
||||
*response.getAmModSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
|
||||
if (response.getFreeDvModSettings()->getReverseApiAddress()) {
|
||||
*response.getFreeDvModSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
|
||||
} else {
|
||||
response.getAmModSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
|
||||
response.getFreeDvModSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
|
||||
}
|
||||
|
||||
response.getAmModSettings()->setReverseApiPort(settings.m_reverseAPIPort);
|
||||
response.getAmModSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
|
||||
response.getAmModSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
|
||||
response.getFreeDvModSettings()->setReverseApiPort(settings.m_reverseAPIPort);
|
||||
response.getFreeDvModSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
|
||||
response.getFreeDvModSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
|
||||
}
|
||||
|
||||
void FreeDVMod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response)
|
||||
@@ -988,6 +1050,9 @@ void FreeDVMod::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, c
|
||||
if (channelSettingsKeys.contains("playLoop") || force) {
|
||||
swgFreeDVModSettings->setPlayLoop(settings.m_playLoop ? 1 : 0);
|
||||
}
|
||||
if (channelSettingsKeys.contains("gaugeInputElseModem") || force) {
|
||||
swgFreeDVModSettings->setPlayLoop(settings.m_gaugeInputElseModem ? 1 : 0);
|
||||
}
|
||||
if (channelSettingsKeys.contains("rgbColor") || force) {
|
||||
swgFreeDVModSettings->setRgbColor(settings.m_rgbColor);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "dsp/fftfilt.h"
|
||||
#include "dsp/cwkeyer.h"
|
||||
#include "audio/audiofifo.h"
|
||||
#include "audio/audioresampler.h"
|
||||
#include "util/message.h"
|
||||
|
||||
#include "freedvmodsettings.h"
|
||||
@@ -325,6 +326,7 @@ private:
|
||||
int16_t *m_speechIn;
|
||||
int16_t *m_modOut;
|
||||
float m_scaleFactor; //!< divide by this amount to scale from int16 to float in [-1.0, 1.0] interval
|
||||
AudioResampler m_audioResampler;
|
||||
|
||||
static const int m_levelNbSamples;
|
||||
|
||||
@@ -334,6 +336,7 @@ private:
|
||||
void applyFreeDVMode(FreeDVModSettings::FreeDVMode mode);
|
||||
void pullAF(Complex& sample);
|
||||
void calculateLevel(Complex& sample);
|
||||
void calculateLevel(qint16& sample);
|
||||
void modulateSample();
|
||||
void openFileStream();
|
||||
void seekFileStream(int seekPercentage);
|
||||
|
||||
@@ -182,6 +182,12 @@ void FreeDVModGUI::on_spanLog2_valueChanged(int value)
|
||||
applyBandwidths(5 - value);
|
||||
}
|
||||
|
||||
void FreeDVModGUI::on_gaugeInput_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_gaugeInputElseModem = checked;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void FreeDVModGUI::on_toneFrequency_valueChanged(int value)
|
||||
{
|
||||
ui->toneFrequencyText->setText(QString("%1k").arg(value / 100.0, 0, 'f', 2));
|
||||
@@ -463,6 +469,7 @@ void FreeDVModGUI::displaySettings()
|
||||
ui->spanLog2->blockSignals(true);
|
||||
|
||||
ui->spanLog2->setValue(5 - m_settings.m_spanLog2);
|
||||
ui->gaugeInput->setChecked(m_settings.m_gaugeInputElseModem);
|
||||
|
||||
QString s = QString::number(m_freeDVMod->getHiCutoff()/1000.0, 'f', 1);
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ private slots:
|
||||
void handleSourceMessages();
|
||||
void on_deltaFrequency_changed(qint64 value);
|
||||
void on_spanLog2_valueChanged(int value);
|
||||
void on_gaugeInput_toggled(bool checked);
|
||||
void on_volume_valueChanged(int value);
|
||||
void on_audioMute_toggled(bool checked);
|
||||
void on_freeDVMode_currentIndexChanged(int index);
|
||||
|
||||
@@ -167,6 +167,16 @@
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="volumeLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="gaugeInput">
|
||||
<property name="toolTip">
|
||||
<string>Check to see input level else shows modem level</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>In</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="volLabel">
|
||||
<property name="text">
|
||||
|
||||
@@ -42,6 +42,7 @@ void FreeDVModSettings::resetToDefaults()
|
||||
m_modAFInput = FreeDVModInputAF::FreeDVModInputNone;
|
||||
m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName;
|
||||
m_freeDVMode = FreeDVMode::FreeDVMode2400A;
|
||||
m_gaugeInputElseModem = false;
|
||||
m_useReverseAPI = false;
|
||||
m_reverseAPIAddress = "127.0.0.1";
|
||||
m_reverseAPIPort = 8888;
|
||||
@@ -66,6 +67,7 @@ QByteArray FreeDVModSettings::serialize() const
|
||||
s.writeBlob(6, m_cwKeyerGUI->serialize());
|
||||
}
|
||||
|
||||
s.writeBool(7, m_gaugeInputElseModem);
|
||||
s.writeS32(8, m_spanLog2);
|
||||
s.writeS32(10, (int) m_freeDVMode);
|
||||
|
||||
@@ -120,6 +122,7 @@ bool FreeDVModSettings::deserialize(const QByteArray& data)
|
||||
m_cwKeyerGUI->deserialize(bytetmp);
|
||||
}
|
||||
|
||||
d.readBool(7, &m_gaugeInputElseModem, false);
|
||||
d.readS32(8, &m_spanLog2, 3);
|
||||
|
||||
d.readS32(10, &tmp, 0);
|
||||
|
||||
@@ -54,6 +54,7 @@ struct FreeDVModSettings
|
||||
FreeDVModInputAF m_modAFInput;
|
||||
QString m_audioDeviceName;
|
||||
FreeDVMode m_freeDVMode;
|
||||
bool m_gaugeInputElseModem; //!< Volume gauge shows speech input level else modem level
|
||||
|
||||
bool m_useReverseAPI;
|
||||
QString m_reverseAPIAddress;
|
||||
|
||||
@@ -1199,17 +1199,17 @@ void SSBMod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& respon
|
||||
|
||||
apiCwKeyerSettings->setWpm(cwKeyerSettings.m_wpm);
|
||||
|
||||
response.getAmModSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
|
||||
response.getSsbModSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
|
||||
|
||||
if (response.getAmModSettings()->getReverseApiAddress()) {
|
||||
*response.getAmModSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
|
||||
if (response.getSsbModSettings()->getReverseApiAddress()) {
|
||||
*response.getSsbModSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
|
||||
} else {
|
||||
response.getAmModSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
|
||||
response.getSsbModSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
|
||||
}
|
||||
|
||||
response.getAmModSettings()->setReverseApiPort(settings.m_reverseAPIPort);
|
||||
response.getAmModSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
|
||||
response.getAmModSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
|
||||
response.getSsbModSettings()->setReverseApiPort(settings.m_reverseAPIPort);
|
||||
response.getSsbModSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
|
||||
response.getSsbModSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
|
||||
}
|
||||
|
||||
void SSBMod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response)
|
||||
|
||||
Reference in New Issue
Block a user