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