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
qaudioroom.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 "qaudioroom.h"
5#include "qaudioroom_p.h"
6
7#include <QtCore/qspan.h>
8
9#include "platforms/common/room_effects_utils.h"
10
12
13namespace {
14
15inline QVector3D toVector(QSpan<const float, 3> f)
16{
17 return QVector3D(f[0], f[1], f[2]);
18}
19
20inline void toFloats(QVector3D v, QSpan<float, 3> f)
21{
22 f[0] = v.x();
23 f[1] = v.y();
24 f[2] = v.z();
25}
26
27inline QQuaternion toQuaternion(QSpan<const float, 4> f)
28{
29 // resonance audio puts the scalar component last
30 return QQuaternion(f[3], f[0], f[1], f[2]);
31}
32
33inline void toFloats(QQuaternion q, QSpan<float, 4> f)
34{
35 f[0] = q.x();
36 f[1] = q.y();
37 f[2] = q.z();
38 f[3] = q.scalar();
39}
40
41// Default values for occlusion and dampening of different wall materials.
42// These values are used as defaults if a wall is only defined by a material
43// and define how sound passes through the wall.
44// We define both occlusion and dampening constants to be able to tune the
45// sound. Dampening only reduces the level of the sound without affecting its
46// tone, while occlusion will dampen higher frequencies more than lower ones
47constexpr struct {
48 float occlusion;
49 float dampening;
50} occlusionAndDampening[] = {
51 { 0.f, 1.f }, // Transparent,
52 { 0.f, .1f }, // AcousticCeilingTiles,
53 { 2.f, .4f }, // BrickBare,
54 { 2.f, .4f }, // BrickPainted,
55 { 4.f, 1.f }, // ConcreteBlockCoarse,
56 { 4.f, 1.f }, // ConcreteBlockPainted,
57 { .7f, .7f }, // CurtainHeavy,
58 { .5f, .5f }, // FiberGlassInsulation,
59 { .2f, .3f }, // GlassThin,
60 { .5f, .2f }, // GlassThick,
61 { 7.f, 1.f }, // Grass,
62 { 4.f, 1.f }, // LinoleumOnConcrete,
63 { 4.f, 1.f }, // Marble,
64 { 0.f, .2f }, // Metal,
65 { 4.f, 1.f }, // ParquetOnConcrete,
66 { 2.f, .4f }, // PlasterRough,
67 { 2.f, .4f }, // PlasterSmooth,
68 { 1.5f, .2f }, // PlywoodPanel,
69 { 4.f, 1.f }, // PolishedConcreteOrTile,
70 { 4.f, 1.f }, // Sheetrock,
71 { 4.f, 1.f }, // WaterOrIceSurface,
72 { 1.f, .3f }, // WoodCeiling,
73 { 1.f, .3f }, // WoodPanel,
74 { 0.f, .0f }, // UniformMaterial,
75};
76
77}
78
79// make sure the wall definitions agree with resonance audio
80
81static_assert(QAudioRoom::LeftWall == 0);
82static_assert(QAudioRoom::RightWall == 1);
83static_assert(QAudioRoom::Floor == 2);
84static_assert(QAudioRoom::Ceiling == 3);
85static_assert(QAudioRoom::FrontWall == 4);
86static_assert(QAudioRoom::BackWall == 5);
87
88void QAudioRoomPrivate::requestUpdate()
89{
90 dirty = true;
91 auto ep = QAudioEnginePrivate::get(engine);
92 if (!ep)
93 return;
94 ep->updateRooms();
95}
96
97float QAudioRoomPrivate::wallOcclusion(QAudioRoom::Wall wall) const
98{
99 return m_wallOcclusion[wall] < 0 ? occlusionAndDampening[roomProperties.material_names[wall]].occlusion : m_wallOcclusion[wall];
100}
101
102float QAudioRoomPrivate::wallDampening(QAudioRoom::Wall wall) const
103{
104 return m_wallDampening[wall] < 0 ? occlusionAndDampening[roomProperties.material_names[wall]].dampening : m_wallDampening[wall];
105}
106
107void QAudioRoomPrivate::update()
108{
109 if (!dirty)
110 return;
111 reflections = vraudio::ComputeReflectionProperties(roomProperties);
112 reverb = vraudio::ComputeReverbProperties(roomProperties);
113 dirty = false;
114}
115
116
117/*!
118 \class QAudioRoom
119 \inmodule QtSpatialAudio
120 \ingroup spatialaudio
121 \ingroup multimedia_audio
122
123 Defines a room for the spatial audio engine.
124
125 If the listener is inside a room, first order sound reflections and reverb
126 matching the rooms properties will get applied to the sound field.
127
128 A room is always square and defined by its center position, its orientation and dimensions.
129 Each of the 6 walls of the room can be made of different materials that will contribute
130 to the computed reflections and reverb that the listener will experience while being inside
131 the room.
132
133 If multiple rooms cover the same position, the engine will use the room with the smallest
134 volume.
135 */
136
137/*!
138 Constructs a QAudioRoom for \a engine.
139
140 \note Must be called with a valid QAudioEngine
141 */
142QAudioRoom::QAudioRoom(QAudioEngine *engine) : QObject(*new QAudioRoomPrivate)
143{
144 if (!engine) {
145 qWarning() << "Cannot create QAudioRoom without a valid QAudioEngine";
146 return;
147 }
148
149 Q_D(QAudioRoom);
150 d->engine = engine;
151 auto *ep = QAudioEnginePrivate::get(engine);
152 if (!ep)
153 return;
154 ep->addRoom(this);
155}
156
157/*!
158 Destroys the room.
159 */
160QAudioRoom::~QAudioRoom()
161{
162 Q_D(QAudioRoom);
163 auto *ep = QAudioEnginePrivate::get(d->engine);
164 if (ep)
165 ep->removeRoom(this);
166}
167
168/*!
169 \enum QAudioRoom::Material
170
171 Defines different materials that can be applied to the different walls of the room.
172
173 \value Transparent The side of the room is open and won't contribute to reflections or reverb.
174 \value AcousticCeilingTiles Acoustic tiles that suppress most reflections and reverb.
175 \value BrickBare Bare brick wall.
176 \value BrickPainted Painted brick wall.
177 \value ConcreteBlockCoarse Raw concrete wall
178 \value ConcreteBlockPainted Painted concrete wall
179 \value CurtainHeavy Heavy curtain. Will mostly reflect low frequencies
180 \value FiberGlassInsulation Fiber glass insulation. Only reflects very low frequencies
181 \value GlassThin Thin glass wall
182 \value GlassThick Thick glass wall
183 \value Grass Grass
184 \value LinoleumOnConcrete Linoleum floor
185 \value Marble Marble floor
186 \value Metal Metal
187 \value ParquetOnConcrete Parquet wooden floor on concrete
188 \value PlasterRough Rough plaster
189 \value PlasterSmooth Smooth plaster
190 \value PlywoodPanel Plywodden panel
191 \value PolishedConcreteOrTile Polished concrete or tiles
192 \value Sheetrock Rock
193 \value WaterOrIceSurface Water or ice
194 \value WoodCeiling Wooden ceiling
195 \value WoodPanel Wooden panel
196 \value UniformMaterial Artificial material giving uniform reflections on all frequencies
197*/
198
199/*!
200 \enum QAudioRoom::Wall
201
202 An enum defining the 6 walls of the room
203
204 \value LeftWall Left wall (negative x)
205 \value RightWall Right wall (positive x)
206 \value Floor Bottom wall (negative y)
207 \value Ceiling Top wall (positive y)
208 \value FrontWall Front wall (negative z)
209 \value BackWall Back wall (positive z)
210*/
211
212
213/*!
214 \property QAudioRoom::position
215
216 Defines the position of the center of the room in 3D space. Units are in centimeters
217 by default.
218
219 \sa dimensions, QAudioEngine::distanceScale
220 */
221void QAudioRoom::setPosition(QVector3D pos)
222{
223 Q_D(QAudioRoom);
224 auto *ep = QAudioEnginePrivate::get(d->engine);
225 if (!ep)
226 return;
227
228 pos *= ep->distanceScale();
229 if (toVector(d->roomProperties.position) == pos)
230 return;
231 toFloats(pos, d->roomProperties.position);
232 d->requestUpdate();
233 emit positionChanged();
234}
235
236QVector3D QAudioRoom::position() const
237{
238 Q_D(const QAudioRoom);
239 auto *ep = QAudioEnginePrivate::get(d->engine);
240 if (!ep)
241 return {};
242
243 auto pos = toVector(d->roomProperties.position);
244 pos /= ep->distanceScale();
245 return pos;
246}
247
248/*!
249 \property QAudioRoom::dimensions
250
251 Defines the dimensions of the room in 3D space. Units are in centimeters
252 by default.
253
254 \sa position, QAudioEngine::distanceScale
255 */
256void QAudioRoom::setDimensions(QVector3D dim)
257{
258 Q_D(QAudioRoom);
259 auto *ep = QAudioEnginePrivate::get(d->engine);
260 if (!ep)
261 return;
262
263 dim *= ep->distanceScale();
264 if (toVector(d->roomProperties.dimensions) == dim)
265 return;
266 toFloats(dim, d->roomProperties.dimensions);
267 d->requestUpdate();
268 emit dimensionsChanged();
269}
270
271QVector3D QAudioRoom::dimensions() const
272{
273 Q_D(const QAudioRoom);
274 auto *ep = QAudioEnginePrivate::get(d->engine);
275 if (!ep)
276 return {};
277
278 auto dim = toVector(d->roomProperties.dimensions);
279 dim /= ep->distanceScale();
280 return dim;
281}
282
283/*!
284 \property QAudioRoom::rotation
285
286 Defines the orientation of the room in 3D space.
287 */
288void QAudioRoom::setRotation(const QQuaternion &q)
289{
290 Q_D(QAudioRoom);
291
292 if (toQuaternion(d->roomProperties.rotation) == q)
293 return;
294 toFloats(q, d->roomProperties.rotation);
295 d->requestUpdate();
296 emit rotationChanged();
297}
298
299QQuaternion QAudioRoom::rotation() const
300{
301 Q_D(const QAudioRoom);
302
303 return toQuaternion(d->roomProperties.rotation);
304}
305
306/*!
307 \fn void QAudioRoom::wallsChanged()
308
309 Signals when the wall material changes.
310*/
311/*!
312 Sets \a wall to \a material.
313
314 Different wall materials have different reflection and reverb properties
315 that influence the sound of the room.
316
317 \sa wallMaterial(), Material, QAudioRoom::Wall
318 */
319void QAudioRoom::setWallMaterial(Wall wall, Material material)
320{
321 Q_D(QAudioRoom);
322
323 static_assert(vraudio::kUniform == int(UniformMaterial));
324 static_assert(vraudio::kTransparent == int(Transparent));
325
326 if (d->roomProperties.material_names[int(wall)] == int(material))
327 return;
328 d->roomProperties.material_names[int(wall)] = vraudio::MaterialName(int(material));
329 d->requestUpdate();
330 emit wallsChanged();
331}
332
333/*!
334 returns the material being used for \a wall.
335
336 \sa setWallMaterial(), Material, QAudioRoom::Wall
337 */
338QAudioRoom::Material QAudioRoom::wallMaterial(Wall wall) const
339{
340 Q_D(const QAudioRoom);
341 return Material(d->roomProperties.material_names[int(wall)]);
342}
343
344/*!
345 \property QAudioRoom::reflectionGain
346
347 A gain factor for reflections generated in this room. A value
348 from 0 to 1 will dampen reflections, while a value larger than 1
349 will apply a gain to reflections, making them louder.
350
351 The default is 1, a factor of 0 disables reflections. Negative
352 values are mapped to 0.
353 */
354void QAudioRoom::setReflectionGain(float factor)
355{
356 Q_D(QAudioRoom);
357
358 if (factor < 0.)
359 factor = 0.;
360 if (d->roomProperties.reflection_scalar == factor)
361 return;
362 d->roomProperties.reflection_scalar = factor;
363 d->requestUpdate();
364 emit reflectionGainChanged();
365}
366
367float QAudioRoom::reflectionGain() const
368{
369 Q_D(const QAudioRoom);
370 return d->roomProperties.reflection_scalar;
371}
372
373/*!
374 \property QAudioRoom::reverbGain
375
376 A gain factor for reverb generated in this room. A value
377 from 0 to 1 will dampen reverb, while a value larger than 1
378 will apply a gain to the reverb, making it louder.
379
380 The default is 1, a factor of 0 disables reverb. Negative
381 values are mapped to 0.
382 */
383void QAudioRoom::setReverbGain(float factor)
384{
385 Q_D(QAudioRoom);
386
387 if (factor < 0)
388 factor = 0;
389 if (d->roomProperties.reverb_gain == factor)
390 return;
391 d->roomProperties.reverb_gain = factor;
392 d->requestUpdate();
393 emit reverbGainChanged();
394}
395
396float QAudioRoom::reverbGain() const
397{
398 Q_D(const QAudioRoom);
399
400 return d->roomProperties.reverb_gain;
401}
402
403/*!
404 \property QAudioRoom::reverbTime
405
406 A factor to be applies to all reverb timings generated for this room.
407 Larger values will lead to longer reverb timings, making the room sound
408 larger.
409
410 The default is 1. Negative values are mapped to 0.
411 */
412void QAudioRoom::setReverbTime(float factor)
413{
414 Q_D(QAudioRoom);
415
416 if (factor < 0)
417 factor = 0;
418 if (d->roomProperties.reverb_time == factor)
419 return;
420 d->roomProperties.reverb_time = factor;
421 d->requestUpdate();
422 emit reverbTimeChanged();
423}
424
425float QAudioRoom::reverbTime() const
426{
427 Q_D(const QAudioRoom);
428
429 return d->roomProperties.reverb_time;
430}
431
432/*!
433 \property QAudioRoom::reverbBrightness
434
435 A brightness factor to be applied to the generated reverb.
436 A positive value will increase reverb for higher frequencies and
437 dampen lower frequencies, a negative value does the reverse.
438
439 The default is 0.
440 */
441void QAudioRoom::setReverbBrightness(float factor)
442{
443 Q_D(QAudioRoom);
444
445 if (d->roomProperties.reverb_brightness == factor)
446 return;
447 d->roomProperties.reverb_brightness = factor;
448 d->requestUpdate();
449 emit reverbBrightnessChanged();
450}
451
452float QAudioRoom::reverbBrightness() const
453{
454 Q_D(const QAudioRoom);
455
456 return d->roomProperties.reverb_brightness;
457}
458
459QT_END_NAMESPACE
460
461#include "moc_qaudioroom.cpp"
Combined button and popup list for selecting options.
QQuaternion toQuaternion(QSpan< const float, 4 > f)
QVector3D toVector(QSpan< const float, 3 > f)
void toFloats(QVector3D v, QSpan< float, 3 > f)