swift
receiversampleprovider.cpp
Go to the documentation of this file.
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 
7 
8 #include <QStringBuilder>
9 
10 #include "misc/logmessage.h"
11 #include "misc/metadatautils.h"
12 #include "sound/sampleprovider/resourcesoundsampleprovider.h"
14 
15 using namespace swift::misc;
16 using namespace swift::misc::audio;
17 using namespace swift::misc::aviation;
18 using namespace swift::sound::sample_provider;
19 
20 namespace swift::core::afv::audio
21 {
22  const QStringList &CReceiverSampleProvider::getLogCategories()
23  {
24  static const QStringList cats { CLogCategories::audio(), CLogCategories::vatsimSpecific() };
25  return cats;
26  }
27 
28  CReceiverSampleProvider::CReceiverSampleProvider(const QAudioFormat &audioFormat, quint16 id, int voiceInputNumber,
29  QObject *parent)
30  : ISampleProvider(parent), m_id(id)
31  {
32  const QString on = QStringLiteral("%1 id: %2").arg(classNameShort(this)).arg(id);
33  this->setObjectName(on);
34 
35  m_mixer = new CMixingSampleProvider(this);
36  for (int i = 0; i < voiceInputNumber; i++)
37  {
38  const auto voiceInput = new CCallsignSampleProvider(audioFormat, this, m_mixer);
39  m_voiceInputs.push_back(voiceInput);
40  m_mixer->addMixerInput(voiceInput);
41  }
42 
43  m_blockTone = new CSinusGenerator(180, this);
44  m_mixer->addMixerInput(m_blockTone);
45  m_volume = new CVolumeSampleProvider(m_mixer);
46  }
47 
49  {
50  for (CCallsignSampleProvider *voiceInput : std::as_const(m_voiceInputs))
51  {
52  voiceInput->setBypassEffects(value);
53  }
54  }
55 
56  void CReceiverSampleProvider::setFrequency(const uint &frequencyHz)
57  {
58  if (frequencyHz != m_frequencyHz)
59  {
60  for (CCallsignSampleProvider *voiceInput : std::as_const(m_voiceInputs)) { voiceInput->clear(); }
61  }
62  m_frequencyHz = frequencyHz;
63  }
64 
66  {
67  const int numberOfCallsigns = static_cast<int>(std::count_if(
68  m_voiceInputs.begin(), m_voiceInputs.end(), [](const CCallsignSampleProvider *p) { return p->inUse(); }));
69  return numberOfCallsigns;
70  }
71 
73  {
74  m_mute = value;
75  if (value)
76  {
77  for (CCallsignSampleProvider *voiceInput : std::as_const(m_voiceInputs)) { voiceInput->clear(); }
78  }
79  }
80 
81  int CReceiverSampleProvider::readSamples(QVector<float> &samples, qint64 count)
82  {
83  int numberOfInUseInputs = activeCallsigns();
84  if (numberOfInUseInputs > 1 && m_doBlockWhenAppropriate)
85  {
86  m_blockTone->setFrequency(180.0);
87  m_blockTone->setGain(m_blockToneGain);
88  }
89  else { m_blockTone->setGain(0.0); }
90 
91  if (m_doClickWhenAppropriate && numberOfInUseInputs == 0)
92  {
93  auto *resourceSound = new CResourceSoundSampleProvider(Samples::instance().click(), m_mixer);
94  m_mixer->addMixerInput(resourceSound);
95  m_doClickWhenAppropriate = false;
96  }
97 
99  if (numberOfInUseInputs != m_lastNumberOfInUseInputs)
100  {
101  QStringList receivingCallsigns;
102  for (const CCallsignSampleProvider *voiceInput : std::as_const(m_voiceInputs))
103  {
104  const QString callsign = voiceInput->callsign();
105  if (!callsign.isEmpty()) { receivingCallsigns.push_back(callsign); }
106  }
107 
108  m_receivingCallsignsString = receivingCallsigns.join(',');
109  m_receivingCallsigns = CCallsignSet(receivingCallsigns);
110  const TransceiverReceivingCallsignsChangedArgs args = { m_id, receivingCallsigns };
111  emit receivingCallsignsChanged(args);
112  }
113  m_lastNumberOfInUseInputs = numberOfInUseInputs;
114  return m_volume->readSamples(samples, count);
115  }
116 
117  void CReceiverSampleProvider::addOpusSamples(const IAudioDto &audioDto, uint frequency, float distanceRatio)
118  {
119  if (m_frequencyHz != frequency) { return; } // Lag in the backend means we get the tail end of a transmission
120  CCallsignSampleProvider *voiceInput = nullptr;
121 
122  auto it =
123  std::find_if(m_voiceInputs.begin(), m_voiceInputs.end(),
124  [audioDto](const CCallsignSampleProvider *p) { return p->callsign() == audioDto.callsign; });
125 
126  if (it != m_voiceInputs.end()) { voiceInput = *it; }
127 
128  if (!voiceInput)
129  {
130  it = std::find_if(m_voiceInputs.begin(), m_voiceInputs.end(),
131  [](const CCallsignSampleProvider *p) { return !p->inUse(); });
132  if (it != m_voiceInputs.end())
133  {
134  voiceInput = *it;
135  voiceInput->active(audioDto.callsign, "");
136  }
137  }
138 
139  if (voiceInput) { voiceInput->addOpusSamples(audioDto, distanceRatio); }
140 
141  const CSettings s = m_audioSettings.get();
142  m_doClickWhenAppropriate = s.afvClicked();
143  m_doBlockWhenAppropriate = s.afvBlocked();
144  }
145 
146  void CReceiverSampleProvider::addSilentSamples(const IAudioDto &audioDto, uint frequency, float distanceRatio)
147  {
148  Q_UNUSED(distanceRatio)
149  if (m_frequencyHz != frequency) { return; } // Lag in the backend means we get the tail end of a transmission
150 
151  CCallsignSampleProvider *voiceInput = nullptr;
152  auto it =
153  std::find_if(m_voiceInputs.begin(), m_voiceInputs.end(),
154  [audioDto](const CCallsignSampleProvider *p) { return p->callsign() == audioDto.callsign; });
155 
156  if (it != m_voiceInputs.end()) { voiceInput = *it; }
157 
158  if (!voiceInput)
159  {
160  it = std::find_if(m_voiceInputs.begin(), m_voiceInputs.end(),
161  [](const CCallsignSampleProvider *p) { return !p->inUse(); });
162  if (it != m_voiceInputs.end())
163  {
164  voiceInput = *it;
165  voiceInput->active(audioDto.callsign, "");
166  }
167  }
168 
169  if (voiceInput) { voiceInput->addSilentSamples(audioDto); }
170  }
171 
172  uint CReceiverSampleProvider::getFrequencyHz() const { return m_frequencyHz; }
173 
174  void CReceiverSampleProvider::logVoiceInputs(const QString &prefix, qint64 timeCheckOffsetMs)
175  {
176  if (timeCheckOffsetMs > 100)
177  {
178  const qint64 now = QDateTime::currentMSecsSinceEpoch();
179  if (m_lastLogMessage + timeCheckOffsetMs > now) { return; }
180  m_lastLogMessage = now;
181  }
182 
183  QString l;
184  int no = 0;
185  for (const CCallsignSampleProvider *sp : std::as_const(m_voiceInputs))
186  {
187  if (!sp || !sp->inUse()) { continue; } // only log the ones in use
188  l += (l.isEmpty() ? QStringLiteral("") : QStringLiteral("\n")) % prefix % QString::number(no++) %
189  QStringLiteral(": ") % sp->toQString();
190  }
191 
192  if (l.isEmpty()) { return; }
193  CLogMessage(this).debug(l);
194  }
195 
196 } // namespace swift::core::afv::audio
void addOpusSamples(const IAudioDto &audioDto, float distanceRatio)
Add samples.
void active(const QString &callsign, const QString &aircraftType)
Is active?
void addSilentSamples(const IAudioDto &audioDto)
Add samples.
void addSilentSamples(const IAudioDto &audioDto, uint frequency, float distanceRatio)
Add samples.
void setFrequency(const uint &frequencyHz)
Frequency.
void receivingCallsignsChanged(const TransceiverReceivingCallsignsChangedArgs &args)
Receving callsigns have changed.
int activeCallsigns() const
Number of active callsign.
int readSamples(QVector< float > &samples, qint64 count)
Read samples.
void addOpusSamples(const IAudioDto &audioDto, uint frequency, float distanceRatio)
Add samples.
T get() const
Get a copy of the current value.
Definition: valuecache.h:408
static const QString & vatsimSpecific()
VATSIM specific.
static const QString & audio()
Audio related.
Definition: logcategories.h:52
Class for emitting a log message.
Definition: logmessage.h:27
Derived & debug()
Set the severity to debug.
Value object encapsulating information of audio related settings.
Definition: audiosettings.h:25
bool afvClicked() const
Simplified functions.
Definition: audiosettings.h:79
bool afvBlocked() const
Simplified functions.
Definition: audiosettings.h:80
Value object for a set of callsigns.
Definition: callsignset.h:26
void addMixerInput(ISampleProvider *provider)
Add a provider.
void setGain(double gain)
Set the gain.
void setFrequency(double frequencyHz)
Set frequency in Hz.
virtual int readSamples(QVector< float > &samples, qint64 count)
Read samples.
Free functions in swift::misc.
QString classNameShort(const QObject *object)
Class name as from QMetaObject::className without namespace.
QString callsign
Callsign that audio originates from.
Definition: dto.h:277