[xiph-commits] r16979 - in trunk/ao: . src src/plugins/macosx

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Mon Mar 15 20:47:45 PDT 2010


Author: xiphmont
Date: 2010-03-15 20:47:45 -0700 (Mon, 15 Mar 2010)
New Revision: 16979

Modified:
   trunk/ao/configure.ac
   trunk/ao/src/audio_out.c
   trunk/ao/src/plugins/macosx/Makefile.am
   trunk/ao/src/plugins/macosx/ao_macosx.c
Log:
Bring MacOSX driver fully up to date with Snow Leapord, move to using AUHAL interface.



Modified: trunk/ao/configure.ac
===================================================================
--- trunk/ao/configure.ac	2010-03-16 01:42:03 UTC (rev 16978)
+++ trunk/ao/configure.ac	2010-03-16 03:47:45 UTC (rev 16979)
@@ -85,10 +85,10 @@
                 PROFILE="-pg -g -O20 -D__NO_MATH_INLINES -fsigned-char" ;;
         *-darwin*)
                 PLUGIN_LDFLAGS="-module -avoid-version"
-                DEBUG="-g -Wall -D__NO_MATH_INLINES -fsigned-char -Ddlsym=dlsym_auto_underscore"
-                CFLAGS="-D__NO_MATH_INLINES -fsigned-char -Ddlsym=dlsym_auto_underscore"
-                PROFILE="-g -pg -D__NO_MATH_INLINES -fsigned-char -Ddlsym=dlsym_auto_underscore" ;;
-        *)
+               	DEBUG="-g -Wall -D__NO_MATH_INLINES -fsigned-char"
+                CFLAGS="-D__NO_MATH_INLINES -fsigned-char"
+                PROFILE="-g -pg -D__NO_MATH_INLINES -fsigned-char" ;;
+     	*)
                 PLUGIN_LDFLAGS="-export-dynamic -avoid-version"
                 DEBUG="-g -Wall -D__NO_MATH_INLINES -fsigned-char"
                 CFLAGS="-O20 -D__NO_MATH_INLINES -fsigned-char"
@@ -137,7 +137,7 @@
 	DLOPEN_FLAG='(RTLD_LAZY)'
 	SHARED_LIB_EXT='.sl'
 	;;    
-    *openbsd* | *netbsd* | *solaris2.7)
+    *openbsd* | *netbsd* | *solaris2.7 | *darwin*)
 	DLOPEN_FLAG='(RTLD_LAZY)'
 	SHARED_LIB_EXT='.so'
 	;;

Modified: trunk/ao/src/audio_out.c
===================================================================
--- trunk/ao/src/audio_out.c	2010-03-16 01:42:03 UTC (rev 16978)
+++ trunk/ao/src/audio_out.c	2010-03-16 03:47:45 UTC (rev 16979)
@@ -1016,7 +1016,7 @@
             int swap = (device->bytewidth>1 &&
                         device->client_byte_format != device->driver_byte_format);
             for(i=0;i<device->output_channels;i++){
-              int ic = device->permute_channels[i];
+              int ic = device->permute_channels ? device->permute_channels[i] : i;
               if(ic==-1){
                 _buffer_zero(device->swap_buffer,i,device->bytewidth,device->output_channels,
                              out_bytes);

Modified: trunk/ao/src/plugins/macosx/Makefile.am
===================================================================
--- trunk/ao/src/plugins/macosx/Makefile.am	2010-03-16 01:42:03 UTC (rev 16978)
+++ trunk/ao/src/plugins/macosx/Makefile.am	2010-03-16 03:47:45 UTC (rev 16979)
@@ -19,7 +19,7 @@
 libdir = $(plugindir)
 lib_LTLIBRARIES = $(macosxltlibs)
 
-libmacosx_la_LDFLAGS = @PLUGIN_LDFLAGS@ -framework CoreAudio
+libmacosx_la_LDFLAGS = @PLUGIN_LDFLAGS@ -framework AudioUnit
 libmacosx_la_SOURCES = $(macosxsources)
 
 EXTRA_DIST = ao_macosx.c

Modified: trunk/ao/src/plugins/macosx/ao_macosx.c
===================================================================
--- trunk/ao/src/plugins/macosx/ao_macosx.c	2010-03-16 01:42:03 UTC (rev 16978)
+++ trunk/ao/src/plugins/macosx/ao_macosx.c	2010-03-16 03:47:45 UTC (rev 16979)
@@ -3,6 +3,8 @@
  *  ao_macosx.c
  *
  *      Original Copyright (C) Timothy J. Wood - Aug 2000
+ *       Modifications (C) Michael Guntsche - March 2008
+ *                         Monty - Feb 2010
  *
  *  This file is part of libao, a cross-platform library.  See
  *  README for a history of this source code.
@@ -26,25 +28,21 @@
  last mod: $Id$
 
  ********************************************************************/
-/*
-  The MacOS X CoreAudio framework doesn't mesh as simply as some
-  simpler frameworks do.  This is due to the fact that CoreAudio pulls
-  audio samples rather than having them pushed at it (which is nice
-  when you are wanting to do good buffering of audio).  */
+/* The MacOS X CoreAudio framework doesn't mesh as simply as some
+   simpler frameworks do.  This is due to the fact that CoreAudio pulls
+   audio samples rather than having them pushed at it (which is nice
+   when you are wanting to do good buffering of audio).  */
 
-#include <CoreAudio/AudioHardware.h>
+#include <AudioUnit/AudioUnit.h>
+#include <AudioUnit/AUComponent.h>
 #include <stdio.h>
 #include <pthread.h>
 
 #include "ao/ao.h"
 #include "ao/plugin.h"
 
-// Set this to 1 to see FIFO debugging messages
-#define DEBUG_PIPE 0
+#define DEFAULT_BUFFER_TIME (250);
 
-//#define BUFFER_COUNT (323)
-#define BUFFER_COUNT (2)
-
 #ifndef MIN
 #define MIN(a,b) ((a) < (b) ? (a) : (b))
 #endif
@@ -52,395 +50,543 @@
 #define true  1
 #define false 0
 
-static char *ao_macosx_options[] = {"matrix","verbose","quiet","debug"};
+static char *ao_macosx_options[] = {"matrix","verbose","quiet","debug","buffer_time"};
 
 static ao_info ao_macosx_info =
 {
 	AO_TYPE_LIVE,
-	"MacOS X output",
-	"macosx",
-	"Timothy J. Wood <tjw at omnigroup.com>",
+	"MacOS X AUHAL output",
+	"macosx_auhal",
+	"Monty <monty at xiph.org>",
 	"",
 	AO_FMT_NATIVE,
 	30,
 	ao_macosx_options,
-	4
+	5
 };
 
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
 
 typedef struct ao_macosx_internal
 {
-    // Stuff describing the CoreAudio device
-    AudioDeviceID                outputDeviceID;
-    AudioStreamBasicDescription  outputStreamBasicDescription;
+  /* Stuff describing the CoreAudio device */
+  AudioComponentInstance       outputAudioUnit;
 
-    // The amount of data CoreAudio wants each time it calls our IO function
-    UInt32                       outputBufferByteCount;
+  /* Keep track of whether the output stream has actually been
+     started/stopped */
+  Boolean                      started;
+  Boolean                      isStopping;
 
-    // Keep track of whether the output stream has actually been started/stopped
-    Boolean                      started;
-    Boolean                      isStopping;
+  /* Our internal queue of samples waiting to be consumed by
+     CoreAudio */
+  void                        *buffer;
+  unsigned int                 bufferByteCount;
+  unsigned int                 firstValidByteOffset;
+  unsigned int                 validByteCount;
 
-    // Synchronization objects between the CoreAudio thread and the enqueuing thread
-    pthread_mutex_t              mutex;
-    pthread_cond_t               condition;
+  unsigned int                 buffer_time;
 
-    // Our internal queue of samples waiting to be consumed by CoreAudio
-    void                        *buffer;
-    unsigned int                 bufferByteCount;
-    unsigned int                 firstValidByteOffset;
-    unsigned int                 validByteCount;
+  /* Need access in the cllbacks for messaging */
+  ao_device                   *device;
+} ao_macosx_internal;
 
-    // Temporary debugging state
-    unsigned int bytesQueued;
-    unsigned int bytesDequeued;
+static OSStatus audioCallback (void *inRefCon, 
+			       AudioUnitRenderActionFlags *inActionFlags,
+			       const AudioTimeStamp *inTimeStamp, 
+			       UInt32 inBusNumber, 
+			       UInt32 inNumberFrames,
+			       AudioBufferList *ioData)
+{
+  OSStatus err = noErr;
+  ao_macosx_internal *internal = (ao_macosx_internal *)inRefCon;
+  unsigned int validByteCount;
+  unsigned int totalBytesToCopy;
+  ao_device *device = internal->device;
 
-    // Need access in the cllbacks for messaging
-    ao_device                   *device;
-} ao_macosx_internal;
+  /* Despite the audio buffer list, playback render can only submit a
+     single buffer. */
 
-// The function that the CoreAudio thread calls when it wants more data
-static OSStatus audioDeviceIOProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inClientData);
+  if(!ioData){
+    aerror("Unexpected number of buffers (NULL)\n");
+    return 0;
+  }
 
+  if(ioData->mNumberBuffers != 1){
+    aerror("Unexpected number of buffers (%d)\n",
+	   ioData->mNumberBuffers);
+    return 0;
+  }
+
+  totalBytesToCopy = ioData->mBuffers[0].mDataByteSize;
+
+  pthread_mutex_lock(&mutex);
+
+  validByteCount = internal->validByteCount;
+
+  if (validByteCount < totalBytesToCopy && !internal->isStopping) {
+    /* Not enough data ... let it build up a bit more before we start
+       copying stuff over. If we are stopping, of course, we should
+       just copy whatever we have. This also happens if an application
+       pauses output. */
+    *inActionFlags = kAudioUnitRenderAction_OutputIsSilence;
+    memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize);
+    pthread_mutex_unlock(&mutex);
+    return 0;
+  }
+
+  {
+    unsigned char *outBuffer = ioData->mBuffers[0].mData;
+    unsigned int outBufSize = ioData->mBuffers[0].mDataByteSize;
+    unsigned int bytesToCopy = MIN(outBufSize, validByteCount);
+    unsigned int firstFrag = bytesToCopy;
+    unsigned char *sample = internal->buffer + internal->firstValidByteOffset;
+
+    /* Check if we have a wrap around in the ring buffer If yes then
+       find out how many bytes we have */
+    if (internal->firstValidByteOffset + bytesToCopy > internal->bufferByteCount) 
+      firstFrag = internal->bufferByteCount - internal->firstValidByteOffset;
+
+    /* If we have a wraparound first copy the remaining bytes off the end
+       and then the rest from the beginning of the ringbuffer */
+    if (firstFrag < bytesToCopy) {
+      memcpy(outBuffer,sample,firstFrag);
+      memcpy(outBuffer+firstFrag,internal->buffer,bytesToCopy-firstFrag);
+    } else {
+      memcpy(outBuffer,sample,bytesToCopy);
+    }
+    if(bytesToCopy < outBufSize) /* the stopping case */
+      memset(outBuffer+bytesToCopy, 0, outBufSize-bytesToCopy);
+
+    internal->validByteCount -= bytesToCopy;
+    internal->firstValidByteOffset = (internal->firstValidByteOffset + bytesToCopy) % 
+      internal->bufferByteCount;
+  }
+
+  pthread_mutex_unlock(&mutex);
+  pthread_cond_signal(&cond);
+
+  return err;
+}
+
 int ao_plugin_test()
 {
+  /* This plugin will only build on a 10.6 or later Mac (Darwin 9+);
+     if it built, default AUHAL is available. */
+  return 1; /* This plugin works in default mode */
 
-	if (/* FIXME */ 0 )
-		return 0; /* Cannot use this plugin with default parameters */
-	else {
-		return 1; /* This plugin works in default mode */
-	}
 }
 
 ao_info *ao_plugin_driver_info(void)
 {
-	return &ao_macosx_info;
+  return &ao_macosx_info;
 }
 
 
 int ao_plugin_device_init(ao_device *device)
 {
-	ao_macosx_internal *internal;
+  ao_macosx_internal *internal;
 
-	internal = (ao_macosx_internal *) malloc(sizeof(ao_macosx_internal));
+  internal = (ao_macosx_internal *) malloc(sizeof(ao_macosx_internal));
 
-	if (internal == NULL)
-		return 0; /* Could not initialize device memory */
+  if (internal == NULL)	
+    return 0; /* Could not initialize device memory */
 
+  internal->buffer_time = DEFAULT_BUFFER_TIME;
+  internal->started = false;
+  internal->isStopping = false;
 
+  internal->device = device;
+  device->internal = internal;
 
-	device->internal = internal;
-	device->internal->device = device;
-
-	return 1; /* Memory alloc successful */
+  return 1; /* Memory alloc successful */
 }
 
 
 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;
+  int buffer;
 
-	/* No options */
+  if (!strcmp(key,"buffer_time")) {
+    buffer = atoi(value);
+    if (buffer < 100) {
+      awarn("Buffer time clipped to 100ms\n");
+      buffer = 100;
+    }
+    internal->buffer_time = buffer;
+  }
+  
+  return 1;
+}
 
-	return 1;
+static char *map[]={
+  "L","R","C","LFE","BL","BR","CL","CR","BC","SL","SR",NULL
+};
+
+static unsigned int _matrix_to_channelmask(char *matrix){
+  unsigned int ret=0;
+  char *p=matrix;
+  while(1){
+    char *h=p;
+    int m=0;
+
+    /* search for seperator */
+    while(*h && *h!=',')h++;
+
+    while(map[m]){
+      if(h-p && !strncmp(map[m],p,h-p) &&
+         strlen(map[m])==h-p)
+        break;
+      m++;
+    }
+    if(map[m])
+      ret |= (1<<m);
+    if(!*h)break;
+    p=h+1;
+  }
+  return ret;
 }
 
+static char *_channelmask_to_matrix(unsigned int mask){
+  int m=0;
+  int count=0;
+  char buffer[80]={0};
+  while(map[m]){
+    if(mask & (1<<m)){
+      if(count)
+        strcat(buffer,",");
+      strcat(buffer,map[m]);
+      count++;
+    }
+    m++;
+  }
+  return strdup(buffer);
+}
 
 int ao_plugin_open(ao_device *device, ao_sample_format *format)
 {
-    ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
-    OSStatus status;
-    UInt32 propertySize;
-    int rc;
+  ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
+  OSStatus result = noErr;
+  AudioComponent comp;
+  AudioComponentDescription desc;
+  AudioStreamBasicDescription requestedDesc;
+  AURenderCallbackStruct      input;
+  UInt32 i_param_size;
 
-    if (format->rate != 44100) {
-        aerror("Only support 44.1kHz right now\n");
-        return 0;
-    }
+  /* Locate the default output audio unit */
+  desc.componentType = kAudioUnitType_Output;
+  desc.componentSubType = kAudioUnitSubType_HALOutput;
+  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+  desc.componentFlags = 0;
+  desc.componentFlagsMask = 0;
 
-    if (format->channels != 2) {
-        aerror("Only two channel audio right now\n");
-        return 0;
-    }
+  comp = AudioComponentFindNext (NULL, &desc);
+  if (comp == NULL) {
+    aerror("Failed to start CoreAudio: AudioComponentFindNext returned NULL");
+    return 0;
+  }
 
-    propertySize = sizeof(internal->outputDeviceID);
-    status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &(internal->outputDeviceID));
-    if (status) {
-        aerror("AudioHardwareGetProperty returned %d\n", (int)status);
-	return 0;
-    }
+  /* Open & initialize the default output audio unit */
+  result = AudioComponentInstanceNew (comp, &internal->outputAudioUnit);
+  if (result) {
+    aerror("AudioComponentInstanceNew() error => %d\n",(int)result);
+    return 0;
+  }
 
-    if (internal->outputDeviceID == kAudioDeviceUnknown) {
-        aerror("AudioHardwareGetProperty: outputDeviceID is kAudioDeviceUnknown\n");
-	return 0;
-    }
+  /* Request desired format of the audio unit.  Let HAL do all
+     conversion since it will probably be doing some internal
+     conversion anyway. */
 
-    propertySize = sizeof(internal->outputStreamBasicDescription);
-    status = AudioDeviceGetProperty(internal->outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &internal->outputStreamBasicDescription);
-    if (status) {
-        aerror("AudioDeviceGetProperty returned %d when getting kAudioDevicePropertyStreamFormat\n", (int)status);
-	return 0;
-    }
+  device->driver_byte_format = format->byte_format;
+  requestedDesc.mFormatID = kAudioFormatLinearPCM;
+  requestedDesc.mFormatFlags = kAudioFormatFlagIsPacked;
+  switch(format->byte_format){
+  case AO_FMT_BIG:
+    requestedDesc.mFormatFlags |= kAudioFormatFlagIsBigEndian;
+    break;
+  case AO_FMT_NATIVE:
+    if(ao_is_big_endian())
+      requestedDesc.mFormatFlags |= kAudioFormatFlagIsBigEndian;
+    break;
+  }
+  if (format->bits > 8)
+    requestedDesc.mFormatFlags |= kAudioFormatFlagIsSignedInteger;
 
-    adebug("hardware format...\n");
-    adebug("%f mSampleRate\n", internal->outputStreamBasicDescription.mSampleRate);
-    adebug("%c%c%c%c mFormatID\n", 
-           (int)(internal->outputStreamBasicDescription.mFormatID & 0xff000000) >> 24,
-           (int)(internal->outputStreamBasicDescription.mFormatID & 0x00ff0000) >> 16,
-           (int)(internal->outputStreamBasicDescription.mFormatID & 0x0000ff00) >>  8,
-           (int)(internal->outputStreamBasicDescription.mFormatID & 0x000000ff) >>  0);
-    adebug("%5d mBytesPerPacket\n", (int)internal->outputStreamBasicDescription.mBytesPerPacket);
-    adebug("%5d mFramesPerPacket\n", (int)internal->outputStreamBasicDescription.mFramesPerPacket);
-    adebug("%5d mBytesPerFrame\n", (int)internal->outputStreamBasicDescription.mBytesPerFrame);
-    adebug("%5d mChannelsPerFrame\n", (int)internal->outputStreamBasicDescription.mChannelsPerFrame);
+  requestedDesc.mChannelsPerFrame = format->channels;
+  requestedDesc.mSampleRate = format->rate;
+  requestedDesc.mBitsPerChannel = format->bits;
+  requestedDesc.mFramesPerPacket = 1;
+  requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8;
+  requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket;
 
-    if (internal->outputStreamBasicDescription.mFormatID != kAudioFormatLinearPCM) {
-        aerror("Default Audio Device doesn't support Linear PCM!\n");
-	return 0;
-    }
+  result = AudioUnitSetProperty (internal->outputAudioUnit,
+				 kAudioUnitProperty_StreamFormat,
+				 kAudioUnitScope_Input,
+				 0,
+				 &requestedDesc,
+				 sizeof(requestedDesc));
 
-    propertySize = sizeof(internal->outputBufferByteCount);
+  if (result) {
+    aerror("AudioUnitSetProperty error => %d\n",(int)result);
+    return 0;
+  }
 
-    internal->outputBufferByteCount = 8192;
-    status = AudioDeviceSetProperty(internal->outputDeviceID, 0, 0, false, kAudioDevicePropertyBufferSize,
-        propertySize, &internal->outputBufferByteCount);
+  /* what format did we actually get? */
+  i_param_size = sizeof(requestedDesc);
+  result = AudioUnitGetProperty(internal->outputAudioUnit,
+			     kAudioUnitProperty_StreamFormat,
+			     kAudioUnitScope_Input,
+			     0,
+			     &requestedDesc,
+			     &i_param_size );
+  if (result) {
+    aerror("Failed to query modified device hardware settings => %d\n",(int)result);
+    return 0;
+  }
 
-    status = AudioDeviceGetProperty(internal->outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &internal->outputBufferByteCount);
-    if (status) {
-      aerror("AudioDeviceGetProperty returned %d when getting kAudioDevicePropertyBufferSize\n", (int)status);
+  /* If any major settings differ, abort */
+  if(fabs(requestedDesc.mSampleRate-format->rate) > format->rate*.05){
+    aerror("Unable to set output sample rate\n");
+    return 0;
+  }
+  if(requestedDesc.mChannelsPerFrame != format->channels){
+    aerror("Could not configure %d channel output\n",format->channels);
+    return 0;
+  }
+  if(requestedDesc.mBitsPerChannel != format->bits){
+    aerror("Could not configure %d bit output\n",format->bits);
+    return 0;
+  }
+  if(requestedDesc.mBitsPerChannel != format->bits){
+    aerror("Could not configure %d bit output\n",format->bits);
+    return 0;
+  }
+  if(requestedDesc.mFormatFlags & kAudioFormatFlagIsFloat){
+    aerror("Could not configure integer sample output\n");
+    return 0;
+  }
+  if((requestedDesc.mFormatFlags & kAudioFormatFlagsNativeEndian) != 
+     kAudioFormatFlagsNativeEndian){
+    aerror("Could not configure output endianness\n");
+    return 0;
+  }
+
+  if (format->bits > 8){
+    if(!(requestedDesc.mFormatFlags & kAudioFormatFlagIsSignedInteger)){
+      aerror("Could not configure signed output\n");
       return 0;
     }
+  }else{
+    if((requestedDesc.mFormatFlags & kAudioFormatFlagIsSignedInteger)){
+      aerror("Could not configure unsigned output\n");
+      return 0;
+    }
+  }
+  if(requestedDesc.mSampleRate != format->rate){
+    awarn("Could not set sample rate to exactly %d; using %g instead.\n",
+	  format->rate,(double)requestedDesc.mSampleRate);
+  }
 
-    adebug("%5d outputBufferByteCount\n", (int)internal->outputBufferByteCount);
+  /* set the channel mapping.  MacOSX AUHAL is capable of mapping any
+     channel format currently representable in the libao matrix. */
 
-    // It appears that AudioDeviceGetProperty lies about this property in DP4
-    // Set the actual value
-    //internal->outputBufferByteCount = 32768;
+  if(format->matrix){
+    AudioChannelLayout layout;
+    memset(&layout,0,sizeof(layout));
 
-    // Set the IO proc that CoreAudio will call when it needs data, but don't start
-    // the stream yet.
-    internal->started = false;
-    status = AudioDeviceAddIOProc(internal->outputDeviceID, audioDeviceIOProc, internal);
-    if (status) {
-        aerror("AudioDeviceAddIOProc returned %d\n", (int)status);
-	return 0;
+    layout.mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelBitmap;
+    
+    if(device->output_matrix){
+      layout.mChannelBitmap = _matrix_to_channelmask(device->output_matrix);
+    }else{
+      layout.mChannelBitmap = _matrix_to_channelmask(format->matrix);
+      device->output_matrix = _channelmask_to_matrix(layout.mChannelBitmap);
     }
 
-    rc = pthread_mutex_init(&internal->mutex, NULL);
-    if (rc) {
-        aerror("pthread_mutex_init returned %d\n", rc);
-	return 0;
+    result = AudioUnitSetProperty(internal->outputAudioUnit,
+				  kAudioUnitProperty_AudioChannelLayout,
+				  kAudioUnitScope_Input, 0, &layout, sizeof(layout));
+    if (result) {
+      aerror("Failed to set audio channel layout => %d\n", (int)result);
     }
+  }
 
-    rc = pthread_cond_init(&internal->condition, NULL);
-    if (rc) {
-        aerror("pthread_cond_init returned %d\n", rc);
-	return 0;
-    }
+  /* Set the audio callback */
+  input.inputProc = (AURenderCallback) audioCallback;
+  input.inputProcRefCon = internal;
+ 
+  result = AudioUnitSetProperty( internal->outputAudioUnit, 
+				 kAudioUnitProperty_SetRenderCallback,
+				 kAudioUnitScope_Input,
+				 0, &input, sizeof( input ));
 
-    /* Since we don't know how big to make the buffer until we open the device
-       we allocate the buffer here instead of ao_plugin_device_init() */
-    internal->bufferByteCount = BUFFER_COUNT * internal->outputBufferByteCount;
-    internal->firstValidByteOffset = 0;
-    internal->validByteCount = 0;
-    internal->buffer = malloc(internal->bufferByteCount);
-    memset(internal->buffer, 0, internal->bufferByteCount);
-    if (!internal->buffer) {
-        aerror("Unable to allocate queue buffer.\n");
-	return 0;
-    }
 
-    /* initialize debugging state */
-    internal->bytesQueued = 0;
-    internal->bytesDequeued = 0;
+  if (result) {
+    aerror("Callback set error => %d\n",(int)result);
+    return 0;
+  }
 
-    device->driver_byte_format = AO_FMT_NATIVE;
+  result = AudioUnitInitialize (internal->outputAudioUnit);
+  if (result) {
+    aerror("AudioUnitInitialize() error => %d\n",(int)result);
+    return 0;
+  }
 
-    /* limited to stereo for now */
-    if(!device->output_matrix)
-      device->output_matrix=strdup("L,R");
+  /* Since we don't know how big to make the buffer until we open the device
+     we allocate the buffer here instead of ao_plugin_device_init() */
 
-    return 1;
+  internal->bufferByteCount =  (internal->buffer_time * format->rate / 1000) *
+    (format->channels * format->bits / 8);
+
+  internal->firstValidByteOffset = 0;
+  internal->validByteCount = 0;
+  internal->buffer = malloc(internal->bufferByteCount);
+  memset(internal->buffer, 0, internal->bufferByteCount);
+  if (!internal->buffer) {
+    aerror("Unable to allocate queue buffer.\n");
+    return 0;
+  }
+
+  /* limited to stereo for now */
+  //if(!device->output_matrix)
+  //device->output_matrix=strdup("L,R");
+  
+  return 1;
 }
 
 
 int ao_plugin_play(ao_device *device, const char *output_samples,
 		uint_32 num_bytes)
 {
-    ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
-    OSStatus status;
+  ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
+  int err;
+  unsigned int bytesToCopy;
+  unsigned int firstEmptyByteOffset, emptyByteCount;
 
-    adebug("Enqueue: 0x%08x %d bytes\n", output_samples, num_bytes);
+  while (num_bytes) {
+		
+    // Get a consistent set of data about the available space in the queue,
+    // figure out the maximum number of bytes we can copy in this chunk,
+    // and claim that amount of space
+    pthread_mutex_lock(&mutex);
 
-    while (num_bytes) {
-        unsigned int bytesToCopy;
-        unsigned int firstEmptyByteOffset, emptyByteCount;
+    // Wait until there is some empty space in the queue
+    emptyByteCount = internal->bufferByteCount - internal->validByteCount;
+    while (emptyByteCount == 0) {
+      if(!internal->started){
+	err = AudioOutputUnitStart(internal->outputAudioUnit);
+	adebug("Starting audio output unit\n");
+	if(err){
+	  pthread_mutex_unlock(&mutex);
+	  aerror("Failed to start audio output => %d\n",(int)err);
+	  return 0;
+	}
+	internal->started = true;
+      }
 
-        // Get a consistent set of data about the available space in the queue,
-        // figure out the maximum number of bytes we can copy in this chunk,
-        // and claim that amount of space
-        pthread_mutex_lock(&internal->mutex);
+      err = pthread_cond_wait(&cond, &mutex);
+      if (err)
+        adebug("pthread_cond_wait() => %d\n",err);
+      emptyByteCount = internal->bufferByteCount - internal->validByteCount;
+    }
 
-        // Wait until there is some empty space in the queue
-        emptyByteCount = internal->bufferByteCount - internal->validByteCount;
-        while (emptyByteCount == 0) {
-            pthread_cond_wait(&internal->condition, &internal->mutex);
-            emptyByteCount = internal->bufferByteCount - internal->validByteCount;
-        }
+    // Compute the offset to the first empty byte and the maximum number of
+    // bytes we can copy given the fact that the empty space might wrap
+    // around the end of the queue.
+    firstEmptyByteOffset = (internal->firstValidByteOffset + internal->validByteCount) % internal->bufferByteCount;
+    if (firstEmptyByteOffset + emptyByteCount > internal->bufferByteCount)
+      bytesToCopy = MIN(num_bytes, internal->bufferByteCount - firstEmptyByteOffset);
+    else
+      bytesToCopy = MIN(num_bytes, emptyByteCount);
 
-        // Compute the offset to the first empty byte and the maximum number of
-        // bytes we can copy given the fact that the empty space might wrap
-        // around the end of the queue.
-        firstEmptyByteOffset = (internal->firstValidByteOffset + internal->validByteCount) % internal->bufferByteCount;
-        if (firstEmptyByteOffset + emptyByteCount > internal->bufferByteCount)
-            bytesToCopy = MIN(num_bytes, internal->bufferByteCount - firstEmptyByteOffset);
-        else
-            bytesToCopy = MIN(num_bytes, emptyByteCount);
+    // Copy the bytes and get ready for the next chunk, if any
+    memcpy(internal->buffer + firstEmptyByteOffset, output_samples, bytesToCopy);
 
-        // Copy the bytes and get ready for the next chunk, if any
-        adebug("Enqueue:\tdst = 0x%08x src=0x%08x count=%d\n",
-               internal->buffer + firstEmptyByteOffset, output_samples, bytesToCopy);
+    num_bytes -= bytesToCopy;
+    output_samples += bytesToCopy;
+    internal->validByteCount += bytesToCopy;
 
-        memcpy(internal->buffer + firstEmptyByteOffset, output_samples, bytesToCopy);
-        /*{
-            unsigned int i;
-            static unsigned char bufferIndex;
+    pthread_mutex_unlock(&mutex);
 
-            bufferIndex++;
-            memset(internal->buffer + firstEmptyByteOffset, bufferIndex, bytesToCopy);
-        }*/
+  }
 
-        num_bytes -= bytesToCopy;
-        output_samples += bytesToCopy;
-        internal->validByteCount += bytesToCopy;
+  return 1;
+}
 
-        internal->bytesQueued += bytesToCopy;
 
-        pthread_mutex_unlock(&internal->mutex);
+int ao_plugin_close(ao_device *device)
+{
+  ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
+  OSStatus status;
+  UInt32 sizeof_running,running;
 
-        // We have to wait to start the device until we have some data queued.
-        // It might be better to wait until we have some minimum amount of data
-        // larger than whatever blob got enqueued here, but if we had a short
-        // stream, we'd have to make sure that ao_macosx_close() would start
-        // AND stop the stream when it had finished.  Yuck.  If the first
-        // blob that is passed to us is large enough (and the caller passes
-        // data quickly enough, this shouldn't be a problem.
-#if 1
-        if (!internal->started) {
-            internal->started = true;
-            status = AudioDeviceStart(internal->outputDeviceID, audioDeviceIOProc);
-            if (status) {
-                aerror("AudioDeviceStart returned %d\n", (int)status);
+  // If we never got started and there's data waiting to be played,
+  // start now.
 
-                // Can we do anything useful here?  The library doesn't expect this call
-                // to be able to fail.
-		return 0;
-            }
-        }
-#endif
+  pthread_mutex_lock(&mutex);
+  internal->isStopping = true;
+
+  if(!internal->started && internal->validByteCount){
+    status = AudioOutputUnitStart(internal->outputAudioUnit);
+    adebug("Starting audio output unit\n");
+    if(status){
+      pthread_mutex_unlock(&mutex);
+      aerror("Failed to start audio output => %d\n",(int)status);
+      return 0;
     }
+    internal->started = true;
+  }
 
+  // For some rare cases (using atexit in your program) Coreaudio tears
+  // down the HAL itself, so we do not need to do that here.
+  // We wouldn't get an error if we did, but AO would hang waiting for the 
+  // AU to flush the buffer
+  sizeof_running = sizeof(UInt32);
+  AudioUnitGetProperty(internal->outputAudioUnit, 
+		       kAudioDevicePropertyDeviceIsRunning,
+		       kAudioUnitScope_Input,
+		       0,
+		       &running, 
+		       &sizeof_running);
+
+  if (!running) {
+    pthread_mutex_unlock(&mutex);
     return 1;
-}
+  }
 
+  // Only stop if we ever got started
+  if (internal->started) {
 
-int ao_plugin_close(ao_device *device)
-{
-    ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
-    OSStatus status;
+    // Wait for any pending data to get flushed
+    while (internal->validByteCount)
+      pthread_cond_wait(&cond, &mutex);
 
-    // Only stop if we ever got started
-    if (internal->started) {
-
-        internal->isStopping = true;
-
-        // Wait for any pending data to get flushed
-        pthread_mutex_lock(&internal->mutex);
-        while (internal->validByteCount)
-            pthread_cond_wait(&internal->condition, &internal->mutex);
-        pthread_mutex_unlock(&internal->mutex);
-
-        status = AudioDeviceStop(internal->outputDeviceID, audioDeviceIOProc);
-        if (status) {
-            aerror("AudioDeviceStop returned %d\n", (int)status);
-            return 0;
-        }
+    pthread_mutex_unlock(&mutex);
+    
+    status = AudioOutputUnitStop(internal->outputAudioUnit);
+    if (status) {
+      awarn("AudioOutputUnitStop returned %d\n", (int)status);
+      return 0;
     }
 
-    status = AudioDeviceRemoveIOProc(internal->outputDeviceID, audioDeviceIOProc);
+    status = AudioUnitUninitialize(internal->outputAudioUnit);
     if (status) {
-      aerror("AudioDeviceRemoveIOProc returned %d\n", (int)status);
+      awarn("AudioUnitUninitialize returned %d\n", (int)status);
       return 0;
     }
+  }else
+    pthread_mutex_unlock(&mutex);
 
-    return 1;
+  return 1;
 }
 
 
 void ao_plugin_device_clear(ao_device *device)
 {
-	ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
+  ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
 
-	free(internal->buffer);
-	free(internal);
+  free(internal->buffer);
+  free(internal);
 }
 
-
-static OSStatus audioDeviceIOProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inClientData)
-{
-    ao_macosx_internal *internal = (ao_macosx_internal *)inClientData;
-    ao_device *device = internal->device;
-    short *sample;
-    unsigned int validByteCount;
-    float scale = (0.5f / SHRT_MAX), *outBuffer;
-    unsigned int bytesToCopy, samplesToCopy;
-
-    // Find the first valid frame and the number of valid frames
-    pthread_mutex_lock(&internal->mutex);
-
-    bytesToCopy = internal->outputBufferByteCount/2;
-    validByteCount = internal->validByteCount;
-    outBuffer = (float *)outOutputData->mBuffers[0].mData;
-
-    if (validByteCount < bytesToCopy && !internal->isStopping) {
-        // Not enough data ... let it build up a bit more before we start copying stuff over.
-        // If we are stopping, of course, we should just copy whatever we have.
-        memset(outBuffer, 0, bytesToCopy);
-        pthread_mutex_unlock(&internal->mutex);
-        return 0;
-    }
-
-    bytesToCopy = MIN(bytesToCopy, validByteCount);
-    sample = internal->buffer + internal->firstValidByteOffset;
-    samplesToCopy = bytesToCopy / sizeof(*sample);
-
-    internal->bytesDequeued += bytesToCopy;
-
-    adebug("IO: outputTime=%f firstValid=%d valid=%d toCopy=%d queued=%d dequeued=%d sample=0x%08x\n",
-           inOutputTime->mSampleTime,
-           internal->firstValidByteOffset, internal->validByteCount,
-           samplesToCopy, internal->bytesQueued, internal->bytesDequeued, sample);
-
-    internal->validByteCount -= bytesToCopy;
-    internal->firstValidByteOffset =
-      (internal->firstValidByteOffset + bytesToCopy) % internal->bufferByteCount;
-
-    // We don't have to deal with wrapping around in the buffer since the buffer is a
-    // multiple of the output buffer size and we only copy on buffer at a time
-    // (except on the last buffer when we may copy only a partial output buffer).
-#warning On the last buffer, zero out the part of the buffer that does not have valid samples
-    while (samplesToCopy--) {
-        short x = *sample;
-#warning The bytes in the buffer are currently in little endian, but we need big endian.  Supposedly these are going to be host endian at some point and the following line of code can go away.
-/* They will go away now, I think. --- Stan */
-/*        x = ((x & 0xff00) >> 8) | ((x & 0x00ff) << 8); */
-        *outBuffer = x * scale;
-        outBuffer++;
-        sample++;
-    }
-
-    pthread_mutex_unlock(&internal->mutex);
-    pthread_cond_signal(&internal->condition);
-
-    return 0;
-}



More information about the commits mailing list