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 <QtMultimedia/private/qaudio_qspan_support_p.h>
7#include <QtMultimedia/private/qmultimedia_ranges_p.h>
8#include <QtMultimedia/private/qmultimedia_enum_to_string_converter_p.h>
9#include <QtCore/qdebug.h>
10
11#include <limits>
12
13QT_BEGIN_NAMESPACE
14
16{
17
18#if defined(Q_CC_GNU)
19# define QT_MM_RESTRICT __restrict__
20#elif defined(Q_CC_MSVC)
21# define QT_MM_RESTRICT __restrict
22#else
23# define QT_MM_RESTRICT /*__restrict__*/
24#endif
25
26namespace {
27
28template<class T>
29inline T applyVolumeOnSample(T sample, float factor)
30{
31 if constexpr (std::is_signed_v<T>) {
32 return sample * factor;
33 } else {
34 using SignedT = std::make_signed_t<T>;
35 // Unsigned samples are biased around 0x80/0x8000
36 constexpr T offset = SignedT(1) << (std::numeric_limits<T>::digits - 1);
37 return offset + (SignedT(sample - offset) * factor);
38 }
39}
40
41template<class T>
42void adjustSamples(float factor,
43 const void *QT_MM_RESTRICT src,
44 void *QT_MM_RESTRICT dst,
45 int samples)
46{
47 const T *pSrc = (const T *)src;
48 T *pDst = (T *)dst;
49
50 for (int i = 0; i < samples; i++)
51 pDst[i] = applyVolumeOnSample(pSrc[i], factor);
52}
53
54} // namespace
55
57 const QAudioFormat &format,
58 const void *src,
59 void *dest,
60 int len) noexcept QT_MM_NONBLOCKING
61{
62 const int samplesCount = len / qMax(1, format.bytesPerSample());
63
64 auto clamp = [](float arg) {
65 float realVolume = std::clamp<float>(arg, 0.f, 1.f);
66 return realVolume;
67 };
68
69 switch (format.sampleFormat()) {
70 case QAudioFormat::UInt8:
72 case QAudioFormat::Int16:
74 case QAudioFormat::Int32:
76 case QAudioFormat::Float:
78 default:
80 }
81}
82
84 const QAudioFormat &format,
85 QSpan<const std::byte> source,
87{
89
90 if (Q_LIKELY(volume == 1.f)) {
91 if (source.data() != destination.data())
93 } else if (volume == 0) {
94 std::byte zero =
95 format.sampleFormat() == QAudioFormat::UInt8 ? std::byte{ 0x80 } : std::byte{ 0 };
96
98 } else {
100 source.size());
101 }
102}
103
104namespace {
105
106template <NativeSampleFormat Format>
108{
109 switch (Format) {
111 constexpr int32_t offset = (1 << 7);
113 int32_t val = (int32_t(smp) - offset) << 24;
114 return val;
115 }
117 union CastUnion {
118 std::int16_t i16;
119 std::byte b[2];
120 } castUnion = {};
121
123
124 int32_t val = castUnion.i16 << 16;
125 return val;
126 }
128 union CastUnion {
129 std::int32_t i32;
130 std::byte b[4];
131 } castUnion = {};
132
134
135 int32_t val = castUnion.i32 << 8;
136 return val;
137 }
139 union CastUnion {
140 std::int32_t i32;
141 std::byte b[4];
142 } castUnion = {};
143
145 int32_t val = castUnion.i32 << 8;
146 return val;
147 }
149 union CastUnion {
150 float f32;
151 std::byte b[4];
152 } castUnion = {};
153
155 constexpr double range = std::numeric_limits<int32_t>::max();
157 return val;
158 }
160 union CastUnion {
161 std::int32_t i32;
162 std::byte b[4];
163 } castUnion = {};
164
166 return castUnion.i32;
167 }
168
169 default:
171 }
172}
173
174template <NativeSampleFormat Format>
177{
178 switch (Format) {
180 value = value >> 24;
181 constexpr int32_t offset = 1 << 7;
183 std::copy_n(reinterpret_cast<const std::byte *>(&sampleValue), 1, destination.data());
184 return;
185 }
187 value = value >> 16;
189 std::copy_n(reinterpret_cast<const std::byte *>(&sampleValue), 2, destination.data());
190 return;
191 }
192
195 std::copy_n(reinterpret_cast<const std::byte *>(&sampleValue), 3, destination.data());
196 return;
197 }
198
200 int32_t sampleValue = (value >> 8) & 0x00ffffff;
201 std::copy_n(reinterpret_cast<const std::byte *>(&sampleValue), 4, destination.data());
202 return;
203 }
205 std::copy_n(reinterpret_cast<const std::byte *>(&value), 4, destination.data());
206 return;
207 }
209 constexpr double normalizationFactor = 1.0 / std::numeric_limits<int32_t>::max();
210 float sampleValue = float(double(value) * normalizationFactor);
211 std::copy_n(reinterpret_cast<const std::byte *>(&sampleValue), 4, destination.data());
212 return;
213 }
214 default:
216 }
217}
218
220struct WordConverter
221{
223 {
224 if constexpr (sourceFormat == destinationFormat) {
226 } else {
229
230 using namespace QtMultimediaPrivate;
231 while (!source.isEmpty()) {
232 if (size_t(source.size()) >= 4 * bytesPerSampleSource) {
233 // unroll by 4.
234 // should help the compiler to vectorize, or at least avoid pipeline stalls.
235
244
249
258
263 } else {
266
269
272 }
273 }
274 }
275 }
276};
277
282{
283 switch (sourceFormat) {
299 default:
301 }
302}
303
304} // namespace
305
309{
310 using namespace QtMultimediaPrivate;
311
314
316
317 switch (destinationFormat) {
336 default:
338 }
339}
340
343{
345
349
350 if (it != candidates.end())
351 return *it;
352
353 qFatal() << "No candidate for conversion found. That should not happen";
355 };
356
357 // heuristics:
358 // select the best format that does not involve precision
359 // if that does not yield a format, find a format with the least loss of precision.
360 switch (fmt.sampleFormat()) {
362 constexpr auto candidates = std::array{
366 };
367
369 }
371 constexpr auto candidates = std::array{
375 };
376
378 }
380 constexpr auto candidates = std::array{
384
385 };
386
388 }
390 constexpr auto candidates = std::array{
394 };
395
397 }
400 qFatal() << "bestNativeSampleFormat called with invalid argument" << fmt.sampleFormat();
402 }
403
404 default: {
406 }
407 }
408}
409
425
443
445{
446 constexpr float epsilon = 1.f / (1 << 22); // good enough for 22bit resolution
447
448 // multiplication is optimized for 0 and 1
449 // values which are sufficiently close to 0 and 1 can be considered as 0 or 1
450 if (volume < epsilon)
451 volume = 0;
452 else if (volume > (1.f - epsilon))
453 volume = 1.f;
454
455 // similar to qFuzzyCompare, but with better heuristics for volume
456 if (std::abs(volume - lastValue) < epsilon)
457 return std::nullopt;
458
459 return volume;
460}
461
468
473
474} // namespace QAudioHelperInternal
475
476#undef QT_MM_RESTRICT
477
478// clang-format off
480 (QAudioHelperInternal::NativeSampleFormat::uint8_t, "uint8_t")
481 (QAudioHelperInternal::NativeSampleFormat::int16_t, "int16_t")
482 (QAudioHelperInternal::NativeSampleFormat::int32_t, "int32_t")
483 (QAudioHelperInternal::NativeSampleFormat::int24_t_3b, "int24_t_3b")
484 (QAudioHelperInternal::NativeSampleFormat::int24_t_4b_low, "int24_t_4b_low")
485 (QAudioHelperInternal::NativeSampleFormat::float32_t, "float32_t")
486 );
487// clang-format on
488
490
491QT_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)