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 <chrono>
22#include <limits>
23
24QT_BEGIN_NAMESPACE
25
26inline QByteArray createSineWaveData(const QAudioFormat &format, std::chrono::microseconds duration,
27 qint32 sampleIndex = 0, qreal frequency = 500,
28 qreal volume = 0.8)
29{
30 if (!format.isValid())
31 return {};
32
33 const qint32 length = format.bytesForDuration(microseconds: duration.count());
34
35 QByteArray data(format.bytesForDuration(microseconds: duration.count()), Qt::Uninitialized);
36 unsigned char *ptr = reinterpret_cast<unsigned char *>(data.data());
37 const auto end = ptr + length;
38
39 auto writeNextFrame = [&](auto value) {
40 Q_ASSERT(sizeof(value) == format.bytesPerSample());
41 *reinterpret_cast<decltype(value) *>(ptr) = value;
42 ptr += sizeof(value);
43 };
44
45 for (; ptr < end; ++sampleIndex) {
46 const qreal x = sin(x: 2 * M_PI * frequency * sampleIndex / format.sampleRate()) * volume;
47 for (int ch = 0; ch < format.channelCount(); ++ch) {
48 switch (format.sampleFormat()) {
49 case QAudioFormat::UInt8:
50 writeNextFrame(static_cast<quint8>(std::round(x: (1.0 + x) / 2 * 255)));
51 break;
52 case QAudioFormat::Int16:
53 writeNextFrame(
54 static_cast<qint16>(std::round(x: x * std::numeric_limits<qint16>::max())));
55 break;
56 case QAudioFormat::Int32:
57 writeNextFrame(
58 static_cast<qint32>(std::round(x: x * std::numeric_limits<qint32>::max())));
59 break;
60 case QAudioFormat::Float:
61 writeNextFrame(static_cast<float>(x));
62 break;
63 case QAudioFormat::Unknown:
64 case QAudioFormat::NSampleFormats:
65 break;
66 }
67 }
68 }
69
70 Q_ASSERT(ptr == end);
71
72 return data;
73}
74
75class AudioGenerator : public QObject
76{
77 Q_OBJECT
78public:
79 AudioGenerator()
80 {
81 m_format.setSampleFormat(QAudioFormat::UInt8);
82 m_format.setSampleRate(8000);
83 m_format.setChannelConfig(QAudioFormat::ChannelConfigMono);
84 }
85
86 void setFormat(const QAudioFormat &format)
87 { //
88 m_format = format;
89 }
90
91 void setBufferCount(int count)
92 { //
93 m_maxBufferCount = std::max(a: count, b: 1);
94 }
95
96 void setDuration(std::chrono::microseconds duration)
97 { //
98 m_duration = duration;
99 }
100
101 void setFrequency(qreal frequency)
102 { //
103 m_frequency = frequency;
104 }
105
106 void emitEmptyBufferOnStop()
107 { //
108 m_emitEmptyBufferOnStop = true;
109 }
110
111 QAudioBuffer createAudioBuffer()
112 {
113 const std::chrono::microseconds bufferDuration = m_duration * (m_bufferIndex + 1) / m_maxBufferCount
114 - m_duration * m_bufferIndex / m_maxBufferCount;
115 QByteArray data = createSineWaveData(format: m_format, duration: bufferDuration, sampleIndex: m_sampleIndex, frequency: m_frequency);
116 Q_ASSERT(m_format.bytesPerSample());
117 m_sampleIndex += data.size() / m_format.bytesPerSample();
118 return QAudioBuffer(data, m_format);
119 }
120
121signals:
122 void done();
123 void audioBufferCreated(const QAudioBuffer &buffer);
124
125public slots:
126 void nextBuffer()
127{
128 if (m_bufferIndex == m_maxBufferCount) {
129 emit done();
130 if (m_emitEmptyBufferOnStop)
131 emit audioBufferCreated(buffer: {});
132 return;
133 }
134
135 const QAudioBuffer buffer = createAudioBuffer();
136
137 emit audioBufferCreated(buffer);
138 ++m_bufferIndex;
139 }
140
141private:
142 int m_maxBufferCount = 1;
143 std::chrono::microseconds m_duration{ std::chrono::seconds{ 1 } };
144 int m_bufferIndex = 0;
145 QAudioFormat m_format;
146 bool m_emitEmptyBufferOnStop = false;
147 qreal m_frequency = 500.;
148 qint32 m_sampleIndex = 0;
149};
150
151QT_END_NAMESPACE
152
153#endif // AUDIOGENERATIONUTILS_H
154

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtmultimedia/src/multimediatestlib/audiogenerationutils_p.h