chiark / gitweb /
Support arbitrary Core Audio devices.
authorRichard Kettlewell <rjk@greenend.org.uk>
Sun, 22 Feb 2009 14:00:46 +0000 (14:00 +0000)
committerRichard Kettlewell <rjk@greenend.org.uk>
Sun, 22 Feb 2009 14:00:46 +0000 (14:00 +0000)
New files lib/coreaudio.[ch] contain the code for looking up devices
by name or UID and is used by the server and playrtp.

Fixes issue 27.

CHANGES.html
clients/playrtp-coreaudio.c
configure.ac
doc/disorder-playrtp.1.in
doc/disorder_config.5.in
lib/Makefile.am
lib/coreaudio.c [new file with mode: 0644]
lib/coreaudio.h [new file with mode: 0644]
server/speaker-coreaudio.c

index 6dca25c..3722b61 100644 (file)
@@ -58,6 +58,42 @@ span.command {
 <p>This file documents recent user-visible changes to <a
  href="http://www.greenend.org.uk/rjk/disorder/">DisOrder</a>.</p>
 
+<h2>Changes up to version 4.4</h2>
+
+  <div class=section>
+  
+    <h3>Mac OS X</h3>
+  
+    <div class=section>
+
+      <p>The <tt>device</tt> configuration option and <tt>--device</tt> option
+      to <tt>disorder-playrtp</tt> now work.  Devices may be specified either
+      by UID or name.  Fixes <a
+       href="http://code.google.com/p/disorder/issues/detail?id=27">Issue
+      27</a>.</p>
+        
+    </div>
+
+    <h3>Bugs fixed</h3>
+  
+    <div class=section>
+
+      <table class=bugs>
+        <tr>
+          <th>ID</th>
+          <th>Description</th>
+        </tr>
+        
+        <tr>
+          <td><a href="http://code.google.com/p/disorder/issues/detail?id=27">#27</a></td>
+          <td>Mac DisOrder uses wrong sound device</td>
+        </tr>
+
+      </table>
+      
+    </div>
+  </div>
+
 <h2>Changes up to version 4.3</h2>
 
   <div class=section>
index 809c89c..dae7027 100644 (file)
 
 #if HAVE_COREAUDIO_AUDIOHARDWARE_H
 #include <pthread.h>
-#include <CoreAudio/AudioHardware.h>
 
 #include "mem.h"
 #include "log.h"
 #include "vector.h"
 #include "heap.h"
 #include "playrtp.h"
+#include "coreaudio.h"
 
 /** @brief Callback from Core Audio */
 static OSStatus adioproc
@@ -112,13 +112,8 @@ void playrtp_coreaudio(void) {
    * excellent reason for that... */
 
   /* TODO report errors as strings not numbers */
-  propertySize = sizeof adid;
-  status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
-                                   &propertySize, &adid);
-  if(status)
-    fatal(0, "AudioHardwareGetProperty: %d", (int)status);
-  if(adid == kAudioDeviceUnknown)
-    fatal(0, "no output device");
+  /* Identify the device to use */
+  adid = coreaudio_getdevice(device);
   propertySize = sizeof asbd;
   status = AudioDeviceGetProperty(adid, 0, false,
                                  kAudioDevicePropertyStreamFormat,
index 1e465ab..e0de228 100644 (file)
@@ -91,7 +91,7 @@ case "$host" in
 *-apple-darwin* )
   AC_MSG_RESULT([Mac OS X])
   if test $want_coreaudio = yes; then
-    COREAUDIO="-framework CoreAudio"
+    COREAUDIO="-framework CoreFoundation -framework CoreAudio"
   fi
   browser=open
   AC_MSG_CHECKING([Mac OS X target version])
index 5e0d091..b8147ad 100644 (file)
@@ -51,8 +51,9 @@ Use Core Audio to play sound.
 .TP
 .B \-\-device \fIDEVICE\fR, \fB\-D \fIDEVICE\fR
 Specifies the audio device to use.
-The exact meaning of this is platform-dependent; on Linux it is the
-ALSA device name.
+See
+.B "DEVICE NAMES"
+below for more information.
 .TP
 .B \-\-config \fIPATH\fR, \fB\-C \fIPATH
 Set the configuration file.
@@ -113,6 +114,16 @@ After the first command the connection is closed.
 Only one connection at a time will be serviced.
 .PP
 This protocol is not guaranteed to be stable.
+.SH "DEVICE NAMES"
+.SS "Core Audio"
+On a Mac, the device name can either be the human-readable name of the desired
+output or its UID.
+To get a list of the human-readable names, visit System Preferences -> Sound;
+the Type column has the name you want.
+.PP
+For example, you might use "Built-in Output" for the built-in speaker
+or "Built-in Line Output" if you have connected external speakers.
+Remember to quote the name.
 .SH "SEE ALSO"
 .BR disobedience (1),
 .BR disorder_config (5),
index e2f1ab0..0364126 100644 (file)
@@ -386,7 +386,12 @@ will be tried.
 .IP
 For \fBapi alsa\fR this is the device name to use.
 .IP
-For \fBapi coreaudio\fR this is currently ignored.
+For \fBapi coreaudio\fR this can be either the UID or the human-readable
+name of the desired device.
+For a list of names, visit System Preferences -> Sound and look at the Type column.
+For example, you might use "Built-in Output" for the built-in speaker
+or "Built-in Line Output" if you have connected external speakers.
+Remember to quote the name.
 .IP
 The default is \fBdefault\fR, which is intended to map to whatever the system's
 default is.
index f7d99ac..b270e82 100644 (file)
@@ -39,6 +39,7 @@ libdisorder_a_SOURCES=charset.c charset.h             \
        client-common.c client-common.h                 \
        configuration.c configuration.h                 \
        cookies.c cookies.h                             \
+       coreaudio.c coreaudio.h                         \
        dateparse.c dateparse.h xgetdate.c              \
        defs.c defs.h                                   \
        eclient.c eclient.h                             \
diff --git a/lib/coreaudio.c b/lib/coreaudio.c
new file mode 100644 (file)
index 0000000..c8732d5
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/coreaudio.c
+ * @brief Support for @ref BACKEND_COREAUDIO
+ */
+
+#include "common.h"
+
+#if HAVE_COREAUDIO_AUDIOHARDWARE_H
+
+#include "coreaudio.h"
+#include "log.h"
+#include <CoreFoundation/CFString.h>
+
+/** @brief Return the default device ID */
+static AudioDeviceID coreaudio_use_default(void) {
+  OSStatus status;
+  UInt32 propertySize;
+  AudioDeviceID adid;
+
+  /* TODO should we use kAudioHardwarePropertyDefaultSystemOutputDevice
+   * instead?  It is listed in the enumeration but is not documented, so we
+   * leave it alone until better information is available. */
+  propertySize = sizeof adid;
+  status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
+                                    &propertySize, &adid);
+  if(status)
+    fatal(0, "AudioHardwareGetProperty kAudioHardwarePropertyDefaultOutputDevice: %d", (int)status);
+  if(adid == kAudioDeviceUnknown)
+    fatal(0, "no output device");
+  return adid;
+}
+
+/** @brief Find a device by some string
+ * @param selector Selector for property to look for
+ * @param description Property description
+ * @param devs List of device IDs
+ * @param ndevs Number of device IDs in @p devs
+ * @param resultp Where to put device ID
+ * @return 1 if found, 0 if not found
+ */
+static int coreaudio_find_device(AudioObjectPropertySelector selector,
+                                 //const char *description,
+                                 const AudioDeviceID *devs,
+                                 unsigned ndevs,
+                                 CFStringRef dev,
+                                 AudioDeviceID *resultp) {
+  OSStatus status;
+  UInt32 propertySize;
+
+  for(unsigned n = 0; n < ndevs; ++n) {
+    CFStringRef name;
+    propertySize = sizeof name;
+    status = AudioDeviceGetProperty(devs[n], 0, FALSE,
+                                    selector,
+                                    &propertySize, &name);
+    if(status)
+      fatal(0, "AudioDeviceGetProperty: %d", (int)status);
+#if 0
+    UInt8 output[1024];
+    CFIndex used;
+    CFRange r = { 0, CFStringGetLength(name) };
+    CFStringGetBytes(name, r, kCFStringEncodingUTF8,
+                     '?', FALSE, output, sizeof output,
+                     &used);
+    output[used] = 0;
+    info("device %u %s: %s", n, description, (char *)output);
+#endif
+    if(CFStringCompare(dev, name,
+                       kCFCompareCaseInsensitive|kCFCompareNonliteral)
+       == kCFCompareEqualTo) {
+      *resultp = devs[n];
+      CFRelease(name);
+      return 1;
+    }
+    CFRelease(name);
+  }
+  return 0;                             /* didn't find it */
+}
+
+/** @brief Identify audio device
+ * @param name Device name
+ * @return Device ID
+ */
+AudioDeviceID coreaudio_getdevice(const char *name) {
+  OSStatus status;
+  UInt32 propertySize;
+  int found;
+  AudioDeviceID adid;
+
+  if(!name
+     || !*name
+     || !strcmp(name, "default"))
+    return coreaudio_use_default();
+  /* Convert the configured device name to a CFString */
+  CFStringRef dev;
+  dev = CFStringCreateWithCString(NULL/*default allocator*/,
+                                  name,
+                                  kCFStringEncodingUTF8);
+  if(!dev)
+    fatal(0, "CFStringCreateWithBytes failed");
+  /* Get a list of available devices */
+  AudioDeviceID devs[1024];
+  unsigned ndevs;
+
+  propertySize = sizeof devs;
+  status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
+                                    &propertySize, devs);
+  if(status)
+    fatal(0, "AudioHardwareGetProperty kAudioHardwarePropertyDevices: %d",
+          (int)status);
+  ndevs = propertySize / sizeof *devs;
+  if(!ndevs)
+    fatal(0, "no sound devices found");
+  /* Try looking up by UID first */
+  found = coreaudio_find_device(kAudioDevicePropertyDeviceUID, //"UID",
+                                devs, ndevs, dev, &adid);
+  /* Failing that try looking up by name */
+  if(!found)
+    found = coreaudio_find_device(kAudioObjectPropertyName, //"name",
+                                  devs, ndevs, dev, &adid);
+  CFRelease(dev);
+  if(!found)
+    fatal(0, "cannot find device '%s'", name);
+  return adid;
+}
+
+#endif
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
diff --git a/lib/coreaudio.h b/lib/coreaudio.h
new file mode 100644 (file)
index 0000000..6e62053
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2009 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/coreaudio.h
+ * @brief Support for @ref BACKEND_COREAUDIO
+ */
+
+#ifndef COREAUDIO_H
+#define COREAUDIO_H
+
+#include <CoreAudio/AudioHardware.h>
+
+AudioDeviceID coreaudio_getdevice(const char *name);
+
+#endif /* COREAUDIO_H */
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index 42c3a36..ac4e7e7 100644 (file)
 #include <poll.h>
 #include <sys/socket.h>
 #include <unistd.h>
-#include <CoreAudio/AudioHardware.h>
 
 #include "configuration.h"
 #include "syscalls.h"
 #include "log.h"
 #include "speaker-protocol.h"
 #include "speaker.h"
+#include "coreaudio.h"
 
 /** @brief Core Audio Device ID */
 static AudioDeviceID adid;
@@ -113,13 +113,7 @@ static void coreaudio_init(void) {
   UInt32 propertySize;
   AudioStreamBasicDescription asbd;
 
-  propertySize = sizeof adid;
-  status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
-                                   &propertySize, &adid);
-  if(status)
-    fatal(0, "AudioHardwareGetProperty: %d", (int)status);
-  if(adid == kAudioDeviceUnknown)
-    fatal(0, "no output device");
+  adid = coreaudio_getdevice(config->device);
   propertySize = sizeof asbd;
   status = AudioDeviceGetProperty(adid, 0, false,
                                  kAudioDevicePropertyStreamFormat,