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 6dca25c96af9e1802ed1089a9aa49b065b6dd977..3722b613bfd004b602587cfabf07025f66e6cd24 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>
 
 <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>
 <h2>Changes up to version 4.3</h2>
 
   <div class=section>
index 809c89c316f08b77275622f45ca1d2aeace9a653..dae70272e6874e619f00746909d6816f28d3c11b 100644 (file)
 
 #if HAVE_COREAUDIO_AUDIOHARDWARE_H
 #include <pthread.h>
 
 #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 "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
 
 /** @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 */
    * 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,
   propertySize = sizeof asbd;
   status = AudioDeviceGetProperty(adid, 0, false,
                                  kAudioDevicePropertyStreamFormat,
index 1e465abf8a37e2a211b04ad19cffaa520212f8d9..e0de22859dd7c9e3ce66d6a2d56c6f86c8313597 100644 (file)
@@ -91,7 +91,7 @@ case "$host" in
 *-apple-darwin* )
   AC_MSG_RESULT([Mac OS X])
   if test $want_coreaudio = yes; then
 *-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])
   fi
   browser=open
   AC_MSG_CHECKING([Mac OS X target version])
index 5e0d09100c674315badd20b060041830fdbf00ac..b8147ad0a76c83305422fab2bf6de2e32f6ef41a 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.
 .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.
 .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.
 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),
 .SH "SEE ALSO"
 .BR disobedience (1),
 .BR disorder_config (5),
index e2f1ab0ec181dd680a2608ea7c70c31d1625491c..0364126c3bd7dd1e4beec93169952cb0f307ef9f 100644 (file)
@@ -386,7 +386,12 @@ will be tried.
 .IP
 For \fBapi alsa\fR this is the device name to use.
 .IP
 .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.
 .IP
 The default is \fBdefault\fR, which is intended to map to whatever the system's
 default is.
index f7d99acb6ee89ec6ee97e6e4b160b348da07602b..b270e82f6db8cb7e5c629a560e7e485936085e76 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                             \
        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                             \
        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 42c3a36083257b2bf32a71d2358b266ce31eab7c..ac4e7e70251360d2d7d8211638cb45dbb1ff688b 100644 (file)
 #include <poll.h>
 #include <sys/socket.h>
 #include <unistd.h>
 #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 "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;
 
 /** @brief Core Audio Device ID */
 static AudioDeviceID adid;
@@ -113,13 +113,7 @@ static void coreaudio_init(void) {
   UInt32 propertySize;
   AudioStreamBasicDescription asbd;
 
   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,
   propertySize = sizeof asbd;
   status = AudioDeviceGetProperty(adid, 0, false,
                                  kAudioDevicePropertyStreamFormat,