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 <QtMultimedia/qaudioformat.h>
20#include <QtMultimedia/qaudiobuffer.h>
21#include <QtCore/qiodevice.h>
22#include <QtCore/qmath.h>
23#include <QtCore/qtemporaryfile.h>
24
25#include <chrono>
26#include <cmath>
27#include <cstdint>
28#include <limits>
29#include <memory>
30
31QT_BEGIN_NAMESPACE
32
33class SineWaveGenerator
34{
35public:
36 static constexpr double tau = 2 * M_PI;
37
38 SineWaveGenerator(std::uint32_t phase, std::uint32_t increment)
39 : m_phase(phase), m_increment(increment)
40 {
41 }
42
43 SineWaveGenerator(double frequency, double sampleRate, double phase = 0.0)
44 : SineWaveGenerator(phaseToFixedPoint(phase), incrementFromFrequency(frequency, sampleRate))
45 {
46 }
47
48 static std::uint32_t phaseToFixedPoint(double phase)
49 {
50 constexpr double scale = static_cast<double>(std::numeric_limits<std::uint32_t>::max());
51 double normalizedPhase = std::fmod(phase, tau);
52 if (normalizedPhase < 0.0)
53 normalizedPhase += tau;
54 return std::uint32_t((normalizedPhase / tau) * scale);
55 }
56
57 static std::uint32_t incrementFromFrequency(double frequency, double sampleRate)
58 {
59 constexpr double scale = static_cast<double>(std::numeric_limits<std::uint32_t>::max());
60 return std::uint32_t((frequency / sampleRate) * scale);
61 }
62
63 double getSample() const
64 {
65 constexpr double scale = static_cast<double>(UINT32_MAX);
66 return std::sin(tau * (static_cast<double>(m_phase) / scale));
67 }
68
69 void advance()
70 {
71 m_phase += m_increment; // wraps naturally
72 }
73
74 double operator()()
75 {
76 double result = getSample();
77 advance();
78 return result;
79 }
80
81private:
82 std::uint32_t m_phase;
83 std::uint32_t m_increment;
84};
85
87{
88 class iterator
89 {
90 public:
91 using iterator_category = std::input_iterator_tag;
92 using value_type = double;
93 using difference_type = std::ptrdiff_t;
94 using pointer = void;
95 using reference = double;
96
97 iterator(std::uint32_t phase, std::uint32_t increment) : m_generator(phase, increment) { }
98
99 double operator*() const { return m_generator.getSample(); }
100
101 iterator &operator++()
102 {
103 m_generator.advance();
104 return *this;
105 }
106
107 bool operator!=(std::nullptr_t) const
108 {
109 return true; // infinite range
110 }
111
112 private:
113 SineWaveGenerator m_generator;
114 };
115
116public:
117 SineWaveSignal(double frequency, double sample_rate, double phase = 0.0)
118 : m_phase(SineWaveGenerator::phaseToFixedPoint(phase)),
119 m_increment(SineWaveGenerator::incrementFromFrequency(frequency, sample_rate))
120 {
121 }
122
123 iterator begin() const
124 {
125 return iterator{
126 m_phase,
127 m_increment,
128 };
129 }
130 std::nullptr_t end() const { return nullptr; }
131
132private:
133 std::uint32_t m_phase;
134 std::uint32_t m_increment;
135};
136
137unsigned char *writeSineSample(unsigned char *ptr, QAudioFormat::SampleFormat sampleFormat,
138 qreal x);
139
140QByteArray createSineWaveData(const QAudioFormat &format, std::chrono::microseconds duration,
141 qint32 sampleIndex = 0, qreal frequency = 500, qreal volume = 0.8);
142
143// Writes a mono, 16-bit PCM WAV file containing a sine wave to a new temporary file and
144// returns it (already closed, ready to be opened for reading via its fileName()).
146 std::chrono::microseconds duration,
147 qreal frequency, qreal volume = 0.8);
148
150{
151public:
152 explicit SineWaveIODevice(const QAudioFormat &format, qreal frequency = 500, qreal volume = 0.8)
153 : m_format(format), m_generator(frequency, format.sampleRate()), m_volume(volume)
154 {
155 open(QIODeviceBase::ReadOnly);
156 }
157
158 qint64 readData(char *data, qint64 len) override
159 {
160 unsigned char *ptr = reinterpret_cast<unsigned char *>(data);
161 const unsigned char *const end = ptr + len - (len % m_format.bytesPerFrame());
162
163 while (ptr < end) {
164 const qreal x = m_generator() * m_volume;
165 for (int ch = 0; ch < m_format.channelCount(); ++ch)
166 ptr = writeSineSample(ptr, m_format.sampleFormat(), x);
167 }
168
169 return ptr - reinterpret_cast<unsigned char *>(data);
170 }
171
172 qint64 writeData(const char *, qint64) override { return 0; }
173 bool isSequential() const override { return true; }
174 qint64 bytesAvailable() const override { return std::numeric_limits<qint64>::max(); }
175
176private:
177 QAudioFormat m_format;
178 SineWaveGenerator m_generator;
179 qreal m_volume;
180};
181
183{
185public:
192
194 { //
196 }
197
199 { //
201 }
202
207
209 { //
211 }
212
214 { //
216 }
217
227
228signals:
229 void done();
230 void audioBufferCreated(const QAudioBuffer &buffer);
231
232public slots:
234 {
236 emit done();
239 return;
240 }
241
243
246 }
247
248private:
249 int m_maxBufferCount = 1;
250 std::chrono::microseconds m_duration{ std::chrono::seconds{ 1 } };
251 int m_bufferIndex = 0;
252 QAudioFormat m_format;
253 bool m_emitEmptyBufferOnStop = false;
254 qreal m_frequency = 500.;
255 qint32 m_sampleIndex = 0;
256};
257
258QT_END_NAMESPACE
259
260#endif // AUDIOGENERATIONUTILS_H
QByteArray createSineWaveData(const QAudioFormat &format, std::chrono::microseconds duration, qint32 sampleIndex, qreal frequency, qreal volume)
std::unique_ptr< QTemporaryFile > makeMonoPcm16WavFile(int sampleRate, std::chrono::microseconds duration, qreal frequency, qreal volume)
QT_BEGIN_NAMESPACE unsigned char * writeSineSample(unsigned char *ptr, QAudioFormat::SampleFormat sampleFormat, qreal x)
void audioBufferCreated(const QAudioBuffer &buffer)
qint64 bytesAvailable() const override
Returns the number of bytes that are available for reading.
qint64 readData(char *data, qint64 len) override
Reads up to maxSize bytes from the device into data, and returns the number of bytes read or -1 if an...
SineWaveIODevice(const QAudioFormat &format, qreal frequency=500, qreal volume=0.8)
bool isSequential() const override
Returns true if this device is sequential; otherwise returns false.
qint64 writeData(const char *, qint64) override
Writes up to maxSize bytes from data to the device.
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