[Core Audio Output] Add more event listeners

Sound output format changes should now happen instantaneously instead of
with a delay.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
swiftingly
Christopher Snowhill 2022-06-16 23:28:25 -07:00
parent 7bc49ccb80
commit 7044a9a852
2 changed files with 81 additions and 17 deletions

View File

@ -58,8 +58,11 @@ using std::atomic_long;
atomic_long bytesRendered; atomic_long bytesRendered;
atomic_long bytesHdcdSustained; atomic_long bytesHdcdSustained;
BOOL listenerapplied; BOOL defaultdevicelistenerapplied;
BOOL currentdevicelistenerapplied;
BOOL devicealivelistenerapplied;
BOOL observersapplied; BOOL observersapplied;
BOOL outputdevicechanged;
float volume; float volume;
float eqPreamp; float eqPreamp;

View File

@ -224,6 +224,23 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
return [this setOutputDeviceByID:-1]; return [this setOutputDeviceByID:-1];
} }
static OSStatus
current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inUserData) {
OutputCoreAudio *this = (__bridge OutputCoreAudio *)inUserData;
for(UInt32 i = 0; i < inNumberAddresses; ++i) {
switch(inAddresses[i].mSelector) {
case kAudioDevicePropertyDeviceIsAlive:
return [this setOutputDeviceByID:-1];
case kAudioDevicePropertyNominalSampleRate:
case kAudioDevicePropertyStreamFormat:
this->outputdevicechanged = YES;
return noErr;
}
}
return noErr;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if(context != kOutputCoreAudioContext) { if(context != kOutputCoreAudioContext) {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
@ -253,17 +270,16 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
running = YES; running = YES;
started = NO; started = NO;
stopNext = NO; stopNext = NO;
size_t eventCount = 0;
atomic_store(&bytesRendered, 0); atomic_store(&bytesRendered, 0);
NSMutableArray *delayedEvents = [[NSMutableArray alloc] init]; NSMutableArray *delayedEvents = [[NSMutableArray alloc] init];
BOOL delayedEventsPopped = YES; BOOL delayedEventsPopped = YES;
while(!stopping) { while(!stopping) {
if(++eventCount == 48) { if(outputdevicechanged) {
[self resetIfOutputChanged]; [self resetIfOutputChanged];
if(restarted) break; outputdevicechanged = NO;
eventCount = 0;
} }
if(restarted) break;
if([outputController shouldReset]) { if([outputController shouldReset]) {
@autoreleasepool { @autoreleasepool {
[[outputController buffer] reset]; [[outputController buffer] reset];
@ -369,14 +385,29 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
} }
if(_au) { if(_au) {
AudioObjectPropertyAddress defaultDeviceAddress = theAddress; if(defaultdevicelistenerapplied && !defaultDevice) {
/* Already set above
if(listenerapplied && !defaultDevice) { * theAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; */
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &defaultDeviceAddress, default_device_changed, (__bridge void *_Nullable)(self)); AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &theAddress, default_device_changed, (__bridge void *_Nullable)(self));
listenerapplied = NO; defaultdevicelistenerapplied = NO;
} }
outputdevicechanged = NO;
if(outputDeviceID != deviceID) { if(outputDeviceID != deviceID) {
if(currentdevicelistenerapplied) {
if(devicealivelistenerapplied) {
theAddress.mSelector = kAudioDevicePropertyDeviceIsAlive;
AudioObjectRemovePropertyListener(outputDeviceID, &theAddress, current_device_listener, (__bridge void *_Nullable)(self));
devicealivelistenerapplied = NO;
}
theAddress.mSelector = kAudioDevicePropertyStreamFormat;
AudioObjectRemovePropertyListener(outputDeviceID, &theAddress, current_device_listener, (__bridge void *_Nullable)(self));
theAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
AudioObjectRemovePropertyListener(outputDeviceID, &theAddress, current_device_listener, (__bridge void *_Nullable)(self));
currentdevicelistenerapplied = NO;
}
DLog(@"Device: %i\n", deviceID); DLog(@"Device: %i\n", deviceID);
outputDeviceID = deviceID; outputDeviceID = deviceID;
@ -385,11 +416,27 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
if(nserr != nil) { if(nserr != nil) {
return (OSErr)[nserr code]; return (OSErr)[nserr code];
} }
outputdevicechanged = YES;
} }
if(!listenerapplied && defaultDevice) { if(!currentdevicelistenerapplied) {
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &defaultDeviceAddress, default_device_changed, (__bridge void *_Nullable)(self)); if(!devicealivelistenerapplied && !defaultDevice) {
listenerapplied = YES; theAddress.mSelector = kAudioDevicePropertyDeviceIsAlive;
AudioObjectAddPropertyListener(outputDeviceID, &theAddress, current_device_listener, (__bridge void *_Nullable)(self));
devicealivelistenerapplied = YES;
}
theAddress.mSelector = kAudioDevicePropertyStreamFormat;
AudioObjectAddPropertyListener(outputDeviceID, &theAddress, current_device_listener, (__bridge void *_Nullable)(self));
theAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
AudioObjectAddPropertyListener(outputDeviceID, &theAddress, current_device_listener, (__bridge void *_Nullable)(self));
currentdevicelistenerapplied = YES;
}
if(!defaultdevicelistenerapplied && defaultDevice) {
theAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &theAddress, default_device_changed, (__bridge void *_Nullable)(self));
defaultdevicelistenerapplied = YES;
} }
} else { } else {
err = noErr; err = noErr;
@ -842,14 +889,28 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
paused = NO; paused = NO;
[writeSemaphore signal]; [writeSemaphore signal];
[readSemaphore signal]; [readSemaphore signal];
if(listenerapplied) { if(defaultdevicelistenerapplied || currentdevicelistenerapplied || devicealivelistenerapplied) {
AudioObjectPropertyAddress theAddress = { AudioObjectPropertyAddress theAddress = {
.mSelector = kAudioHardwarePropertyDefaultOutputDevice,
.mScope = kAudioObjectPropertyScopeGlobal, .mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMaster .mElement = kAudioObjectPropertyElementMaster
}; };
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &theAddress, default_device_changed, (__bridge void *_Nullable)(self)); if(defaultdevicelistenerapplied) {
listenerapplied = NO; theAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &theAddress, default_device_changed, (__bridge void *_Nullable)(self));
defaultdevicelistenerapplied = NO;
}
if(devicealivelistenerapplied) {
theAddress.mSelector = kAudioDevicePropertyDeviceIsAlive;
AudioObjectRemovePropertyListener(outputDeviceID, &theAddress, current_device_listener, (__bridge void *_Nullable)(self));
devicealivelistenerapplied = NO;
}
if(currentdevicelistenerapplied) {
theAddress.mSelector = kAudioDevicePropertyStreamFormat;
AudioObjectRemovePropertyListener(outputDeviceID, &theAddress, current_device_listener, (__bridge void *_Nullable)(self));
theAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
AudioObjectRemovePropertyListener(outputDeviceID, &theAddress, current_device_listener, (__bridge void *_Nullable)(self));
currentdevicelistenerapplied = NO;
}
} }
if(_au) { if(_au) {
if(started) if(started)