-
Notifications
You must be signed in to change notification settings - Fork 826
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Devices with no Inputs will crash EZAudio and Solution is to extend the Framework to handle Aggregated Devices. #389
Comments
ok.. didn't make a Fork to push commits.. to bizzy with my project.. /**
Enumerates a specific Aggregate Device.
- OSX only
@param deviceID aggregate AudioDeviceID to check for its SubDeviceList
@param block When enumerating this block executes repeatedly for each EZAudioDevice found. It contains two arguments - first, the EZAudioDevice found, then a pointer to a stop BOOL to allow breaking out of the enumeration)
*/
+ (void)enumerateSubDevice:(AudioDeviceID)deviceID UsingBlock:(void(^)(EZAudioDevice *subdevice, BOOL *stop))block; and in @property (nonatomic, assign, readwrite) BOOL isAggregateDevice; then exchange the #elif TARGET_OS_MAC
+ (void)enumerateDevicesUsingBlock:(void(^)(EZAudioDevice *device, BOOL *stop))block
{
if (!block)
{
return;
}
// get the present system devices
AudioObjectPropertyAddress address = EZAudioDevice_addressForPropertySelector(kAudioHardwarePropertyDevices);
UInt32 devicesDataSize;
[EZAudioUtilities checkResult:AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
&address,
0,
NULL,
&devicesDataSize)
operation:"Failed to get data size"];
// enumerate devices
NSInteger count = devicesDataSize / sizeof(AudioDeviceID);
AudioDeviceID *deviceIDs = (AudioDeviceID *)malloc(devicesDataSize);
// fill in the devices
[EZAudioUtilities checkResult:AudioObjectGetPropertyData(kAudioObjectSystemObject,
&address,
0,
NULL,
&devicesDataSize,
deviceIDs)
operation:"Failed to get device IDs for available devices on OSX"];
BOOL stop = NO;
for (UInt32 i = 0; i < count; i++)
{
AudioDeviceID deviceID = deviceIDs[i];
EZAudioDevice *device = [EZAudioDevice new];
device.deviceID = deviceID;
device.manufacturer = EZAudioDevice_manufacturerForDeviceID(deviceID);
device.name = EZAudioDevice_namePropertyForDeviceID(deviceID);
device.UID = EZAudioDevice_UIDPropertyForDeviceID(deviceID);
//
// AggregateDevices could crash EZAudio. AggregateDevices can have 0 inputs and/or 0 outputs
// and will not respond to kAudioObjectPropertyScopeInput or kAudioObjectPropertyScopeOutput as is
// instead we lookup kAudioAggregateDevicePropertyActiveSubDeviceList and climb into those devices to find in & outputs
bool isAggregate = EZAudioDevice_isAggregateDevice(deviceID);
device.isAggregateDevice = isAggregate;
if (!isAggregate)
{
device.inputChannelCount = EZAudioDevice_channelCountForScope(kAudioObjectPropertyScopeInput , deviceID);
device.outputChannelCount = EZAudioDevice_channelCountForScope(kAudioObjectPropertyScopeOutput, deviceID);
}
else
{
__block NSInteger inputs = 0;
__block NSInteger outputs = 0;
[self enumerateSubDevice:deviceID UsingBlock:^(EZAudioDevice *subdevice, BOOL *stop)
{
//printf("subdevice.name=%s has %lu InputChannels and %lu OutputChannels\n",subdevice.name.UTF8String, subdevice.inputChannelCount, subdevice.outputChannelCount);
if (subdevice.inputChannelCount > 0)
{
inputs +=subdevice.inputChannelCount;
}
if (subdevice.outputChannelCount > 0) {
outputs +=subdevice.outputChannelCount;
}
}];
device.inputChannelCount = inputs;
device.outputChannelCount = outputs;
}
block(device, &stop);
if (stop)
{
break;
}
}
free(deviceIDs);
}
//------------------------------------------------------------------------------
static AudioDevicePropertyID EZAudioDevice_getDeviceTransportType(AudioDeviceID deviceID) {
AudioDevicePropertyID deviceTransportType = 0;
UInt32 propSize = sizeof(AudioDevicePropertyID);
AudioObjectPropertyAddress propAddress = EZAudioDevice_addressForPropertySelector(kAudioDevicePropertyTransportType);
[EZAudioUtilities checkResult:AudioObjectGetPropertyData(deviceID,
&propAddress,
0,
nil,
&propSize,
&deviceTransportType)
operation:"EZAudioDevice failed getDeviceTransportType"];
return deviceTransportType;
}
//------------------------------------------------------------------------------
static bool EZAudioDevice_isAggregateDevice(AudioDeviceID deviceID) {
AudioDevicePropertyID deviceType = EZAudioDevice_getDeviceTransportType(deviceID);
return deviceType == kAudioDeviceTransportTypeAggregate;
}
//------------------------------------------------------------------------------
+ (void)enumerateSubDevice:(AudioDeviceID)deviceID UsingBlock:(void(^)(EZAudioDevice *subdevice, BOOL *stop))block
{
if (!block)
{
return;
}
NSString *aggregatedDeviceName = EZAudioDevice_namePropertyForDeviceID(deviceID);
UInt32 devicesDataSize = 0;
AudioObjectPropertyAddress propAddress = EZAudioDevice_addressForPropertySelector(kAudioAggregateDevicePropertyActiveSubDeviceList);
UInt32 subcount = EZAudioDevice_getNumberOfSubDevices(deviceID, propAddress, &devicesDataSize);
AudioDeviceID *subdeviceIDs = (AudioDeviceID *)malloc(devicesDataSize);
[EZAudioUtilities checkResult:AudioObjectGetPropertyData(deviceID,
&propAddress,
0,
NULL,
&devicesDataSize,
&subdeviceIDs[0])
operation:"EZAudioDevice failed getAggregateDeviceSubDeviceList"];
BOOL stop = NO;
for (int a = 0; a < subcount; a++)
{
AudioDeviceID subdeviceID = subdeviceIDs[a];
EZAudioDevice *device = [[EZAudioDevice alloc] init];
device.deviceID = subdeviceID;
device.manufacturer = EZAudioDevice_manufacturerForDeviceID(subdeviceID);
device.name = [NSString stringWithFormat:@"%@ (%@)",aggregatedDeviceName, EZAudioDevice_namePropertyForDeviceID(subdeviceID)];
device.UID = EZAudioDevice_UIDPropertyForDeviceID(subdeviceID);
device.inputChannelCount = EZAudioDevice_channelCountForScope(kAudioObjectPropertyScopeInput , subdeviceID);
device.outputChannelCount = EZAudioDevice_channelCountForScope(kAudioObjectPropertyScopeOutput, subdeviceID);
// kAudioSubDeviceInputChannelsKey
device.isAggregateDevice = NO; // subdevices are not aggregateDevices, they are aggregated!
block(device, &stop);
if (stop)
{
break;
}
}
free(subdeviceIDs);
}
//------------------------------------------------------------------------------
static UInt32 EZAudioDevice_getNumberOfSubDevices(AudioDeviceID deviceID, AudioObjectPropertyAddress propAddress, UInt32 *devicesDataSize)
{
UInt32 propSize = 0;
[EZAudioUtilities checkResult:AudioObjectGetPropertyDataSize(deviceID,
&propAddress,
0,
nil,
&propSize)
operation:"EZAudioDevice failed to getNumberOfSubDevices"];
*devicesDataSize = propSize;
return propSize / (UInt32)sizeof(AudioDeviceID);
}
//------------------------------------------------------------------------------
+ (NSArray *)devices
{
__block NSMutableArray *devices = [NSMutableArray array];
[self enumerateDevicesUsingBlock:^(EZAudioDevice *device, BOOL *stop)
{
[devices addObject:device];
}];
return devices;
}
//------------------------------------------------------------------------------
EZAudioDevice *EZAudioDevice_deviceWithPropertySelector(AudioObjectPropertySelector propertySelector)
{
AudioDeviceID deviceID;
UInt32 propSize = sizeof(AudioDeviceID);
AudioObjectPropertyAddress address = EZAudioDevice_addressForPropertySelector(propertySelector);
[EZAudioUtilities checkResult:AudioObjectGetPropertyData(kAudioObjectSystemObject,
&address,
0,
NULL,
&propSize,
&deviceID)
operation:"Failed to get device device on OSX"];
EZAudioDevice *device = [[EZAudioDevice alloc] init];
device.deviceID = deviceID;
device.manufacturer = EZAudioDevice_manufacturerForDeviceID(deviceID);
device.name = EZAudioDevice_namePropertyForDeviceID(deviceID);
device.UID = EZAudioDevice_UIDPropertyForDeviceID(deviceID);
bool isAggregate = EZAudioDevice_isAggregateDevice(deviceID);
device.isAggregateDevice = isAggregate;
if (!isAggregate) {
device.inputChannelCount = EZAudioDevice_channelCountForScope(kAudioObjectPropertyScopeInput, deviceID);
device.outputChannelCount = EZAudioDevice_channelCountForScope(kAudioObjectPropertyScopeOutput, deviceID);
} else {
__block NSInteger inputs = 0;
__block NSInteger outputs = 0;
[EZAudioDevice enumerateSubDevice:deviceID UsingBlock:^(EZAudioDevice *subdevice, BOOL *stop)
{
if (subdevice.inputChannelCount > 0)
{
inputs +=subdevice.inputChannelCount;
}
if (subdevice.outputChannelCount > 0) {
outputs +=subdevice.outputChannelCount;
}
}];
device.inputChannelCount = inputs;
device.outputChannelCount = outputs;
}
return device;
}
//------------------------------------------------------------------------------
+ (EZAudioDevice *)currentInputDevice
{
return EZAudioDevice_deviceWithPropertySelector(kAudioHardwarePropertyDefaultInputDevice);
}
//------------------------------------------------------------------------------
+ (EZAudioDevice *)currentOutputDevice
{
return EZAudioDevice_deviceWithPropertySelector(kAudioHardwarePropertyDefaultOutputDevice);
}
//------------------------------------------------------------------------------
+ (NSArray *)inputDevices
{
__block NSMutableArray *devices = [NSMutableArray array];
[self enumerateDevicesUsingBlock:^(EZAudioDevice *device, BOOL *stop)
{
if (device.inputChannelCount > 0)
{
[devices addObject:device];
}
}];
return devices;
}
//------------------------------------------------------------------------------
+ (NSArray *)outputDevices
{
__block NSMutableArray *devices = [NSMutableArray array];
[self enumerateDevicesUsingBlock:^(EZAudioDevice *device, BOOL *stop)
{
if (device.outputChannelCount > 0)
{
[devices addObject:device];
}
}];
return devices;
}
//------------------------------------------------------------------------------
#pragma mark - Utility
//------------------------------------------------------------------------------
AudioObjectPropertyAddress EZAudioDevice_addressForPropertySelector(AudioObjectPropertySelector selector)
{
AudioObjectPropertyAddress address;
address.mScope = kAudioObjectPropertyScopeGlobal;
address.mElement = kAudioObjectPropertyElementMaster;
address.mSelector = selector;
return address;
}
//------------------------------------------------------------------------------
NSString *EZAudioDevice_stringPropertyForSelector(AudioObjectPropertySelector selector, AudioDeviceID deviceID)
{
AudioObjectPropertyAddress address = EZAudioDevice_addressForPropertySelector(selector);
CFStringRef string;
UInt32 propSize = sizeof(CFStringRef);
[EZAudioUtilities checkResult:AudioObjectGetPropertyData(deviceID,
&address,
0,
NULL,
&propSize,
&string)
operation:[NSString stringWithFormat:@"Failed to get device property (%u)",(unsigned int)selector].UTF8String];
return (__bridge_transfer NSString *)string;
}
//------------------------------------------------------------------------------
NSInteger EZAudioDevice_channelCountForScope(AudioObjectPropertyScope scope, AudioDeviceID deviceID)
{
AudioObjectPropertyAddress address;
address.mScope = scope;
address.mElement = kAudioObjectPropertyElementMaster;
address.mSelector = kAudioDevicePropertyStreamConfiguration;
// - - - - - - - - - - - - -
AudioBufferList streamConfiguration;
UInt32 propSize = sizeof(streamConfiguration);
[EZAudioUtilities checkResult:AudioObjectGetPropertyData(deviceID,
&address,
0,
NULL,
&propSize,
&streamConfiguration)
operation:"Failed to get frame size"];
NSInteger channelCount = 0;
for (NSInteger i = 0; i < streamConfiguration.mNumberBuffers; i++)
{
channelCount += streamConfiguration.mBuffers[i].mNumberChannels;
}
return channelCount;
}
//------------------------------------------------------------------------------
NSString *EZAudioDevice_manufacturerForDeviceID(AudioDeviceID deviceID)
{
return EZAudioDevice_stringPropertyForSelector(kAudioDevicePropertyDeviceManufacturerCFString, deviceID);
}
//------------------------------------------------------------------------------
NSString *EZAudioDevice_namePropertyForDeviceID(AudioDeviceID deviceID)
{
return EZAudioDevice_stringPropertyForSelector(kAudioDevicePropertyDeviceNameCFString, deviceID);
}
//------------------------------------------------------------------------------
NSString *EZAudioDevice_UIDPropertyForDeviceID(AudioDeviceID deviceID)
{
return EZAudioDevice_stringPropertyForSelector(kAudioDevicePropertyDeviceUID, deviceID);
}
//------------------------------------------------------------------------------
- (NSString *)description
{
return [NSString stringWithFormat:@"%@ { deviceID: %i, manufacturer: %@, name: %@, UID: %@, inputChannelCount: %ld, outputChannelCount: %ld }",
[super description],
self.deviceID,
self.manufacturer,
self.name,
self.UID,
self.inputChannelCount,
self.outputChannelCount];
}
//------------------------------------------------------------------------------
- (BOOL)isEqual:(id)object
{
if ([object isKindOfClass:self.class])
{
EZAudioDevice *device = (EZAudioDevice *)object;
return [self.UID isEqualToString:device.UID];
}
else
{
return [super isEqual:object];
}
}
//------------------------------------------------------------------------------
#endif cheers.. |
As of 2021 aggregated Devices exist or how to call them.. Combined Devices which can be setup to only use Inputs or Outputs just the same as some devices will just have no inputs or outputs.
When i try to instantiate EZAudioDevice or enumerating it will crash in
EZAudioDevice.m
msg:
[AudioHAL_Client] HALC_ProxyIOContext.cpp:1984:GetPropertyData: HALC_ProxyIOContext::_GetPropertyData: bad property data size for kAudioDevicePropertyStreamConfiguration
[AudioHAL_Client] HALC_ShellObject.cpp:401:GetPropertyData: HALC_ShellObject::GetPropertyData: call to the proxy failed, Error: 561211770 (!siz)
[AudioHAL_Client] HALPlugIn.cpp:292:ObjectGetPropertyData: HALPlugIn::ObjectGetPropertyData: got an error from the plug-in routine, Error: 561211770 (!siz)
`
i guess this is because EZAudio is fairly an old framework that didnt know about Aggregated Devices.
but why, we could improve it.
after googling a found some hints how to do it in this gist
maybe someone wants to join solving this issue
ps: here a screenshot of my audio devices..

as you can read, it is also possible to create an Aggregate Device with no inputs and outputs at all.
when i change back the system setup to a normal audio device and erase the aggregated device my App does not crash and just works flawless also showing properly the channels.. it is not a Blackhole issue, as this works also perfectly with EZAudio.
The text was updated successfully, but these errors were encountered: