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

source code of qtmultimedia/src/spatialaudio/qaudioroom.cpp