Qt 6.x
The Qt SDK
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
69 m_name(device),
70 m_timer(new QTimer(this))
71{
72 aldata = new ALData();
73 connect(m_timer, &QTimer::timeout, this, [this](){
74 Q_ASSERT(m_running);
75 if (m_pullMode)
77 else
78 if (bytesReady() > 0)
79 emit m_device->readyRead();
80 });
81}
82
84{
85 m_device = device;
86 start(true);
87}
88
90{
91 m_device = new QWasmAudioSourceDevice(this);
92 m_device->open(QIODevice::ReadOnly);
93 start(false);
94 return m_device;
95}
96
98{
99 m_pullMode = mode;
100 auto formatError = [this](){
101 qWarning() << "Unsupported audio format " << m_format;
103 };
104 ALCenum format;
105 switch (m_format.sampleFormat()) {
107 switch (m_format.channelCount()) {
108 case 1:
109 format = AL_FORMAT_MONO8;
110 break;
111 case 2:
112 format = AL_FORMAT_STEREO8;
113 break;
114 default:
115 return formatError();
116 }
117 break;
119 switch (m_format.channelCount()) {
120 case 1:
121 format = AL_FORMAT_MONO16;
122 break;
123 case 2:
124 format = AL_FORMAT_STEREO16;
125 break;
126 default:
127 return formatError();
128 }
129 break;
131 switch (m_format.channelCount()) {
132 case 1:
134 break;
135 case 2:
137 break;
138 default:
139 return formatError();
140 }
141 break;
142 default:
143 return formatError();
144 }
145 if (m_tmpData)
146 delete[] m_tmpData;
147 if (m_pullMode)
148 m_tmpData = new char[m_bufferSize];
149 else
150 m_tmpData = nullptr;
151 m_timer->setInterval(m_format.durationForBytes(m_bufferSize) / 3'000);
152
153 aldata->device = alcCaptureOpenDevice(m_name.data(), m_format.sampleRate(), format,
154 m_format.framesForBytes(m_bufferSize));
155
156 auto err = alcGetError(aldata->device);
157 if (err) {
158 qWarning() << "alcCaptureOpenDevice" << alcGetString(aldata->device, err);
160 }
161 alcCaptureStart(aldata->device);
162 m_elapsedTimer.start();
163 auto cerr = alcGetError(aldata->device);
164 if (cerr) {
165 qWarning() << "alcCaptureStart" << alcGetString(aldata->device, cerr);
167 }
168 m_processed = 0;
169 m_running = true;
170 m_timer->start();
171}
172
174{
175 if (m_pullMode)
176 writeBuffer();
177 alcCaptureStop(aldata->device);
178 alcCaptureCloseDevice(aldata->device);
179 m_elapsedTimer.invalidate();
180 if (m_tmpData) {
181 delete[] m_tmpData;
182 m_tmpData = nullptr;
183 }
184 if (!m_pullMode)
185 m_device->deleteLater();
186 m_timer->stop();
187 m_running = false;
188}
189
191{
192 stop();
193 if (m_tmpData) {
194 delete[] m_tmpData;
195 m_tmpData = nullptr;
196 }
197 m_running = false;
198 m_processed = 0;
199 m_error = QAudio::NoError;
200}
201
203{
204 if (!m_running)
205 return;
206
207 m_suspended = true;
208 alcCaptureStop(aldata->device);
209}
210
212{
213 if (!m_running)
214 return;
215
216 m_suspended = false;
217 alcCaptureStart(aldata->device);
218}
219
221{
222 if (!m_running)
223 return 0;
224 int samples;
225 alcGetIntegerv(aldata->device, ALC_CAPTURE_SAMPLES, 1, &samples);
226 return m_format.bytesForFrames(samples);
227}
228
230{
231 if (!m_running)
232 return;
233 m_bufferSize = value;
234}
235
237{
238 return m_bufferSize;
239}
240
242{
243 return m_format.durationForBytes(m_processed);
244}
245
247{
248 return m_error;
249}
250
252{
253 if (m_running)
254 return QAudio::ActiveState;
255 else
257}
258
260{
261 m_format = fmt;
262 m_bufferSize = m_format.bytesForDuration(DEFAULT_BUFFER_DURATION);
263}
264
266{
267 return m_format;
268}
269
271{
272 m_volume = volume;
273}
274
276{
277 return m_volume;
278}
279
281{
282
283}
284
286{
287 int samples;
288 alcGetIntegerv(m_in->aldata->device, ALC_CAPTURE_SAMPLES, 1, &samples);
289 samples = qMin(samples, m_in->m_format.framesForBytes(maxlen));
290 auto bytes = m_in->m_format.bytesForFrames(samples);
291 alcGetError(m_in->aldata->device);
292 alcCaptureSamples(m_in->aldata->device, data, samples);
293 if (m_in->m_volume < 1)
294 QAudioHelperInternal::qMultiplySamples(m_in->m_volume, m_in->m_format, data, data, bytes);
295 auto err = alcGetError(m_in->aldata->device);
296 if (err) {
297 qWarning() << alcGetString(m_in->aldata->device, err);
299 return 0;
300 }
301 m_in->m_processed += bytes;
302 return bytes;
303}
304
306{
307 Q_UNREACHABLE();
308 Q_UNUSED(data);
309 Q_UNUSED(len);
310 return 0;
311}
312
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.
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
void invalidate() noexcept
Marks this QElapsedTimer object as invalid.
void start() noexcept
Starts this timer.
\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:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
void deleteLater()
\threadsafe
Definition qobject.cpp:2352
void errorChanged(QAudio::Error error)
\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:208
void setInterval(int msec)
Definition qtimer.cpp:607
void stop()
Stops the timer.
Definition qtimer.cpp:226
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
QWasmAudioSource(const QByteArray &device, QObject *parent)
void stop() override
friend class QWasmAudioSourceDevice
int bufferSize() const override
void setBufferSize(int value) override
QAudioFormat format() const override
void setVolume(qreal volume) override
void reset() override
void setError(const QAudio::Error &error)
int 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
\value ActiveState Audio data is being processed, this state is set after start() is called and while...
Definition qaudio.h:25
@ StoppedState
Definition qaudio.h:25
@ ActiveState
Definition qaudio.h:25
Error
\value NoError No errors have occurred \value OpenError An error occurred opening the audio device \v...
Definition qaudio.h:24
@ FatalError
Definition qaudio.h:24
@ OpenError
Definition qaudio.h:24
@ NoError
Definition qaudio.h:24
Combined button and popup list for selecting options.
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:162
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
GLenum GLsizei len
GLuint writeBuffer
GLuint in
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
#define Q_UNUSED(x)
long long qint64
Definition qtypes.h:55
double qreal
Definition qtypes.h:92
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()
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent