1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-26 09:48:45 -05:00

Modulator plugins with configurable audio: fixed audio sample rate handling

This commit is contained in:
f4exb 2020-08-02 10:11:41 +02:00
parent 7917d595f5
commit 674a4ccd27
43 changed files with 542 additions and 81 deletions

View File

@ -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)));
}
}
int AMMod::getAudioSampleRate() const
{
return m_basebandSource->getAudioSampleRate();
}
int AMMod::getFeedbackAudioSampleRate() const
{
return m_basebandSource->getFeedbackAudioSampleRate();
}

View File

@ -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;

View File

@ -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);

View File

@ -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(); }

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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<double, double, 16> 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;

View File

@ -624,7 +624,7 @@ void FreeDVMod::networkManagerFinished(QNetworkReply *reply)
reply->deleteLater();
}
uint32_t FreeDVMod::getAudioSampleRate() const
int FreeDVMod::getAudioSampleRate() const
{
return m_basebandSource->getAudioSampleRate();
}

View File

@ -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;

View File

@ -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);

View File

@ -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(); }

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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<double, double, 16> m_movingAverage;
quint32 m_audioSampleRate;
int m_audioSampleRate;
AudioVector m_audioBuffer;
uint m_audioBufferFill;
AudioFifo m_audioFifo;

View File

@ -710,4 +710,14 @@ void NFMMod::setLevelMeter(QObject *levelMeter)
uint32_t NFMMod::getNumberOfDeviceStreams() const
{
return m_deviceAPI->getNbSinkStreams();
}
}
int NFMMod::getAudioSampleRate() const
{
return m_basebandSource->getAudioSampleRate();
}
int NFMMod::getFeedbackAudioSampleRate() const
{
return m_basebandSource->getFeedbackAudioSampleRate();
}

View File

@ -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;

View File

@ -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);

View File

@ -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(); }

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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<double, double, 16> 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;

View File

@ -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();

View File

@ -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;

View File

@ -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);

View File

@ -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(); }

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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<double, double, 16> 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;

View File

@ -637,4 +637,14 @@ void WFMMod::setLevelMeter(QObject *levelMeter)
uint32_t WFMMod::getNumberOfDeviceStreams() const
{
return m_deviceAPI->getNbSinkStreams();
}
}
int WFMMod::getAudioSampleRate() const
{
return m_basebandSource->getAudioSampleRate();
}
int WFMMod::getFeedbackAudioSampleRate() const
{
return m_basebandSource->getFeedbackAudioSampleRate();
}

View File

@ -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;

View File

@ -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);

View File

@ -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(); }

View File

@ -17,6 +17,7 @@
#include <QDockWidget>
#include <QMainWindow>
#include <QColor>
#include <QFileDialog>
#include <QTime>
#include <QDebug>
@ -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();

View File

@ -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();
};

View File

@ -39,7 +39,7 @@
<rect>
<x>2</x>
<y>2</y>
<width>280</width>
<width>291</width>
<height>271</height>
</rect>
</property>
@ -56,7 +56,16 @@
<property name="spacing">
<number>3</number>
</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>
</property>
<item>
@ -476,13 +485,6 @@
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="mic">
<property name="toolTip">
@ -500,6 +502,75 @@
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="feedbackEnable">
<property name="toolTip">
<string>Left: enable / disable audio feedback - Right: select audio output device</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/sound_off.png</normaloff>
<normalon>:/sound_on.png</normalon>:/sound_off.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="feedbackVolume">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Audio feedback volume</string>
</property>
<property name="minimum">
<number>0</number>
</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="feedbackVolumeText">
<property name="maximumSize">
<size>
<width>30</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Audio feedback volume</string>
</property>
<property name="text">
<string>1.00</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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<double, double, 16> 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();
};