Spatial Audio Panning Example▲
Sélectionnez
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include <QAudioEngine>
#include <QAudioListener>
#include <QAudioRoom>
#include <QCheckBox>
#include <QComboBox>
#include <QCommandLineParser>
#include <QFileDialog>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QLibraryInfo>
#include <QLineEdit>
#include <QPropertyAnimation>
#include <QPushButton>
#include <QSlider>
#include <QSpatialSound>
#include <QStandardPaths>
class
AudioWidget : public
QWidget
{
public
:
AudioWidget();
void
setFile(const
QString &
amp;file);
private
slots:
void
updatePosition();
void
newOcclusion();
void
modeChanged();
void
fileChanged(const
QString &
amp;file);
void
openFileDialog();
void
updateRoom();
void
animateChanged();
private
:
QLineEdit *
fileEdit =
nullptr
;
QPushButton *
fileDialogButton =
nullptr
;
QSlider *
azimuth =
nullptr
;
QSlider *
elevation =
nullptr
;
QSlider *
distance =
nullptr
;
QSlider *
occlusion =
nullptr
;
QSlider *
roomDimension =
nullptr
;
QSlider *
reverbGain =
nullptr
;
QSlider *
reflectionGain =
nullptr
;
QComboBox *
mode =
nullptr
;
QCheckBox *
animateButton =
nullptr
;
QPropertyAnimation *
animation =
nullptr
;
QAudioEngine engine;
QAudioListener *
listener =
nullptr
;
QSpatialSound *
sound =
nullptr
;
QAudioRoom *
room =
nullptr
;
QFileDialog *
fileDialog =
nullptr
;
}
;
AudioWidget::
AudioWidget()
:
QWidget()
{
setMinimumSize(400
, 300
);
auto
*
form =
new
QFormLayout(this
);
auto
*
fileLayout =
new
QHBoxLayout;
fileEdit =
new
QLineEdit;
fileEdit-&
gt;setPlaceholderText(tr("Audio File"
));
fileLayout-&
gt;addWidget(fileEdit);
fileDialogButton =
new
QPushButton(tr("Choose..."
));
fileLayout-&
gt;addWidget(fileDialogButton);
form-&
gt;addRow(fileLayout);
azimuth =
new
QSlider(Qt::
Horizontal);
azimuth-&
gt;setRange(-
180
, 180
);
form-&
gt;addRow(tr("Azimuth (-180 - 180 degree):"
), azimuth);
elevation =
new
QSlider(Qt::
Horizontal);
elevation-&
gt;setRange(-
90
, 90
);
form-&
gt;addRow(tr("Elevation (-90 - 90 degree)"
), elevation);
distance =
new
QSlider(Qt::
Horizontal);
distance-&
gt;setRange(0
, 1000
);
distance-&
gt;setValue(100
);
form-&
gt;addRow(tr("Distance (0 - 10 meter):"
), distance);
occlusion =
new
QSlider(Qt::
Horizontal);
occlusion-&
gt;setRange(0
, 400
);
form-&
gt;addRow(tr("Occlusion (0 - 4):"
), occlusion);
roomDimension =
new
QSlider(Qt::
Horizontal);
roomDimension-&
gt;setRange(0
, 10000
);
roomDimension-&
gt;setValue(1000
);
form-&
gt;addRow(tr("Room dimension (0 - 100 meter):"
), roomDimension);
reverbGain =
new
QSlider(Qt::
Horizontal);
reverbGain-&
gt;setRange(0
, 500
);
reverbGain-&
gt;setValue(0
);
form-&
gt;addRow(tr("Reverb gain (0-5):"
), reverbGain);
reflectionGain =
new
QSlider(Qt::
Horizontal);
reflectionGain-&
gt;setRange(0
, 500
);
reflectionGain-&
gt;setValue(0
);
form-&
gt;addRow(tr("Reflection gain (0-5):"
), reflectionGain);
mode =
new
QComboBox;
mode-&
gt;addItem(tr("Surround"
), QVariant::
fromValue(QAudioEngine::
Surround));
mode-&
gt;addItem(tr("Stereo"
), QVariant::
fromValue(QAudioEngine::
Stereo));
mode-&
gt;addItem(tr("Headphone"
), QVariant::
fromValue(QAudioEngine::
Headphone));
form-&
gt;addRow(tr("Output mode:"
), mode);
animateButton =
new
QCheckBox(tr("Animate sound position"
));
form-&
gt;addRow(animateButton);
connect(fileEdit, &
amp;QLineEdit::
textChanged, this
, &
amp;AudioWidget::
fileChanged);
connect(fileDialogButton, &
amp;QPushButton::
clicked, this
, &
amp;AudioWidget::
openFileDialog);
connect(azimuth, &
amp;QSlider::
valueChanged, this
, &
amp;AudioWidget::
updatePosition);
connect(elevation, &
amp;QSlider::
valueChanged, this
, &
amp;AudioWidget::
updatePosition);
connect(distance, &
amp;QSlider::
valueChanged, this
, &
amp;AudioWidget::
updatePosition);
connect(occlusion, &
amp;QSlider::
valueChanged, this
, &
amp;AudioWidget::
newOcclusion);
connect(roomDimension, &
amp;QSlider::
valueChanged, this
, &
amp;AudioWidget::
updateRoom);
connect(reverbGain, &
amp;QSlider::
valueChanged, this
, &
amp;AudioWidget::
updateRoom);
connect(reflectionGain, &
amp;QSlider::
valueChanged, this
, &
amp;AudioWidget::
updateRoom);
connect(mode, &
amp;QComboBox::
currentIndexChanged, this
, &
amp;AudioWidget::
modeChanged);
room =
new
QAudioRoom(&
amp;engine);
room-&
gt;setWallMaterial(QAudioRoom::
BackWall, QAudioRoom::
BrickBare);
room-&
gt;setWallMaterial(QAudioRoom::
FrontWall, QAudioRoom::
BrickBare);
room-&
gt;setWallMaterial(QAudioRoom::
LeftWall, QAudioRoom::
BrickBare);
room-&
gt;setWallMaterial(QAudioRoom::
RightWall, QAudioRoom::
BrickBare);
room-&
gt;setWallMaterial(QAudioRoom::
Floor, QAudioRoom::
Marble);
room-&
gt;setWallMaterial(QAudioRoom::
Ceiling, QAudioRoom::
WoodCeiling);
updateRoom();
listener =
new
QAudioListener(&
amp;engine);
listener-&
gt;setPosition({}
);
listener-&
gt;setRotation({}
);
engine.start();
sound =
new
QSpatialSound(&
amp;engine);
updatePosition();
animation =
new
QPropertyAnimation(azimuth, "value"
);
animation-&
gt;setDuration(10000
);
animation-&
gt;setStartValue(-
180
);
animation-&
gt;setEndValue(180
);
animation-&
gt;setLoopCount(-
1
);
connect(animateButton, &
amp;QCheckBox::
toggled, this
, &
amp;AudioWidget::
animateChanged);
}
void
AudioWidget::
setFile(const
QString &
amp;file)
{
fileEdit-&
gt;setText(file);
}
void
AudioWidget::
updatePosition()
{
const
float
az =
azimuth-&
gt;value() /
180.
*
M_PI;
const
float
el =
elevation-&
gt;value() /
180.
*
M_PI;
const
float
d =
distance-&
gt;value();
const
float
x =
d *
sin(az) *
cos(el);
const
float
y =
d *
sin(el);
const
float
z =
-
d *
cos(az) *
cos(el);
sound-&
gt;setPosition({
x, y, z}
);
}
void
AudioWidget::
newOcclusion()
{
sound-&
gt;setOcclusionIntensity(occlusion-&
gt;value() /
100.
);
}
void
AudioWidget::
modeChanged()
{
engine.setOutputMode(mode-&
gt;currentData().value&
lt;QAudioEngine::
OutputMode&
gt;());
}
void
AudioWidget::
fileChanged(const
QString &
amp;file)
{
sound-&
gt;setSource(QUrl::
fromLocalFile(file));
sound-&
gt;setSize(5
);
sound-&
gt;setLoops(QSpatialSound::
Infinite);
}
void
AudioWidget::
openFileDialog()
{
if
(fileDialog ==
nullptr
) {
const
QString dir =
QStandardPaths::
writableLocation(QStandardPaths::
MusicLocation);
fileDialog =
new
QFileDialog(this
, tr("Open Audio File"
), dir);
fileDialog-&
gt;setAcceptMode(QFileDialog::
AcceptOpen);
const
QStringList mimeTypes{
"audio/mpeg"
, "audio/aac"
, "audio/x-ms-wma"
,
"audio/x-flac+ogg"
, "audio/x-wav"
}
;
fileDialog-&
gt;setMimeTypeFilters(mimeTypes);
fileDialog-&
gt;selectMimeTypeFilter(mimeTypes.constFirst());
}
if
(fileDialog-&
gt;exec() ==
QDialog::
Accepted)
fileEdit-&
gt;setText(fileDialog-&
gt;selectedFiles().constFirst());
}
void
AudioWidget::
updateRoom()
{
const
float
d =
roomDimension-&
gt;value();
room-&
gt;setDimensions(QVector3D(d, d, 400
));
room-&
gt;setReflectionGain(float
(reflectionGain-&
gt;value()) /
100
);
room-&
gt;setReverbGain(float
(reverbGain-&
gt;value()) /
100
);
}
void
AudioWidget::
animateChanged()
{
if
(animateButton-&
gt;isChecked())
animation-&
gt;start();
else
animation-&
gt;stop();
}
int
main(int
argc, char
**
argv)
{
QApplication app(argc, argv);
QCoreApplication::
setApplicationVersion(qVersion());
QGuiApplication::
setApplicationDisplayName(AudioWidget::
tr("Spatial Audio test application"
));
QCommandLineParser commandLineParser;
commandLineParser.addVersionOption();
commandLineParser.addHelpOption();
commandLineParser.addPositionalArgument("Audio File"
,
"Audio File to play"
);
commandLineParser.process(app);
AudioWidget w;
w.show();
if
(!
commandLineParser.positionalArguments().isEmpty())
w.setFile(commandLineParser.positionalArguments().constFirst());
return
app.exec();
}