Improve output handling, 2.

Fix issues with above.
CQTexperiment
Jan Weiß 2020-02-01 14:44:07 +01:00
parent 57ebc65ba0
commit b22c5964e4
2 changed files with 81 additions and 54 deletions

View File

@ -115,18 +115,26 @@ static OSStatus Sound_Renderer(void *inRefCon, AudioUnitRenderActionFlags *ioAc
// Try matching by name. // Try matching by name.
NSString *userDeviceName = deviceDict[@"name"]; NSString *userDeviceName = deviceDict[@"name"];
[self enumerateAudioOutputsUsingBlock: [self enumerateAudioOutputsUsingBlock:
^(NSString *deviceName, AudioDeviceID deviceID, BOOL *stop) { ^(NSString *deviceName, AudioDeviceID deviceID, AudioDeviceID systemDefaultID, BOOL *stop) {
if ([deviceName isEqualToString:userDeviceName]) { if ([deviceName isEqualToString:userDeviceName]) {
err = [self setOutputDeviceByID:deviceID]; err = [self setOutputDeviceByID:deviceID];
#if 0
// Disable. Would cause loop by triggering `-observeValueForKeyPath:ofObject:change:context:` above.
// Update `outputDevice`, in case the ID has changed. // Update `outputDevice`, in case the ID has changed.
NSDictionary *deviceInfo = @{ NSDictionary *deviceInfo = @{
@"name": deviceName, @"name": deviceName,
@"deviceID": [NSNumber numberWithUnsignedInt:deviceID], @"deviceID": [NSNumber numberWithUnsignedInt:deviceID],
}; };
[[NSUserDefaults standardUserDefaults] setObject:deviceInfo forKey:@"outputDevice"]; [[NSUserDefaults standardUserDefaults] setObject:deviceInfo forKey:@"outputDevice"];
#endif
return;
} }
}]; }];
}
if (err != noErr) {
ALog(@"No output device could be found, your random error code is %d. Have a nice day!", err); ALog(@"No output device could be found, your random error code is %d. Have a nice day!", err);
@ -138,18 +146,19 @@ static OSStatus Sound_Renderer(void *inRefCon, AudioUnitRenderActionFlags *ioAc
// The following is largely a copy pasta of -awakeFromNib from "OutputsArrayController.m". // The following is largely a copy pasta of -awakeFromNib from "OutputsArrayController.m".
// TODO: Share the code. (How to do this across xcodeproj?) // TODO: Share the code. (How to do this across xcodeproj?)
- (void)enumerateAudioOutputsUsingBlock:(void (NS_NOESCAPE ^ _Nonnull)(NSString *deviceName, AudioDeviceID deviceID, BOOL *stop))block { - (void)enumerateAudioOutputsUsingBlock:(void (NS_NOESCAPE ^ _Nonnull)(NSString *deviceName, AudioDeviceID deviceID, AudioDeviceID systemDefaultID, BOOL *stop))block
{
UInt32 propsize; UInt32 propsize;
AudioObjectPropertyAddress theAddress = { AudioObjectPropertyAddress theAddress = {
.mSelector = kAudioHardwarePropertyDevices, .mSelector = kAudioHardwarePropertyDevices,
.mScope = kAudioObjectPropertyScopeGlobal, .mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMaster .mElement = kAudioObjectPropertyElementMaster
}; };
__Verify_noErr(AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize)); __Verify_noErr(AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize));
int nDevices = propsize / sizeof(AudioDeviceID); UInt32 nDevices = propsize / (UInt32)sizeof(AudioDeviceID);
AudioDeviceID *devids = malloc(propsize); AudioDeviceID *devids = malloc(propsize);
__Verify_noErr(AudioObjectGetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize, devids)); __Verify_noErr(AudioObjectGetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize, devids));
int i;
theAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; theAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
AudioDeviceID systemDefault; AudioDeviceID systemDefault;
@ -158,7 +167,7 @@ static OSStatus Sound_Renderer(void *inRefCon, AudioUnitRenderActionFlags *ioAc
theAddress.mScope = kAudioDevicePropertyScopeOutput; theAddress.mScope = kAudioDevicePropertyScopeOutput;
for (i = 0; i < nDevices; ++i) { for (UInt32 i = 0; i < nDevices; ++i) {
CFStringRef name = NULL; CFStringRef name = NULL;
propsize = sizeof(name); propsize = sizeof(name);
theAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; theAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
@ -180,6 +189,7 @@ static OSStatus Sound_Renderer(void *inRefCon, AudioUnitRenderActionFlags *ioAc
BOOL stop = NO; BOOL stop = NO;
block([NSString stringWithString:(__bridge NSString *)name], block([NSString stringWithString:(__bridge NSString *)name],
devids[i], devids[i],
systemDefault,
&stop); &stop);
CFRelease(name); CFRelease(name);

View File

@ -8,54 +8,16 @@
[self setSelectsInsertedObjects:NO]; [self setSelectsInsertedObjects:NO];
UInt32 propsize;
AudioObjectPropertyAddress theAddress = {
.mSelector = kAudioHardwarePropertyDevices,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMaster
};
__Verify_noErr(AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize));
int nDevices = propsize / sizeof(AudioDeviceID);
AudioDeviceID *devids = malloc(propsize);
__Verify_noErr(AudioObjectGetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize, devids));
int i;
theAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
AudioDeviceID systemDefault;
propsize = sizeof(systemDefault);
__Verify_noErr(AudioObjectGetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize, &systemDefault));
NSDictionary *defaultDevice = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"outputDevice"]; NSDictionary *defaultDevice = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"outputDevice"];
theAddress.mScope = kAudioDevicePropertyScopeOutput; [self enumerateAudioOutputsUsingBlock:
^(NSString *deviceName, AudioDeviceID deviceID, AudioDeviceID systemDefaultID, BOOL *stop) {
for (i = 0; i < nDevices; ++i) {
CFStringRef name = NULL;
propsize = sizeof(name);
theAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
__Verify_noErr(AudioObjectGetPropertyData(devids[i], &theAddress, 0, NULL, &propsize, &name));
propsize = 0;
theAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
__Verify_noErr(AudioObjectGetPropertyDataSize(devids[i], &theAddress, 0, NULL, &propsize));
if (propsize < sizeof(UInt32)) continue;
AudioBufferList * bufferList = (AudioBufferList *) malloc(propsize);
__Verify_noErr(AudioObjectGetPropertyData(devids[i], &theAddress, 0, NULL, &propsize, bufferList));
UInt32 bufferCount = bufferList->mNumberBuffers;
free(bufferList);
if (!bufferCount) continue;
NSDictionary *deviceInfo = @{ NSDictionary *deviceInfo = @{
@"name": [NSString stringWithString:(__bridge NSString*)name], @"name": deviceName,
@"deviceID": [NSNumber numberWithUnsignedInt:devids[i]], @"deviceID": [NSNumber numberWithUnsignedInt:deviceID],
}; };
[self addObject:deviceInfo]; [self addObject:deviceInfo];
CFRelease(name);
if (defaultDevice) { if (defaultDevice) {
if (([[defaultDevice objectForKey:@"deviceID"] isEqualToNumber:[deviceInfo objectForKey:@"deviceID"]]) || if (([[defaultDevice objectForKey:@"deviceID"] isEqualToNumber:[deviceInfo objectForKey:@"deviceID"]]) ||
([[defaultDevice objectForKey:@"name"] isEqualToString:[deviceInfo objectForKey:@"name"]])) { ([[defaultDevice objectForKey:@"name"] isEqualToString:[deviceInfo objectForKey:@"name"]])) {
@ -65,16 +27,71 @@
} }
} }
else { else {
if ( devids[i] == systemDefault ) { if (deviceID == systemDefaultID) {
[self setSelectedObjects:[NSArray arrayWithObject:deviceInfo]]; [self setSelectedObjects:[NSArray arrayWithObject:deviceInfo]];
} }
} }
} }];
free(devids);
if (!defaultDevice) {
if (!defaultDevice)
[self setSelectionIndex:0]; [self setSelectionIndex:0];
}
}
- (void)enumerateAudioOutputsUsingBlock:(void (NS_NOESCAPE ^ _Nonnull)(NSString *deviceName, AudioDeviceID deviceID, AudioDeviceID systemDefaultID, BOOL *stop))block
{
UInt32 propsize;
AudioObjectPropertyAddress theAddress = {
.mSelector = kAudioHardwarePropertyDevices,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMaster
};
__Verify_noErr(AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize));
UInt32 nDevices = propsize / (UInt32)sizeof(AudioDeviceID);
AudioDeviceID *devids = malloc(propsize);
__Verify_noErr(AudioObjectGetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize, devids));
theAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
AudioDeviceID systemDefault;
propsize = sizeof(systemDefault);
__Verify_noErr(AudioObjectGetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize, &systemDefault));
theAddress.mScope = kAudioDevicePropertyScopeOutput;
for (UInt32 i = 0; i < nDevices; ++i) {
CFStringRef name = NULL;
propsize = sizeof(name);
theAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
__Verify_noErr(AudioObjectGetPropertyData(devids[i], &theAddress, 0, NULL, &propsize, &name));
propsize = 0;
theAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
__Verify_noErr(AudioObjectGetPropertyDataSize(devids[i], &theAddress, 0, NULL, &propsize));
if (propsize < sizeof(UInt32)) continue;
AudioBufferList * bufferList = (AudioBufferList *) malloc(propsize);
__Verify_noErr(AudioObjectGetPropertyData(devids[i], &theAddress, 0, NULL, &propsize, bufferList));
UInt32 bufferCount = bufferList->mNumberBuffers;
free(bufferList);
if (!bufferCount) continue;
BOOL stop = NO;
block([NSString stringWithString:(__bridge NSString *)name],
devids[i],
systemDefault,
&stop);
CFRelease(name);
if (stop) {
break;
}
}
free(devids);
} }
@end @end