[xiph-commits] r18881 - trunk/ao/src/plugins/macosx

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Thu Mar 14 01:44:55 PDT 2013


Author: xiphmont
Date: 2013-03-14 01:44:55 -0700 (Thu, 14 Mar 2013)
New Revision: 18881

Modified:
   trunk/ao/src/plugins/macosx/ao_macosx.c
Log:
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)



Modified: trunk/ao/src/plugins/macosx/ao_macosx.c
===================================================================
--- trunk/ao/src/plugins/macosx/ao_macosx.c	2013-03-13 07:48:01 UTC (rev 18880)
+++ trunk/ao/src/plugins/macosx/ao_macosx.c	2013-03-14 08:44:55 UTC (rev 18881)
@@ -52,7 +52,7 @@
 #define true  1
 #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 =
 {
@@ -73,8 +73,9 @@
 typedef struct ao_macosx_internal
 {
   /* Stuff describing the CoreAudio device */
-  ComponentInstance       outputAudioUnit;
-  int                     output_p;
+  AudioDeviceID                outputDevice;
+  ComponentInstance            outputAudioUnit;
+  int                          output_p;
 
   /* Keep track of whether the output stream has actually been
      started/stopped */
@@ -117,7 +118,7 @@
 
   if(ioData->mNumberBuffers != 1){
     aerror("Unexpected number of buffers (%d)\n",
-	   ioData->mNumberBuffers);
+	   (int)ioData->mNumberBuffers);
     return 0;
   }
 
@@ -174,7 +175,7 @@
 
 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. */
   return 1; /* This plugin works in default mode */
 
@@ -204,18 +205,179 @@
   device->output_matrix_order = AO_OUTPUT_MATRIX_COLLAPSIBLE;
   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 */
 }
 
+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)
   {
   ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
@@ -228,6 +390,15 @@
       buffer = 100;
     }
     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;
@@ -262,6 +433,20 @@
     aerror("AudioComponentInstanceNew() error => %d\n",(int)result);
     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;
 
   /* Request desired format of the audio unit.  Let HAL do all



More information about the commits mailing list