parent
57ebc65ba0
commit
b22c5964e4
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue