[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
parent
7bc49ccb80
commit
7044a9a852
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue