Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qaudioengine.cpp
Go to the documentation of this file.
1
// Copyright (C) 2022 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only
3
4
#
include
"qaudioengine.h"
5
#
include
"qaudioengine_p.h"
6
7
#
include
<
QtSpatialAudio
/
private
/
qaudioengine_threaded_p
.
h
>
8
#
include
<
QtSpatialAudio
/
private
/
qaudioengine_withplayer_p
.
h
>
9
#
include
<
QtSpatialAudio
/
private
/
qaudioroom_p
.
h
>
10
#
include
<
QtMultimedia
/
qaudiosink
.
h
>
11
#
include
<
QtMultimedia
/
qmediadevices
.
h
>
12
#
include
<
QtMultimedia
/
private
/
qaudiosystem_p
.
h
>
13
#
include
<
QtMultimedia
/
private
/
qmultimedia_ranges_p
.
h
>
14
#
include
<
QtMultimedia
/
private
/
qplatformaudiodevices_p
.
h
>
15
#
include
<
QtMultimedia
/
private
/
qplatformmediaintegration_p
.
h
>
16
#
include
<
QtCore
/
q20vector
.
h
>
17
#
include
<
QtCore
/
qspan
.
h
>
18
19
#
include
<
resonance_audio
.
h
>
20
21
QT_BEGIN_NAMESPACE
22
23
Q_LOGGING_CATEGORY
(qLcSpatialAudioEngine,
"qt.spatialaudio.engine"
)
24
25
QAudioEnginePrivate::QAudioEnginePrivate(
int
sampleRate)
26
: m_sampleRate(sampleRate),
27
resonanceAudio{
28
std::make_unique<vraudio::ResonanceAudio>(2, qToUnderlying(framesPerBuffer), sampleRate),
29
}
30
{
31
resonanceAudio->api->SetStereoSpeakerMode(outputMode() != QAudioEngine::Headphone);
32
resonanceAudio->api->SetMasterVolume(masterVolume());
33
}
34
35
QAudioEnginePrivate
::~
QAudioEnginePrivate
() =
default
;
36
37
void
QAudioEnginePrivate
::
setDistanceScale
(
float
scale)
38
{
39
if
(scale == m_distanceScale)
40
return
;
41
m_distanceScale = scale;
42
Q_Q(QAudioEngine);
43
Q_EMIT q->distanceScaleChanged();
44
}
45
46
float
QAudioEnginePrivate
::
distanceScale
()
const
47
{
48
return
m_distanceScale;
49
}
50
51
void
QAudioEnginePrivate
::
setMasterVolume
(
float
volume)
52
{
53
if
(
m_masterVolume
== volume)
54
return
;
55
m_masterVolume
= volume;
56
resonanceAudio->api->SetMasterVolume(volume);
57
Q_Q(QAudioEngine);
58
Q_EMIT q->masterVolumeChanged();
59
}
60
61
float
QAudioEnginePrivate
::
masterVolume
()
const
62
{
63
return
m_masterVolume
;
64
}
65
66
void
QAudioEnginePrivate
::
setListenerPosition
(std::optional<QVector3D> pos)
67
{
68
if
(pos == m_position)
69
return
;
70
71
m_position = pos;
72
73
QVector3D posValue = pos.value_or(QVector3D{});
74
resonanceAudio->api->SetHeadPosition(posValue.x(), posValue.y(), posValue.z());
75
76
updateRooms
(
)
;
77
}
78
79
void
QAudioEnginePrivate
::
setListenerRotation
(
const
QQuaternion &rotation)
80
{
81
resonanceAudio->api->SetHeadRotation(rotation.x(), rotation.y(), rotation.z(),
82
rotation.scalar());
83
}
84
85
void
QAudioEnginePrivate
::
setRoomEffectsEnabled
(
bool
enabled)
86
{
87
if
(resonanceAudio->roomEffectsEnabled == enabled)
88
return
;
89
resonanceAudio->roomEffectsEnabled = enabled;
90
}
91
92
bool
QAudioEnginePrivate
::
roomEffectsEnabled
()
const
93
{
94
return
resonanceAudio->roomEffectsEnabled;
95
}
96
97
void
QAudioEnginePrivate
::
setOutputMode
(QAudioEngine::OutputMode mode)
98
{
99
if
(m_outputMode == mode)
100
return
;
101
m_outputMode = mode;
102
resonanceAudio->api->SetStereoSpeakerMode(mode != QAudioEngine::Headphone);
103
104
Q_Q(QAudioEngine);
105
Q_EMIT q->outputModeChanged();
106
}
107
108
QAudioEngine::OutputMode
QAudioEnginePrivate
::outputMode()
const
109
{
110
return
m_outputMode;
111
}
112
113
void
QAudioEnginePrivate
::
addRoom
(QAudioRoom *room)
114
{
115
Q_ASSERT(!QtMultimediaPrivate::ranges::contains(rooms, room));
116
rooms.push_back(room);
117
}
118
119
void
QAudioEnginePrivate
::
removeRoom
(QAudioRoom *room)
120
{
121
Q_ASSERT(QtMultimediaPrivate::ranges::contains(rooms, room));
122
q20::erase(rooms, room);
123
}
124
125
QAudioRoom *
QAudioEnginePrivate
::
currentRoom
()
const
126
{
127
return
m_currentRoom;
128
}
129
130
void
QAudioEnginePrivate
::
updateRooms
()
131
{
132
if
(!
roomEffectsEnabled
(
)
)
133
return
;
134
135
bool
roomDirty =
false
;
136
for
(
const
auto
&room : rooms) {
137
auto
*rd = QAudioRoomPrivate::get(room);
138
if
(rd->dirty) {
139
roomDirty =
true
;
140
rd->update();
141
}
142
}
143
144
auto
inferredRoom = findSmallestRoomForListener(rooms);
145
if
(inferredRoom.room != m_currentRoom)
146
roomDirty =
true
;
147
const
bool
previousRoom = m_currentRoom;
148
m_currentRoom = inferredRoom.room;
149
150
if
(!roomDirty)
151
return
;
152
153
// apply room to engine
154
if
(!m_currentRoom) {
155
resonanceAudio->api->EnableRoomEffects(
false
);
156
return
;
157
}
158
if
(!previousRoom)
159
resonanceAudio->api->EnableRoomEffects(
true
);
160
161
QAudioRoomPrivate *rp = QAudioRoomPrivate::get(m_currentRoom);
162
resonanceAudio->api->SetReflectionProperties(rp->reflections);
163
resonanceAudio->api->SetReverbProperties(rp->reverb);
164
165
// update room effects for all sound sources
166
updateRoomEffects
(
)
;
167
}
168
169
QAudioEnginePrivate
::
SmallestRoomForListenerResult
170
QAudioEnginePrivate
::
findSmallestRoomForListener
(QSpan<QAudioRoom *> rooms)
const
171
{
172
const
std::optional<QVector3D> listenerPos = listenerPosition();
173
174
if
(!listenerPos)
175
return
SmallestRoomForListenerResult
{
176
nullptr
,
177
0.f,
178
};
179
180
std
::optional<
float
> roomVolume;
181
QAudioRoom *room =
nullptr
;
182
183
for
(QAudioRoom *r : std::as_const(rooms)) {
184
QVector3D dim2 = r->dimensions() / 2.;
185
float
vol = dim2.x() * dim2.y() * dim2.z();
186
if
(roomVolume && vol > roomVolume)
187
continue
;
188
QVector3D dist = r->position() - *listenerPos;
189
// transform into room coordinates
190
dist = r->rotation().rotatedVector(dist);
191
if
(qAbs(dist.x()) <= dim2.x() && qAbs(dist.y()) <= dim2.y()
192
&& qAbs(dist.z()) <= dim2.z()) {
193
room = r;
194
roomVolume = vol;
195
}
196
}
197
198
return
SmallestRoomForListenerResult
{
199
room,
200
roomVolume.value_or(0.f),
201
};
202
}
203
204
namespace
{
205
206
QAudioEnginePrivate
*makeAudioEnginePrivate(
int
sampleRate)
207
{
208
bool
hasCallbackApi = QPlatformMediaIntegration::instance()->audioDevices()->hasCallbackApi();
209
if
(hasCallbackApi)
210
return
new
QAudioEngineWithPlayer(sampleRate);
211
else
212
return
new
QAudioEngineThreaded(sampleRate);
213
}
214
215
}
// namespace
216
217
/*!
218
\class QAudioEngine
219
\inmodule QtSpatialAudio
220
\ingroup spatialaudio
221
\ingroup multimedia_audio
222
223
\brief QAudioEngine manages a three dimensional sound field.
224
225
You can use an instance of QAudioEngine to manage a sound field in
226
three dimensions. A sound field is defined by several QSpatialSound
227
objects that define a sound at a specified location in 3D space. You can also
228
add stereo overlays using QAmbientSound.
229
230
You can use QAudioListener to define the position of the person listening
231
to the sound field relative to the sound sources. Sound sources will be less audible
232
if the listener is further away from source. They will also get mapped to the corresponding
233
loudspeakers depending on the direction between listener and source.
234
235
QAudioEngine offers two output modes. The first mode renders the sound field to a set of
236
speakers, either a stereo speaker pair or a surround configuration. The second mode provides
237
an immersive 3D sound experience when using headphones.
238
239
Perception of sound localization is driven mainly by two factors. The first factor is timing
240
differences of the sound waves between left and right ear. The second factor comes from various
241
ways how sounds coming from different direcations create different types of reflections from our
242
ears and heads. See \l{https://en.wikipedia.org/wiki/Sound_localization} for more details.
243
244
The spatial audio engine emulates those timing differences and reflections through
245
Head related transfer functions (HRTF, see
246
\l{https://en.wikipedia.org/wiki/Head-related_transfer_function}). The functions used emulates those
247
effects for an average persons ears and head. It provides a good and immersive 3D sound localization
248
experience for most persons when using headphones.
249
250
The engine is rather versatile allowing you to define room properties and reverb settings to emulate
251
different types of rooms.
252
253
Sound sources can also be occluded dampening the sound coming from those sources.
254
255
The audio engine uses a coordinate system that is in centimeters by default. The axes are aligned with the
256
typical coordinate system used in 3D. Positive x points to the right, positive y points up and positive z points
257
backwards.
258
259
*/
260
261
/*!
262
\fn QAudioEngine::QAudioEngine()
263
\fn QAudioEngine::QAudioEngine(QObject *parent)
264
\fn QAudioEngine::QAudioEngine(int sampleRate, QObject *parent = nullptr)
265
266
Constructs a spatial audio engine with \a parent, if any.
267
268
The engine will operate with a sample rate given by \a sampleRate. The
269
default sample rate, if none is provided, is 44100 (44.1kHz).
270
271
Sound content that is not provided at that sample rate will automatically
272
get resampled to \a sampleRate when being processed by the engine. The
273
default sample rate is fine in most cases, but you can define a different
274
rate if most of your sound files are sampled with a different rate, and
275
avoid some CPU overhead for resampling.
276
*/
277
QAudioEngine::QAudioEngine(
int
sampleRate, QObject *parent)
278
: QObject(*makeAudioEnginePrivate(sampleRate), parent)
279
{
280
}
281
282
/*!
283
Destroys the spatial audio engine.
284
*/
285
QAudioEngine::~QAudioEngine()
286
{
287
stop();
288
}
289
290
/*! \enum QAudioEngine::OutputMode
291
\value Surround Map the sounds to the loudspeaker configuration of the output device.
292
This is normally a stereo or surround speaker setup.
293
\note OutputMode::Surround will disable playback of QAmbientSound
294
\value Stereo Map the sounds to the stereo loudspeaker configuration of the output device.
295
This will ignore any additional speakers and only use the left and right channels
296
to create a stero rendering of the sound field.
297
\value Headphone Use Headphone spatialization to create a 3D audio effect when listening
298
to the sound field through headphones
299
*/
300
301
/*!
302
\property QAudioEngine::outputMode
303
304
Sets or retrieves the current output mode of the engine.
305
306
\sa QAudioEngine::OutputMode
307
*/
308
void
QAudioEngine::setOutputMode(OutputMode mode)
309
{
310
Q_D(QAudioEngine);
311
d->setOutputMode(mode);
312
}
313
314
QAudioEngine::OutputMode QAudioEngine::outputMode()
const
315
{
316
Q_D(
const
QAudioEngine);
317
return
d->outputMode();
318
}
319
320
/*!
321
Returns the sample rate the engine has been configured with.
322
*/
323
int
QAudioEngine::sampleRate()
const
324
{
325
Q_D(
const
QAudioEngine);
326
return
d->sampleRate();
327
}
328
329
/*!
330
\property QAudioEngine::outputDevice
331
332
Sets or returns the device that is being used for playing the sound field.
333
*/
334
void
QAudioEngine::setOutputDevice(
const
QAudioDevice &device)
335
{
336
Q_D(QAudioEngine);
337
d->setOutputDevice(device);
338
}
339
340
QAudioDevice QAudioEngine::outputDevice()
const
341
{
342
Q_D(
const
QAudioEngine);
343
return
d->outputDevice();
344
}
345
346
/*!
347
\property QAudioEngine::masterVolume
348
349
Sets or returns volume being used to render the sound field.
350
*/
351
void
QAudioEngine::setMasterVolume(
float
volume)
352
{
353
Q_D(QAudioEngine);
354
return
d->setMasterVolume(volume);
355
}
356
357
float
QAudioEngine::masterVolume()
const
358
{
359
Q_D(
const
QAudioEngine);
360
return
d->masterVolume();
361
}
362
363
/*!
364
Starts the engine.
365
*/
366
void
QAudioEngine::start()
367
{
368
Q_D(QAudioEngine);
369
d->start();
370
}
371
372
/*!
373
Stops the engine.
374
*/
375
void
QAudioEngine::stop()
376
{
377
Q_D(QAudioEngine);
378
d->stop();
379
}
380
381
/*!
382
\property QAudioEngine::paused
383
384
Pauses the spatial audio engine.
385
*/
386
void
QAudioEngine::setPaused(
bool
paused)
387
{
388
Q_D(QAudioEngine);
389
d->setPaused(paused);
390
}
391
392
bool
QAudioEngine::paused()
const
393
{
394
Q_D(
const
QAudioEngine);
395
return
d->isPaused();
396
}
397
398
/*!
399
Enables room effects such as echos and reverb.
400
401
Enables room effects if \a enabled is true.
402
Room effects will only apply if you create one or more \l QAudioRoom objects
403
and the listener is inside at least one of the rooms. If the listener is inside
404
multiple rooms, the room with the smallest volume will be used.
405
*/
406
void
QAudioEngine::setRoomEffectsEnabled(
bool
enabled)
407
{
408
Q_D(QAudioEngine);
409
d->setRoomEffectsEnabled(enabled);
410
}
411
412
/*!
413
Returns true if room effects are enabled.
414
*/
415
bool
QAudioEngine::roomEffectsEnabled()
const
416
{
417
Q_D(
const
QAudioEngine);
418
return
d->roomEffectsEnabled();
419
}
420
421
/*!
422
\property QAudioEngine::distanceScale
423
424
Defines the scale of the coordinate system being used by the spatial audio engine.
425
By default, all units are in centimeters, in line with the default units being
426
used by Qt Quick 3D.
427
428
Set the distance scale to QAudioEngine::DistanceScaleMeter to get units in meters.
429
*/
430
void
QAudioEngine::setDistanceScale(
float
scale)
431
{
432
Q_D(QAudioEngine);
433
// multiply with 100, to get the conversion to meters that resonance audio uses
434
scale /= 100.f;
435
if
(scale <= 0.0f) {
436
qWarning() <<
"QAudioEngine: Invalid distance scale."
;
437
return
;
438
}
439
d->setDistanceScale(scale);
440
}
441
442
float
QAudioEngine::distanceScale()
const
443
{
444
Q_D(
const
QAudioEngine);
445
return
d->distanceScale() * 100.f;
446
}
447
448
/*!
449
\fn void QAudioEngine::pause()
450
451
Pauses playback.
452
*/
453
/*!
454
\fn void QAudioEngine::resume()
455
456
Resumes playback.
457
*/
458
/*!
459
\variable QAudioEngine::DistanceScaleCentimeter
460
\internal
461
*/
462
/*!
463
\variable QAudioEngine::DistanceScaleMeter
464
\internal
465
*/
466
467
QT_END_NAMESPACE
468
469
#
include
"moc_qaudioengine.cpp"
QAudioEnginePrivate
Definition
qaudioengine_p.h:40
QAudioEnginePrivate::removeRoom
void removeRoom(QAudioRoom *)
Definition
qaudioengine.cpp:119
QAudioEnginePrivate::setMasterVolume
void setMasterVolume(float)
Definition
qaudioengine.cpp:51
QAudioEnginePrivate::updateRoomEffects
virtual void updateRoomEffects()=0
QAudioEnginePrivate::addRoom
void addRoom(QAudioRoom *)
Definition
qaudioengine.cpp:113
QAudioEnginePrivate::roomEffectsEnabled
bool roomEffectsEnabled() const
Definition
qaudioengine.cpp:92
QAudioEnginePrivate::distanceScale
float distanceScale() const
Definition
qaudioengine.cpp:46
QAudioEnginePrivate::m_masterVolume
float m_masterVolume
Definition
qaudioengine_p.h:61
QAudioEnginePrivate::currentRoom
QAudioRoom * currentRoom() const
Definition
qaudioengine.cpp:125
QAudioEnginePrivate::setOutputMode
virtual void setOutputMode(QAudioEngine::OutputMode)
Definition
qaudioengine.cpp:97
QAudioEnginePrivate::findSmallestRoomForListener
SmallestRoomForListenerResult findSmallestRoomForListener(QSpan< QAudioRoom * > rooms) const
Definition
qaudioengine.cpp:170
QAudioEnginePrivate::setListenerPosition
void setListenerPosition(std::optional< QVector3D >)
Definition
qaudioengine.cpp:66
QAudioEnginePrivate::masterVolume
float masterVolume() const
Definition
qaudioengine.cpp:61
QAudioEnginePrivate::setDistanceScale
void setDistanceScale(float scale)
Definition
qaudioengine.cpp:37
QAudioEnginePrivate::updateRooms
void updateRooms()
Definition
qaudioengine.cpp:130
QAudioEnginePrivate::setListenerRotation
void setListenerRotation(const QQuaternion &)
Definition
qaudioengine.cpp:79
QAudioEnginePrivate::~QAudioEnginePrivate
~QAudioEnginePrivate() override
QAudioEnginePrivate::setRoomEffectsEnabled
void setRoomEffectsEnabled(bool)
Definition
qaudioengine.cpp:85
std
[33]
Definition
src_corelib_tools_qhash.cpp:421
Q_LOGGING_CATEGORY
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
QAudioEnginePrivate::SmallestRoomForListenerResult
Definition
qaudioengine_p.h:98
qtmultimedia
src
spatialaudio
qaudioengine.cpp
Generated on
for Qt by
1.18.0