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 
68  int CCallsignSampleProvider::readSamples(QVector<float> &samples, qint64 count)
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.
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