swift
audioutilities.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 
4 #include "audioutilities.h"
5 
6 #include <QAudioInput>
7 #include <QAudioSink>
8 #include <QAudioSource>
9 #include <QMediaDevices>
10 #include <QStringBuilder>
11 
12 #include "config/buildconfig.h"
14 
15 using namespace swift::config;
16 using namespace swift::misc::audio;
17 
18 namespace swift::sound
19 {
20  QVector<float> convertBytesTo32BitFloatPCM(const QByteArray &input)
21  {
22  int inputSamples = input.size() / 2; // 16 bit input, so 2 bytes per sample
23  QVector<float> output;
24  output.fill(0, inputSamples);
25 
26  for (int n = 0; n < inputSamples; n++)
27  {
28  output[n] = *reinterpret_cast<const qint16 *>(input.data() + n * 2);
29  output[n] /= 32767.0f;
30  }
31  return output;
32  }
33 
34  QVector<qint16> convertBytesTo16BitPCM(const QByteArray &input)
35  {
36  const int inputSamples = input.size() / 2; // 16 bit input, so 2 bytes per sample
37  QVector<qint16> output;
38  output.fill(0, inputSamples);
39  for (int n = 0; n < inputSamples; n++) { output[n] = *reinterpret_cast<const qint16 *>(input.data() + n * 2); }
40  return output;
41  }
42 
43  QVector<float> convertFromMonoToStereo(const QVector<float> &mono)
44  {
45  QVector<float> stereo;
46  stereo.reserve(mono.size() * 2);
47  for (float sample : mono)
48  {
49  stereo << sample;
50  stereo << sample;
51  }
52  return stereo;
53  }
54 
55  QVector<qint16> convertFromStereoToMono(const QVector<qint16> &stereo)
56  {
57  QVector<qint16> mono;
58  mono.reserve(stereo.size() / 2);
59  for (int i = 0; i < stereo.size(); i = i + 2) { mono.append(stereo.at(i)); }
60  return mono;
61  }
62 
63  QVector<float> convertFromShortToFloat(const QVector<qint16> &input)
64  {
65  QVector<float> output;
66  for (auto sample : input) { output.push_back(sample / 32768.0f); }
67  return output;
68  }
69 
70  QAudioDevice getLowestLatencyDevice(const CAudioDeviceInfo &device, QAudioFormat &format)
71  {
72  if (device.isDefault() || !device.isValid())
73  {
74  const QAudioDevice defDevice =
75  device.isInputDevice() ? QMediaDevices::defaultAudioInput() : QMediaDevices::defaultAudioOutput();
76  Q_ASSERT_X(defDevice.isFormatSupported(format), Q_FUNC_INFO, "Device does not support format");
77  return defDevice;
78  }
79 
80  const QList<QAudioDevice> allQtDevices = device.isInputDevice() ? CAudioDeviceInfoList::allQtInputDevices() :
81  CAudioDeviceInfoList::allQtOutputDevices();
82 
83  // Find the one with lowest latency.
84  QList<QAudioDevice> supportedDevices;
85  for (const QAudioDevice &d : allQtDevices)
86  {
87  if (d.description() == device.getName() && d.isFormatSupported(format)) { supportedDevices.push_back(d); }
88  }
89 
90  if (supportedDevices.isEmpty()) { return {}; }
91 
92  QAudioDevice deviceWithLowestLatency = supportedDevices.at(0);
93 
94  if (supportedDevices.size() > 1)
95  {
96  int lowestBufferSize = std::numeric_limits<int>::max();
97  for (const QAudioDevice &d : supportedDevices)
98  {
99  if (!d.isFormatSupported(format)) continue;
100  int bufferSize = 0;
101  if (device.getType() == CAudioDeviceInfo::InputDevice)
102  {
103  QAudioSource input(d, format);
104  input.start();
105  input.stop();
106  bufferSize = input.bufferSize();
107  }
108  else
109  {
110  QAudioSink output(d, format);
111  output.start();
112  output.stop();
113  bufferSize = output.bufferSize();
114  }
115 
116  if (bufferSize < lowestBufferSize)
117  {
118  deviceWithLowestLatency = d;
119  lowestBufferSize = bufferSize;
120  }
121  }
122  }
123  return deviceWithLowestLatency;
124  }
125 
126  QAudioDevice getHighestCompatibleOutputDevice(const CAudioDeviceInfo &device, QAudioFormat &format)
127  {
128  if (device.isDefault()) { return CAudioDeviceInfoList::defaultQtOutputDevice(); }
129  const QList<QAudioDevice> allQtDevices = CAudioDeviceInfoList::allQtOutputDevices();
130 
131  QList<QAudioDevice> supportedDevices;
132  for (const QAudioDevice &d : allQtDevices)
133  {
134  if (d.description() == device.getName())
135  {
136  // exact match, format supported
137  if (d.isFormatSupported(format)) { return d; }
138  }
139  supportedDevices.push_back(d);
140  }
141 
142  // no suitable device
143  if (supportedDevices.isEmpty())
144  {
145  format = QAudioFormat();
146  return {};
147  }
148 
149  // here we could "search the best device", currently only first is taken
150  QAudioDevice usedDevice = supportedDevices.front();
151  Q_ASSERT_X(usedDevice.isFormatSupported(format), Q_FUNC_INFO, "Device does not support format");
152  return usedDevice;
153  }
154 
155  QString toQString(const QAudioFormat &format)
156  {
157  return QStringLiteral("Sample rate: %1 channels: %2 sample format: %3 bytes/frame: %4")
158  .arg(format.sampleRate())
159  .arg(format.channelCount())
160  .arg(format.sampleFormat())
161  .arg(format.bytesPerFrame());
162  }
163 
164  const QString &toQString(QSysInfo::Endian s)
165  {
166  static const QString l("little");
167  static const QString b("big");
168  switch (s)
169  {
170  case QSysInfo::BigEndian: return b;
171  case QSysInfo::LittleEndian: return l;
172  default: break;
173  }
174  static const QString u("??");
175  return u;
176  }
177 
178  const QString &toQString(QAudioFormat::SampleFormat e)
179  {
180  static const QString s("signed int 16");
181  static const QString u("unsigned int 8");
182  static const QString f("float");
183  switch (e)
184  {
185  case QAudioFormat::Int16: return s;
186  case QAudioFormat::UInt8: return u;
187  case QAudioFormat::Float: return f;
188  case QAudioFormat::Unknown:
189  default: break;
190  }
191  static const QString unknown("unknown");
192  return unknown;
193  }
194 
195  double normalize0to100(double in)
196  {
197  if (in < 0) { return 0; }
198  return (in >= 1.0) ? 1.0 : in;
199  }
200 
201  qreal normalize0to100qr(double in) { return static_cast<qreal>(normalize0to100(in)); }
202 
203  void occupyAudioInputDevice() { static const QAudioInput input(QMediaDevices::defaultAudioInput()); }
204 
205 } // namespace swift::sound
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
Value object encapsulating information of a audio device.
bool isInputDevice() const
Input device.
bool isDefault() const
Is this a default device?
bool isValid() const
Valid audio device object?
const QString & getName() const
Get the device name.
Value object encapsulating a list of audio devices.