Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qaudiohelpers.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtCore/qdebug.h>
7#include <QtMultimedia/private/qaudio_qspan_support_p.h>
8#include <QtMultimedia/private/qmultimedia_enum_to_string_converter_p.h>
9
10#include <limits>
11
12QT_BEGIN_NAMESPACE
13
15{
16
17#if defined(Q_CC_GNU)
18# define QT_MM_RESTRICT __restrict__
19#elif defined(Q_CC_MSVC)
20# define QT_MM_RESTRICT __restrict
21#else
22# define QT_MM_RESTRICT /*__restrict__*/
23#endif
24
25namespace {
26
27template<class T>
28inline T applyVolumeOnSample(T sample, float factor)
29{
30 if constexpr (std::is_signed_v<T>) {
31 return sample * factor;
32 } else {
33 using SignedT = std::make_signed_t<T>;
34 // Unsigned samples are biased around 0x80/0x8000
35 constexpr T offset = SignedT(1) << (std::numeric_limits<T>::digits - 1);
36 return offset + (SignedT(sample - offset) * factor);
37 }
38}
39
40template<class T>
41void adjustSamples(float factor,
42 const void *QT_MM_RESTRICT src,
43 void *QT_MM_RESTRICT dst,
44 int samples)
45{
46 const T *pSrc = (const T *)src;
47 T *pDst = (T *)dst;
48
49 for (int i = 0; i < samples; i++)
50 pDst[i] = applyVolumeOnSample(pSrc[i], factor);
51}
52
53} // namespace
54
56 const QAudioFormat &format,
57 const void *src,
58 void *dest,
59 int len) noexcept QT_MM_NONBLOCKING
60{
61 const int samplesCount = len / qMax(1, format.bytesPerSample());
62
63 auto clamp = [](float arg) {
64 float realVolume = std::clamp<float>(arg, 0.f, 1.f);
65 return realVolume;
66 };
67
68 switch (format.sampleFormat()) {
69 case QAudioFormat::UInt8:
71 case QAudioFormat::Int16:
73 case QAudioFormat::Int32:
75 case QAudioFormat::Float:
77 default:
79 }
80}
81
83 const QAudioFormat &format,
84 QSpan<const std::byte> source,
86{
88
89 if (Q_LIKELY(volume == 1.f)) {
90 if (source.data() != destination.data())
92 } else if (volume == 0) {
93 std::byte zero =
94 format.sampleFormat() == QAudioFormat::UInt8 ? std::byte{ 0x80 } : std::byte{ 0 };
95
97 } else {
99 source.size());
100 }
101}
102
103namespace {
104
105template <NativeSampleFormat Format>
107{
108 switch (Format) {
110 constexpr int32_t offset = (1 << 7);
112 int32_t val = (int32_t(smp) - offset) << 24;
113 return val;
114 }
116 union CastUnion {
117 std::int16_t i16;
118 std::byte b[2];
119 } castUnion = {};
120
122
123 int32_t val = castUnion.i16 << 16;
124 return val;
125 }
127 union CastUnion {
128 std::int32_t i32;
129 std::byte b[4];
130 } castUnion = {};
131
133
134 int32_t val = castUnion.i32 << 8;
135 return val;
136 }
138 union CastUnion {
139 std::int32_t i32;
140 std::byte b[4];
141 } castUnion = {};
142
144 int32_t val = castUnion.i32 << 8;
145 return val;
146 }
148 union CastUnion {
149 float f32;
150 std::byte b[4];
151 } castUnion = {};
152
154 constexpr double range = std::numeric_limits<int32_t>::max();
156 return val;
157 }
159 union CastUnion {
160 std::int32_t i32;
161 std::byte b[4];
162 } castUnion = {};
163
165 return castUnion.i32;
166 }
167
168 default:
170 }
171}
172
173template <NativeSampleFormat Format>
176{
177 switch (Format) {
179 value = value >> 24;
180 constexpr int32_t offset = 1 << 7;
182 std::copy_n(reinterpret_cast<const std::byte *>(&sampleValue), 1, destination.data());
183 return;
184 }
186 value = value >> 16;
188 std::copy_n(reinterpret_cast<const std::byte *>(&sampleValue), 2, destination.data());
189 return;
190 }
191
194 std::copy_n(reinterpret_cast<const std::byte *>(&sampleValue), 3, destination.data());
195 return;
196 }
197
199 int32_t sampleValue = (value >> 8) & 0x00ffffff;
200 std::copy_n(reinterpret_cast<const std::byte *>(&sampleValue), 4, destination.data());
201 return;
202 }
204 std::copy_n(reinterpret_cast<const std::byte *>(&value), 4, destination.data());
205 return;
206 }
208 constexpr double normalizationFactor = 1.0 / std::numeric_limits<int32_t>::max();
209 float sampleValue = float(double(value) * normalizationFactor);
210 std::copy_n(reinterpret_cast<const std::byte *>(&sampleValue), 4, destination.data());
211 return;
212 }
213 default:
215 }
216}
217
219struct WordConverter
220{
222 {
223 if constexpr (sourceFormat == destinationFormat) {
225 } else {
228
229 using namespace QtMultimediaPrivate;
230 while (!source.isEmpty()) {
231 if (size_t(source.size()) >= 4 * bytesPerSampleSource) {
232 // unroll by 4.
233 // should help the compiler to vectorize, or at least avoid pipeline stalls.
234
243
248
257
262 } else {
265
268
271 }
272 }
273 }
274 }
275};
276
281{
282 switch (sourceFormat) {
298 default:
300 }
301}
302
303} // namespace
304
308{
309 using namespace QtMultimediaPrivate;
310
313
315
316 switch (destinationFormat) {
335 default:
337 }
338}
339
342{
344
348
349 if (it != candidates.end())
350 return *it;
351
352 qFatal() << "No candidate for conversion found. That should not happen";
354 };
355
356 // heuristics:
357 // select the best format that does not involve precision
358 // if that does not yield a format, find a format with the least loss of precision.
359 switch (fmt.sampleFormat()) {
361 constexpr auto candidates = std::array{
365 };
366
368 }
370 constexpr auto candidates = std::array{
374 };
375
377 }
379 constexpr auto candidates = std::array{
383
384 };
385
387 }
389 constexpr auto candidates = std::array{
393 };
394
396 }
399 qFatal() << "bestNativeSampleFormat called with invalid argument" << fmt.sampleFormat();
401 }
402
403 default: {
405 }
406 }
407}
408
424
442
444{
445 constexpr float epsilon = 1.f / (1 << 22); // good enough for 22bit resolution
446
447 // multiplication is optimized for 0 and 1
448 // values which are sufficiently close to 0 and 1 can be considered as 0 or 1
449 if (volume < epsilon)
450 volume = 0;
451 else if (volume > (1.f - epsilon))
452 volume = 1.f;
453
454 // similar to qFuzzyCompare, but with better heuristics for volume
455 if (std::abs(volume - lastValue) < epsilon)
456 return std::nullopt;
457
458 return volume;
459}
460
461} // namespace QAudioHelperInternal
462
463#undef QT_MM_RESTRICT
464
465// clang-format off
467 (QAudioHelperInternal::NativeSampleFormat::uint8_t, "uint8_t")
468 (QAudioHelperInternal::NativeSampleFormat::int16_t, "int16_t")
469 (QAudioHelperInternal::NativeSampleFormat::int32_t, "int32_t")
470 (QAudioHelperInternal::NativeSampleFormat::int24_t_3b, "int24_t_3b")
471 (QAudioHelperInternal::NativeSampleFormat::int24_t_4b_low, "int24_t_4b_low")
472 (QAudioHelperInternal::NativeSampleFormat::float32_t, "float32_t")
473 );
474// clang-format on
475
477
478QT_END_NAMESPACE
QT_MM_MAKE_STRING_RESOLVER(QAudioHelperInternal::NativeSampleFormat, QtMultimediaPrivate::EnumName,(QAudioHelperInternal::NativeSampleFormat::uint8_t, "uint8_t")(QAudioHelperInternal::NativeSampleFormat::int16_t, "int16_t")(QAudioHelperInternal::NativeSampleFormat::int32_t, "int32_t")(QAudioHelperInternal::NativeSampleFormat::int24_t_3b, "int24_t_3b")(QAudioHelperInternal::NativeSampleFormat::int24_t_4b_low, "int24_t_4b_low")(QAudioHelperInternal::NativeSampleFormat::float32_t, "float32_t"))
#define QT_MM_RESTRICT
QT_MM_DEFINE_QDEBUG_ENUM(QAudioHelperInternal::NativeSampleFormat)