Audio/HRTF: Make head tracking optional, add reset button

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2025-01-03 15:23:02 -08:00
parent 68c334d96e
commit 8bc2d3cd38
10 changed files with 115 additions and 9 deletions

View file

@ -98,7 +98,9 @@ NSString *CogPlaybackDidStopNotificiation = @"CogPlaybackDidStopNotificiation";
@"GraphicEQpreset": @(-1),
@"GraphicEQtrackgenre": @(NO),
@"volumeLimit": @(YES),
@"headphoneVirtualization": @(NO) };
@"enableHrtf": @(NO),
@"enableHeadTracking": @(NO)
};
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsDictionary];
}

View file

@ -75,6 +75,9 @@ using std::atomic_long;
BOOL eqEnabled;
BOOL eqInitialized;
BOOL enableHeadTracking;
BOOL lastEnableHeadTracking;
BOOL streamFormatStarted;
BOOL streamFormatChanged;
@ -84,6 +87,7 @@ using std::atomic_long;
BOOL currentdevicelistenerapplied;
BOOL devicealivelistenerapplied;
BOOL observersapplied;
BOOL htlistenerapplied;
BOOL outputdevicechanged;
float volume;
@ -182,6 +186,7 @@ using std::atomic_long;
- (void)sustainHDCD;
- (void)reportMotion:(simd_float4x4)matrix;
- (void)resetReferencePosition;
- (void)setPitch:(double)p;
- (void)setTempo:(double)t;

View file

@ -31,6 +31,8 @@ extern void scale_by_volume(float *buffer, size_t count, float volume);
static NSString *CogPlaybackDidBeginNotificiation = @"CogPlaybackDidBeginNotificiation";
static NSString *CogPlaybackDidResetHeadTracking = @"CogPlaybackDigResetHeadTracking";
#define tts ((RubberBandState)ts)
simd_float4x4 convertMatrix(CMRotationMatrix r) {
@ -394,8 +396,10 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
} else if([keyPath isEqualToString:@"values.eqPreamp"]) {
float preamp = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] floatForKey:@"eqPreamp"];
eqPreamp = pow(10.0, preamp / 20.0);
} else if([keyPath isEqualToString:@"values.enableHrtf"]) {
} else if([keyPath isEqualToString:@"values.enableHrtf"] ||
[keyPath isEqualToString:@"values.enableHeadTracking"]) {
enableHrtf = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"enableHrtf"];
enableHeadTracking = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"enableHeadTracking"];
if(streamFormatStarted)
resetStreamFormat = YES;
} else if([keyPath isEqualToString:@"values.enableFSurround"]) {
@ -793,12 +797,27 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
rotationMatrixUpdated = NO;
simd_float4x4 matrix;
if(!referenceMatrixSet) {
if(!referenceMatrixSet || !enableHeadTracking) {
referenceMatrixSet = NO;
matrix = matrix_identity_float4x4;
self->referenceMatrix = matrix;
registerMotionListener(self);
if(enableHeadTracking) {
lastEnableHeadTracking = YES;
registerMotionListener(self);
} else if(lastEnableHeadTracking) {
lastEnableHeadTracking = NO;
unregisterMotionListener();
}
} else {
matrix = simd_mul(rotationMatrix, referenceMatrix);
simd_float4x4 mirrorTransform = {
simd_make_float4(-1.0, 0.0, 0.0, 0.0),
simd_make_float4(0.0, 1.0, 0.0, 0.0),
simd_make_float4(0.0, 0.0, 1.0, 0.0),
simd_make_float4(0.0, 0.0, 0.0, 1.0)
};
matrix = simd_mul(mirrorTransform, rotationMatrix);
matrix = simd_mul(matrix, referenceMatrix);
}
[outputLock lock];
@ -808,7 +827,10 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
channels = 2;
channelConfig = AudioChannelSideLeft | AudioChannelSideRight;
} else {
unregisterMotionListener();
if(lastEnableHeadTracking) {
lastEnableHeadTracking = NO;
unregisterMotionListener();
}
referenceMatrixSet = NO;
[outputLock lock];
@ -976,7 +998,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
int simpleSpeedInput = samplesRendered;
int simpleSpeedRendered = 0;
int channels = realStreamFormat.mChannelsPerFrame;
size_t max_block_len = blockSize;
//size_t max_block_len = blockSize;
if (fabs(pitch - lastPitch) > 1e-5 ||
fabs(tempo - lastTempo) > 1e-5) {
@ -1309,9 +1331,13 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.GraphicEQenable" options:0 context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.eqPreamp" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.enableHrtf" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.enableHeadTracking" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.enableFSurround" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
observersapplied = YES;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resetReferencePosition:) name:CogPlaybackDidResetHeadTracking object:nil];
htlistenerapplied = YES;
bzero(&timeStamp, sizeof(timeStamp));
timeStamp.mFlags = kAudioTimeStampSampleTimeValid;
@ -1381,14 +1407,20 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
}
@synchronized(self) {
stopInvoked = YES;
if(hrtf) {
if(hrtf && lastEnableHeadTracking) {
lastEnableHeadTracking = NO;
unregisterMotionListener();
}
if(htlistenerapplied) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:CogPlaybackDidResetHeadTracking object:nil];
htlistenerapplied = NO;
}
if(observersapplied) {
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.outputDevice" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.GraphicEQenable" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.eqPreamp" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.enableHrtf" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.enableHeadTracking" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.enableFSurround" context:kOutputCoreAudioContext];
observersapplied = NO;
}
@ -1505,4 +1537,8 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
rotationMatrixUpdated = YES;
}
- (void)resetReferencePosition:(NSNotification *)notification {
referenceMatrixSet = NO;
}
@end

View file

@ -232,6 +232,8 @@
<userDefaultsController representsSharedInstance="YES" id="52" userLabel="Shared Defaults"/>
<customObject id="57" userLabel="OutputPane" customClass="OutputPane">
<connections>
<outlet property="headRecenter" destination="BBu-91-qdU" id="zg0-q7-dpf"/>
<outlet property="headTracking" destination="mz3-1e-AVD" id="vH2-Wq-sdT"/>
<outlet property="outputDevices" destination="59" id="76"/>
<outlet property="view" destination="58" id="73"/>
</connections>
@ -274,7 +276,7 @@
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rG5-80-FId">
<rect key="frame" x="18" y="80" width="602" height="18"/>
<rect key="frame" x="18" y="80" width="200" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Enable HRTF filter" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="NGx-0c-WVR">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@ -284,6 +286,28 @@
<binding destination="52" name="value" keyPath="values.enableHrtf" id="BD0-cP-SfB"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mz3-1e-AVD" userLabel="Head Tracking">
<rect key="frame" x="224" y="80" width="184" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Head Tracking" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="Lpr-85-geO" userLabel="Head Tracking">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<binding destination="52" name="value" keyPath="values.enableHeadTracking" id="XCn-hn-2n4"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="BBu-91-qdU" userLabel="Recenter button">
<rect key="frame" x="450" y="72" width="89" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Recenter" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="aQP-kn-fMB">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<connections>
<action selector="resetHeadTracking:" target="57" id="0KV-Tf-z7V"/>
</connections>
</buttonCell>
</button>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="65">
<rect key="frame" x="6" y="53" width="134" height="17"/>
<autoresizingMask key="autoresizingMask"/>

View file

@ -12,8 +12,11 @@
@interface OutputPane : GeneralPreferencePane {
IBOutlet OutputsArrayController *outputDevices;
IBOutlet NSButton *headTracking;
IBOutlet NSButton *headRecenter;
}
- (IBAction)takeDeviceID:(id)sender;
- (IBAction)resetHeadTracking:(id)sender;
@end

View file

@ -8,6 +8,8 @@
#import "OutputPane.h"
static NSString *CogPlaybackDidResetHeadTracking = @"CogPlaybackDigResetHeadTracking";
@implementation OutputPane
- (NSString *)title {
@ -15,6 +17,12 @@
}
- (NSImage *)icon {
if(@available(macOS 14.0, *)) {
/* do nothing */
} else {
[headTracking setHidden:YES];
[headRecenter setHidden:YES];
}
if(@available(macOS 11.0, *))
return [NSImage imageWithSystemSymbolName:@"hifispeaker.2.fill" accessibilityDescription:nil];
return [[NSImage alloc] initWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForImageResource:@"output"]];
@ -25,4 +33,8 @@
[[NSUserDefaults standardUserDefaults] setObject:device forKey:@"outputDevice"];
}
- (IBAction)resetHeadTracking:(id)sender {
[[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidResetHeadTracking object:nil];
}
@end

View file

@ -233,6 +233,12 @@
/* Class = "NSButtonCell"; title = "Enable HRTF filter"; ObjectID = "NGx-0c-WVR"; */
"NGx-0c-WVR.title" = "Enable HRTF filter";
/* Class = "NSButtonCell"; title = "Head Tracking"; ObjectID = "Lpr-85-geO"; */
"Lpr-85-geO.title" = "Head Tracking";
/* Class = "NSButtonCell"; title = "Recenter"; ObjectID = "aQP-kn-fMB"; */
"aQP-kn-fMB.title" = "Recenter";
/* Class = "NSButtonCell"; title = "Enable FreeSurround stereo to surround upmixing"; ObjectID = "F0i-UK-6Nu"; */
"F0i-UK-6Nu.title" = "Enable FreeSurround stereo to surround upmixing";

View file

@ -229,6 +229,12 @@
/* Class = "NSButtonCell"; title = "Enable HRTF filter"; ObjectID = "NGx-0c-WVR"; */
"NGx-0c-WVR.title" = "Activar filtro HRTF";
/* Class = "NSButtonCell"; title = "Head Tracking"; ObjectID = "Lpr-85-geO"; */
"Lpr-85-geO.title" = "Seguimiento de cabeza";
/* Class = "NSButtonCell"; title = "Recenter"; ObjectID = "aQP-kn-fMB"; */
"aQP-kn-fMB.title" = "Recalibrar";
/* Class = "NSButtonCell"; title = "Automatically check for updates on startup"; ObjectID = "207"; */
"207.title" = "Buscar actualizaciones al abrir";

View file

@ -194,6 +194,12 @@
/* Class = "NSButtonCell"; title = "Enable HRTF filter"; ObjectID = "NGx-0c-WVR"; */
"NGx-0c-WVR.title" = "Включить фильтр HRTF";
/* Class = "NSButtonCell"; title = "Head Tracking"; ObjectID = "Lpr-85-geO"; */
"Lpr-85-geO.title" = "Трекинг головы";
/* Class = "NSButtonCell"; title = "Recenter"; ObjectID = "aQP-kn-fMB"; */
"aQP-kn-fMB.title" = "Перецентрировать";
/* Class = "NSButtonCell"; title = "Enable FreeSurround stereo to surround upmixing"; ObjectID = "F0i-UK-6Nu"; */
"F0i-UK-6Nu.title" = "Включить FreeSurround апмикс стерео в объемный звук";

View file

@ -131,6 +131,12 @@
/* Class = "NSButtonCell"; title = "Enable HRTF filter"; ObjectID = "NGx-0c-WVR"; */
"NGx-0c-WVR.title" = "HRTF filtresini etkinleştir";
/* Class = "NSButtonCell"; title = "Head Tracking"; ObjectID = "Lpr-85-geO"; */
"Lpr-85-geO.title" = "Kafa Takibi";
/* Class = "NSButtonCell"; title = "Recenter"; ObjectID = "aQP-kn-fMB"; */
"aQP-kn-fMB.title" = "Yeniden Merkeze Getir";
/* Class = "NSButtonCell"; title = "Use 3D rendered spectrum"; ObjectID = "NMg-TO-amV"; */
"NMg-TO-amV.title" = "3D işlenmiş spektrum kullanın";