swift
soundcardsampleprovider.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 "config/buildconfig.h"
7 #include "misc/metadatautils.h"
8 
9 using namespace swift::config;
10 using namespace swift::misc;
11 using namespace swift::sound::sample_provider;
12 
13 namespace swift::core::afv::audio
14 {
15  CSoundcardSampleProvider::CSoundcardSampleProvider(int sampleRate, const QVector<quint16> &transceiverIDs,
16  QObject *parent)
17  : ISampleProvider(parent), m_mixer(new CMixingSampleProvider())
18  {
19  const QString on = QStringLiteral("%1 sample rate: %2, transceivers: %3")
20  .arg(classNameShort(this))
21  .arg(sampleRate)
22  .arg(transceiverIDs.size());
23  this->setObjectName(on);
24 
25  m_waveFormat.setSampleRate(sampleRate);
26  m_waveFormat.setChannelCount(1);
27  m_waveFormat.setSampleFormat(QAudioFormat::Int16);
28  static_assert(Q_BYTE_ORDER == Q_LITTLE_ENDIAN);
29 
30  m_mixer = new CMixingSampleProvider(this);
31  m_receiverIDs = transceiverIDs;
32 
33  constexpr int voiceInputNumber = 4; // number of CallsignSampleProviders
34  for (quint16 transceiverID : transceiverIDs)
35  {
36  auto transceiverInput = new CReceiverSampleProvider(m_waveFormat, transceiverID, voiceInputNumber, m_mixer);
39  m_receiverInputs.push_back(transceiverInput);
40  m_receiverIDs.push_back(transceiverID);
41  m_mixer->addMixerInput(transceiverInput);
42  }
43  }
44 
46  {
47  for (CReceiverSampleProvider *receiverInput : std::as_const(m_receiverInputs))
48  {
49  receiverInput->setBypassEffects(value);
50  }
51  }
52 
53  void CSoundcardSampleProvider::pttUpdate(bool active, const QVector<TxTransceiverDto> &txTransceivers)
54  {
55  if (active)
56  {
57  if (!txTransceivers.isEmpty())
58  {
59  QVector<TxTransceiverDto> txTransceiversFiltered = txTransceivers;
60 
61  txTransceiversFiltered.erase(
62  std::remove_if(txTransceiversFiltered.begin(), txTransceiversFiltered.end(),
63  [this](const TxTransceiverDto &d) { return !m_receiverIDs.contains(d.id); }),
64  txTransceiversFiltered.end());
65 
66  for (const TxTransceiverDto &txTransceiver : txTransceiversFiltered)
67  {
68  auto it = std::find_if(
69  m_receiverInputs.begin(), m_receiverInputs.end(),
70  [txTransceiver](const CReceiverSampleProvider *p) { return p->getId() == txTransceiver.id; });
71 
72  if (it != m_receiverInputs.end()) { (*it)->setMute(true); }
73  }
74  }
75  }
76  else
77  {
78  for (CReceiverSampleProvider *receiverInput : std::as_const(m_receiverInputs))
79  {
80  receiverInput->setMute(false);
81  }
82  }
83  }
84 
86  {
87  return m_mixer->readSamples(samples, count);
88  }
89 
91  const QVector<RxTransceiverDto> &rxTransceivers)
92  {
93  QVector<RxTransceiverDto> rxTransceiversFilteredAndSorted = rxTransceivers;
94 
95  rxTransceiversFilteredAndSorted.erase(
96  std::remove_if(rxTransceiversFilteredAndSorted.begin(), rxTransceiversFilteredAndSorted.end(),
97  [this](const RxTransceiverDto &r) { return !m_receiverIDs.contains(r.id); }),
98  rxTransceiversFilteredAndSorted.end());
99 
100  std::sort(rxTransceiversFilteredAndSorted.begin(), rxTransceiversFilteredAndSorted.end(),
101  [](const RxTransceiverDto &a, const RxTransceiverDto &b) -> bool {
102  return a.distanceRatio > b.distanceRatio;
103  });
104 
105  if (!rxTransceiversFilteredAndSorted.isEmpty())
106  {
107  bool audioPlayed = false;
108  QVector<quint16> handledTransceiverIDs;
109  for (const auto rxTransceiver : rxTransceiversFilteredAndSorted)
110  {
111  if (!handledTransceiverIDs.contains(rxTransceiver.id))
112  {
113  handledTransceiverIDs.push_back(rxTransceiver.id);
114 
115  CReceiverSampleProvider *receiverInput = nullptr;
116  auto it = std::find_if(
117  m_receiverInputs.begin(), m_receiverInputs.end(),
118  [rxTransceiver](const CReceiverSampleProvider *p) { return p->getId() == rxTransceiver.id; });
119 
120  if (it != m_receiverInputs.end()) { receiverInput = *it; }
121 
122  if (!receiverInput) { continue; }
123  if (receiverInput->getMute()) { continue; }
124 
125  if (!audioPlayed)
126  {
127  receiverInput->addOpusSamples(audioDto, rxTransceiver.frequency, rxTransceiver.distanceRatio);
128  audioPlayed = true;
129  }
130  else
131  {
132  receiverInput->addSilentSamples(audioDto, rxTransceiver.frequency, rxTransceiver.distanceRatio);
133  }
134 
135  // debug ONLY
136  if (CBuildConfig::isLocalDeveloperDebugBuild())
137  {
138  receiverInput->logVoiceInputs(QStringLiteral("Transceiver %1 ").arg(rxTransceiver.id), 1500);
139  }
140  }
141  } // each transceiver
142  } // filtered rx transceivers
143  }
144 
146  {
147  for (const TransceiverDto &radioTransceiver : radioTransceivers)
148  {
149  auto it = std::find_if(
150  m_receiverInputs.begin(), m_receiverInputs.end(),
151  [radioTransceiver](const CReceiverSampleProvider *p) { return p->getId() == radioTransceiver.id; });
152 
153  if (it != m_receiverInputs.end()) { (*it)->setFrequency(radioTransceiver.frequencyHz); }
154  }
155 
156  for (CReceiverSampleProvider *receiverInput : std::as_const(m_receiverInputs))
157  {
158  const quint16 transceiverID = receiverInput->getId();
159  const bool contains = std::any_of(radioTransceivers.cbegin(), radioTransceivers.cend(),
160  [transceiverID](const auto &tx) { return transceiverID == tx.id; });
161  if (!contains) { receiverInput->setFrequency(0); }
162  }
163  }
164 
166  {
167  return m_receiverInputs.at(transceiverID)->getReceivingCallsignsString();
168  }
169 
170  bool CSoundcardSampleProvider::setGainRatioForTransceiver(quint16 transceiverID, double gainRatio)
171  {
172  auto receiverInput = std::find_if(m_receiverInputs.begin(), m_receiverInputs.end(),
173  [&](const auto receiver) { return receiver->getId() == transceiverID; });
174  if (receiverInput == m_receiverInputs.end()) { return false; }
175  return (*receiverInput)->setGainRatio(gainRatio);
176  }
177 
179  {
180  return m_receiverInputs.at(transceiverID)->getReceivingCallsigns();
181  }
182 
183 } // namespace swift::core::afv::audio
void addSilentSamples(const IAudioDto &audioDto, uint frequency, float distanceRatio)
Add samples.
void receivingCallsignsChanged(const TransceiverReceivingCallsignsChangedArgs &args)
Receving callsigns have changed.
void addOpusSamples(const IAudioDto &audioDto, uint frequency, float distanceRatio)
Add samples.
int readSamples(QVector< float > &samples, qint64 count)
Read samples.
bool setGainRatioForTransceiver(quint16 transceiverID, double gainRatio)
Setting gain for specified receiver.
swift::misc::aviation::CCallsignSet getReceivingCallsigns(quint16 transceiverID) const
Receiving callsign as single string.
void receivingCallsignsChanged(const TransceiverReceivingCallsignsChangedArgs &args)
Changed callsigns.
void updateRadioTransceivers(const QVector< TransceiverDto > &radioTransceivers)
Update all tranceivers.
QString getReceivingCallsignsString(quint16 transceiverID) const
Receiving callsign as single string.
void pttUpdate(bool active, const QVector< TxTransceiverDto > &txTransceivers)
Update PTT.
void addOpusSamples(const IAudioDto &audioDto, const QVector< RxTransceiverDto > &rxTransceivers)
Add OPUS samples.
Value object for a set of callsigns.
Definition: callsignset.h:26
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.
QList< T >::iterator begin()
QList< T >::const_iterator cbegin() const const
QList< T >::const_iterator cend() const const
QList< T >::iterator end()
QList< T >::iterator erase(QList< T >::const_iterator begin, QList< T >::const_iterator end)
bool isEmpty() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void setObjectName(QAnyStringView name)
QString arg(Args &&... args) const const
Receive transceiver DTO.
Definition: dto.h:206
Transceiver DTO.
Definition: dto.h:117
Transmit transceiver DTO.
Definition: dto.h:220