diff options
Diffstat (limited to 'src/spatialaudio/qaudioroom.cpp')
-rw-r--r-- | src/spatialaudio/qaudioroom.cpp | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/src/spatialaudio/qaudioroom.cpp b/src/spatialaudio/qaudioroom.cpp new file mode 100644 index 000000000..3187abd10 --- /dev/null +++ b/src/spatialaudio/qaudioroom.cpp @@ -0,0 +1,399 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-3.0-only +#include <qaudioroom_p.h> + +QT_BEGIN_NAMESPACE + +namespace { +inline QVector3D toVector(const float *f) +{ + return QVector3D(f[0], f[1], f[2]); +} + +inline void toFloats(const QVector3D &v, float *f) +{ + f[0] = v.x(); + f[1] = v.y(); + f[2] = v.z(); +} + +inline QQuaternion toQuaternion(const float *f) +{ + // resonance audio puts the scalar component last + return QQuaternion(f[3], f[0], f[1], f[2]); +} + +inline void toFloats(const QQuaternion &q, float *f) +{ + f[0] = q.x(); + f[1] = q.y(); + f[2] = q.z(); + f[3] = q.scalar(); +} + +// Default values for occlusion and dampening of different wall materials. +// These values are used as defaults if a wall is only defined by a material +// and define how sound passes through the wall. +// We define both occlusion and dampening constants to be able to tune the +// sound. Dampening only reduces the level of the sound without affecting its +// tone, while occlusion will dampen higher frequencies more than lower ones +struct { + float occlusion; + float dampening; +} occlusionAndDampening[] = { + { 0.f, 1.f }, // Transparent, + { 0.f, .1f }, // AcousticCeilingTiles, + { 2.f, .4f }, // BrickBare, + { 2.f, .4f }, // BrickPainted, + { 4.f, 1.f }, // ConcreteBlockCoarse, + { 4.f, 1.f }, // ConcreteBlockPainted, + { .7f, .7f }, // CurtainHeavy, + { .5f, .5f }, // FiberGlassInsulation, + { .2f, .3f }, // GlassThin, + { .5f, .2f }, // GlassThick, + { 7.f, 1.f }, // Grass, + { 4.f, 1.f }, // LinoleumOnConcrete, + { 4.f, 1.f }, // Marble, + { 0.f, .2f }, // Metal, + { 4.f, 1.f }, // ParquetOnConcrete, + { 2.f, .4f }, // PlasterRough, + { 2.f, .4f }, // PlasterSmooth, + { 1.5f, .2f }, // PlywoodPanel, + { 4.f, 1.f }, // PolishedConcreteOrTile, + { 4.f, 1.f }, // Sheetrock, + { 4.f, 1.f }, // WaterOrIceSurface, + { 1.f, .3f }, // WoodCeiling, + { 1.f, .3f }, // WoodPanel, + { 0.f, .0f }, // UniformMaterial, +}; + +} + +// make sure the wall definitions agree with resonance audio + +static_assert(QAudioRoom::LeftWall == 0); +static_assert(QAudioRoom::RightWall == 1); +static_assert(QAudioRoom::Floor == 2); +static_assert(QAudioRoom::Ceiling == 3); +static_assert(QAudioRoom::FrontWall == 4); +static_assert(QAudioRoom::BackWall == 5); + +float QAudioRoomPrivate::wallOcclusion(QAudioRoom::Wall wall) const +{ + return m_wallOcclusion[wall] < 0 ? occlusionAndDampening[roomProperties.material_names[wall]].occlusion : m_wallOcclusion[wall]; +} + +float QAudioRoomPrivate::wallDampening(QAudioRoom::Wall wall) const +{ + return m_wallDampening[wall] < 0 ? occlusionAndDampening[roomProperties.material_names[wall]].dampening : m_wallDampening[wall]; +} + +void QAudioRoomPrivate::update() +{ + if (!dirty) + return; + reflections = vraudio::ComputeReflectionProperties(roomProperties); + reverb = vraudio::ComputeReverbProperties(roomProperties); + dirty = false; +} + + +/*! + \class QAudioRoom + \inmodule QtSpatialAudio + \ingroup spatialaudio + \ingroup multimedia_audio + + Defines a room for the spatial audio engine. + + If the listener is inside a room, first order sound reflections and reverb + matching the rooms properties will get applied to the sound field. + + A room is always square and defined by its center position, its orientation and dimensions. + Each of the 6 walls of the room can be made of different materials that will contribute + to the computed reflections and reverb that the listener will experience while being inside + the room. + + If multiple rooms cover the same position, the engine will use the room with the smallest + volume. + */ + +/*! + Constructs a QAudioRoom for \a engine. + */ +QAudioRoom::QAudioRoom(QAudioEngine *engine) + : d(new QAudioRoomPrivate) +{ + Q_ASSERT(engine); + d->engine = engine; + auto *ep = QAudioEnginePrivate::get(engine); + ep->addRoom(this); +} + +/*! + Destroys the room. + */ +QAudioRoom::~QAudioRoom() +{ + auto *ep = QAudioEnginePrivate::get(d->engine); + if (ep) + ep->removeRoom(this); + delete d; +} + +/*! + \enum QAudioRoom::Material + + Defines different materials that can be applied to the different walls of the room. + + \value Transparent The side of the room is open and won't contribute to reflections or reverb. + \value AcousticCeilingTiles Acoustic tiles that suppress most reflections and reverb. + \value BrickBare Bare brick wall. + \value BrickPainted Painted brick wall. + \value ConcreteBlockCoarse Raw concrete wall + \value ConcreteBlockPainted Painted concrete wall + \value CurtainHeavy Heavy curtain. Will mostly reflect low frequencies + \value FiberGlassInsulation Fiber glass insulation. Only reflects very low frequencies + \value GlassThin Thin glass wall + \value GlassThick Thick glass wall + \value Grass Grass + \value LinoleumOnConcrete Linoleum floor + \value Marble Marble floor + \value Metal Metal + \value ParquetOnConcrete Parquet wooden floor on concrete + \value PlasterRough Rough plaster + \value PlasterSmooth Smooth plaster + \value PlywoodPanel Plywodden panel + \value PolishedConcreteOrTile Polished concrete or tiles + \value Sheetrock Rock + \value WaterOrIceSurface Water or ice + \value WoodCeiling Wooden ceiling + \value WoodPanel Wooden panel + \value UniformMaterial Artificial material giving uniform reflections on all frequencies +*/ + +/*! + \enum QAudioRoom::Wall + + An enum defining the 6 walls of the room + + \value LeftWall Left wall (negative x) + \value RightWall Right wall (positive x) + \value Floor Bottom wall (negative y) + \value Ceiling Top wall (positive y) + \value FrontWall Front wall (negative z) + \value BackWall Back wall (positive z) +*/ + + +/*! + \property QAudioRoom::position + + Defines the position of the center of the room in 3D space. Units are in centimeters + by default. + + \sa dimensions, QAudioEngine::distanceScale + */ +void QAudioRoom::setPosition(QVector3D pos) +{ + auto *ep = QAudioEnginePrivate::get(d->engine); + pos *= ep->distanceScale; + if (toVector(d->roomProperties.position) == pos) + return; + toFloats(pos, d->roomProperties.position); + d->dirty = true; + emit positionChanged(); +} + +QVector3D QAudioRoom::position() const +{ + auto *ep = QAudioEnginePrivate::get(d->engine); + auto pos = toVector(d->roomProperties.position); + pos /= ep->distanceScale; + return pos; +} + +/*! + \property QAudioRoom::dimensions + + Defines the dimensions of the room in 3D space. Units are in centimeters + by default. + + \sa position, QAudioEngine::distanceScale + */ +void QAudioRoom::setDimensions(QVector3D dim) +{ + auto *ep = QAudioEnginePrivate::get(d->engine); + dim *= ep->distanceScale; + if (toVector(d->roomProperties.dimensions) == dim) + return; + toFloats(dim, d->roomProperties.dimensions); + d->dirty = true; + emit dimensionsChanged(); +} + +QVector3D QAudioRoom::dimensions() const +{ + auto *ep = QAudioEnginePrivate::get(d->engine); + auto dim = toVector(d->roomProperties.dimensions); + dim /= ep->distanceScale; + return dim; +} + +/*! + \property QAudioRoom::rotation + + Defines the orientation of the room in 3D space. + */ +void QAudioRoom::setRotation(const QQuaternion &q) +{ + if (toQuaternion(d->roomProperties.rotation) == q) + return; + toFloats(q, d->roomProperties.rotation); + d->dirty = true; + emit rotationChanged(); +} + +QQuaternion QAudioRoom::rotation() const +{ + return toQuaternion(d->roomProperties.rotation); +} + +/*! + \fn void QAudioRoom::wallsChanged() + + Signals when the wall material changes. +*/ +/*! + Sets \a wall to \a material. + + Different wall materials have different reflection and reverb properties + that influence the sound of the room. + + \sa wallMaterial(), Material, QAudioRoom::Wall + */ +void QAudioRoom::setWallMaterial(Wall wall, Material material) +{ + static_assert(vraudio::kUniform == int(UniformMaterial)); + static_assert(vraudio::kTransparent == int(Transparent)); + + if (d->roomProperties.material_names[int(wall)] == int(material)) + return; + d->roomProperties.material_names[int(wall)] = vraudio::MaterialName(int(material)); + d->dirty = true; + emit wallsChanged(); +} + +/*! + returns the material being used for \a wall. + + \sa setWallMaterial(), Material, QAudioRoom::Wall + */ +QAudioRoom::Material QAudioRoom::wallMaterial(Wall wall) const +{ + return Material(d->roomProperties.material_names[int(wall)]); +} + +/*! + \property QAudioRoom::reflectionGain + + A gain factor for reflections generated in this room. A value + from 0 to 1 will dampen reflections, while a value larger than 1 + will apply a gain to reflections, making them louder. + + The default is 1, a factor of 0 disables reflections. Negative + values are mapped to 0. + */ +void QAudioRoom::setReflectionGain(float factor) +{ + if (factor < 0.) + factor = 0.; + if (d->roomProperties.reflection_scalar == factor) + return; + d->roomProperties.reflection_scalar = factor; + d->dirty = true; + emit reflectionGainChanged(); +} + +float QAudioRoom::reflectionGain() const +{ + return d->roomProperties.reflection_scalar; +} + +/*! + \property QAudioRoom::reverbGain + + A gain factor for reverb generated in this room. A value + from 0 to 1 will dampen reverb, while a value larger than 1 + will apply a gain to the reverb, making it louder. + + The default is 1, a factor of 0 disables reverb. Negative + values are mapped to 0. + */ +void QAudioRoom::setReverbGain(float factor) +{ + if (factor < 0) + factor = 0; + if (d->roomProperties.reverb_gain == factor) + return; + d->roomProperties.reverb_gain = factor; + d->dirty = true; + emit reverbGainChanged(); +} + +float QAudioRoom::reverbGain() const +{ + return d->roomProperties.reverb_gain; +} + +/*! + \property QAudioRoom::reverbTime + + A factor to be applies to all reverb timings generated for this room. + Larger values will lead to longer reverb timings, making the room sound + larger. + + The default is 1. Negative values are mapped to 0. + */ +void QAudioRoom::setReverbTime(float factor) +{ + if (factor < 0) + factor = 0; + if (d->roomProperties.reverb_time == factor) + return; + d->roomProperties.reverb_time = factor; + d->dirty = true; + emit reverbTimeChanged(); +} + +float QAudioRoom::reverbTime() const +{ + return d->roomProperties.reverb_time; +} + +/*! + \property QAudioRoom::reverbBrightness + + A brightness factor to be applied to the generated reverb. + A positive value will increase reverb for higher frequencies and + dampen lower frequencies, a negative value does the reverse. + + The default is 0. + */ +void QAudioRoom::setReverbBrightness(float factor) +{ + if (d->roomProperties.reverb_brightness == factor) + return; + d->roomProperties.reverb_brightness = factor; + d->dirty = true; + emit reverbBrightnessChanged(); +} + +float QAudioRoom::reverbBrightness() const +{ + return d->roomProperties.reverb_brightness; +} + +QT_END_NAMESPACE + +#include "moc_qaudioroom.cpp" |