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
audiogenerationutils_p.h
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#ifndef AUDIOGENERATIONUTILS_H
5#define AUDIOGENERATIONUTILS_H
6
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include <QAudioFormat>
20#include <QAudioBuffer>
21#include <QtCore/qmath.h>
22
23#include <chrono>
24#include <cmath>
25#include <cstdint>
26#include <limits>
27
28QT_BEGIN_NAMESPACE
29
30class SineWaveGenerator
31{
32public:
33 static constexpr double tau = 2 * M_PI;
34
35 SineWaveGenerator(std::uint32_t phase, std::uint32_t increment)
36 : m_phase(phase), m_increment(increment)
37 {
38 }
39
40 double getSample() const
41 {
42 constexpr double scale = static_cast<double>(UINT32_MAX);
43 return std::sin(tau * (static_cast<double>(m_phase) / scale));
44 }
45
46 void advance()
47 {
48 m_phase += m_increment; // wraps naturally
49 }
50
51 double operator()()
52 {
53 double result = getSample();
54 advance();
55 return result;
56 }
57
58private:
59 std::uint32_t m_phase;
60 std::uint32_t m_increment;
61};
62
64{
65 class iterator
66 {
67 public:
68 using iterator_category = std::input_iterator_tag;
69 using value_type = double;
70 using difference_type = std::ptrdiff_t;
71 using pointer = void;
72 using reference = double;
73
74 iterator(std::uint32_t phase, std::uint32_t increment) : m_generator(phase, increment) { }
75
76 double operator*() const { return m_generator.getSample(); }
77
78 iterator &operator++()
79 {
80 m_generator.advance();
81 return *this;
82 }
83
84 bool operator!=(std::nullptr_t) const
85 {
86 return true; // infinite range
87 }
88
89 private:
90 SineWaveGenerator m_generator;
91 };
92
93public:
94 SineWaveSignal(double frequency, double sample_rate, double phase = 0.0)
95 {
96 constexpr auto scale = double(std::numeric_limits<std::uint32_t>::max());
97 double normalizedPhase = std::fmod(phase, SineWaveGenerator::tau);
98 if (normalizedPhase < 0.0)
99 normalizedPhase += SineWaveGenerator::tau;
100 m_phase = std::uint32_t((normalizedPhase / SineWaveGenerator::tau) * scale);
101 m_increment = std::uint32_t((frequency / sample_rate) * scale);
102 }
103
104 iterator begin() const
105 {
106 return iterator{
107 m_phase,
108 m_increment,
109 };
110 }
111 std::nullptr_t end() const { return nullptr; }
112
113private:
114 std::uint32_t m_phase;
115 std::uint32_t m_increment;
116};
117
118inline QByteArray createSineWaveData(const QAudioFormat &format, std::chrono::microseconds duration,
119 qint32 sampleIndex = 0, qreal frequency = 500,
120 qreal volume = 0.8)
121{
122 if (!format.isValid())
123 return {};
124
125 const qint32 length = format.bytesForDuration(duration.count());
126
127 QByteArray data(format.bytesForDuration(duration.count()), Qt::Uninitialized);
128 unsigned char *ptr = reinterpret_cast<unsigned char *>(data.data());
129 const auto end = ptr + length;
130
131 auto writeNextFrame = [&](auto value) {
132 Q_ASSERT(sizeof(value) == format.bytesPerSample());
133 *reinterpret_cast<decltype(value) *>(ptr) = value;
134 ptr += sizeof(value);
135 };
136
137 const double initialPhase = 2.0 * M_PI * frequency * sampleIndex / format.sampleRate();
138 SineWaveSignal generator(frequency, format.sampleRate(), initialPhase);
139 for (double rawSample : generator) {
140 if (ptr >= end)
141 break;
142 const qreal x = rawSample * volume;
143 for (int ch = 0; ch < format.channelCount(); ++ch) {
144 switch (format.sampleFormat()) {
145 case QAudioFormat::UInt8:
146 writeNextFrame(static_cast<quint8>(std::round((1.0 + x) / 2 * 255)));
147 break;
148 case QAudioFormat::Int16:
149 writeNextFrame(
150 static_cast<qint16>(std::round(x * std::numeric_limits<qint16>::max())));
151 break;
152 case QAudioFormat::Int32:
153 writeNextFrame(
154 static_cast<qint32>(std::round(x * std::numeric_limits<qint32>::max())));
155 break;
156 case QAudioFormat::Float:
157 writeNextFrame(static_cast<float>(x));
158 break;
159 case QAudioFormat::Unknown:
160 case QAudioFormat::NSampleFormats:
161 break;
162 }
163 }
164 }
165
166 Q_ASSERT(ptr == end);
167
168 return data;
169}
170
172{
174public:
181
183 { //
185 }
186
188 { //
190 }
191
196
198 { //
200 }
201
203 { //
205 }
206
216
217signals:
218 void done();
219 void audioBufferCreated(const QAudioBuffer &buffer);
220
221public slots:
223{
225 emit done();
228 return;
229 }
230
232
235 }
236
237private:
238 int m_maxBufferCount = 1;
239 std::chrono::microseconds m_duration{ std::chrono::seconds{ 1 } };
240 int m_bufferIndex = 0;
241 QAudioFormat m_format;
242 bool m_emitEmptyBufferOnStop = false;
243 qreal m_frequency = 500.;
244 qint32 m_sampleIndex = 0;
245};
246
247QT_END_NAMESPACE
248
249#endif // AUDIOGENERATIONUTILS_H
QByteArray createSineWaveData(const QAudioFormat &format, std::chrono::microseconds duration, qint32 sampleIndex=0, qreal frequency=500, qreal volume=0.8)
void audioBufferCreated(const QAudioBuffer &buffer)
iterator begin() const
SineWaveSignal(double frequency, double sample_rate, double phase=0.0)
std::nullptr_t end() const
#define M_PI
Definition qmath.h:201