Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qspatialsound.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#include "qaudioroom_p.h"
4#include "qspatialsound_p.h"
5#include "qaudiolistener.h"
6#include "qaudioengine_p.h"
7#include "resonance_audio.h"
8#include <qaudiosink.h>
9#include <qurl.h>
10#include <qdebug.h>
11#include <qaudiodecoder.h>
12
14
40
45{
46 setEngine(nullptr);
47}
48
58{
59 auto *ep = QAudioEnginePrivate::get(d->engine);
60 pos *= ep->distanceScale;
61 d->pos = pos;
62 if (ep)
63 ep->resonanceAudio->api->SetSourcePosition(d->sourceId, pos.x(), pos.y(), pos.z());
65}
66
68{
69 auto *ep = QAudioEnginePrivate::get(d->engine);
70 return d->pos/ep->distanceScale;
71}
72
79{
80 d->rotation = q;
81 auto *ep = QAudioEnginePrivate::get(d->engine);
82 if (ep)
83 ep->resonanceAudio->api->SetSourceRotation(d->sourceId, q.x(), q.y(), q.z(), q.scalar());
85}
86
88{
89 return d->rotation;
90}
91
100void QSpatialSound::setVolume(float volume)
101{
102 if (d->volume == volume)
103 return;
104 d->volume = volume;
105 auto *ep = QAudioEnginePrivate::get(d->engine);
106 if (ep)
107 ep->resonanceAudio->api->SetSourceVolume(d->sourceId, d->volume*d->wallDampening);
109}
110
112{
113 return d->volume;
114}
115
145
147{
148 if (!engine || sourceId < 0)
149 return;
151
152 vraudio::DistanceRolloffModel dm = vraudio::kLogarithmic;
153 switch (distanceModel) {
155 dm = vraudio::kLinear;
156 break;
158 dm = vraudio::kNone;
159 break;
160 default:
161 break;
162 }
163
164 ep->resonanceAudio->api->SetSourceDistanceModel(sourceId, dm, size, distanceCutoff);
165}
166
168{
169 if (!engine || sourceId < 0)
170 return;
172 if (!ep->currentRoom)
173 return;
174 auto *rp = QAudioRoomPrivate::get(ep->currentRoom);
175 if (!rp)
176 return;
177
178 QVector3D roomDim2 = ep->currentRoom->dimensions()/2.;
179 QVector3D roomPos = ep->currentRoom->position();
180 QQuaternion roomRot = ep->currentRoom->rotation();
181 QVector3D dist = pos - roomPos;
182 // transform into room coordinates
183 dist = roomRot.rotatedVector(dist);
184 if (qAbs(dist.x()) <= roomDim2.x() &&
185 qAbs(dist.y()) <= roomDim2.y() &&
186 qAbs(dist.z()) <= roomDim2.z()) {
187 // Source is inside room, apply
188 ep->resonanceAudio->api->SetSourceRoomEffectsGain(sourceId, 1);
189 wallDampening = 1.;
190 wallOcclusion = 0.;
191 } else {
192 // ### calculate room occlusion and dampening
193 // This is a bit of heuristics on top of the heuristic dampening/occlusion numbers for walls
194 //
195 // We basically cast a ray from the listener through the walls. If walls have different characteristics
196 // and we get close to a corner, we try to use some averaging to avoid abrupt changes
197 auto relativeListenerPos = ep->listenerPosition() - roomPos;
198 relativeListenerPos = roomRot.rotatedVector(relativeListenerPos);
199
200 auto direction = dist.normalized();
201 enum {
202 X, Y, Z
203 };
204 // Very rough approximation, use the size of the source plus twice the size of our head.
205 // One could probably improve upon this.
206 const float transitionDistance = size + 0.4;
207 QAudioRoom::Wall walls[3];
210 walls[Z] = direction.z() > 0 ? QAudioRoom::Ceiling : QAudioRoom::Floor;
211 float factors[3] = { 0., 0., 0. };
212 bool foundWall = false;
213 if (direction.x() != 0) {
214 float sign = direction.x() > 0 ? 1.f : -1.f;
215 float dx = sign * roomDim2.x() - relativeListenerPos.x();
216 QVector3D intersection = relativeListenerPos + direction*dx/direction.x();
217 float dy = roomDim2.y() - qAbs(intersection.y());
218 float dz = roomDim2.z() - qAbs(intersection.z());
219 if (dy > 0 && dz > 0) {
220// qDebug() << "Hit with wall X" << walls[0] << dy << dz;
221 // Ray is hitting this wall
222 factors[Y] = qMax(0.f, 1.f/3.f - dy/transitionDistance);
223 factors[Z] = qMax(0.f, 1.f/3.f - dz/transitionDistance);
224 factors[X] = 1.f - factors[Y] - factors[Z];
225 foundWall = true;
226 }
227 }
228 if (!foundWall && direction.y() != 0) {
229 float sign = direction.y() > 0 ? 1.f : -1.f;
230 float dy = sign * roomDim2.y() - relativeListenerPos.y();
231 QVector3D intersection = relativeListenerPos + direction*dy/direction.y();
232 float dx = roomDim2.x() - qAbs(intersection.x());
233 float dz = roomDim2.z() - qAbs(intersection.z());
234 if (dx > 0 && dz > 0) {
235 // Ray is hitting this wall
236// qDebug() << "Hit with wall Y" << walls[1] << dx << dy;
237 factors[X] = qMax(0.f, 1.f/3.f - dx/transitionDistance);
238 factors[Z] = qMax(0.f, 1.f/3.f - dz/transitionDistance);
239 factors[Y] = 1.f - factors[X] - factors[Z];
240 foundWall = true;
241 }
242 }
243 if (!foundWall) {
244 Q_ASSERT(direction.z() != 0);
245 float sign = direction.z() > 0 ? 1.f : -1.f;
246 float dz = sign * roomDim2.z() - relativeListenerPos.z();
247 QVector3D intersection = relativeListenerPos + direction*dz/direction.z();
248 float dx = roomDim2.x() - qAbs(intersection.x());
249 float dy = roomDim2.y() - qAbs(intersection.y());
250 if (dx > 0 && dy > 0) {
251 // Ray is hitting this wall
252// qDebug() << "Hit with wall Z" << walls[2];
253 factors[X] = qMax(0.f, 1.f/3.f - dx/transitionDistance);
254 factors[Y] = qMax(0.f, 1.f/3.f - dy/transitionDistance);
255 factors[Z] = 1.f - factors[X] - factors[Y];
256 foundWall = true;
257 }
258 }
259 wallDampening = 0;
260 wallOcclusion = 0;
261 for (int i = 0; i < 3; ++i) {
262 wallDampening += factors[i]*rp->wallDampening(walls[i]);
263 wallOcclusion += factors[i]*rp->wallOcclusion(walls[i]);
264 }
265
266// qDebug() << "intersection with wall" << walls[0] << walls[1] << walls[2] << factors[0] << factors[1] << factors[2] << wallDampening << wallOcclusion;
267 ep->resonanceAudio->api->SetSourceRoomEffectsGain(sourceId, 0);
268 }
269 ep->resonanceAudio->api->SetSoundObjectOcclusionIntensity(sourceId, occlusionIntensity + wallOcclusion);
270 ep->resonanceAudio->api->SetSourceVolume(sourceId, volume*wallDampening);
271}
272
277
286{
287 auto *ep = QAudioEnginePrivate::get(d->engine);
288 size *= ep->distanceScale;
289 if (d->size == size)
290 return;
291 d->size = size;
292
295}
296
298{
299 auto *ep = QAudioEnginePrivate::get(d->engine);
300 return d->size/ep->distanceScale;
301}
302
311{
312 auto *ep = QAudioEnginePrivate::get(d->engine);
313 cutoff *= ep->distanceScale;
314 if (d->distanceCutoff == cutoff)
315 return;
316 d->distanceCutoff = cutoff;
317
320}
321
323{
324 auto *ep = QAudioEnginePrivate::get(d->engine);
325 return d->distanceCutoff/ep->distanceScale;
326}
327
335{
336 if (d->manualAttenuation == attenuation)
337 return;
338 d->manualAttenuation = attenuation;
339 auto *ep = QAudioEnginePrivate::get(d->engine);
340 if (ep)
341 ep->resonanceAudio->api->SetSourceDistanceAttenuation(d->sourceId, d->manualAttenuation);
343}
344
346{
347 return d->manualAttenuation;
348}
349
367{
368 if (d->occlusionIntensity == occlusion)
369 return;
370 d->occlusionIntensity = occlusion;
371 auto *ep = QAudioEnginePrivate::get(d->engine);
372 if (ep)
373 ep->resonanceAudio->api->SetSoundObjectOcclusionIntensity(d->sourceId, d->occlusionIntensity + d->wallOcclusion);
375}
376
378{
379 return d->occlusionIntensity;
380}
381
392{
393 alpha = qBound(0., alpha, 1.);
394 if (alpha == d->directivity)
395 return;
396 d->directivity = alpha;
397
398 auto *ep = QAudioEnginePrivate::get(d->engine);
399 if (ep)
400 ep->resonanceAudio->api->SetSoundObjectDirectivity(d->sourceId, d->directivity, d->directivityOrder);
401
403}
404
406{
407 return d->directivity;
408}
409
419{
420 order = qMax(order, 1.);
421 if (order == d->directivityOrder)
422 return;
424
425 auto *ep = QAudioEnginePrivate::get(d->engine);
426 if (ep)
427 ep->resonanceAudio->api->SetSoundObjectDirectivity(d->sourceId, d->directivity, d->directivityOrder);
428
430}
431
433{
434 return d->directivityOrder;
435}
436
445{
446 gain = qBound(0., gain, 1.);
447 if (gain == d->nearFieldGain)
448 return;
449 d->nearFieldGain = gain;
450
451 auto *ep = QAudioEnginePrivate::get(d->engine);
452 if (ep)
453 ep->resonanceAudio->api->SetSoundObjectNearFieldEffectGain(d->sourceId, d->nearFieldGain*9.f);
454
456
457}
458
460{
461 return d->nearFieldGain;
462}
463
470{
471 if (d->url == url)
472 return;
473 d->url = url;
474
475 d->load();
477}
478
480{
481 return d->url;
482}
483
501{
502 return d->m_loops.loadRelaxed();
503}
504
506{
507 int oldLoops = d->m_loops.fetchAndStoreRelaxed(loops);
508 if (oldLoops != loops)
510}
511
521{
522 return d->m_autoPlay.loadRelaxed();
523}
524
525void QSpatialSound::setAutoPlay(bool autoPlay)
526{
528 if (old != autoPlay)
530}
531
536{
537 d->play();
538}
539
544{
545 d->pause();
546}
547
553{
554 d->stop();
555}
556
560void QSpatialSound::setEngine(QAudioEngine *engine)
561{
562 if (d->engine == engine)
563 return;
564
565 // Remove self from old engine (if necessary)
566 auto *ep = QAudioEnginePrivate::get(d->engine);
567 if (ep)
568 ep->removeSpatialSound(this);
569
570 d->engine = engine;
571
572 // Add self to new engine if necessary
574 if (ep) {
575 ep->addSpatialSound(this);
576 ep->resonanceAudio->api->SetSourcePosition(d->sourceId, d->pos.x(), d->pos.y(), d->pos.z());
577 ep->resonanceAudio->api->SetSourceRotation(d->sourceId, d->rotation.x(), d->rotation.y(), d->rotation.z(), d->rotation.scalar());
578 ep->resonanceAudio->api->SetSourceVolume(d->sourceId, d->volume);
579 ep->resonanceAudio->api->SetSoundObjectDirectivity(d->sourceId, d->directivity, d->directivityOrder);
580 ep->resonanceAudio->api->SetSoundObjectNearFieldEffectGain(d->sourceId, d->nearFieldGain);
582 }
583}
584
589{
590 return d->engine;
591}
592
594
595#include "moc_qspatialsound.cpp"
QAudioEngine * engine
QAtomicInteger< bool > m_autoPlay
static QAudioEnginePrivate * get(QAudioEngine *engine)
\inmodule QtSpatialAudio
static QAudioRoomPrivate * get(const QAudioRoom *r)
Wall
An enum defining the 6 walls of the room.
Definition qaudioroom.h:57
T fetchAndStoreRelaxed(T newValue) noexcept
T loadRelaxed() const noexcept
The QQuaternion class represents a quaternion consisting of a vector and scalar.
QSpatialSound::DistanceModel distanceModel
void stop()
Stops sound playback and resets the current position and current loop count to 0.
void setVolume(float volume)
void setSize(float size)
void manualAttenuationChanged()
void pause()
Pauses sound playback.
void setPosition(QVector3D pos)
void setNearFieldGain(float gain)
void play()
Starts playing back the sound.
void rotationChanged()
void setDistanceModel(DistanceModel model)
float nearFieldGain
Defines the near field gain for the sound source.
void autoPlayChanged()
float manualAttenuation
Defines a manual attenuation factor if \l distanceModel is set to QSpatialSound::DistanceModel::Manua...
void occlusionIntensityChanged()
DistanceModel distanceModel
Defines distance model for this sound source.
void setManualAttenuation(float attenuation)
void setSource(const QUrl &url)
~QSpatialSound()
Destroys the sound source.
float directivity
Defines the directivity of the sound source.
void setLoops(int loops)
void distanceModelChanged()
void setDirectivityOrder(float alpha)
void positionChanged()
void volumeChanged()
QAudioEngine * engine() const
Returns the engine associated with this listener.
QUrl source
The source file for the sound to be played.
float directivityOrder
Defines the order of the directivity of the sound source.
void setDistanceCutoff(float cutoff)
void setAutoPlay(bool autoPlay)
void setDirectivity(float alpha)
void sourceChanged()
bool autoPlay
Determines whether the sound should automatically start playing when a source gets specified.
void setOcclusionIntensity(float occlusion)
QQuaternion rotation
Defines the orientation of the sound source in 3D space.
void distanceCutoffChanged()
float size
Defines the size of the sound source.
int loops
Determines how many times the sound is played before the player stops.
void nearFieldGainChanged()
void setRotation(const QQuaternion &q)
float volume
Defines the volume of the sound.
float occlusionIntensity
Defines how much the object is occluded.
float distanceCutoff
Defines a distance beyond which sound coming from the source will cutoff.
QSpatialSound(QAudioEngine *engine)
Creates a spatial sound source for engine.
void loopsChanged()
QVector3D position
Defines the position of the sound source in 3D space.
DistanceModel
Defines how the volume of the sound scales with distance to the listener.
void sizeChanged()
void directivityChanged()
\inmodule QtCore
Definition qurl.h:94
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:671
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:670
constexpr float z() const noexcept
Returns the z coordinate of this point.
Definition qvectornd.h:672
#define this
Definition dialogs.cpp:9
direction
Combined button and popup list for selecting options.
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLfloat GLfloat GLfloat alpha
Definition qopenglext.h:418
GLfixed GLfixed GLint GLint order
#define X(name)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
static int sign(int x)
QSqlQueryModel * model
[16]
std::uniform_real_distribution dist(1, 2.5)
[2]
QUrl url("example.com")
[constructor-url-reference]
QJSEngine engine
[0]