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
75void QWasmAudioSource::start(QIODevice *device)
76{
77 m_device = device;
78 start(true);
79}
80
82{
83 m_device = new QWasmAudioSourceDevice(this);
84 m_device->open(QIODevice::ReadOnly);
85 start(false);
86 return m_device;
87}
88
89void QWasmAudioSource::start(bool mode)
90{
91 m_pullMode = mode;
92 auto formatError = [this](){
93 qWarning() << "Unsupported audio format " << m_format;
94 setError(QAudio::OpenError);
95 };
96 ALCenum format;
97 switch (m_format.sampleFormat()) {
98 case QAudioFormat::UInt8:
99 switch (m_format.channelCount()) {
100 case 1:
101 format = AL_FORMAT_MONO8;
102 break;
103 case 2:
104 format = AL_FORMAT_STEREO8;
105 break;
106 default:
107 return formatError();
108 }
109 break;
110 case QAudioFormat::Int16:
111 switch (m_format.channelCount()) {
112 case 1:
113 format = AL_FORMAT_MONO16;
114 break;
115 case 2:
116 format = AL_FORMAT_STEREO16;
117 break;
118 default:
119 return formatError();
120 }
121 break;
122 case QAudioFormat::Float:
123 switch (m_format.channelCount()) {
124 case 1:
125 format = AL_FORMAT_MONO_FLOAT32;
126 break;
127 case 2:
129 break;
130 default:
131 return formatError();
132 }
133 break;
134 default:
135 return formatError();
136 }
137 if (m_tmpData)
138 delete[] m_tmpData;
139 if (m_pullMode)
140 m_tmpData = new char[m_bufferSize];
141 else
142 m_tmpData = nullptr;
143 m_timer->setInterval(m_format.durationForBytes(m_bufferSize) / 3000);
144 m_timer->start();
145
146 alcGetError(aldata->device); // clear error state
147 aldata->device = alcCaptureOpenDevice(m_audioDevice.id().data(), m_format.sampleRate(), format,
148 m_format.framesForBytes(m_bufferSize));
149
150 auto err = alcGetError(aldata->device);
151 if (err) {
152 qWarning() << "alcCaptureOpenDevice" << alcGetString(aldata->device, err);
153 return setError(QAudio::OpenError);
154 }
155 alcCaptureStart(aldata->device);
156 m_elapsedTimer.start();
157 auto cerr = alcGetError(aldata->device);
158 if (cerr) {
159 qWarning() << "alcCaptureStart" << alcGetString(aldata->device, cerr);
160 return setError(QAudio::OpenError);
161 }
162 m_processed = 0;
163 m_running = true;
164}
165
167{
168 if (m_pullMode)
169 writeBuffer();
170 alcCaptureStop(aldata->device);
171 alcCaptureCloseDevice(aldata->device);
172 m_elapsedTimer.invalidate();
173 if (m_tmpData) {
174 delete[] m_tmpData;
175 m_tmpData = nullptr;
176 }
177 if (!m_pullMode)
178 m_device->deleteLater();
179 m_timer->stop();
180 m_running = false;
181}
182
184{
185 stop();
186 if (m_tmpData) {
187 delete[] m_tmpData;
188 m_tmpData = nullptr;
189 }
190 m_running = false;
191 m_processed = 0;
192 setError(QAudio::NoError);
193}
194
196{
197 if (!m_running)
198 return;
199
200 m_suspended = true;
201 alcCaptureStop(aldata->device);
202}
203
205{
206 if (!m_running)
207 return;
208
209 m_suspended = false;
210 alcCaptureStart(aldata->device);
211}
212
214{
215 if (!m_running)
216 return 0;
217 int samples;
218 alcGetIntegerv(aldata->device, ALC_CAPTURE_SAMPLES, 1, &samples);
219 return m_format.bytesForFrames(samples);
220}
221
222void QWasmAudioSource::setBufferSize(qsizetype value)
223{
224 if (!m_running)
225 return;
226 m_bufferSize = value;
227}
228
230{
231 return m_bufferSize;
232}
233
235{
236 return m_format.durationForBytes(m_processed);
237}
238
240{
241 if (m_running)
242 return QAudio::ActiveState;
243 else
244 return QAudio::StoppedState;
245}
246
251
252qint64 QWasmAudioSourceDevice::readData(char *data, qint64 maxlen)
253{
254 int samples;
255 alcGetIntegerv(m_in->aldata->device, ALC_CAPTURE_SAMPLES, 1, &samples);
256 samples = qMin(samples, m_in->m_format.framesForBytes(maxlen));
257 auto bytes = m_in->m_format.bytesForFrames(samples);
258 alcGetError(m_in->aldata->device);
259 alcCaptureSamples(m_in->aldata->device, data, samples);
260 if (m_in->volume() < 1)
261 QAudioHelperInternal::qMultiplySamples(m_in->volume(), m_in->m_format, data, data, bytes);
262 auto err = alcGetError(m_in->aldata->device);
263 if (err) {
264 qWarning() << alcGetString(m_in->aldata->device, err);
265 m_in->setError(QAudio::FatalError);
266 return 0;
267 }
268 m_in->m_processed += bytes;
269 return bytes;
270}
271
272qint64 QWasmAudioSourceDevice::writeData(const char *data, qint64 len)
273{
274 Q_UNREACHABLE();
275 Q_UNUSED(data);
276 Q_UNUSED(len);
277 return 0;
278}
279
280QT_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
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