From 674a4ccd272298f57627cb91d07a487993a0a2e7 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 2 Aug 2020 10:11:41 +0200 Subject: [PATCH] Modulator plugins with configurable audio: fixed audio sample rate handling --- plugins/channeltx/modam/ammod.cpp | 12 ++- plugins/channeltx/modam/ammod.h | 2 + plugins/channeltx/modam/ammodbaseband.cpp | 15 ++- plugins/channeltx/modam/ammodbaseband.h | 4 +- plugins/channeltx/modam/ammodgui.cpp | 28 +++++ plugins/channeltx/modam/ammodgui.h | 2 + plugins/channeltx/modam/ammodsource.cpp | 16 ++- plugins/channeltx/modam/ammodsource.h | 12 +-- plugins/channeltx/modfreedv/freedvmod.cpp | 2 +- plugins/channeltx/modfreedv/freedvmod.h | 2 +- .../channeltx/modfreedv/freedvmodbaseband.cpp | 3 +- .../channeltx/modfreedv/freedvmodbaseband.h | 2 +- plugins/channeltx/modfreedv/freedvmodgui.cpp | 14 +++ plugins/channeltx/modfreedv/freedvmodgui.h | 1 + .../channeltx/modfreedv/freedvmodsource.cpp | 6 ++ plugins/channeltx/modfreedv/freedvmodsource.h | 4 +- plugins/channeltx/modnfm/nfmmod.cpp | 12 ++- plugins/channeltx/modnfm/nfmmod.h | 2 + plugins/channeltx/modnfm/nfmmodbaseband.cpp | 16 ++- plugins/channeltx/modnfm/nfmmodbaseband.h | 4 +- plugins/channeltx/modnfm/nfmmodgui.cpp | 28 +++++ plugins/channeltx/modnfm/nfmmodgui.h | 2 + plugins/channeltx/modnfm/nfmmodsource.cpp | 20 +++- plugins/channeltx/modnfm/nfmmodsource.h | 12 +-- plugins/channeltx/modssb/ssbmod.cpp | 7 +- plugins/channeltx/modssb/ssbmod.h | 3 +- plugins/channeltx/modssb/ssbmodbaseband.cpp | 10 +- plugins/channeltx/modssb/ssbmodbaseband.h | 4 +- plugins/channeltx/modssb/ssbmodgui.cpp | 28 +++++ plugins/channeltx/modssb/ssbmodgui.h | 2 + plugins/channeltx/modssb/ssbmodsource.cpp | 20 +++- plugins/channeltx/modssb/ssbmodsource.h | 12 +-- plugins/channeltx/modwfm/wfmmod.cpp | 12 ++- plugins/channeltx/modwfm/wfmmod.h | 2 + plugins/channeltx/modwfm/wfmmodbaseband.cpp | 8 +- plugins/channeltx/modwfm/wfmmodbaseband.h | 3 +- plugins/channeltx/modwfm/wfmmodgui.cpp | 62 +++++++++++ plugins/channeltx/modwfm/wfmmodgui.h | 6 ++ plugins/channeltx/modwfm/wfmmodgui.ui | 89 ++++++++++++++-- plugins/channeltx/modwfm/wfmmodsettings.cpp | 9 ++ plugins/channeltx/modwfm/wfmmodsettings.h | 3 + plugins/channeltx/modwfm/wfmmodsource.cpp | 100 ++++++++++++++++-- plugins/channeltx/modwfm/wfmmodsource.h | 22 +++- 43 files changed, 542 insertions(+), 81 deletions(-) diff --git a/plugins/channeltx/modam/ammod.cpp b/plugins/channeltx/modam/ammod.cpp index a812851c6..9684a16de 100644 --- a/plugins/channeltx/modam/ammod.cpp +++ b/plugins/channeltx/modam/ammod.cpp @@ -642,4 +642,14 @@ CWKeyer *AMMod::getCWKeyer() void AMMod::setLevelMeter(QObject *levelMeter) { connect(m_basebandSource, SIGNAL(levelChanged(qreal, qreal, int)), levelMeter, SLOT(levelChanged(qreal, qreal, int))); -} \ No newline at end of file +} + +int AMMod::getAudioSampleRate() const +{ + return m_basebandSource->getAudioSampleRate(); +} + +int AMMod::getFeedbackAudioSampleRate() const +{ + return m_basebandSource->getFeedbackAudioSampleRate(); +} diff --git a/plugins/channeltx/modam/ammod.h b/plugins/channeltx/modam/ammod.h index ddae3e11b..2e5a62bd3 100644 --- a/plugins/channeltx/modam/ammod.h +++ b/plugins/channeltx/modam/ammod.h @@ -225,6 +225,8 @@ public: double getMagSq() const; CWKeyer *getCWKeyer(); void setLevelMeter(QObject *levelMeter); + int getAudioSampleRate() const; + int getFeedbackAudioSampleRate() const; static const QString m_channelIdURI; static const QString m_channelId; diff --git a/plugins/channeltx/modam/ammodbaseband.cpp b/plugins/channeltx/modam/ammodbaseband.cpp index 48afd1bef..82c1e5052 100644 --- a/plugins/channeltx/modam/ammodbaseband.cpp +++ b/plugins/channeltx/modam/ammodbaseband.cpp @@ -159,6 +159,7 @@ bool AMModBaseband::handleMessage(const Message& cmd) m_sampleFifo.resize(SampleSourceFifo::getSizePolicy(notif.getSampleRate())); m_channelizer->setBasebandSampleRate(notif.getSampleRate()); m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_source.applyAudioSampleRate(m_source.getAudioSampleRate()); // reapply in case of channel sample rate change return true; } @@ -183,18 +184,23 @@ void AMModBaseband::applySettings(const AMModSettings& settings, bool force) { if ((m_settings.m_inputFrequencyOffset != settings.m_inputFrequencyOffset) || force) { - m_channelizer->setChannelization(48000, settings.m_inputFrequencyOffset); + m_channelizer->setChannelization(m_source.getAudioSampleRate(), settings.m_inputFrequencyOffset); m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_source.applyAudioSampleRate(m_source.getAudioSampleRate()); // reapply in case of channel sample rate change } if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName); + audioDeviceManager->removeAudioSource(getAudioFifo()); audioDeviceManager->addAudioSource(getAudioFifo(), getInputMessageQueue(), audioDeviceIndex); - uint32_t audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex); + int audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex); - if (getAudioSampleRate() != audioSampleRate) { + if (getAudioSampleRate() != audioSampleRate) + { + m_channelizer->setChannelization(audioSampleRate, settings.m_inputFrequencyOffset); + m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); m_source.applyAudioSampleRate(audioSampleRate); } } @@ -203,8 +209,9 @@ void AMModBaseband::applySettings(const AMModSettings& settings, bool force) { AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_feedbackAudioDeviceName); + audioDeviceManager->removeAudioSink(getFeedbackAudioFifo()); audioDeviceManager->addAudioSink(getFeedbackAudioFifo(), getInputMessageQueue(), audioDeviceIndex); - uint32_t audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex); + int audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex); if (getFeedbackAudioSampleRate() != audioSampleRate) { m_source.applyFeedbackAudioSampleRate(audioSampleRate); diff --git a/plugins/channeltx/modam/ammodbaseband.h b/plugins/channeltx/modam/ammodbaseband.h index 78fffc6f7..5094ac10b 100644 --- a/plugins/channeltx/modam/ammodbaseband.h +++ b/plugins/channeltx/modam/ammodbaseband.h @@ -63,8 +63,8 @@ public: MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication CWKeyer& getCWKeyer() { return m_source.getCWKeyer(); } double getMagSq() const { return m_source.getMagSq(); } - unsigned int getAudioSampleRate() const { return m_source.getAudioSampleRate(); } - unsigned int getFeedbackAudioSampleRate() const { return m_source.getFeedbackAudioSampleRate(); } + int getAudioSampleRate() const { return m_source.getAudioSampleRate(); } + int getFeedbackAudioSampleRate() const { return m_source.getFeedbackAudioSampleRate(); } int getChannelSampleRate() const; void setInputFileStream(std::ifstream *ifstream) { m_source.setInputFileStream(ifstream); } AudioFifo *getAudioFifo() { return m_source.getAudioFifo(); } diff --git a/plugins/channeltx/modam/ammodgui.cpp b/plugins/channeltx/modam/ammodgui.cpp index b2b936076..5da465fc7 100644 --- a/plugins/channeltx/modam/ammodgui.cpp +++ b/plugins/channeltx/modam/ammodgui.cpp @@ -346,6 +346,8 @@ AMModGUI::AMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampl m_recordLength(0), m_recordSampleRate(48000), m_samplesCount(0), + m_audioSampleRate(-1), + m_feedbackAudioSampleRate(-1), m_tickCount(0), m_enableNavTime(false) { @@ -526,6 +528,32 @@ void AMModGUI::tick() m_channelPowerDbAvg(powDb); ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.asDouble(), 0, 'f', 1)); + int audioSampleRate = m_amMod->getAudioSampleRate(); + + if (audioSampleRate != m_audioSampleRate) + { + if (audioSampleRate < 0) { + ui->mic->setColor(QColor("red")); + } else { + ui->mic->resetColor(); + } + + m_audioSampleRate = audioSampleRate; + } + + int feedbackAudioSampleRate = m_amMod->getFeedbackAudioSampleRate(); + + if (feedbackAudioSampleRate != m_feedbackAudioSampleRate) + { + if (feedbackAudioSampleRate < 0) { + ui->feedbackEnable->setStyleSheet("QToolButton { background-color : red; }"); + } else { + ui->feedbackEnable->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + } + + m_feedbackAudioSampleRate = feedbackAudioSampleRate; + } + if (((++m_tickCount & 0xf) == 0) && (m_settings.m_modAFInput == AMModSettings::AMModInputFile)) { AMMod::MsgConfigureFileSourceStreamTiming* message = AMMod::MsgConfigureFileSourceStreamTiming::create(); diff --git a/plugins/channeltx/modam/ammodgui.h b/plugins/channeltx/modam/ammodgui.h index 2c6dbcb79..217ba3e5a 100644 --- a/plugins/channeltx/modam/ammodgui.h +++ b/plugins/channeltx/modam/ammodgui.h @@ -73,6 +73,8 @@ private: quint32 m_recordLength; int m_recordSampleRate; int m_samplesCount; + int m_audioSampleRate; + int m_feedbackAudioSampleRate; std::size_t m_tickCount; bool m_enableNavTime; MessageQueue m_inputMessageQueue; diff --git a/plugins/channeltx/modam/ammodsource.cpp b/plugins/channeltx/modam/ammodsource.cpp index 7d0610887..f3518c65d 100644 --- a/plugins/channeltx/modam/ammodsource.cpp +++ b/plugins/channeltx/modam/ammodsource.cpp @@ -262,8 +262,14 @@ void AMModSource::calculateLevel(Real& sample) } } -void AMModSource::applyAudioSampleRate(unsigned int sampleRate) +void AMModSource::applyAudioSampleRate(int sampleRate) { + if (sampleRate < 0) + { + qWarning("AMModSource::applyAudioSampleRate: invalid sample rate %d", sampleRate); + return; + } + qDebug("AMModSource::applyAudioSampleRate: %d", sampleRate); m_interpolatorDistanceRemain = 0; @@ -277,8 +283,14 @@ void AMModSource::applyAudioSampleRate(unsigned int sampleRate) applyFeedbackAudioSampleRate(m_feedbackAudioSampleRate); } -void AMModSource::applyFeedbackAudioSampleRate(unsigned int sampleRate) +void AMModSource::applyFeedbackAudioSampleRate(int sampleRate) { + if (sampleRate < 0) + { + qWarning("AMModSource::applyFeedbackAudioSampleRate: invalid sample rate %d", sampleRate); + return; + } + qDebug("AMModSource::applyFeedbackAudioSampleRate: %u", sampleRate); m_feedbackInterpolatorDistanceRemain = 0; diff --git a/plugins/channeltx/modam/ammodsource.h b/plugins/channeltx/modam/ammodsource.h index ca2f72106..63d1ddd13 100644 --- a/plugins/channeltx/modam/ammodsource.h +++ b/plugins/channeltx/modam/ammodsource.h @@ -46,10 +46,10 @@ public: void setInputFileStream(std::ifstream *ifstream) { m_ifstream = ifstream; } AudioFifo *getAudioFifo() { return &m_audioFifo; } AudioFifo *getFeedbackAudioFifo() { return &m_feedbackAudioFifo; } - void applyAudioSampleRate(unsigned int sampleRate); - void applyFeedbackAudioSampleRate(unsigned int sampleRate); - unsigned int getAudioSampleRate() const { return m_audioSampleRate; } - unsigned int getFeedbackAudioSampleRate() const { return m_feedbackAudioSampleRate; } + void applyAudioSampleRate(int sampleRate); + void applyFeedbackAudioSampleRate(int sampleRate); + int getAudioSampleRate() const { return m_audioSampleRate; } + int getFeedbackAudioSampleRate() const { return m_feedbackAudioSampleRate; } CWKeyer& getCWKeyer() { return m_cwKeyer; } double getMagSq() const { return m_magsq; } void getLevels(qreal& rmsLevel, qreal& peakLevel, int& numSamples) const @@ -83,12 +83,12 @@ private: double m_magsq; MovingAverageUtil m_movingAverage; - quint32 m_audioSampleRate; + int m_audioSampleRate; AudioVector m_audioBuffer; uint m_audioBufferFill; AudioFifo m_audioFifo; - quint32 m_feedbackAudioSampleRate; + int m_feedbackAudioSampleRate; AudioVector m_feedbackAudioBuffer; uint m_feedbackAudioBufferFill; AudioFifo m_feedbackAudioFifo; diff --git a/plugins/channeltx/modfreedv/freedvmod.cpp b/plugins/channeltx/modfreedv/freedvmod.cpp index 6ee04c7ba..06f3c4944 100644 --- a/plugins/channeltx/modfreedv/freedvmod.cpp +++ b/plugins/channeltx/modfreedv/freedvmod.cpp @@ -624,7 +624,7 @@ void FreeDVMod::networkManagerFinished(QNetworkReply *reply) reply->deleteLater(); } -uint32_t FreeDVMod::getAudioSampleRate() const +int FreeDVMod::getAudioSampleRate() const { return m_basebandSource->getAudioSampleRate(); } diff --git a/plugins/channeltx/modfreedv/freedvmod.h b/plugins/channeltx/modfreedv/freedvmod.h index d41b800d5..c690329dd 100644 --- a/plugins/channeltx/modfreedv/freedvmod.h +++ b/plugins/channeltx/modfreedv/freedvmod.h @@ -226,7 +226,7 @@ public: SWGSDRangel::SWGChannelSettings& response); SpectrumVis *getSpectrumVis() { return &m_spectrumVis; } - uint32_t getAudioSampleRate() const; + int getAudioSampleRate() const; uint32_t getModemSampleRate() const; Real getLowCutoff() const; Real getHiCutoff() const; diff --git a/plugins/channeltx/modfreedv/freedvmodbaseband.cpp b/plugins/channeltx/modfreedv/freedvmodbaseband.cpp index 9135a13da..0eb676b57 100644 --- a/plugins/channeltx/modfreedv/freedvmodbaseband.cpp +++ b/plugins/channeltx/modfreedv/freedvmodbaseband.cpp @@ -194,8 +194,9 @@ void FreeDVModBaseband::applySettings(const FreeDVModSettings& settings, bool fo { AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName); + audioDeviceManager->removeAudioSource(getAudioFifo()); audioDeviceManager->addAudioSource(getAudioFifo(), getInputMessageQueue(), audioDeviceIndex); - uint32_t audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex); + int audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex); if (getAudioSampleRate() != audioSampleRate) { m_source.applyAudioSampleRate(audioSampleRate); diff --git a/plugins/channeltx/modfreedv/freedvmodbaseband.h b/plugins/channeltx/modfreedv/freedvmodbaseband.h index 43d905bd8..088e85602 100644 --- a/plugins/channeltx/modfreedv/freedvmodbaseband.h +++ b/plugins/channeltx/modfreedv/freedvmodbaseband.h @@ -67,7 +67,7 @@ public: void setInputFileStream(std::ifstream *ifstream) { m_source.setInputFileStream(ifstream); } AudioFifo *getAudioFifo() { return m_source.getAudioFifo(); } void setSpectrumSampleSink(BasebandSampleSink* sampleSink) { m_source.setSpectrumSink(sampleSink); } - unsigned int getAudioSampleRate() const { return m_source.getAudioSampleRate(); } + int getAudioSampleRate() const { return m_source.getAudioSampleRate(); } unsigned int getModemSampleRate() const { return m_source.getModemSampleRate(); } Real getLowCutoff() const { return m_source.getLowCutoff(); } Real getHiCutoff() const { return m_source.getHiCutoff(); } diff --git a/plugins/channeltx/modfreedv/freedvmodgui.cpp b/plugins/channeltx/modfreedv/freedvmodgui.cpp index 36d8b972e..f747d0bbe 100644 --- a/plugins/channeltx/modfreedv/freedvmodgui.cpp +++ b/plugins/channeltx/modfreedv/freedvmodgui.cpp @@ -361,6 +361,7 @@ FreeDVModGUI::FreeDVModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb m_recordLength(0), m_recordSampleRate(48000), m_samplesCount(0), + m_audioSampleRate(-1), m_tickCount(0), m_enableNavTime(false) { @@ -559,6 +560,19 @@ void FreeDVModGUI::tick() m_channelPowerDbAvg(powDb); ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.asDouble(), 0, 'f', 1)); + int audioSampleRate = m_freeDVMod->getAudioSampleRate(); + + if (audioSampleRate != m_audioSampleRate) + { + if (audioSampleRate < 0) { + ui->mic->setColor(QColor("red")); + } else { + ui->mic->resetColor(); + } + + m_audioSampleRate = audioSampleRate; + } + if (((++m_tickCount & 0xf) == 0) && (m_settings.m_modAFInput == FreeDVModSettings::FreeDVModInputFile)) { FreeDVMod::MsgConfigureFileSourceStreamTiming* message = FreeDVMod::MsgConfigureFileSourceStreamTiming::create(); diff --git a/plugins/channeltx/modfreedv/freedvmodgui.h b/plugins/channeltx/modfreedv/freedvmodgui.h index 63842c2a6..20774464a 100644 --- a/plugins/channeltx/modfreedv/freedvmodgui.h +++ b/plugins/channeltx/modfreedv/freedvmodgui.h @@ -76,6 +76,7 @@ private: quint32 m_recordLength; int m_recordSampleRate; int m_samplesCount; + int m_audioSampleRate; std::size_t m_tickCount; bool m_enableNavTime; MessageQueue m_inputMessageQueue; diff --git a/plugins/channeltx/modfreedv/freedvmodsource.cpp b/plugins/channeltx/modfreedv/freedvmodsource.cpp index a66034478..1f77926d6 100644 --- a/plugins/channeltx/modfreedv/freedvmodsource.cpp +++ b/plugins/channeltx/modfreedv/freedvmodsource.cpp @@ -364,6 +364,12 @@ void FreeDVModSource::calculateLevel(qint16& sample) void FreeDVModSource::applyAudioSampleRate(unsigned int sampleRate) { + if (sampleRate < 0) + { + qWarning("FreeDVModSource::applyAudioSampleRate: invalid sample rate %d", sampleRate); + return; + } + qDebug("FreeDVModSource::applyAudioSampleRate: %d", sampleRate); // TODO: put up simple IIR interpolator when sampleRate < m_modemSampleRate diff --git a/plugins/channeltx/modfreedv/freedvmodsource.h b/plugins/channeltx/modfreedv/freedvmodsource.h index f381180e0..d2a6d27f7 100644 --- a/plugins/channeltx/modfreedv/freedvmodsource.h +++ b/plugins/channeltx/modfreedv/freedvmodsource.h @@ -58,7 +58,7 @@ public: peakLevel = m_peakLevelOut; numSamples = m_levelNbSamples; } - unsigned int getAudioSampleRate() const { return m_audioSampleRate; } + int getAudioSampleRate() const { return m_audioSampleRate; } unsigned int getModemSampleRate() const { return m_modemSampleRate; } Real getLowCutoff() const { return m_lowCutoff; } Real getHiCutoff() const { return m_hiCutoff; } @@ -100,7 +100,7 @@ private: double m_magsq; MovingAverageUtil m_movingAverage; - quint32 m_audioSampleRate; + int m_audioSampleRate; AudioVector m_audioBuffer; uint m_audioBufferFill; AudioFifo m_audioFifo; diff --git a/plugins/channeltx/modnfm/nfmmod.cpp b/plugins/channeltx/modnfm/nfmmod.cpp index 2f7f1b0b0..2047fd222 100644 --- a/plugins/channeltx/modnfm/nfmmod.cpp +++ b/plugins/channeltx/modnfm/nfmmod.cpp @@ -710,4 +710,14 @@ void NFMMod::setLevelMeter(QObject *levelMeter) uint32_t NFMMod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSinkStreams(); -} \ No newline at end of file +} + +int NFMMod::getAudioSampleRate() const +{ + return m_basebandSource->getAudioSampleRate(); +} + +int NFMMod::getFeedbackAudioSampleRate() const +{ + return m_basebandSource->getFeedbackAudioSampleRate(); +} diff --git a/plugins/channeltx/modnfm/nfmmod.h b/plugins/channeltx/modnfm/nfmmod.h index c50479246..6fac80972 100644 --- a/plugins/channeltx/modnfm/nfmmod.h +++ b/plugins/channeltx/modnfm/nfmmod.h @@ -225,6 +225,8 @@ public: CWKeyer *getCWKeyer(); void setLevelMeter(QObject *levelMeter); uint32_t getNumberOfDeviceStreams() const; + int getAudioSampleRate() const; + int getFeedbackAudioSampleRate() const; static const QString m_channelIdURI; static const QString m_channelId; diff --git a/plugins/channeltx/modnfm/nfmmodbaseband.cpp b/plugins/channeltx/modnfm/nfmmodbaseband.cpp index c4c7b999e..d5fd7ffe9 100644 --- a/plugins/channeltx/modnfm/nfmmodbaseband.cpp +++ b/plugins/channeltx/modnfm/nfmmodbaseband.cpp @@ -159,6 +159,7 @@ bool NFMModBaseband::handleMessage(const Message& cmd) m_sampleFifo.resize(SampleSourceFifo::getSizePolicy(notif.getSampleRate())); m_channelizer->setBasebandSampleRate(notif.getSampleRate()); m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_source.applyAudioSampleRate(m_source.getAudioSampleRate()); // reapply in case of channel sample rate change return true; } @@ -182,18 +183,24 @@ void NFMModBaseband::applySettings(const NFMModSettings& settings, bool force) { if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { - m_channelizer->setChannelization(48000, settings.m_inputFrequencyOffset); // Fixed 48000 S/s source sample rate + m_channelizer->setChannelization(m_source.getAudioSampleRate(), settings.m_inputFrequencyOffset); m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_source.applyAudioSampleRate(m_source.getAudioSampleRate()); // reapply in case of channel sample rate change + } if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName); + audioDeviceManager->removeAudioSource(getAudioFifo()); audioDeviceManager->addAudioSource(getAudioFifo(), getInputMessageQueue(), audioDeviceIndex); - uint32_t audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex); + int audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex); - if (getAudioSampleRate() != audioSampleRate) { + if (getAudioSampleRate() != audioSampleRate) + { + m_channelizer->setChannelization(audioSampleRate, settings.m_inputFrequencyOffset); + m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); m_source.applyAudioSampleRate(audioSampleRate); } } @@ -202,8 +209,9 @@ void NFMModBaseband::applySettings(const NFMModSettings& settings, bool force) { AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_feedbackAudioDeviceName); + audioDeviceManager->removeAudioSink(getFeedbackAudioFifo()); audioDeviceManager->addAudioSink(getFeedbackAudioFifo(), getInputMessageQueue(), audioDeviceIndex); - uint32_t audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex); + int audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex); if (getFeedbackAudioSampleRate() != audioSampleRate) { m_source.applyFeedbackAudioSampleRate(audioSampleRate); diff --git a/plugins/channeltx/modnfm/nfmmodbaseband.h b/plugins/channeltx/modnfm/nfmmodbaseband.h index c4382721a..378e10777 100644 --- a/plugins/channeltx/modnfm/nfmmodbaseband.h +++ b/plugins/channeltx/modnfm/nfmmodbaseband.h @@ -63,8 +63,8 @@ public: MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication CWKeyer& getCWKeyer() { return m_source.getCWKeyer(); } double getMagSq() const { return m_source.getMagSq(); } - unsigned int getAudioSampleRate() const { return m_source.getAudioSampleRate(); } - unsigned int getFeedbackAudioSampleRate() const { return m_source.getFeedbackAudioSampleRate(); } + int getAudioSampleRate() const { return m_source.getAudioSampleRate(); } + int getFeedbackAudioSampleRate() const { return m_source.getFeedbackAudioSampleRate(); } int getChannelSampleRate() const; void setInputFileStream(std::ifstream *ifstream) { m_source.setInputFileStream(ifstream); } AudioFifo *getAudioFifo() { return m_source.getAudioFifo(); } diff --git a/plugins/channeltx/modnfm/nfmmodgui.cpp b/plugins/channeltx/modnfm/nfmmodgui.cpp index f08971689..2a0e63af6 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.cpp +++ b/plugins/channeltx/modnfm/nfmmodgui.cpp @@ -364,6 +364,8 @@ NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_recordLength(0), m_recordSampleRate(48000), m_samplesCount(0), + m_audioSampleRate(-1), + m_feedbackAudioSampleRate(-1), m_tickCount(0), m_enableNavTime(false) { @@ -561,6 +563,32 @@ void NFMModGUI::tick() m_channelPowerDbAvg(powDb); ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.asDouble(), 0, 'f', 1)); + int audioSampleRate = m_nfmMod->getAudioSampleRate(); + + if (audioSampleRate != m_audioSampleRate) + { + if (audioSampleRate < 0) { + ui->mic->setColor(QColor("red")); + } else { + ui->mic->resetColor(); + } + + m_audioSampleRate = audioSampleRate; + } + + int feedbackAudioSampleRate = m_nfmMod->getFeedbackAudioSampleRate(); + + if (feedbackAudioSampleRate != m_feedbackAudioSampleRate) + { + if (feedbackAudioSampleRate < 0) { + ui->feedbackEnable->setStyleSheet("QToolButton { background-color : red; }"); + } else { + ui->feedbackEnable->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + } + + m_feedbackAudioSampleRate = feedbackAudioSampleRate; + } + if (((++m_tickCount & 0xf) == 0) && (m_settings.m_modAFInput == NFMModSettings::NFMModInputFile)) { NFMMod::MsgConfigureFileSourceStreamTiming* message = NFMMod::MsgConfigureFileSourceStreamTiming::create(); diff --git a/plugins/channeltx/modnfm/nfmmodgui.h b/plugins/channeltx/modnfm/nfmmodgui.h index 65948033e..fcbbc61f4 100644 --- a/plugins/channeltx/modnfm/nfmmodgui.h +++ b/plugins/channeltx/modnfm/nfmmodgui.h @@ -71,6 +71,8 @@ private: quint32 m_recordLength; int m_recordSampleRate; int m_samplesCount; + int m_audioSampleRate; + int m_feedbackAudioSampleRate; std::size_t m_tickCount; bool m_enableNavTime; NFMModSettings::NFMModInputAF m_modAFInput; diff --git a/plugins/channeltx/modnfm/nfmmodsource.cpp b/plugins/channeltx/modnfm/nfmmodsource.cpp index 29d031487..d3731ffe7 100644 --- a/plugins/channeltx/modnfm/nfmmodsource.cpp +++ b/plugins/channeltx/modnfm/nfmmodsource.cpp @@ -282,9 +282,15 @@ void NFMModSource::calculateLevel(Real& sample) } } -void NFMModSource::applyAudioSampleRate(unsigned int sampleRate) +void NFMModSource::applyAudioSampleRate(int sampleRate) { - qDebug("NFMModSource::applyAudioSampleRate: %u", sampleRate); + if (sampleRate < 0) + { + qWarning("NFMModSource::applyAudioSampleRate: invalid sample rate %d", sampleRate); + return; + } + + qDebug("NFMModSource::applyAudioSampleRate: %d", sampleRate); m_interpolatorDistanceRemain = 0; m_interpolatorConsumed = false; @@ -301,9 +307,15 @@ void NFMModSource::applyAudioSampleRate(unsigned int sampleRate) applyFeedbackAudioSampleRate(m_feedbackAudioSampleRate); } -void NFMModSource::applyFeedbackAudioSampleRate(unsigned int sampleRate) +void NFMModSource::applyFeedbackAudioSampleRate(int sampleRate) { - qDebug("NFMModSource::applyFeedbackAudioSampleRate: %u", sampleRate); + if (sampleRate < 0) + { + qWarning("NFMModSource::applyFeedbackAudioSampleRate: invalid sample rate %d", sampleRate); + return; + } + + qDebug("NFMModSource::applyFeedbackAudioSampleRate: %d", sampleRate); m_feedbackInterpolatorDistanceRemain = 0; m_feedbackInterpolatorConsumed = false; diff --git a/plugins/channeltx/modnfm/nfmmodsource.h b/plugins/channeltx/modnfm/nfmmodsource.h index 929859c11..b03d5cf42 100644 --- a/plugins/channeltx/modnfm/nfmmodsource.h +++ b/plugins/channeltx/modnfm/nfmmodsource.h @@ -49,10 +49,10 @@ public: void setInputFileStream(std::ifstream *ifstream) { m_ifstream = ifstream; } AudioFifo *getAudioFifo() { return &m_audioFifo; } AudioFifo *getFeedbackAudioFifo() { return &m_feedbackAudioFifo; } - void applyAudioSampleRate(unsigned int sampleRate); - void applyFeedbackAudioSampleRate(unsigned int sampleRate); - unsigned int getAudioSampleRate() const { return m_audioSampleRate; } - unsigned int getFeedbackAudioSampleRate() const { return m_feedbackAudioSampleRate; } + void applyAudioSampleRate(int sampleRate); + void applyFeedbackAudioSampleRate(int sampleRate); + int getAudioSampleRate() const { return m_audioSampleRate; } + int getFeedbackAudioSampleRate() const { return m_feedbackAudioSampleRate; } CWKeyer& getCWKeyer() { return m_cwKeyer; } double getMagSq() const { return m_magsq; } void getLevels(qreal& rmsLevel, qreal& peakLevel, int& numSamples) const @@ -92,12 +92,12 @@ private: double m_magsq; MovingAverageUtil m_movingAverage; - quint32 m_audioSampleRate; + int m_audioSampleRate; AudioVector m_audioBuffer; uint m_audioBufferFill; AudioFifo m_audioFifo; - quint32 m_feedbackAudioSampleRate; + int m_feedbackAudioSampleRate; AudioVector m_feedbackAudioBuffer; uint m_feedbackAudioBufferFill; AudioFifo m_feedbackAudioFifo; diff --git a/plugins/channeltx/modssb/ssbmod.cpp b/plugins/channeltx/modssb/ssbmod.cpp index 63d042cd4..8bdcc32a0 100644 --- a/plugins/channeltx/modssb/ssbmod.cpp +++ b/plugins/channeltx/modssb/ssbmod.cpp @@ -694,11 +694,16 @@ void SSBMod::setLevelMeter(QObject *levelMeter) connect(m_basebandSource, SIGNAL(levelChanged(qreal, qreal, int)), levelMeter, SLOT(levelChanged(qreal, qreal, int))); } -unsigned int SSBMod::getAudioSampleRate() const +int SSBMod::getAudioSampleRate() const { return m_basebandSource->getAudioSampleRate(); } +int SSBMod::getFeedbackAudioSampleRate() const +{ + return m_basebandSource->getFeedbackAudioSampleRate(); +} + uint32_t SSBMod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSinkStreams(); diff --git a/plugins/channeltx/modssb/ssbmod.h b/plugins/channeltx/modssb/ssbmod.h index 33c4a3171..a8c1c3ce8 100644 --- a/plugins/channeltx/modssb/ssbmod.h +++ b/plugins/channeltx/modssb/ssbmod.h @@ -227,7 +227,8 @@ public: double getMagSq() const; CWKeyer *getCWKeyer(); void setLevelMeter(QObject *levelMeter); - unsigned int getAudioSampleRate() const; + int getAudioSampleRate() const; + int getFeedbackAudioSampleRate() const; uint32_t getNumberOfDeviceStreams() const; static const QString m_channelIdURI; diff --git a/plugins/channeltx/modssb/ssbmodbaseband.cpp b/plugins/channeltx/modssb/ssbmodbaseband.cpp index 973ae77f7..a5aba2fa4 100644 --- a/plugins/channeltx/modssb/ssbmodbaseband.cpp +++ b/plugins/channeltx/modssb/ssbmodbaseband.cpp @@ -159,6 +159,7 @@ bool SSBModBaseband::handleMessage(const Message& cmd) m_sampleFifo.resize(SampleSourceFifo::getSizePolicy(notif.getSampleRate())); m_channelizer->setBasebandSampleRate(notif.getSampleRate()); m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_source.applyAudioSampleRate(m_source.getAudioSampleRate()); // reapply in case of channel sample rate change return true; } @@ -184,20 +185,22 @@ void SSBModBaseband::applySettings(const SSBModSettings& settings, bool force) { m_channelizer->setChannelization(m_source.getAudioSampleRate(), settings.m_inputFrequencyOffset); m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_source.applyAudioSampleRate(m_source.getAudioSampleRate()); // reapply in case of channel sample rate change } if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName); + audioDeviceManager->removeAudioSource(getAudioFifo()); audioDeviceManager->addAudioSource(getAudioFifo(), getInputMessageQueue(), audioDeviceIndex); - uint32_t audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex); + int audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex); if (getAudioSampleRate() != audioSampleRate) { - m_source.applyAudioSampleRate(audioSampleRate); m_channelizer->setChannelization(audioSampleRate, m_settings.m_inputFrequencyOffset); m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_source.applyAudioSampleRate(audioSampleRate); } } @@ -205,8 +208,9 @@ void SSBModBaseband::applySettings(const SSBModSettings& settings, bool force) { AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_feedbackAudioDeviceName); + audioDeviceManager->removeAudioSink(getFeedbackAudioFifo()); audioDeviceManager->addAudioSink(getFeedbackAudioFifo(), getInputMessageQueue(), audioDeviceIndex); - uint32_t audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex); + int audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex); if (getFeedbackAudioSampleRate() != audioSampleRate) { m_source.applyFeedbackAudioSampleRate(audioSampleRate); diff --git a/plugins/channeltx/modssb/ssbmodbaseband.h b/plugins/channeltx/modssb/ssbmodbaseband.h index a69dcaac3..8f08a3ecb 100644 --- a/plugins/channeltx/modssb/ssbmodbaseband.h +++ b/plugins/channeltx/modssb/ssbmodbaseband.h @@ -64,8 +64,8 @@ public: MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication CWKeyer& getCWKeyer() { return m_source.getCWKeyer(); } double getMagSq() const { return m_source.getMagSq(); } - unsigned int getAudioSampleRate() const { return m_source.getAudioSampleRate(); } - unsigned int getFeedbackAudioSampleRate() const { return m_source.getFeedbackAudioSampleRate(); } + int getAudioSampleRate() const { return m_source.getAudioSampleRate(); } + int getFeedbackAudioSampleRate() const { return m_source.getFeedbackAudioSampleRate(); } int getChannelSampleRate() const; void setInputFileStream(std::ifstream *ifstream) { m_source.setInputFileStream(ifstream); } AudioFifo *getAudioFifo() { return m_source.getAudioFifo(); } diff --git a/plugins/channeltx/modssb/ssbmodgui.cpp b/plugins/channeltx/modssb/ssbmodgui.cpp index 6da1cc782..c7f08d266 100644 --- a/plugins/channeltx/modssb/ssbmodgui.cpp +++ b/plugins/channeltx/modssb/ssbmodgui.cpp @@ -411,6 +411,8 @@ SSBModGUI::SSBModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_recordLength(0), m_recordSampleRate(48000), m_samplesCount(0), + m_audioSampleRate(-1), + m_feedbackAudioSampleRate(-1), m_tickCount(0), m_enableNavTime(false) { @@ -769,6 +771,32 @@ void SSBModGUI::tick() m_channelPowerDbAvg(powDb); ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.asDouble(), 0, 'f', 1)); + int audioSampleRate = m_ssbMod->getAudioSampleRate(); + + if (audioSampleRate != m_audioSampleRate) + { + if (audioSampleRate < 0) { + ui->mic->setColor(QColor("red")); + } else { + ui->mic->resetColor(); + } + + m_audioSampleRate = audioSampleRate; + } + + int feedbackAudioSampleRate = m_ssbMod->getFeedbackAudioSampleRate(); + + if (feedbackAudioSampleRate != m_feedbackAudioSampleRate) + { + if (feedbackAudioSampleRate < 0) { + ui->feedbackEnable->setStyleSheet("QToolButton { background-color : red; }"); + } else { + ui->feedbackEnable->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + } + + m_feedbackAudioSampleRate = feedbackAudioSampleRate; + } + if (((++m_tickCount & 0xf) == 0) && (m_settings.m_modAFInput == SSBModSettings::SSBModInputFile)) { SSBMod::MsgConfigureFileSourceStreamTiming* message = SSBMod::MsgConfigureFileSourceStreamTiming::create(); diff --git a/plugins/channeltx/modssb/ssbmodgui.h b/plugins/channeltx/modssb/ssbmodgui.h index aeb2d36d5..a752b2d40 100644 --- a/plugins/channeltx/modssb/ssbmodgui.h +++ b/plugins/channeltx/modssb/ssbmodgui.h @@ -76,6 +76,8 @@ private: quint32 m_recordLength; int m_recordSampleRate; int m_samplesCount; + int m_audioSampleRate; + int m_feedbackAudioSampleRate; std::size_t m_tickCount; bool m_enableNavTime; MessageQueue m_inputMessageQueue; diff --git a/plugins/channeltx/modssb/ssbmodsource.cpp b/plugins/channeltx/modssb/ssbmodsource.cpp index 89e831f20..76f4094bf 100644 --- a/plugins/channeltx/modssb/ssbmodsource.cpp +++ b/plugins/channeltx/modssb/ssbmodsource.cpp @@ -510,9 +510,15 @@ void SSBModSource::calculateLevel(Complex& sample) } } -void SSBModSource::applyAudioSampleRate(unsigned int sampleRate) +void SSBModSource::applyAudioSampleRate(int sampleRate) { - qDebug("SSBModSource::applyAudioSampleRate: %u", sampleRate); + if (sampleRate < 0) + { + qWarning("SSBModSource::applyAudioSampleRate: invalid sample rate %d", sampleRate); + return; + } + + qDebug("SSBModSource::applyAudioSampleRate: %d", sampleRate); m_interpolatorDistanceRemain = 0; m_interpolatorConsumed = false; @@ -551,9 +557,15 @@ void SSBModSource::applyAudioSampleRate(unsigned int sampleRate) applyFeedbackAudioSampleRate(m_feedbackAudioSampleRate); } -void SSBModSource::applyFeedbackAudioSampleRate(unsigned int sampleRate) +void SSBModSource::applyFeedbackAudioSampleRate(int sampleRate) { - qDebug("SSBModSource::applyFeedbackAudioSampleRate: %u", sampleRate); + if (sampleRate < 0) + { + qWarning("SSBModSource::applyFeedbackAudioSampleRate: invalid sample rate %d", sampleRate); + return; + } + + qDebug("SSBModSource::applyFeedbackAudioSampleRate: %d", sampleRate); m_feedbackInterpolatorDistanceRemain = 0; m_feedbackInterpolatorConsumed = false; diff --git a/plugins/channeltx/modssb/ssbmodsource.h b/plugins/channeltx/modssb/ssbmodsource.h index 8697cc3bd..eb2f32e40 100644 --- a/plugins/channeltx/modssb/ssbmodsource.h +++ b/plugins/channeltx/modssb/ssbmodsource.h @@ -49,10 +49,10 @@ public: void setInputFileStream(std::ifstream *ifstream) { m_ifstream = ifstream; } AudioFifo *getAudioFifo() { return &m_audioFifo; } AudioFifo *getFeedbackAudioFifo() { return &m_feedbackAudioFifo; } - void applyAudioSampleRate(unsigned int sampleRate); - void applyFeedbackAudioSampleRate(unsigned int sampleRate); - unsigned int getAudioSampleRate() const { return m_audioSampleRate; } - unsigned int getFeedbackAudioSampleRate() const { return m_feedbackAudioSampleRate; } + void applyAudioSampleRate(int sampleRate); + void applyFeedbackAudioSampleRate(int sampleRate); + int getAudioSampleRate() const { return m_audioSampleRate; } + int getFeedbackAudioSampleRate() const { return m_feedbackAudioSampleRate; } CWKeyer& getCWKeyer() { return m_cwKeyer; } double getMagSq() const { return m_magsq; } void getLevels(qreal& rmsLevel, qreal& peakLevel, int& numSamples) const @@ -102,12 +102,12 @@ private: double m_magsq; MovingAverageUtil m_movingAverage; - quint32 m_audioSampleRate; + int m_audioSampleRate; AudioVector m_audioBuffer; uint m_audioBufferFill; AudioFifo m_audioFifo; - quint32 m_feedbackAudioSampleRate; + int m_feedbackAudioSampleRate; AudioVector m_feedbackAudioBuffer; uint m_feedbackAudioBufferFill; AudioFifo m_feedbackAudioFifo; diff --git a/plugins/channeltx/modwfm/wfmmod.cpp b/plugins/channeltx/modwfm/wfmmod.cpp index a71c20553..ca65ae8bd 100644 --- a/plugins/channeltx/modwfm/wfmmod.cpp +++ b/plugins/channeltx/modwfm/wfmmod.cpp @@ -637,4 +637,14 @@ void WFMMod::setLevelMeter(QObject *levelMeter) uint32_t WFMMod::getNumberOfDeviceStreams() const { return m_deviceAPI->getNbSinkStreams(); -} \ No newline at end of file +} + +int WFMMod::getAudioSampleRate() const +{ + return m_basebandSource->getAudioSampleRate(); +} + +int WFMMod::getFeedbackAudioSampleRate() const +{ + return m_basebandSource->getFeedbackAudioSampleRate(); +} diff --git a/plugins/channeltx/modwfm/wfmmod.h b/plugins/channeltx/modwfm/wfmmod.h index 6b3060e94..a7c85f718 100644 --- a/plugins/channeltx/modwfm/wfmmod.h +++ b/plugins/channeltx/modwfm/wfmmod.h @@ -225,6 +225,8 @@ public: CWKeyer *getCWKeyer(); void setLevelMeter(QObject *levelMeter); uint32_t getNumberOfDeviceStreams() const; + int getAudioSampleRate() const; + int getFeedbackAudioSampleRate() const; static const QString m_channelIdURI; static const QString m_channelId; diff --git a/plugins/channeltx/modwfm/wfmmodbaseband.cpp b/plugins/channeltx/modwfm/wfmmodbaseband.cpp index bd07da7bb..1aa75a934 100644 --- a/plugins/channeltx/modwfm/wfmmodbaseband.cpp +++ b/plugins/channeltx/modwfm/wfmmodbaseband.cpp @@ -43,6 +43,9 @@ WFMModBaseband::WFMModBaseband() : DSPEngine::instance()->getAudioDeviceManager()->addAudioSource(m_source.getAudioFifo(), getInputMessageQueue()); m_source.applyAudioSampleRate(DSPEngine::instance()->getAudioDeviceManager()->getInputSampleRate()); + DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(m_source.getFeedbackAudioFifo(), getInputMessageQueue()); + m_source.applyFeedbackAudioSampleRate(DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate()); + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); } @@ -155,6 +158,7 @@ bool WFMModBaseband::handleMessage(const Message& cmd) m_sampleFifo.resize(SampleSourceFifo::getSizePolicy(notif.getSampleRate())); m_channelizer->setBasebandSampleRate(notif.getSampleRate()); m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_source.applyAudioSampleRate(m_source.getAudioSampleRate()); // reapply in case of channel sample rate change return true; } @@ -181,14 +185,16 @@ void WFMModBaseband::applySettings(const WFMModSettings& settings, bool force) { m_channelizer->setChannelization(settings.m_rfBandwidth, settings.m_inputFrequencyOffset); m_source.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_source.applyAudioSampleRate(m_source.getAudioSampleRate()); // reapply in case of channel sample rate change } if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName); + audioDeviceManager->removeAudioSource(getAudioFifo()); audioDeviceManager->addAudioSource(getAudioFifo(), getInputMessageQueue(), audioDeviceIndex); - uint32_t audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex); + int audioSampleRate = audioDeviceManager->getInputSampleRate(audioDeviceIndex); if (getAudioSampleRate() != audioSampleRate) { m_source.applyAudioSampleRate(audioSampleRate); diff --git a/plugins/channeltx/modwfm/wfmmodbaseband.h b/plugins/channeltx/modwfm/wfmmodbaseband.h index adc861875..d63f2e4e7 100644 --- a/plugins/channeltx/modwfm/wfmmodbaseband.h +++ b/plugins/channeltx/modwfm/wfmmodbaseband.h @@ -63,7 +63,8 @@ public: MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication CWKeyer& getCWKeyer() { return m_source.getCWKeyer(); } double getMagSq() const { return m_source.getMagSq(); } - unsigned int getAudioSampleRate() const { return m_source.getAudioSampleRate(); } + int getAudioSampleRate() const { return m_source.getAudioSampleRate(); } + int getFeedbackAudioSampleRate() const { return m_source.getFeedbackAudioSampleRate(); } int getChannelSampleRate() const; void setInputFileStream(std::ifstream *ifstream) { m_source.setInputFileStream(ifstream); } AudioFifo *getAudioFifo() { return m_source.getAudioFifo(); } diff --git a/plugins/channeltx/modwfm/wfmmodgui.cpp b/plugins/channeltx/modwfm/wfmmodgui.cpp index f699d0fb3..a72d9792b 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.cpp +++ b/plugins/channeltx/modwfm/wfmmodgui.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -242,6 +243,19 @@ void WFMModGUI::on_mic_toggled(bool checked) applySettings(); } +void WFMModGUI::on_feedbackEnable_toggled(bool checked) +{ + m_settings.m_feedbackAudioEnable = checked; + applySettings(); +} + +void WFMModGUI::on_feedbackVolume_valueChanged(int value) +{ + ui->feedbackVolumeText->setText(QString("%1").arg(value / 100.0, 0, 'f', 2)); + m_settings.m_feedbackVolumeFactor = value / 100.0; + applySettings(); +} + void WFMModGUI::on_navTimeSlider_valueChanged(int value) { if (m_enableNavTime && ((value >= 0) && (value <= 100))) @@ -339,6 +353,8 @@ WFMModGUI::WFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam m_recordLength(0), m_recordSampleRate(48000), m_samplesCount(0), + m_audioSampleRate(-1), + m_feedbackAudioSampleRate(-1), m_tickCount(0), m_enableNavTime(false) { @@ -366,6 +382,9 @@ WFMModGUI::WFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->mic); connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); + CRightClickEnabler *feedbackRightClickEnabler = new CRightClickEnabler(ui->feedbackEnable); + connect(feedbackRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioFeedbackSelect())); + ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->deltaFrequency->setValueRange(false, 8, -99999999, 99999999); @@ -469,6 +488,10 @@ void WFMModGUI::displaySettings() ui->play->setChecked(m_settings.m_modAFInput == WFMModSettings::WFMModInputAF::WFMModInputFile); ui->morseKeyer->setChecked(m_settings.m_modAFInput == WFMModSettings::WFMModInputAF::WFMModInputCWTone); + ui->feedbackEnable->setChecked(m_settings.m_feedbackAudioEnable); + ui->feedbackVolume->setValue(roundf(m_settings.m_feedbackVolumeFactor * 100.0)); + ui->feedbackVolumeText->setText(QString("%1").arg(m_settings.m_feedbackVolumeFactor, 0, 'f', 2)); + blockApplySettings(false); } @@ -504,12 +527,51 @@ void WFMModGUI::audioSelect() } } +void WFMModGUI::audioFeedbackSelect() +{ + qDebug("WFMModGUI::audioFeedbackSelect"); + AudioSelectDialog audioSelect(DSPEngine::instance()->getAudioDeviceManager(), m_settings.m_audioDeviceName, false); // false for output + audioSelect.exec(); + + if (audioSelect.m_selected) + { + m_settings.m_feedbackAudioDeviceName = audioSelect.m_audioDeviceName; + applySettings(); + } +} + void WFMModGUI::tick() { double powDb = CalcDb::dbPower(m_wfmMod->getMagSq()); m_channelPowerDbAvg(powDb); ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.asDouble(), 0, 'f', 1)); + int audioSampleRate = m_wfmMod->getAudioSampleRate(); + + if (audioSampleRate != m_audioSampleRate) + { + if (audioSampleRate < 0) { + ui->mic->setColor(QColor("red")); + } else { + ui->mic->resetColor(); + } + + m_audioSampleRate = audioSampleRate; + } + + int feedbackAudioSampleRate = m_wfmMod->getFeedbackAudioSampleRate(); + + if (feedbackAudioSampleRate != m_feedbackAudioSampleRate) + { + if (feedbackAudioSampleRate < 0) { + ui->feedbackEnable->setStyleSheet("QToolButton { background-color : red; }"); + } else { + ui->feedbackEnable->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + } + + m_feedbackAudioSampleRate = feedbackAudioSampleRate; + } + if (((++m_tickCount & 0xf) == 0) && (m_settings.m_modAFInput == WFMModSettings::WFMModInputFile)) { WFMMod::MsgConfigureFileSourceStreamTiming* message = WFMMod::MsgConfigureFileSourceStreamTiming::create(); diff --git a/plugins/channeltx/modwfm/wfmmodgui.h b/plugins/channeltx/modwfm/wfmmodgui.h index b5aa2dc64..e1d847e95 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.h +++ b/plugins/channeltx/modwfm/wfmmodgui.h @@ -71,6 +71,8 @@ private: quint32 m_recordLength; int m_recordSampleRate; int m_samplesCount; + int m_audioSampleRate; + int m_feedbackAudioSampleRate; std::size_t m_tickCount; bool m_enableNavTime; MessageQueue m_inputMessageQueue; @@ -116,11 +118,15 @@ private slots: void on_navTimeSlider_valueChanged(int value); void on_showFileDialog_clicked(bool checked); + void on_feedbackEnable_toggled(bool checked); + void on_feedbackVolume_valueChanged(int value); + void onWidgetRolled(QWidget* widget, bool rollDown); void onMenuDialogCalled(const QPoint& p); void configureFileName(); void audioSelect(); + void audioFeedbackSelect(); void tick(); }; diff --git a/plugins/channeltx/modwfm/wfmmodgui.ui b/plugins/channeltx/modwfm/wfmmodgui.ui index e69ef7ee6..d05fbfb18 100644 --- a/plugins/channeltx/modwfm/wfmmodgui.ui +++ b/plugins/channeltx/modwfm/wfmmodgui.ui @@ -39,7 +39,7 @@ 2 2 - 280 + 291 271 @@ -56,7 +56,16 @@ 3 - + + 2 + + + 2 + + + 2 + + 2 @@ -476,13 +485,6 @@ - - - - Qt::Vertical - - - @@ -500,6 +502,75 @@ + + + + Qt::Vertical + + + + + + + Left: enable / disable audio feedback - Right: select audio output device + + + ... + + + + :/sound_off.png + :/sound_on.png:/sound_off.png + + + true + + + + + + + + 24 + 24 + + + + Audio feedback volume + + + 0 + + + 100 + + + 1 + + + 50 + + + + + + + + 30 + 16777215 + + + + Audio feedback volume + + + 1.00 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + diff --git a/plugins/channeltx/modwfm/wfmmodsettings.cpp b/plugins/channeltx/modwfm/wfmmodsettings.cpp index 0b5afca0b..3cd05f148 100644 --- a/plugins/channeltx/modwfm/wfmmodsettings.cpp +++ b/plugins/channeltx/modwfm/wfmmodsettings.cpp @@ -50,6 +50,9 @@ void WFMModSettings::resetToDefaults() m_title = "WFM Modulator"; m_modAFInput = WFMModInputNone; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_feedbackAudioDeviceName = AudioDeviceManager::m_defaultDeviceName; + m_feedbackVolumeFactor = 0.5f; + m_feedbackAudioEnable = false; m_streamIndex = 0; m_useReverseAPI = false; m_reverseAPIAddress = "127.0.0.1"; @@ -89,6 +92,9 @@ QByteArray WFMModSettings::serialize() const s.writeU32(16, m_reverseAPIDeviceIndex); s.writeU32(17, m_reverseAPIChannelIndex); s.writeS32(18, m_streamIndex); + s.writeString(19, m_feedbackAudioDeviceName); + s.writeReal(20, m_feedbackVolumeFactor); + s.writeBool(21, m_feedbackAudioEnable); return s.final(); } @@ -155,6 +161,9 @@ bool WFMModSettings::deserialize(const QByteArray& data) d.readU32(17, &utmp, 0); m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; d.readS32(18, &m_streamIndex, 0); + d.readString(19, &m_feedbackAudioDeviceName, AudioDeviceManager::m_defaultDeviceName); + d.readReal(20, &m_feedbackVolumeFactor, 1.0); + d.readBool(21, &m_feedbackAudioEnable, false); return true; } diff --git a/plugins/channeltx/modwfm/wfmmodsettings.h b/plugins/channeltx/modwfm/wfmmodsettings.h index 6e4c3c3c9..5e0259476 100644 --- a/plugins/channeltx/modwfm/wfmmodsettings.h +++ b/plugins/channeltx/modwfm/wfmmodsettings.h @@ -50,6 +50,9 @@ struct WFMModSettings QString m_title; WFMModInputAF m_modAFInput; QString m_audioDeviceName; //!< This is the audio device you get the audio samples from + QString m_feedbackAudioDeviceName; //!< This is the audio device you send the audio samples to for audio feedback + float m_feedbackVolumeFactor; + bool m_feedbackAudioEnable; int m_streamIndex; bool m_useReverseAPI; QString m_reverseAPIAddress; diff --git a/plugins/channeltx/modwfm/wfmmodsource.cpp b/plugins/channeltx/modwfm/wfmmodsource.cpp index f4e6fe0ec..aeabf4b3e 100644 --- a/plugins/channeltx/modwfm/wfmmodsource.cpp +++ b/plugins/channeltx/modwfm/wfmmodsource.cpp @@ -26,11 +26,13 @@ WFMModSource::WFMModSource() : m_channelFrequencyOffset(0), m_modPhasor(0.0f), m_audioFifo(4800), + m_feedbackAudioFifo(48000), m_levelCalcCount(0), m_peakLevel(0.0f), m_levelSum(0.0f), m_ifstream(nullptr), - m_audioSampleRate(48000) + m_audioSampleRate(48000), + m_feedbackAudioSampleRate(48000) { m_rfFilter = new fftfilt(-62500.0 / 384000.0, 62500.0 / 384000.0, m_rfFilterFFTLength); m_rfFilterBuffer = new Complex[m_rfFilterFFTLength]; @@ -39,6 +41,8 @@ WFMModSource::WFMModSource() : m_audioBuffer.resize(1<<14); m_audioBufferFill = 0; m_magsq = 0.0; + m_feedbackAudioBuffer.resize(1<<14); + m_feedbackAudioBufferFill = 0; applySettings(m_settings, true); applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true); @@ -76,7 +80,8 @@ void WFMModSource::pullOne(Sample& sample) Real t; if ((m_settings.m_modAFInput == WFMModSettings::WFMModInputFile) - || (m_settings.m_modAFInput == WFMModSettings::WFMModInputAudio)) + || (m_settings.m_modAFInput == WFMModSettings::WFMModInputAudio) + || (m_settings.m_modAFInput == WFMModSettings::WFMModInputCWTone)) { if (m_interpolatorDistance > 1.0f) // decimate { @@ -141,6 +146,10 @@ void WFMModSource::modulateAudio() calculateLevel(t); m_modSample.real(t); m_modSample.imag(0.0f); + + if (m_settings.m_feedbackAudioEnable) { + pushFeedback(t * m_settings.m_feedbackVolumeFactor * 16384.0f); + } } void WFMModSource::prefetch(unsigned int nbSamples) @@ -217,18 +226,18 @@ void WFMModSource::pullAF(Real& sample) if (m_cwKeyer.getSample()) { m_cwKeyer.getCWSmoother().getFadeSample(true, fadeFactor); - sample = m_toneNco.next() * m_settings.m_volumeFactor * fadeFactor; + sample = m_cwToneNco.next() * m_settings.m_volumeFactor * fadeFactor; } else { if (m_cwKeyer.getCWSmoother().getFadeSample(false, fadeFactor)) { - sample = m_toneNco.next() * m_settings.m_volumeFactor * fadeFactor; + sample = m_cwToneNco.next() * m_settings.m_volumeFactor * fadeFactor; } else { sample = 0.0f; - m_toneNco.setPhase(0); + m_cwToneNco.setPhase(0); } } break; @@ -239,6 +248,49 @@ void WFMModSource::pullAF(Real& sample) } } +void WFMModSource::pushFeedback(Complex c) +{ + Complex ci; + + if (m_feedbackInterpolatorDistance < 1.0f) // interpolate + { + while (!m_feedbackInterpolator.interpolate(&m_feedbackInterpolatorDistanceRemain, c, &ci)) + { + processOneSample(ci); + m_feedbackInterpolatorDistanceRemain += m_feedbackInterpolatorDistance; + } + } + else // decimate + { + if (m_feedbackInterpolator.decimate(&m_feedbackInterpolatorDistanceRemain, c, &ci)) + { + processOneSample(ci); + m_feedbackInterpolatorDistanceRemain += m_feedbackInterpolatorDistance; + } + } +} + +void WFMModSource::processOneSample(Complex& ci) +{ + m_feedbackAudioBuffer[m_feedbackAudioBufferFill].l = ci.real(); + m_feedbackAudioBuffer[m_feedbackAudioBufferFill].r = ci.imag(); + ++m_feedbackAudioBufferFill; + + if (m_feedbackAudioBufferFill >= m_feedbackAudioBuffer.size()) + { + unsigned int res = m_feedbackAudioFifo.write((const quint8*)&m_feedbackAudioBuffer[0], m_feedbackAudioBufferFill); + + if (res != m_feedbackAudioBufferFill) + { + qDebug("WFMModSource::processOneSample: %u/%u audio samples written m_feedbackInterpolatorDistance: %f", + res, m_feedbackAudioBufferFill, m_feedbackInterpolatorDistance); + m_feedbackAudioFifo.clear(); + } + + m_feedbackAudioBufferFill = 0; + } +} + void WFMModSource::calculateLevel(const Real& sample) { if (m_levelCalcCount < m_levelNbSamples) @@ -257,16 +309,44 @@ void WFMModSource::calculateLevel(const Real& sample) } } -void WFMModSource::applyAudioSampleRate(unsigned int sampleRate) +void WFMModSource::applyAudioSampleRate(int sampleRate) { + if (sampleRate < 0) + { + qWarning("WFMModSource::applyAudioSampleRate: %d", sampleRate); + return; + } + qDebug("WFMModSource::applyAudioSampleRate: %d", sampleRate); m_interpolatorDistanceRemain = 0; m_interpolatorConsumed = false; m_interpolatorDistance = (Real) sampleRate / (Real) m_channelSampleRate; m_interpolator.create(48, sampleRate, m_settings.m_rfBandwidth / 2.2, 3.0); - + m_cwToneNco.setFreq(m_settings.m_toneFrequency, sampleRate); + m_cwKeyer.setSampleRate(sampleRate); + m_cwKeyer.reset(); m_audioSampleRate = sampleRate; + applyFeedbackAudioSampleRate(m_feedbackAudioSampleRate); +} + +void WFMModSource::applyFeedbackAudioSampleRate(int sampleRate) +{ + if (sampleRate < 0) + { + qWarning("WFMModSource::applyFeedbackAudioSampleRate: invalid sample rate %d", sampleRate); + return; + } + + qDebug("WFMModSource::applyFeedbackAudioSampleRate: %d", sampleRate); + + m_feedbackInterpolatorDistanceRemain = 0; + m_feedbackInterpolatorConsumed = false; + m_feedbackInterpolatorDistance = (Real) sampleRate / (Real) m_audioSampleRate; + Real cutoff = std::min(sampleRate, m_audioSampleRate) / 2.2f; + m_feedbackInterpolator.create(48, sampleRate, cutoff, 3.0); + + m_feedbackAudioSampleRate = sampleRate; } void WFMModSource::applySettings(const WFMModSettings& settings, bool force) @@ -286,8 +366,10 @@ void WFMModSource::applySettings(const WFMModSettings& settings, bool force) m_rfFilter->create_filter(lowCut, hiCut); } - if ((settings.m_toneFrequency != m_settings.m_toneFrequency) || force) { + if ((settings.m_toneFrequency != m_settings.m_toneFrequency) || force) + { m_toneNco.setFreq(settings.m_toneFrequency, m_channelSampleRate); + m_cwToneNco.setFreq(settings.m_toneFrequency, m_audioSampleRate); } m_settings = settings; @@ -314,8 +396,6 @@ void WFMModSource::applyChannelSettings(int channelSampleRate, int channelFreque Real hiCut = (m_settings.m_rfBandwidth / 2.0) / channelSampleRate; m_rfFilter->create_filter(lowCut, hiCut); m_toneNco.setFreq(m_settings.m_toneFrequency, channelSampleRate); - m_cwKeyer.setSampleRate(channelSampleRate); - m_cwKeyer.reset(); } m_channelSampleRate = channelSampleRate; diff --git a/plugins/channeltx/modwfm/wfmmodsource.h b/plugins/channeltx/modwfm/wfmmodsource.h index 16fa7859b..e263f74f7 100644 --- a/plugins/channeltx/modwfm/wfmmodsource.h +++ b/plugins/channeltx/modwfm/wfmmodsource.h @@ -46,9 +46,11 @@ public: void setInputFileStream(std::ifstream *ifstream) { m_ifstream = ifstream; } AudioFifo *getAudioFifo() { return &m_audioFifo; } - void applyAudioSampleRate(unsigned int sampleRate); - void applyFeedbackAudioSampleRate(unsigned int sampleRate); - unsigned int getAudioSampleRate() const { return m_audioSampleRate; } + AudioFifo *getFeedbackAudioFifo() { return &m_feedbackAudioFifo; } + void applyAudioSampleRate(int sampleRate); + void applyFeedbackAudioSampleRate(int sampleRate); + int getAudioSampleRate() const { return m_audioSampleRate; } + int getFeedbackAudioSampleRate() const { return m_feedbackAudioSampleRate; } CWKeyer& getCWKeyer() { return m_cwKeyer; } double getMagSq() const { return m_magsq; } void getLevels(qreal& rmsLevel, qreal& peakLevel, int& numSamples) const @@ -67,6 +69,7 @@ private: NCO m_carrierNco; NCOF m_toneNco; + NCOF m_cwToneNco; float m_modPhasor; //!< baseband modulator phasor Complex m_modSample; @@ -75,6 +78,11 @@ private: Real m_interpolatorDistanceRemain; bool m_interpolatorConsumed; + Interpolator m_feedbackInterpolator; + Real m_feedbackInterpolatorDistance; + Real m_feedbackInterpolatorDistanceRemain; + bool m_feedbackInterpolatorConsumed; + fftfilt* m_rfFilter; static const int m_rfFilterFFTLength; fftfilt::cmplx *m_rfFilterBuffer; @@ -83,11 +91,16 @@ private: double m_magsq; MovingAverageUtil m_movingAverage; - quint32 m_audioSampleRate; + int m_audioSampleRate; AudioVector m_audioBuffer; uint m_audioBufferFill; AudioFifo m_audioFifo; + int m_feedbackAudioSampleRate; + AudioVector m_feedbackAudioBuffer; + uint m_feedbackAudioBufferFill; + AudioFifo m_feedbackAudioFifo; + quint32 m_levelCalcCount; qreal m_rmsLevel; qreal m_peakLevelOut; @@ -102,6 +115,7 @@ private: void processOneSample(Complex& ci); void pullAF(Real& sample); void pullAudio(unsigned int nbSamples); + void pushFeedback(Complex sample); void calculateLevel(const Real& sample); void modulateAudio(); };