swift
Loading...
Searching...
No Matches
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
15using namespace swift::config;
16using namespace swift::misc::audio;
17
18namespace 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++)
40 {
41 output[n] = *reinterpret_cast<const qint16 *>(input.data() + n * 2);
42 }
43 return output;
44 }
45
46 QVector<float> convertFromMonoToStereo(const QVector<float> &mono)
47 {
48 QVector<float> stereo;
49 stereo.reserve(mono.size() * 2);
50 for (float sample : mono)
51 {
52 stereo << sample;
53 stereo << sample;
54 }
55 return stereo;
56 }
57
58 QVector<qint16> convertFromStereoToMono(const QVector<qint16> &stereo)
59 {
60 QVector<qint16> mono;
61 mono.reserve(stereo.size() / 2);
62 for (int i = 0; i < stereo.size(); i = i + 2)
63 {
64 mono.append(stereo.at(i));
65 }
66 return mono;
67 }
68
69 QVector<float> convertFromShortToFloat(const QVector<qint16> &input)
70 {
71 QVector<float> output;
72 for (auto sample : input)
73 {
74 output.push_back(sample / 32768.0f);
75 }
76 return output;
77 }
78
79 QAudioDevice getLowestLatencyDevice(const CAudioDeviceInfo &device, QAudioFormat &format)
80 {
81 Q_ASSERT_X(device.getType() != CAudioDeviceInfo::Unknown, Q_FUNC_INFO, "Device type must not be unknown");
82 if (device.isDefault() || !device.isValid())
83 {
84 const QAudioDevice defDevice =
85 device.isInputDevice() ? QMediaDevices::defaultAudioInput() : QMediaDevices::defaultAudioOutput();
86
87 if (defDevice.isNull())
88 {
89 // No default device available
90 return {};
91 }
92
93 if (!defDevice.isFormatSupported(format))
94 {
95 // Device doesn't support the required format
96 return {};
97 }
98 return defDevice;
99 }
100
101 const QList<QAudioDevice> allQtDevices = device.isInputDevice() ? CAudioDeviceInfoList::allQtInputDevices() :
102 CAudioDeviceInfoList::allQtOutputDevices();
103
104 // Find the one with lowest latency.
105 QList<QAudioDevice> supportedDevices;
106 for (const QAudioDevice &d : allQtDevices)
107 {
108 if (d.description() == device.getName() && d.isFormatSupported(format))
109 {
110 supportedDevices.push_back(d);
111 }
112 }
113
114 if (supportedDevices.isEmpty())
115 {
116 return {};
117 }
118
119 QAudioDevice deviceWithLowestLatency = supportedDevices.at(0);
120
121 if (supportedDevices.size() > 1)
122 {
123 int lowestBufferSize = std::numeric_limits<int>::max();
124 for (const QAudioDevice &d : supportedDevices)
125 {
126 if (!d.isFormatSupported(format)) continue;
127 int bufferSize = 0;
128 if (device.getType() == CAudioDeviceInfo::InputDevice)
129 {
130 QAudioSource input(d, format);
131 input.start();
132 input.stop();
133 bufferSize = input.bufferSize();
134 }
135 else
136 {
137 QAudioSink output(d, format);
138 output.start();
139 output.stop();
140 bufferSize = output.bufferSize();
141 }
142
143 if (bufferSize < lowestBufferSize)
144 {
145 deviceWithLowestLatency = d;
146 lowestBufferSize = bufferSize;
147 }
148 }
149 }
150 return deviceWithLowestLatency;
151 }
152
153 QAudioDevice getHighestCompatibleOutputDevice(const CAudioDeviceInfo &device, QAudioFormat &format)
154 {
155 if (device.isDefault())
156 {
158 }
160
161 QList<QAudioDevice> supportedDevices;
162 for (const QAudioDevice &d : allQtDevices)
163 {
164 if (d.description() == device.getName())
165 {
166 // exact match, format supported
167 if (d.isFormatSupported(format))
168 {
169 return d;
170 }
171 }
172 supportedDevices.push_back(d);
173 }
174
175 // no suitable device
176 if (supportedDevices.isEmpty())
177 {
178 format = QAudioFormat();
179 return {};
180 }
181
182 // here we could "search the best device", currently only first is taken
183 QAudioDevice usedDevice = supportedDevices.front();
184 Q_ASSERT_X(usedDevice.isFormatSupported(format), Q_FUNC_INFO, "Device does not support format");
185 return usedDevice;
186 }
187
188 QString toQString(const QAudioFormat &format)
189 {
190 return QStringLiteral("Sample rate: %1 channels: %2 sample format: %3 bytes/frame: %4")
191 .arg(format.sampleRate())
192 .arg(format.channelCount())
193 .arg(format.sampleFormat())
194 .arg(format.bytesPerFrame());
195 }
196
197 const QString &toQString(QSysInfo::Endian s)
198 {
199 static const QString l("little");
200 static const QString b("big");
201 switch (s)
202 {
203 case QSysInfo::BigEndian: return b;
204 case QSysInfo::LittleEndian: return l;
205 default: break;
206 }
207 static const QString u("??");
208 return u;
209 }
210
211 const QString &toQString(QAudioFormat::SampleFormat e)
212 {
213 static const QString s("signed int 16");
214 static const QString u("unsigned int 8");
215 static const QString f("float");
216 switch (e)
217 {
218 case QAudioFormat::Int16: return s;
219 case QAudioFormat::UInt8: return u;
220 case QAudioFormat::Float: return f;
221 case QAudioFormat::Unknown:
222 default: break;
223 }
224 static const QString unknown("unknown");
225 return unknown;
226 }
227
228 double normalize0to100(double in)
229 {
230 if (in < 0)
231 {
232 return 0;
233 }
234 return (in >= 1.0) ? 1.0 : in;
235 }
236
237 qreal normalize0to100qr(double in) { return static_cast<qreal>(normalize0to100(in)); }
238
239 void occupyAudioInputDevice() { static const QAudioInput input(QMediaDevices::defaultAudioInput()); }
240
241} // namespace swift::sound
Value object encapsulating information of a audio device.
bool isInputDevice() const
Input device.
bool isDefault() const
Is this a default device?
const QString & getName() const
Get the device name.
bool isValid() const
Valid audio device object?
static QList< QAudioDevice > allQtOutputDevices()
Lists of all available devices.
static QList< QAudioDevice > allQtInputDevices()
Lists of all available devices.
static QAudioDevice defaultQtOutputDevice()
Lists of all available devices.
char * data()
qsizetype size() const const
void append(QList< T > &&value)
QList< T >::const_reference at(qsizetype i) const const
QList< T > & fill(QList< T >::parameter_type value, qsizetype size)
QList< T >::reference front()
bool isEmpty() const const
void push_back(QList< T >::parameter_type value)
void reserve(qsizetype size)
qsizetype size() const const
QString arg(Args &&... args) const const