Commit b134142c authored by Monty Montgomery's avatar Monty Montgomery
Browse files

OS X driver patch from Kyle McKay:

use device names (or partial names) or, if you have it, the audio
device UID (UIDs look like
"AppleHDAEngineOutputDP:8,5,1,0:0:{2D4C-05ED-00000000}") with the
-d/--device option.  With the partial matching you can use "-d
speaker" or "-d headphones" or "-d hdmi" or "-d spdif" or "-d iMic"
etc. to select the output device.  (It's a case insensitive match.)

pull out the attempt to set the AudioHardware's CFRunLoop to NULL
because that's just wrong (and fortunately was being ignored by
CoreAudio)



git-svn-id: http://svn.xiph.org/trunk/ao@18881 0101bb08-14d6-0310-b084-bc0e0c8e3800
parent a954e851
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
#define true 1 #define true 1
#define false 0 #define false 0
static char *ao_macosx_options[] = {"matrix","verbose","quiet","debug","buffer_time"}; static char *ao_macosx_options[] = {"matrix","verbose","quiet","debug","buffer_time","dev"};
static ao_info ao_macosx_info = static ao_info ao_macosx_info =
{ {
...@@ -73,8 +73,9 @@ static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; ...@@ -73,8 +73,9 @@ static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
typedef struct ao_macosx_internal typedef struct ao_macosx_internal
{ {
/* Stuff describing the CoreAudio device */ /* Stuff describing the CoreAudio device */
ComponentInstance outputAudioUnit; AudioDeviceID outputDevice;
int output_p; ComponentInstance outputAudioUnit;
int output_p;
/* Keep track of whether the output stream has actually been /* Keep track of whether the output stream has actually been
started/stopped */ started/stopped */
...@@ -117,7 +118,7 @@ static OSStatus audioCallback (void *inRefCon, ...@@ -117,7 +118,7 @@ static OSStatus audioCallback (void *inRefCon,
if(ioData->mNumberBuffers != 1){ if(ioData->mNumberBuffers != 1){
aerror("Unexpected number of buffers (%d)\n", aerror("Unexpected number of buffers (%d)\n",
ioData->mNumberBuffers); (int)ioData->mNumberBuffers);
return 0; return 0;
} }
...@@ -174,7 +175,7 @@ static OSStatus audioCallback (void *inRefCon, ...@@ -174,7 +175,7 @@ static OSStatus audioCallback (void *inRefCon,
int ao_plugin_test() int ao_plugin_test()
{ {
/* This plugin will only build on a 10.6 or later Mac (Darwin 9+); /* This plugin will only build on a 10.4 or later Mac (Darwin 8+);
if it built, default AUHAL is available. */ if it built, default AUHAL is available. */
return 1; /* This plugin works in default mode */ return 1; /* This plugin works in default mode */
...@@ -204,18 +205,179 @@ int ao_plugin_device_init(ao_device *device) ...@@ -204,18 +205,179 @@ int ao_plugin_device_init(ao_device *device)
device->output_matrix_order = AO_OUTPUT_MATRIX_COLLAPSIBLE; device->output_matrix_order = AO_OUTPUT_MATRIX_COLLAPSIBLE;
device->output_matrix = strdup("L,R,C,LFE,BL,BR,CL,CR,BC,SL,SR"); device->output_matrix = strdup("L,R,C,LFE,BL,BR,CL,CR,BC,SL,SR");
CFRunLoopRef theRunLoop = NULL;
AudioObjectPropertyAddress theAddress = {
kAudioHardwarePropertyRunLoop,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
AudioObjectSetPropertyData(kAudioObjectSystemObject, &theAddress, 0,
NULL, sizeof(CFRunLoopRef), &theRunLoop);
return 1; /* Memory alloc successful */ return 1; /* Memory alloc successful */
} }
static void lowercasestr(char *str)
{
while (*str) {
if ('A' <= *str && *str <= 'Z')
*str += 'a' - 'A';
++str;
}
}
static char *cfstringdupe(CFStringRef cfstr)
{
CFIndex maxlen;
char *cstr;
if (!cfstr)
return NULL;
maxlen = 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),kCFStringEncodingUTF8);
cstr = (char *) malloc(maxlen);
if (!cstr)
return NULL;
if (!CFStringGetCString(cfstr, cstr, maxlen, kCFStringEncodingUTF8)) {
free(cstr);
return NULL;
}
return cstr;
}
static int isAudioOutputDevice(AudioDeviceID aid)
{
if (aid != kAudioObjectUnknown) {
AudioObjectPropertyAddress theAddress = {
kAudioDevicePropertyDeviceCanBeDefaultDevice,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
};
UInt32 val;
UInt32 size = sizeof(val);
OSStatus err;
err = AudioObjectGetPropertyData(aid, &theAddress, 0, NULL, &size, &val);
if (!err && val)
return 1;
}
return 0;
}
static AudioDeviceID findAudioOutputDevice(const char *name)
{
OSStatus err;
AudioDeviceID aid;
/* First see if it's a valid device UID */
{
CFStringRef namestr;
AudioValueTranslation avt = {&namestr, sizeof(namestr), &aid, sizeof(aid)};
UInt32 size = sizeof(avt);
namestr = CFStringCreateWithCStringNoCopy(NULL, name, kCFStringEncodingUTF8,
kCFAllocatorNull);
if (!namestr)
return kAudioObjectUnknown;
err = AudioHardwareGetProperty(kAudioHardwarePropertyDeviceForUID, &size, &avt);
CFRelease(namestr);
if (!err && aid != kAudioObjectUnknown)
return isAudioOutputDevice(aid) ? aid : kAudioObjectUnknown;
}
/* otherwise fall back to a device name search */
{
AudioDeviceID *devices;
UInt32 size;
size_t count, i, match, matches;
char *lcname = strdup(name);
if (!lcname)
return kAudioObjectUnknown; /* no memory */
lowercasestr(lcname);
err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, NULL);
if (err) {
free(lcname);
return kAudioObjectUnknown;
}
devices = (AudioDeviceID *) malloc(size);
if (!devices) {
free(lcname);
return kAudioObjectUnknown; /* no memory */
}
err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &size, devices);
if (err) {
free(lcname);
free(devices);
return kAudioObjectUnknown; /* no device list */
}
count = size / sizeof(AudioDeviceID);
match = 0;
matches = 0;
for (i = 0; i < count; ++i) {
AudioObjectPropertyAddress theAddress = {
kAudioObjectPropertyName,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
CFStringRef devstr;
UInt32 srcnum;
UInt32 size = sizeof(devstr);
char *devname;
char *srcname = NULL;
if (!isAudioOutputDevice(devices[i]))
continue;
err = AudioObjectGetPropertyData(devices[i], &theAddress, 0, NULL, &size, &devstr);
if (err || devstr == NULL)
continue;
devname = cfstringdupe(devstr);
CFRelease(devstr);
if (!devname)
continue;
lowercasestr(devname);
if (!strcmp(lcname,devname)) {
/* exact match always wins */
match = i;
matches = 1;
free(devname);
break;
}
/* Check the source name too */
size = sizeof(srcnum);
err = AudioDeviceGetProperty(devices[i], 0, FALSE, kAudioDevicePropertyDataSource,
&size, &srcnum);
if (!err) {
CFStringRef srcstr;
AudioValueTranslation avt = {&srcnum, sizeof(srcnum), &srcstr, sizeof(srcstr)};
size = sizeof(avt);
err = AudioDeviceGetProperty(devices[i], 0, FALSE,
kAudioDevicePropertyDataSourceNameForIDCFString,
&size, &avt);
if (!err && srcstr) {
srcname = cfstringdupe(srcstr);
CFRelease(srcstr);
if (srcname)
lowercasestr(srcname);
}
}
if (srcname && !strcmp(lcname,srcname)) {
/* exact match always wins */
match = i;
matches = 1;
free(srcname);
free(devname);
break;
}
/* tally partial matches */
if (strstr(devname,lcname) || (srcname && strstr(srcname,lcname))) {
match = i;
++matches;
}
free(devname);
if (srcname)
free(srcname);
}
if (matches == 1)
aid = devices[match];
else
aid = kAudioObjectUnknown;
free(lcname);
free(devices);
return aid;
}
}
int ao_plugin_set_option(ao_device *device, const char *key, const char *value) int ao_plugin_set_option(ao_device *device, const char *key, const char *value)
{ {
ao_macosx_internal *internal = (ao_macosx_internal *) device->internal; ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
...@@ -228,6 +390,15 @@ int ao_plugin_set_option(ao_device *device, const char *key, const char *value) ...@@ -228,6 +390,15 @@ int ao_plugin_set_option(ao_device *device, const char *key, const char *value)
buffer = 100; buffer = 100;
} }
internal->buffer_time = buffer; internal->buffer_time = buffer;
} else if (!strcmp(key,"dev")) {
if (!value || !value[0]) {
/* permit switching back to default device with empty string */
internal->outputDevice = kAudioObjectUnknown;
} else {
internal->outputDevice = findAudioOutputDevice(value);
if (internal->outputDevice == kAudioObjectUnknown)
return 0;
}
} }
return 1; return 1;
...@@ -262,6 +433,20 @@ int ao_plugin_open(ao_device *device, ao_sample_format *format) ...@@ -262,6 +433,20 @@ int ao_plugin_open(ao_device *device, ao_sample_format *format)
aerror("AudioComponentInstanceNew() error => %d\n",(int)result); aerror("AudioComponentInstanceNew() error => %d\n",(int)result);
return 0; return 0;
} }
/* Set the desired output device if not default */
if (internal->outputDevice != kAudioObjectUnknown) {
result = AudioUnitSetProperty (internal->outputAudioUnit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
0,
&internal->outputDevice,
sizeof(internal->outputDevice));
if (result) {
aerror("AudioComponentSetDevice() error => %d\n",(int)result);
CloseComponent(internal->outputAudioUnit);
return 0;
}
}
internal->output_p=1; internal->output_p=1;
/* Request desired format of the audio unit. Let HAL do all /* Request desired format of the audio unit. Let HAL do all
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment