Skip to content

Commit 2c41dbd

Browse files
committed
CoreAudio: add UI to enable/disable spatial audio and head-tracking
1 parent 9036e37 commit 2c41dbd

File tree

5 files changed

+111
-9
lines changed

5 files changed

+111
-9
lines changed

app/gui/SettingsView.qml

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,78 @@ Flickable {
881881
}
882882
}
883883

884+
Label {
885+
width: parent.width
886+
id: resSpatialAudioTitle
887+
text: qsTr("Spatial audio")
888+
font.pointSize: 12
889+
wrapMode: Text.Wrap
890+
visible: Qt.platform.os == "osx"
891+
}
892+
893+
Row {
894+
spacing: 5
895+
width: parent.width
896+
visible: Qt.platform.os == "osx"
897+
898+
AutoResizingComboBox {
899+
// ignore setting the index at first, and actually set it when the component is loaded
900+
Component.onCompleted: {
901+
var saved_sac = StreamingPreferences.spatialAudioConfig
902+
currentIndex = 0
903+
for (var i = 0; i < spatialAudioListModel.count; i++) {
904+
var el_audio = spatialAudioListModel.get(i).val;
905+
if (saved_sac === el_audio) {
906+
currentIndex = i
907+
break
908+
}
909+
}
910+
activated(currentIndex)
911+
}
912+
913+
id: spatialAudioComboBox
914+
enabled: StreamingPreferences.audioConfig != StreamingPreferences.AC_STEREO
915+
textRole: "text"
916+
model: ListModel {
917+
id: spatialAudioListModel
918+
ListElement {
919+
text: qsTr("Enabled")
920+
val: StreamingPreferences.SAC_AUTO
921+
}
922+
ListElement {
923+
text: qsTr("Disabled")
924+
val: StreamingPreferences.SAC_DISABLED
925+
}
926+
}
927+
928+
// ::onActivated must be used, as it only listens for when the index is changed by a human
929+
onActivated : {
930+
StreamingPreferences.spatialAudioConfig = spatialAudioListModel.get(currentIndex).val
931+
}
932+
933+
ToolTip.delay: 1000
934+
ToolTip.timeout: 5000
935+
ToolTip.visible: hovered
936+
ToolTip.text: qsTr("Spatial audio will be used when using any type of headphones, built-in Macbook speakers, and 2-channel USB devices.")
937+
}
938+
939+
CheckBox {
940+
id: spatialHeadTracking
941+
enabled: StreamingPreferences.audioConfig != StreamingPreferences.AC_STEREO && StreamingPreferences.spatialAudioConfig != StreamingPreferences.SAC_DISABLED
942+
width: parent.width
943+
text: qsTr("Enable head-tracking")
944+
font.pointSize: 12
945+
checked: StreamingPreferences.spatialHeadTracking
946+
onCheckedChanged: {
947+
StreamingPreferences.spatialHeadTracking = checked
948+
}
949+
950+
ToolTip.delay: 1000
951+
ToolTip.timeout: 5000
952+
ToolTip.visible: hovered
953+
ToolTip.text: qsTr("Requires supported Apple or Beats headphones")
954+
}
955+
}
884956

885957
CheckBox {
886958
id: audioPcCheck
@@ -1176,7 +1248,7 @@ Flickable {
11761248
ListElement {
11771249
text: qsTr("Maximized")
11781250
val: StreamingPreferences.UI_MAXIMIZED
1179-
}
1251+
}
11801252
ListElement {
11811253
text: qsTr("Fullscreen")
11821254
val: StreamingPreferences.UI_FULLSCREEN

app/settings/streamingpreferences.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
#define SER_FULLSCREEN "fullscreen"
2020
#define SER_VSYNC "vsync"
2121
#define SER_GAMEOPTS "gameopts"
22+
#define SER_HEADTRACKING "headtracking"
2223
#define SER_HOSTAUDIO "hostaudio"
2324
#define SER_MULTICONT "multicontroller"
2425
#define SER_AUDIOCFG "audiocfg"
26+
#define SER_SPATIALAUDIOCFG "spatialaudiocfg"
2527
#define SER_VIDEOCFG "videocfg"
2628
#define SER_HDR "hdr"
2729
#define SER_YUV444 "yuv444"
@@ -124,6 +126,7 @@ void StreamingPreferences::reload()
124126
unlockBitrate = settings.value(SER_UNLOCK_BITRATE, false).toBool();
125127
enableVsync = settings.value(SER_VSYNC, true).toBool();
126128
gameOptimizations = settings.value(SER_GAMEOPTS, true).toBool();
129+
spatialHeadTracking = settings.value(SER_HEADTRACKING, false).toBool();
127130
playAudioOnHost = settings.value(SER_HOSTAUDIO, false).toBool();
128131
multiController = settings.value(SER_MULTICONT, true).toBool();
129132
enableMdns = settings.value(SER_MDNS, true).toBool();
@@ -148,6 +151,8 @@ void StreamingPreferences::reload()
148151
static_cast<int>(CaptureSysKeysMode::CSK_OFF)).toInt());
149152
audioConfig = static_cast<AudioConfig>(settings.value(SER_AUDIOCFG,
150153
static_cast<int>(AudioConfig::AC_STEREO)).toInt());
154+
spatialAudioConfig = static_cast<SpatialAudioConfig>(settings.value(SER_SPATIALAUDIOCFG,
155+
static_cast<int>(SpatialAudioConfig::SAC_AUTO)).toInt());
151156
videoCodecConfig = static_cast<VideoCodecConfig>(settings.value(SER_VIDEOCFG,
152157
static_cast<int>(VideoCodecConfig::VCC_AUTO)).toInt());
153158
videoDecoderSelection = static_cast<VideoDecoderSelection>(settings.value(SER_VIDEODEC,
@@ -314,6 +319,7 @@ void StreamingPreferences::save()
314319
settings.setValue(SER_UNLOCK_BITRATE, unlockBitrate);
315320
settings.setValue(SER_VSYNC, enableVsync);
316321
settings.setValue(SER_GAMEOPTS, gameOptimizations);
322+
settings.setValue(SER_HEADTRACKING, spatialHeadTracking);
317323
settings.setValue(SER_HOSTAUDIO, playAudioOnHost);
318324
settings.setValue(SER_MULTICONT, multiController);
319325
settings.setValue(SER_MDNS, enableMdns);
@@ -328,6 +334,7 @@ void StreamingPreferences::save()
328334
settings.setValue(SER_DETECTNETBLOCKING, detectNetworkBlocking);
329335
settings.setValue(SER_SHOWPERFOVERLAY, showPerformanceOverlay);
330336
settings.setValue(SER_AUDIOCFG, static_cast<int>(audioConfig));
337+
settings.setValue(SER_SPATIALAUDIOCFG, static_cast<int>(spatialAudioConfig));
331338
settings.setValue(SER_HDR, enableHdr);
332339
settings.setValue(SER_YUV444, enableYUV444);
333340
settings.setValue(SER_VIDEOCFG, static_cast<int>(videoCodecConfig));

app/settings/streamingpreferences.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ class StreamingPreferences : public QObject
2626
};
2727
Q_ENUM(AudioConfig)
2828

29+
enum SpatialAudioConfig
30+
{
31+
SAC_AUTO,
32+
SAC_DISABLED
33+
};
34+
Q_ENUM(SpatialAudioConfig)
35+
2936
enum VideoCodecConfig
3037
{
3138
VCC_AUTO,
@@ -112,6 +119,7 @@ class StreamingPreferences : public QObject
112119
Q_PROPERTY(bool unlockBitrate MEMBER unlockBitrate NOTIFY unlockBitrateChanged)
113120
Q_PROPERTY(bool enableVsync MEMBER enableVsync NOTIFY enableVsyncChanged)
114121
Q_PROPERTY(bool gameOptimizations MEMBER gameOptimizations NOTIFY gameOptimizationsChanged)
122+
Q_PROPERTY(bool spatialHeadTracking MEMBER spatialHeadTracking NOTIFY spatialHeadTrackingChanged)
115123
Q_PROPERTY(bool playAudioOnHost MEMBER playAudioOnHost NOTIFY playAudioOnHostChanged)
116124
Q_PROPERTY(bool multiController MEMBER multiController NOTIFY multiControllerChanged)
117125
Q_PROPERTY(bool enableMdns MEMBER enableMdns NOTIFY enableMdnsChanged)
@@ -125,6 +133,7 @@ class StreamingPreferences : public QObject
125133
Q_PROPERTY(bool detectNetworkBlocking MEMBER detectNetworkBlocking NOTIFY detectNetworkBlockingChanged)
126134
Q_PROPERTY(bool showPerformanceOverlay MEMBER showPerformanceOverlay NOTIFY showPerformanceOverlayChanged)
127135
Q_PROPERTY(AudioConfig audioConfig MEMBER audioConfig NOTIFY audioConfigChanged)
136+
Q_PROPERTY(SpatialAudioConfig spatialAudioConfig MEMBER spatialAudioConfig NOTIFY spatialAudioConfigChanged)
128137
Q_PROPERTY(VideoCodecConfig videoCodecConfig MEMBER videoCodecConfig NOTIFY videoCodecConfigChanged)
129138
Q_PROPERTY(bool enableHdr MEMBER enableHdr NOTIFY enableHdrChanged)
130139
Q_PROPERTY(bool enableYUV444 MEMBER enableYUV444 NOTIFY enableYUV444Changed)
@@ -151,6 +160,7 @@ class StreamingPreferences : public QObject
151160
bool unlockBitrate;
152161
bool enableVsync;
153162
bool gameOptimizations;
163+
bool spatialHeadTracking;
154164
bool playAudioOnHost;
155165
bool multiController;
156166
bool enableMdns;
@@ -171,6 +181,7 @@ class StreamingPreferences : public QObject
171181
bool keepAwake;
172182
int packetSize;
173183
AudioConfig audioConfig;
184+
SpatialAudioConfig spatialAudioConfig;
174185
VideoCodecConfig videoCodecConfig;
175186
bool enableHdr;
176187
bool enableYUV444;
@@ -187,6 +198,7 @@ class StreamingPreferences : public QObject
187198
void unlockBitrateChanged();
188199
void enableVsyncChanged();
189200
void gameOptimizationsChanged();
201+
void spatialHeadTrackingChanged();
190202
void playAudioOnHostChanged();
191203
void multiControllerChanged();
192204
void unsupportedFpsChanged();
@@ -195,6 +207,7 @@ class StreamingPreferences : public QObject
195207
void absoluteMouseModeChanged();
196208
void absoluteTouchModeChanged();
197209
void audioConfigChanged();
210+
void spatialAudioConfigChanged();
198211
void videoCodecConfigChanged();
199212
void enableHdrChanged();
200213
void enableYUV444Changed();

app/streaming/audio/renderers/coreaudio/au_spatial_renderer.mm

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#import "au_spatial_renderer.h"
22
#import "coreaudio_helpers.h"
33
#import "AllocatedAudioBufferList.h"
4+
#include "settings/streamingpreferences.h"
45

56
#import <AVFoundation/AVFoundation.h>
67
#import <QtGlobal>
@@ -211,12 +212,14 @@ OSStatus inputCallback(void *inRefCon,
211212
if (outputType == kSpatialMixerOutputType_Headphones) {
212213
// XXX: Both of these might require the builder to have a paid Apple developer account due to the use of entitlements.
213214

214-
// XXX Head-tracking doesn't work well currently, with audio glitches on my system. It's off by default pending a config option.
215215
// For devices that support it, enable head-tracking.
216216
// Apps that use low-latency head-tracking in iOS/tvOS need to set
217217
// the audio session category to ambient or run in Game Mode.
218218
// Head tracking requires the entitlement com.apple.developer.coremotion.head-pose.
219-
if (0) {
219+
220+
// XXX Head-tracking may cause audio glitches. It's off by default.
221+
StreamingPreferences *prefs = StreamingPreferences::get();
222+
if (prefs->spatialHeadTracking) {
220223
uint32_t ht = 1;
221224
status = AudioUnitSetProperty(m_Mixer, kAudioUnitProperty_SpatialMixerEnableHeadTracking, kAudioUnitScope_Global, 0, &ht, sizeof(uint32_t));
222225
if (status != noErr) {

app/streaming/audio/renderers/coreaudio/coreaudio.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "coreaudio.h"
22
#include "coreaudio_helpers.h"
3+
#include "settings/streamingpreferences.h"
34

45
#if TARGET_OS_OSX
56
#include <IOKit/audio/IOAudioTypes.h>
@@ -151,8 +152,8 @@ void CoreAudioRenderer::stringifyAudioStats(AUDIO_STATS& stats, char *output, in
151152
snprintf(
152153
output, length,
153154
"Audio stream: %s-channel Opus low-delay @ 48 kHz\n"
154-
"Output device: %s @ %.1f kHz, %u-channel\n"
155-
"Render mode: %s, device type: %s %s, %s\n"
155+
"Output device: %s @ %.1f kHz, %u-channel, %s %s\n"
156+
"Render mode: %s %s\n"
156157
"Opus config: %s, frame size: %.1fms, bitrate: %dkbps\n"
157158
"Buffer: length: %0.1f packets (%.1fms), current %.1f%%\n"
158159
"Packet loss from network: %.2f%%, packets dropped due to resync: %.2f%%\n"
@@ -163,8 +164,6 @@ void CoreAudioRenderer::stringifyAudioStats(AUDIO_STATS& stats, char *output, in
163164
m_OutputDeviceName,
164165
m_OutputASBD.mSampleRate / 1000.0,
165166
m_OutputASBD.mChannelsPerFrame,
166-
167-
m_Spatial ? (m_SpatialAU.m_PersonalizedHRTF ? "personalized spatial" : "spatial") : "passthrough",
168167
!strcmp(m_OutputTransportType, "blue") ? "Bluetooth"
169168
: !strcmp(m_OutputTransportType, "bltn") ? "built-in"
170169
: !strcmp(m_OutputTransportType, "usb ") ? "USB"
@@ -175,7 +174,9 @@ void CoreAudioRenderer::stringifyAudioStats(AUDIO_STATS& stats, char *output, in
175174
: !strcmp(m_OutputDataSource, "ispk") ? "internal speakers"
176175
: !strcmp(m_OutputDataSource, "espk") ? "external speakers"
177176
: m_OutputDataSource,
178-
m_Spatial && m_SpatialAU.m_HeadTracking ? "head-tracking: yes" : "",
177+
178+
m_Spatial ? (m_SpatialAU.m_PersonalizedHRTF ? "personalized spatial" : "spatial") : "passthrough",
179+
m_Spatial && m_SpatialAU.m_HeadTracking ? "with head-tracking" : "",
179180

180181
// Work out if we're getting high or low quality from Sunshine. coupled surround is designed for physical speakers
181182
((m_opusConfig->channelCount == 2 && stats.opusBitsPerSec > 128) || !m_opusConfig->coupledStreams)
@@ -300,13 +301,19 @@ bool CoreAudioRenderer::prepareForPlayback(const OPUS_MULTISTREAM_CONFIGURATION*
300301

301302
DEBUG_TRACE("CoreAudioRenderer getSpatialMixerOutputType = %d", outputType);
302303

303-
// XXX: let the user choose this if they want
304304
if (opusConfig->channelCount > 2) {
305305
if (outputType != kSpatialMixerOutputType_ExternalSpeakers) {
306306
m_Spatial = true;
307307
}
308308
}
309309

310+
StreamingPreferences *prefs = StreamingPreferences::get();
311+
if (prefs->spatialAudioConfig == StreamingPreferences::SAC_DISABLED) {
312+
// User has disabled spatial audio
313+
DEBUG_TRACE("CoreAudioRenderer user has disabled spatial audio");
314+
m_Spatial = false;
315+
}
316+
310317
// indicate the format our callback will provide samples in
311318
// If necessary, the OS takes care of resampling (but not downmixing, hmm)
312319
AudioStreamBasicDescription streamDesc;

0 commit comments

Comments
 (0)