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
qwasmaudiosource.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 <emscripten.h>
7#include <AL/al.h>
8#include <AL/alc.h>
9#include <QDataStream>
10#include <QDebug>
11#include <QtMath>
12#include <private/qaudiohelpers_p.h>
13#include <QIODevice>
14
16
17#define AL_FORMAT_MONO_FLOAT32 0x10010
18#define AL_FORMAT_STEREO_FLOAT32 0x10011
19
20constexpr unsigned int DEFAULT_BUFFER_DURATION = 50'000;
21
23{
24 QWasmAudioSource *m_in;
25
26public:
28
29protected:
30 qint64 readData(char *data, qint64 maxlen) override;
31 qint64 writeData(const char *data, qint64 len) override;
32};
33
34class ALData {
35public:
36 ALCdevice *device = nullptr;
37 ALCcontext *context = nullptr;
38};
39
40void QWasmAudioSource::writeBuffer()
41{
42 int samples = 0;
43 alcGetIntegerv(aldata->device, ALC_CAPTURE_SAMPLES, 1, &samples);
44 samples = qMin(samples, m_format.framesForBytes(m_bufferSize));
45 auto bytes = m_format.bytesForFrames(samples);
46 auto err = alcGetError(aldata->device);
47 alcCaptureSamples(aldata->device, m_tmpData, samples);
48 err = alcGetError(aldata->device);
49 if (err) {
50 qWarning() << alcGetString(aldata->device, err);
51 return setError(QAudio::FatalError);
52 }
53 if (volume() < 1)
54 QAudioHelperInternal::qMultiplySamples(volume(), m_format, m_tmpData, m_tmpData, bytes);
55 m_processed += bytes;
56 m_device->write(m_tmpData,bytes);
57}
58
59QWasmAudioSource::QWasmAudioSource(QAudioDevice device, const QAudioFormat &fmt, QObject *parent)
61{
62 if (device.id().contains("Emscripten")) {
63 aldata = new ALData();
64 connect(m_timer, &QTimer::timeout, this, [this](){
65 Q_ASSERT(m_running);
66 if (m_pullMode)
67 writeBuffer();
68 else if (bytesReady() > 0)
69 emit m_device->readyRead();
70 });
71 }
72 m_bufferSize = m_format.bytesForDuration(DEFAULT_BUFFER_DURATION);
73}
74
76 = default;
77
78void QWasmAudioSource::start(QIODevice *device)
79{
80 m_device = device;
81 start(true);
82}
83
85{
86 m_device = new QWasmAudioSourceDevice(this);
87 m_device->open(QIODevice::ReadOnly);
88 start(false);
89 return m_device;
90}
91
92void QWasmAudioSource::start(bool mode)
93{
94 m_pullMode = mode;
95 auto formatError = [this](){
96 qWarning() << "Unsupported audio format " << m_format;
97 setError(QAudio::OpenError);
98 };
99 ALCenum format;
100 switch (m_format.sampleFormat()) {
101 case QAudioFormat::UInt8:
102 switch (m_format.channelCount()) {
103 case 1:
104 format = AL_FORMAT_MONO8;
105 break;
106 case 2:
107 format = AL_FORMAT_STEREO8;
108 break;
109 default:
110 return formatError();
111 }
112 break;
113 case QAudioFormat::Int16:
114 switch (m_format.channelCount()) {
115 case 1:
116 format = AL_FORMAT_MONO16;
117 break;
118 case 2:
119 format = AL_FORMAT_STEREO16;
120 break;
121 default:
122 return formatError();
123 }
124 break;
125 case QAudioFormat::Float:
126 switch (m_format.channelCount()) {
127 case 1:
128 format = AL_FORMAT_MONO_FLOAT32;
129 break;
130 case 2:
132 break;
133 default:
134 return formatError();
135 }
136 break;
137 default:
138 return formatError();
139 }
140 if (m_tmpData)
141 delete[] m_tmpData;
142 if (m_pullMode)
143 m_tmpData = new char[m_bufferSize];
144 else
145 m_tmpData = nullptr;
146 m_timer->setInterval(m_format.durationForBytes(m_bufferSize) / 3000);
147 m_timer->start();
148
149 alcGetError(aldata->device); // clear error state
150 aldata->device = alcCaptureOpenDevice(m_audioDevice.id().data(), m_format.sampleRate(), format,
151 m_format.framesForBytes(m_bufferSize));
152
153 auto err = alcGetError(aldata->device);
154 if (err) {
155 qWarning() << "alcCaptureOpenDevice" << alcGetString(aldata->device, err);
156 return setError(QAudio::OpenError);
157 }
158 alcCaptureStart(aldata->device);
159 m_elapsedTimer.start();
160 auto cerr = alcGetError(aldata->device);
161 if (cerr) {
162 qWarning() << "alcCaptureStart" << alcGetString(aldata->device, cerr);
163 return setError(QAudio::OpenError);
164 }
165 m_processed = 0;
166 m_running = true;
167}
168
170{
171 if (m_pullMode)
172 writeBuffer();
173 alcCaptureStop(aldata->device);
174 alcCaptureCloseDevice(aldata->device);
175 m_elapsedTimer.invalidate();
176 if (m_tmpData) {
177 delete[] m_tmpData;
178 m_tmpData = nullptr;
179 }
180 if (!m_pullMode)
181 m_device->deleteLater();
182 m_timer->stop();
183 m_running = false;
184}
185
187{
188 stop();
189 if (m_tmpData) {
190 delete[] m_tmpData;
191 m_tmpData = nullptr;
192 }
193 m_running = false;
194 m_processed = 0;
195 setError(QAudio::NoError);
196}
197
199{
200 if (!m_running)
201 return;
202
203 m_suspended = true;
204 alcCaptureStop(aldata->device);
205}
206
208{
209 if (!m_running)
210 return;
211
212 m_suspended = false;
213 alcCaptureStart(aldata->device);
214}
215
217{
218 if (!m_running)
219 return 0;
220 int samples;
221 alcGetIntegerv(aldata->device, ALC_CAPTURE_SAMPLES, 1, &samples);
222 return m_format.bytesForFrames(samples);
223}
224
225void QWasmAudioSource::setBufferSize(qsizetype value)
226{
227 if (!m_running)
228 return;
229 m_bufferSize = value;
230}
231
233{
234 return m_bufferSize;
235}
236
238{
239 return m_format.durationForBytes(m_processed);
240}
241
243{
244 if (m_running)
245 return QAudio::ActiveState;
246 else
247 return QAudio::StoppedState;
248}
249
254
255qint64 QWasmAudioSourceDevice::readData(char *data, qint64 maxlen)
256{
257 int samples;
258 alcGetIntegerv(m_in->aldata->device, ALC_CAPTURE_SAMPLES, 1, &samples);
259 samples = qMin(samples, m_in->m_format.framesForBytes(maxlen));
260 auto bytes = m_in->m_format.bytesForFrames(samples);
261 alcGetError(m_in->aldata->device);
262 alcCaptureSamples(m_in->aldata->device, data, samples);
263 if (m_in->volume() < 1)
264 QAudioHelperInternal::qMultiplySamples(m_in->volume(), m_in->m_format, data, data, bytes);
265 auto err = alcGetError(m_in->aldata->device);
266 if (err) {
267 qWarning() << alcGetString(m_in->aldata->device, err);
268 m_in->setError(QAudio::FatalError);
269 return 0;
270 }
271 m_in->m_processed += bytes;
272 return bytes;
273}
274
275qint64 QWasmAudioSourceDevice::writeData(const char *data, qint64 len)
276{
277 Q_UNREACHABLE();
278 Q_UNUSED(data);
279 Q_UNUSED(len);
280 return 0;
281}
282
283QT_END_NAMESPACE
ALCcontext * context
ALCdevice * device
qint64 readData(char *data, qint64 maxlen) override
Reads up to maxSize bytes from the device into data, and returns the number of bytes read or -1 if an...
qint64 writeData(const char *data, qint64 len) override
Writes up to maxSize bytes from data to the device.
QWasmAudioSourceDevice(QWasmAudioSource *in)
qint64 processedUSecs() const override
QAudio::State state() const override
QIODevice * start() override
void suspend() override
qsizetype bufferSize() const override
void stop() override
~QWasmAudioSource() override
void setBufferSize(qsizetype value) override
void start(QIODevice *device) override
QWasmAudioSource(QAudioDevice, const QAudioFormat &, QObject *parent)
void start(bool mode)
void reset() override
qsizetype bytesReady() const override
void resume() override
#define AL_FORMAT_STEREO_FLOAT32
#define AL_FORMAT_MONO_FLOAT32
constexpr unsigned int DEFAULT_BUFFER_DURATION