swift
callsignsampleprovider.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2019 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QStringBuilder>
7 #include <QStringLiteral>
8 #include <QtMath>
9 
10 #include "config/buildconfig.h"
13 #include "misc/logmessage.h"
14 #include "misc/metadatautils.h"
15 #include "sound/audioutilities.h"
17 
18 using namespace swift::misc;
19 using namespace swift::sound::sample_provider;
20 using namespace swift::config;
21 
22 namespace swift::core::afv::audio
23 {
24  CCallsignSampleProvider::CCallsignSampleProvider(const QAudioFormat &audioFormat,
25  const CReceiverSampleProvider *receiver, QObject *parent)
26  : ISampleProvider(parent), m_audioFormat(audioFormat), m_receiver(receiver),
27  m_decoder(audioFormat.sampleRate(), 1)
28  {
29  Q_ASSERT(audioFormat.channelCount() == 1);
30  Q_ASSERT(receiver);
31 
32  const QString on = QStringLiteral("%1").arg(classNameShort(this));
33  this->setObjectName(on);
34 
35  m_mixer = new CMixingSampleProvider(this);
36  m_crackleSoundProvider = new CResourceSoundSampleProvider(Samples::instance().crackle(), m_mixer);
37  m_crackleSoundProvider->setLooping(true);
38  m_crackleSoundProvider->setGain(0.0);
39  m_whiteNoise = new CResourceSoundSampleProvider(Samples::instance().whiteNoise(), m_mixer);
40  m_whiteNoise->setLooping(true);
41  m_whiteNoise->setGain(0.0);
42  m_hfWhiteNoise = new CResourceSoundSampleProvider(Samples::instance().hfWhiteNoise(), m_mixer);
43  m_hfWhiteNoise->setLooping(true);
44  m_hfWhiteNoise->setGain(0.0);
45  m_acBusNoise = new CSawToothGenerator(400, m_mixer);
46  m_audioInput = new CBufferedWaveProvider(audioFormat, m_mixer);
47 
48  // Create the compressor
49  m_simpleCompressorEffect = new CSimpleCompressorEffect(m_audioInput, m_mixer);
50  m_simpleCompressorEffect->setMakeUpGain(-5.5);
51 
52  // Create the voice EQ
53  m_voiceEqualizer =
54  new CEqualizerSampleProvider(m_simpleCompressorEffect, EqualizerPresets::VHFEmulation, m_mixer);
55 
56  m_mixer->addMixerInput(m_whiteNoise);
57  m_mixer->addMixerInput(m_acBusNoise);
58  m_mixer->addMixerInput(m_hfWhiteNoise);
59  m_mixer->addMixerInput(m_voiceEqualizer);
60 
61  m_timer = new QTimer(this);
62  m_timer->setObjectName(this->objectName() + ":m_timer");
63 
64  m_timer->setInterval(100);
65  connect(m_timer, &QTimer::timeout, this, &CCallsignSampleProvider::timerElapsed);
66  }
67 
69  {
70  const int noOfSamples = m_mixer->readSamples(samples, count);
71 
72  if (m_inUse && m_lastPacketLatch && m_audioInput->getBufferedBytes() == 0)
73  {
74  idle();
75  m_lastPacketLatch = false;
76  }
77 
78  if (m_inUse && !m_underflow && m_audioInput->getBufferedBytes() == 0)
79  {
80  if (verbose()) { CLogMessage(this).debug(u"[%1] [Delay++]") << m_callsign; }
82  m_underflow = true;
83  }
84 
85  return noOfSamples;
86  }
87 
88  void CCallsignSampleProvider::timerElapsed()
89  {
90  if (m_inUse && m_audioInput->getBufferedBytes() == 0 &&
91  m_lastSamplesAddedUtc.msecsTo(QDateTime::currentDateTimeUtc()) > m_idleTimeoutMs)
92  {
93  idle();
94  }
95  }
96 
97  void CCallsignSampleProvider::active(const QString &callsign, const QString &aircraftType)
98  {
99  m_callsign = callsign;
101  m_aircraftType = aircraftType;
102  m_decoder.resetState();
103  m_inUse = true;
104  setEffects();
105  m_underflow = false;
106 
107  const int delayMs = CallsignDelayCache::instance().get(callsign);
108  if (verbose()) { CLogMessage(this).debug(u"[%1] [Delay %2ms]") << m_callsign << delayMs; }
109  if (delayMs > 0)
110  {
111  const int phaseDelayLength = (m_audioFormat.sampleRate() / 1000) * delayMs;
112  const QVector<float> phaseDelay(phaseDelayLength * 2, 0);
113  m_audioInput->addSamples(phaseDelay);
114  }
115  }
116 
117  void CCallsignSampleProvider::activeSilent(const QString &callsign, const QString &aircraftType)
118  {
119  m_callsign = callsign;
121  m_aircraftType = aircraftType;
122  m_decoder.resetState();
123  m_inUse = true;
124  setEffects(true);
125  m_underflow = true;
126  }
127 
129  {
130  idle();
131  m_audioInput->clearBuffer();
132  }
133 
134  void CCallsignSampleProvider::addOpusSamples(const IAudioDto &audioDto, float distanceRatio)
135  {
136  m_distanceRatio = distanceRatio;
137  setEffects();
138 
139  const QVector<qint16> audio = decodeOpus(audioDto.audio);
140  m_audioInput->addSamples(swift::sound::convertFromShortToFloat(audio));
141  m_lastPacketLatch = audioDto.lastPacket;
142  if (audioDto.lastPacket && !m_underflow) { CallsignDelayCache::instance().success(m_callsign); }
143  m_lastSamplesAddedUtc = QDateTime::currentDateTimeUtc();
144  if (!m_timer->isActive()) { m_timer->start(); }
145  }
146 
148  {
149  // Disable all audio effects
150  setEffects(true);
151 
152  // TODO audioInput->addSamples(decoderByteBuffer, 0, frameCount * 2);
153  m_lastPacketLatch = audioDto.lastPacket;
154 
155  m_lastSamplesAddedUtc = QDateTime::currentDateTimeUtc();
156  if (!m_timer->isActive()) { m_timer->start(); }
157  }
158 
159  void CCallsignSampleProvider::idle()
160  {
161  m_timer->stop();
162  m_inUse = false;
163  setEffects();
164  m_callsign.clear();
165  m_aircraftType.clear();
166  }
167 
168  QVector<qint16> CCallsignSampleProvider::decodeOpus(const QByteArray &opusData)
169  {
170  int decodedLength = 0;
171  const QVector<qint16> decoded = m_decoder.decode(opusData, opusData.size(), &decodedLength);
172  return decoded;
173  }
174 
175  void CCallsignSampleProvider::setEffects(bool noEffects)
176  {
177  if (noEffects || m_bypassEffects || !m_inUse)
178  {
179  m_crackleSoundProvider->setGain(0.0);
180  m_whiteNoise->setGain(0.0);
181  m_hfWhiteNoise->setGain(0.0);
182  m_acBusNoise->setGain(0.0);
183  m_simpleCompressorEffect->setEnabled(false);
184  m_voiceEqualizer->setBypassEffects(true);
185  }
186  else
187  {
188  if (m_receiver->getFrequencyHz() < 30000000)
189  {
196  m_hfWhiteNoise->setGain(m_hfWhiteNoiseGainMin);
197  m_acBusNoise->setGain(m_acBusGainMin + 0.001f);
198  m_simpleCompressorEffect->setEnabled(true);
199  m_voiceEqualizer->setBypassEffects(false);
200  m_voiceEqualizer->setOutputGain(0.38);
201  m_whiteNoise->setGain(0.0);
202  }
203  else
204  {
205  double crackleFactor = (((qExp(m_distanceRatio) * qPow(m_distanceRatio, -4.0)) / 350.0) - 0.00776652);
206 
207  crackleFactor = std::clamp(crackleFactor, 0.0, 0.2);
208 
209  m_crackleSoundProvider->setGain(crackleFactor * 2);
210  m_whiteNoise->setGain(m_whiteNoiseGainMin);
211  m_acBusNoise->setGain(m_acBusGainMin);
212  m_simpleCompressorEffect->setEnabled(true);
213  m_voiceEqualizer->setBypassEffects(false);
214  m_voiceEqualizer->setOutputGain(1.0 - crackleFactor * 3.7);
215  }
216  }
217  }
218 
220  {
221  m_bypassEffects = bypassEffects;
222  setEffects();
223  }
224 
226  {
227  return QStringLiteral("In use: ") % boolToYesNo(m_inUse) % QStringLiteral(" cs: ") % m_callsign %
228  QStringLiteral(" type: ") % m_aircraftType;
229  }
230 
231 } // namespace swift::core::afv::audio
const QString & callsign() const
The callsign.
void setBypassEffects(bool bypassEffects)
Bypass effects.
void addOpusSamples(const IAudioDto &audioDto, float distanceRatio)
Add samples.
int readSamples(QVector< float > &samples, qint64 count)
Read samples.
void active(const QString &callsign, const QString &aircraftType)
Is active?
void activeSilent(const QString &callsign, const QString &aircraftType)
Is active?
void addSilentSamples(const IAudioDto &audioDto)
Add samples.
void initialise(const QString &callsign)
Initialize.
static CallsignDelayCache & instance()
Singleton.
int get(const QString &callsign)
Callsign index.
void success(const QString &callsign)
Success.
void underflow(const QString &callsign)
Underflow.
Class for emitting a log message.
Definition: logmessage.h:27
Derived & debug()
Set the severity to debug.
QVector< qint16 > decode(const QByteArray &opusData, int dataLength, int *decodedLength)
Decode.
Definition: opusdecoder.cpp:24
void addSamples(const QVector< float > &samples)
Add samples.
virtual int readSamples(QVector< float > &samples, qint64 count)
Read samples.
void addMixerInput(ISampleProvider *provider)
Add a provider.
Free functions in swift::misc.
QString classNameShort(const QObject *object)
Class name as from QMetaObject::className without namespace.
qsizetype size() const const
QDateTime currentDateTimeUtc()
qint64 msecsTo(const QDateTime &other) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void setObjectName(QAnyStringView name)
QString arg(Args &&... args) const const
void clear()
void setInterval(int msec)
bool isActive() const const
void start()
void stop()
void timeout()
bool lastPacket
Used to indicate to receiver that the sender has stopped sending.
Definition: dto.h:280
QByteArray audio
Opus compressed audio.
Definition: dto.h:279