Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
41{
42 if (m_error == error)
43 return;
44 m_error = error;
46}
47
48void QWasmAudioSource::writeBuffer()
49{
50 int samples = 0;
51 alcGetIntegerv(aldata->device, ALC_CAPTURE_SAMPLES, 1, &samples);
52 samples = qMin(samples, m_format.framesForBytes(m_bufferSize));
53 auto bytes = m_format.bytesForFrames(samples);
54 auto err = alcGetError(aldata->device);
55 alcCaptureSamples(aldata->device, m_tmpData, samples);
56 err = alcGetError(aldata->device);
57 if (err) {
58 qWarning() << alcGetString(aldata->device, err);
60 }
61 if (m_volume < 1)
62 QAudioHelperInternal::qMultiplySamples(m_volume, m_format, m_tmpData, m_tmpData, bytes);
63 m_processed += bytes;
64 m_device->write(m_tmpData,bytes);
65}
66
68 : QPlatformAudioSource(parent),
69 m_name(device),
70 m_timer(new QTimer(this))
71{
72 if (device.contains("Emscripten")) {
73 aldata = new ALData();
74 connect(m_timer, &QTimer::timeout, this, [this](){
75 Q_ASSERT(m_running);
76 if (m_pullMode)
78 else if (bytesReady() > 0)
79 emit m_device->readyRead();
80 });
81 }
82}
83
85{
86 m_device = device;
87 start(true);
88}
89
91{
92 m_device = new QWasmAudioSourceDevice(this);
93 m_device->open(QIODevice::ReadOnly);
94 start(false);
95 return m_device;
96}
97
99{
100 m_pullMode = mode;
101 auto formatError = [this](){
102 qWarning() << "Unsupported audio format " << m_format;
104 };
105 ALCenum format;
106 switch (m_format.sampleFormat()) {
108 switch (m_format.channelCount()) {
109 case 1:
110 format = AL_FORMAT_MONO8;
111 break;
112 case 2:
113 format = AL_FORMAT_STEREO8;
114 break;
115 default:
116 return formatError();
117 }
118 break;
120 switch (m_format.channelCount()) {
121 case 1:
122 format = AL_FORMAT_MONO16;
123 break;
124 case 2:
125 format = AL_FORMAT_STEREO16;
126 break;
127 default:
128 return formatError();
129 }
130 break;
132 switch (m_format.channelCount()) {
133 case 1:
135 break;
136 case 2:
138 break;
139 default:
140 return formatError();
141 }
142 break;
143 default:
144 return formatError();
145 }
146 if (m_tmpData)
147 delete[] m_tmpData;
148 if (m_pullMode)
149 m_tmpData = new char[m_bufferSize];
150 else
151 m_tmpData = nullptr;
152 m_timer->setInterval(m_format.durationForBytes(m_bufferSize) / 3000);
153 m_timer->start();
154
155 alcGetError(aldata->device); // clear error state
156 aldata->device = alcCaptureOpenDevice(m_name.data(), m_format.sampleRate(), format,
157 m_format.framesForBytes(m_bufferSize));
158
159 auto err = alcGetError(aldata->device);
160 if (err) {
161 qWarning() << "alcCaptureOpenDevice" << alcGetString(aldata->device, err);
163 }
164 alcCaptureStart(aldata->device);
165 m_elapsedTimer.start();
166 auto cerr = alcGetError(aldata->device);
167 if (cerr) {
168 qWarning() << "alcCaptureStart" << alcGetString(aldata->device, cerr);
170 }
171 m_processed = 0;
172 m_running = true;
173}
174
176{
177 if (m_pullMode)
178 writeBuffer();
179 alcCaptureStop(aldata->device);
180 alcCaptureCloseDevice(aldata->device);
181 m_elapsedTimer.invalidate();
182 if (m_tmpData) {
183 delete[] m_tmpData;
184 m_tmpData = nullptr;
185 }
186 if (!m_pullMode)
187 m_device->deleteLater();
188 m_timer->stop();
189 m_running = false;
190}
191
193{
194 stop();
195 if (m_tmpData) {
196 delete[] m_tmpData;
197 m_tmpData = nullptr;
198 }
199 m_running = false;
200 m_processed = 0;
201 m_error = QAudio::NoError;
202}
203
205{
206 if (!m_running)
207 return;
208
209 m_suspended = true;
210 alcCaptureStop(aldata->device);
211}
212
214{
215 if (!m_running)
216 return;
217
218 m_suspended = false;
219 alcCaptureStart(aldata->device);
220}
221
223{
224 if (!m_running)
225 return 0;
226 int samples;
227 alcGetIntegerv(aldata->device, ALC_CAPTURE_SAMPLES, 1, &samples);
228 return m_format.bytesForFrames(samples);
229}
230
232{
233 if (!m_running)
234 return;
235 m_bufferSize = value;
236}
237
239{
240 return m_bufferSize;
241}
242
244{
245 return m_format.durationForBytes(m_processed);
246}
247
249{
250 return m_error;
251}
252
254{
255 if (m_running)
256 return QAudio::ActiveState;
257 else
259}
260
262{
263 m_format = fmt;
264 m_bufferSize = m_format.bytesForDuration(DEFAULT_BUFFER_DURATION);
265}
266
268{
269 return m_format;
270}
271
273{
274 m_volume = volume;
275}
276
278{
279 return m_volume;
280}
281
286
288{
289 int samples;
290 alcGetIntegerv(m_in->aldata->device, ALC_CAPTURE_SAMPLES, 1, &samples);
291 samples = qMin(samples, m_in->m_format.framesForBytes(maxlen));
292 auto bytes = m_in->m_format.bytesForFrames(samples);
293 alcGetError(m_in->aldata->device);
294 alcCaptureSamples(m_in->aldata->device, data, samples);
295 if (m_in->m_volume < 1)
296 QAudioHelperInternal::qMultiplySamples(m_in->m_volume, m_in->m_format, data, data, bytes);
297 auto err = alcGetError(m_in->aldata->device);
298 if (err) {
299 qWarning() << alcGetString(m_in->aldata->device, err);
301 return 0;
302 }
303 m_in->m_processed += bytes;
304 return bytes;
305}
306
308{
309 Q_UNREACHABLE();
310 Q_UNUSED(data);
311 Q_UNUSED(len);
312 return 0;
313}
314
IOBluetoothDevice * device
ALCcontext * context
ALCdevice * device
The QAudioFormat class stores audio stream parameter information.
constexpr int channelCount() const noexcept
Returns the current channel count value.
Q_MULTIMEDIA_EXPORT qint32 bytesForFrames(qint32 frameCount) const
Returns the number of bytes required for frameCount frames of this format.
constexpr int sampleRate() const noexcept
Returns the current sample rate in Hertz.
constexpr SampleFormat sampleFormat() const noexcept
Returns the current sample format.
Q_MULTIMEDIA_EXPORT qint32 bytesForDuration(qint64 microseconds) const
Returns the number of bytes required for this audio format for microseconds.
Q_MULTIMEDIA_EXPORT qint64 durationForBytes(qint32 byteCount) const
Returns the number of microseconds represented by bytes in this format.
Q_MULTIMEDIA_EXPORT qint32 framesForBytes(qint32 byteCount) const
Returns the number of frames represented by byteCount in this format.
void errorChanged(QAudio::Error error)
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
void invalidate() noexcept
Marks this QElapsedTimer object as invalid.
void start() noexcept
\typealias QElapsedTimer::Duration Synonym for std::chrono::nanoseconds.
\inmodule QtCore \reentrant
Definition qiodevice.h:34
virtual bool open(QIODeviceBase::OpenMode mode)
Opens the device and sets its OpenMode to mode.
void readyRead()
This signal is emitted once every time new data is available for reading from the device's current re...
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
void deleteLater()
\threadsafe
Definition qobject.cpp:2435
\inmodule QtCore
Definition qtimer.h:20
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:241
void setInterval(int msec)
Definition qtimer.cpp:579
void stop()
Stops the timer.
Definition qtimer.cpp:267
void timeout(QPrivateSignal)
This signal is emitted when the timer times out.
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 setFormat(const QAudioFormat &fmt) override
void suspend() override
qreal volume() const override
qsizetype bufferSize() const override
QWasmAudioSource(const QByteArray &device, QObject *parent)
void stop() override
friend class QWasmAudioSourceDevice
void setBufferSize(qsizetype value) override
QAudioFormat format() const override
void setVolume(qreal volume) override
void reset() override
void setError(const QAudio::Error &error)
qsizetype bytesReady() const override
void resume() override
QAudio::Error error() const override
#define this
Definition dialogs.cpp:9
void qMultiplySamples(qreal factor, const QAudioFormat &format, const void *src, void *dest, int len)
State
Definition qaudio.h:29
@ StoppedState
Definition qaudio.h:29
@ ActiveState
Definition qaudio.h:29
Error
Definition qaudio.h:28
@ FatalError
Definition qaudio.h:28
@ OpenError
Definition qaudio.h:28
@ NoError
Definition qaudio.h:28
Combined button and popup list for selecting options.
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:166
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLsizei samples
GLenum mode
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLsizei GLsizei GLenum format
GLuint writeBuffer
GLuint in
GLenum GLsizei len
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
QVideoFrameFormat::PixelFormat fmt
#define AL_FORMAT_STEREO_FLOAT32
#define AL_FORMAT_MONO_FLOAT32
constexpr unsigned int DEFAULT_BUFFER_DURATION
constexpr unsigned int DEFAULT_BUFFER_DURATION
QByteArray readData()