Ticket #13412: patch-AU-src__plugins__macosx__ao_macosx.c

File patch-AU-src__plugins__macosx__ao_macosx.c, 27.3 KB (added by mike@…, 17 years ago)

Finally a fixed version for Leopard. MPG321 now also works with this patch.

Line 
1--- src/plugins/macosx/ao_macosx.c.orig 2004-11-09 09:20:26.000000000 +0100
2+++ src/plugins/macosx/ao_macosx.c      2007-11-30 13:37:22.000000000 +0100
3@@ -29,7 +29,8 @@
4   audio samples rather than having them pushed at it (which is nice
5   when you are wanting to do good buffering of audio).  */
6 
7-#include <CoreAudio/AudioHardware.h>
8+#include <AudioUnit/AudioUnit.h>
9+#include <AudioUnit/AUNTComponent.h>
10 #include <stdio.h>
11 #include <pthread.h>
12 
13@@ -39,8 +39,7 @@
14 // Set this to 1 to see FIFO debugging messages
15 #define DEBUG_PIPE 0
16 
17-//#define BUFFER_COUNT (323)
18-#define BUFFER_COUNT (2)
19+#define DEFAULT_BUFFER_TIME (250);
20 
21 #ifndef MIN
22 #define MIN(a,b) ((a) < (b) ? (a) : (b))
23@@ -52,9 +51,9 @@
24 static ao_info ao_macosx_info =
25 {
26        AO_TYPE_LIVE,
27-       "MacOS X output",
28-       "macosx",
29-       "Timothy J. Wood <tjw@omnigroup.com>",
30+       "MacOS X AUHAL output",
31+       "macosxau",
32+       "Michael Guntsche <mike@it-loops.com>",
33        "",
34        AO_FMT_NATIVE,
35        30,
36@@ -65,374 +64,409 @@
37 
38 typedef struct ao_macosx_internal
39 {
40-    // Stuff describing the CoreAudio device
41-    AudioDeviceID                outputDeviceID;
42-    AudioStreamBasicDescription  outputStreamBasicDescription;
43-   
44-    // The amount of data CoreAudio wants each time it calls our IO function
45-    UInt32                       outputBufferByteCount;
46-   
47-    // Keep track of whether the output stream has actually been started/stopped
48-    Boolean                      started;
49-    Boolean                      isStopping;
50-   
51-    // Synchronization objects between the CoreAudio thread and the enqueuing thread
52-    pthread_mutex_t              mutex;
53-    pthread_cond_t               condition;
54-
55-    // Our internal queue of samples waiting to be consumed by CoreAudio
56-    void                        *buffer;
57-    unsigned int                 bufferByteCount;
58-    unsigned int                 firstValidByteOffset;
59-    unsigned int                 validByteCount;
60-   
61-    // Temporary debugging state
62-    unsigned int bytesQueued;
63-    unsigned int bytesDequeued;
64+  // Stuff describing the CoreAudio device
65+  AudioUnit                    outputAudioUnit;
66+
67+  // Keep track of whether the output stream has actually been started/stopped
68+  Boolean                      started;
69+  Boolean                      isStopping;
70+
71+  // Synchronization objects between the CoreAudio thread and the enqueuing thread
72+  pthread_mutex_t              mutex;
73+  pthread_cond_t               condition;
74+
75+  // Our internal queue of samples waiting to be consumed by CoreAudio
76+  void                        *buffer;
77+  unsigned int                 bufferByteCount;
78+  unsigned int                 firstValidByteOffset;
79+  unsigned int                 validByteCount;
80+
81+  unsigned int                 buffer_time;
82+  // Temporary debugging state
83+  unsigned int bytesQueued;
84+  unsigned int bytesDequeued;
85 } ao_macosx_internal;
86 
87 // The function that the CoreAudio thread calls when it wants more data
88-static OSStatus audioDeviceIOProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inClientData);
89+static OSStatus     audioCallback (void                             *inRefCon, 
90+                                                                  AudioUnitRenderActionFlags      inActionFlags,
91+                                                                  const AudioTimeStamp            *inTimeStamp, 
92+                                                                  UInt32                          inBusNumber, 
93+                                                                  AudioBuffer                     *ioData)
94+
95+{
96+  ao_macosx_internal *internal = (ao_macosx_internal *)inRefCon;
97+  short *sample;
98+  unsigned int validByteCount;
99+  unsigned char *outBuffer;
100+  unsigned int bytesToCopy, samplesToCopy, firstFrag;
101+
102+
103+
104+  // Find the first valid frame and the number of valid frames
105+  pthread_mutex_lock(&internal->mutex);
106+
107+
108+  bytesToCopy = ioData->mDataByteSize;
109+  firstFrag = bytesToCopy;
110+  validByteCount = internal->validByteCount;
111+  outBuffer = ioData->mData;
112+
113+
114+#if DEBUG_PIPE
115+  fprintf(stderr,"BTC: %i Valid: %i\n",bytesToCopy,validByteCount);
116+#endif
117+
118+  if (validByteCount < bytesToCopy && !internal->isStopping) {
119+    // Not enough data ... let it build up a bit more before we start copying stuff over.
120+    // If we are stopping, of course, we should just copy whatever we have.
121+    // This also happens if an application pauses output.
122+    inActionFlags = kAudioUnitRenderAction_OutputIsSilence;
123+    memset(outBuffer, 0, ioData->mDataByteSize);
124+    pthread_mutex_unlock(&internal->mutex);
125+    return 0;
126+  }
127+
128+
129+  bytesToCopy = MIN(bytesToCopy, validByteCount);
130+  sample = internal->buffer + internal->firstValidByteOffset;
131+  samplesToCopy = bytesToCopy / sizeof(*sample);
132+
133+  /* Check if we habe a wrap around in the ring buffer
134+   * If yes then find out how many bytes we have */
135+  if (internal->firstValidByteOffset + bytesToCopy > internal->bufferByteCount) 
136+    firstFrag = internal->bufferByteCount - internal->firstValidByteOffset;
137+
138+  #if DEBUG_PIPE
139+  fprintf(stderr, "IO: firstValid=%d valid=%d toCopy=%d queued=%d dequeued=%d sample=0x%08x\n",
140+     
141+      internal->firstValidByteOffset, internal->validByteCount, samplesToCopy, internal->bytesQueued, internal->bytesDequeued, sample);
142+  #endif
143+
144+  internal->validByteCount -= bytesToCopy;
145+  internal->firstValidByteOffset = (internal->firstValidByteOffset + bytesToCopy) % internal->bufferByteCount;
146+
147+  /* If we have a wraparound first copy the remaining bytes off the end
148+   * and then the rest from the beginning of the ringbuffer */
149+  if (firstFrag < bytesToCopy) {
150+    memcpy(outBuffer,sample,firstFrag);
151+    memcpy(outBuffer+firstFrag,internal->buffer,bytesToCopy-firstFrag);
152+  }
153+  else {
154+    memcpy(outBuffer,sample,bytesToCopy);
155+  }
156+
157+  pthread_mutex_unlock(&internal->mutex);
158+  pthread_cond_signal(&internal->condition);
159+
160+  return 0;
161+}
162+
163+
164+
165 
166 int ao_plugin_test()
167 {
168       
169-       if (/* FIXME */ 0 )
170-               return 0; /* Cannot use this plugin with default parameters */
171-       else {
172-               return 1; /* This plugin works in default mode */
173-       }
174+  if (/* FIXME */ 0 )
175+    return 0; /* Cannot use this plugin with default parameters */
176+  else {
177+    return 1; /* This plugin works in default mode */
178+  }
179 }
180 
181 ao_info *ao_plugin_driver_info(void)
182 {
183-       return &ao_macosx_info;
184+  return &ao_macosx_info;
185 }
186 
187 
188 int ao_plugin_device_init(ao_device *device)
189 {
190-       ao_macosx_internal *internal;
191+  ao_macosx_internal *internal;
192 
193-       internal = (ao_macosx_internal *) malloc(sizeof(ao_macosx_internal));
194+  internal = (ao_macosx_internal *) malloc(sizeof(ao_macosx_internal));
195 
196-       if (internal == NULL)   
197-               return 0; /* Could not initialize device memory */
198-       
199+  if (internal == NULL)       
200+    return 0; /* Could not initialize device memory */
201 
202-       
203-       device->internal = internal;
204+  internal->buffer_time = DEFAULT_BUFFER_TIME;
205+  internal->started = false;
206+  internal->isStopping = false;
207 
208-       return 1; /* Memory alloc successful */
209-}
210+  device->internal = internal;
211 
212+  return 1; /* Memory alloc successful */
213+  }
214 
215-int ao_plugin_set_option(ao_device *device, const char *key, const char *value)
216-{
217-       ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
218 
219-       /* No options */
220-
221-       return 1;
222+int ao_plugin_set_option(ao_device *device, const char *key, const char *value)
223+  {
224+  ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
225+  int buffer;
226+
227+  if (!strcmp(key,"buffer_time")) {
228+    buffer = atoi(value);
229+    if (buffer < 100) {
230+      fprintf(stderr,"Buffer time clipped to 100ms\n");
231+      buffer = DEFAULT_BUFFER_TIME;
232+    }
233+    internal->buffer_time = buffer;
234+  }
235+ 
236+  /* No options */
237+  return 1;
238 }
239 
240 
241 int ao_plugin_open(ao_device *device, ao_sample_format *format)
242 {
243-    ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
244-    OSStatus status;
245-    UInt32 propertySize;
246-    int rc;
247-   
248-    if (format->rate != 44100) {
249-        fprintf(stderr, "ao_macosx_open: Only support 44.1kHz right now\n");
250-        return 0;
251-    }
252-   
253-    if (format->channels != 2) {
254-        fprintf(stderr, "ao_macosx_open: Only two channel audio right now\n");
255-        return 0;
256-    }
257+  ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
258 
259-    propertySize = sizeof(internal->outputDeviceID);
260-    status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &(internal->outputDeviceID));
261-    if (status) {
262-        fprintf(stderr, "ao_macosx_open: AudioHardwareGetProperty returned %d\n", (int)status);
263-       return 0;
264-    }
265-   
266-    if (internal->outputDeviceID == kAudioDeviceUnknown) {
267-        fprintf(stderr, "ao_macosx_open: AudioHardwareGetProperty: outputDeviceID is kAudioDeviceUnknown\n");
268-       return 0;
269-    }
270-   
271-    propertySize = sizeof(internal->outputStreamBasicDescription);
272-    status = AudioDeviceGetProperty(internal->outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &internal->outputStreamBasicDescription);
273-    if (status) {
274-        fprintf(stderr, "ao_macosx_open: AudioDeviceGetProperty returned %d when getting kAudioDevicePropertyStreamFormat\n", (int)status);
275-       return 0;
276-    }
277+  OSStatus result = noErr;
278+  Component comp;
279+  ComponentDescription desc;
280+  struct AudioUnitInputCallback callback;
281+  AudioStreamBasicDescription requestedDesc;
282+  UInt32 i_param_size;
283+  int rc;
284+
285+
286+  /* Setup a AudioStreamBasicDescription with the requested format */
287+  requestedDesc.mFormatID = kAudioFormatLinearPCM;
288+  requestedDesc.mFormatFlags = kAudioFormatFlagIsPacked;
289+
290+       if (ao_is_big_endian())
291+               requestedDesc.mFormatFlags |= kAudioFormatFlagIsBigEndian;
292+
293+  if (format->bits > 8)
294+    requestedDesc.mFormatFlags |= kAudioFormatFlagIsSignedInteger;
295+
296+  requestedDesc.mChannelsPerFrame = format->channels;
297+  requestedDesc.mSampleRate = format->rate;
298+  requestedDesc.mBitsPerChannel = format->bits;
299+  requestedDesc.mFramesPerPacket = 1;
300+  requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8;
301+  requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket;
302+
303+  /* Locate the default output audio unit */
304+  desc.componentType = kAudioUnitComponentType;
305+  desc.componentSubType = kAudioUnitSubType_Output;
306+  desc.componentManufacturer = kAudioUnitID_DefaultOutput;
307+  desc.componentFlags = 0;
308+  desc.componentFlagsMask = 0;
309+
310+
311+  comp = FindNextComponent (NULL, &desc);
312+  if (comp == NULL) {
313+    fprintf (stderr,"Failed to start CoreAudio: FindNextComponent returned NULL");
314+    return 0;
315+  }
316 
317-    fprintf(stderr, "hardware format...\n");
318-    fprintf(stderr, "%f mSampleRate\n", internal->outputStreamBasicDescription.mSampleRate);
319-    fprintf(stderr, "%c%c%c%c mFormatID\n", (int)(internal->outputStreamBasicDescription.mFormatID & 0xff000000) >> 24,
320-                                            (int)(internal->outputStreamBasicDescription.mFormatID & 0x00ff0000) >> 16,
321-                                            (int)(internal->outputStreamBasicDescription.mFormatID & 0x0000ff00) >>  8,
322-                                            (int)(internal->outputStreamBasicDescription.mFormatID & 0x000000ff) >>  0);
323-    fprintf(stderr, "%5d mBytesPerPacket\n", (int)internal->outputStreamBasicDescription.mBytesPerPacket);
324-    fprintf(stderr, "%5d mFramesPerPacket\n", (int)internal->outputStreamBasicDescription.mFramesPerPacket);
325-    fprintf(stderr, "%5d mBytesPerFrame\n", (int)internal->outputStreamBasicDescription.mBytesPerFrame);
326-    fprintf(stderr, "%5d mChannelsPerFrame\n", (int)internal->outputStreamBasicDescription.mChannelsPerFrame);
327-
328-    if (internal->outputStreamBasicDescription.mFormatID != kAudioFormatLinearPCM) {
329-        fprintf(stderr, "ao_macosx_open: Default Audio Device doesn't support Linear PCM!\n");
330-       return 0;
331-    }
332+  /* Open & initialize the default output audio unit */
333+    result = OpenAComponent (comp, &internal->outputAudioUnit);
334+  if (result) 
335+    fprintf(stderr,"Comp error\n");
336+  result = AudioUnitInitialize (internal->outputAudioUnit);
337+  if (result) 
338+    fprintf(stderr,"Init error\n");
339+
340+  /* Set the audio callback */
341+  callback.inputProc = audioCallback;
342+  callback.inputProcRefCon = internal;
343+  result = AudioUnitSetProperty (internal->outputAudioUnit, 
344+      kAudioUnitProperty_SetInputCallback, 
345+      kAudioUnitScope_Input, 
346+      0,
347+      &callback, 
348+      sizeof(callback));
349 
350-    propertySize = sizeof(internal->outputBufferByteCount);
351-   
352-    internal->outputBufferByteCount = 8192;
353-    status = AudioDeviceSetProperty(internal->outputDeviceID, 0, 0, false, kAudioDevicePropertyBufferSize,
354-        propertySize, &internal->outputBufferByteCount);
355-       
356-    status = AudioDeviceGetProperty(internal->outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &internal->outputBufferByteCount);
357-    if (status) {
358-        fprintf(stderr, "ao_macosx_open: AudioDeviceGetProperty returned %d when getting kAudioDevicePropertyBufferSize\n", (int)status);
359-       return 0;
360-    }
361+  if (result) {
362 
363-    fprintf(stderr, "%5d outputBufferByteCount\n", (int)internal->outputBufferByteCount);
364+    fprintf(stderr,"Callback %i\n",result);
365+    return 0;
366+  }
367 
368-    // It appears that AudioDeviceGetProperty lies about this property in DP4
369-    // Set the actual value
370-    //internal->outputBufferByteCount = 32768;
371-
372-    // Set the IO proc that CoreAudio will call when it needs data, but don't start
373-    // the stream yet.
374-    internal->started = false;
375-    status = AudioDeviceAddIOProc(internal->outputDeviceID, audioDeviceIOProc, internal);
376-    if (status) {
377-        fprintf(stderr, "ao_macosx_open: AudioDeviceAddIOProc returned %d\n", (int)status);
378-       return 0;
379-    }
380 
381-    rc = pthread_mutex_init(&internal->mutex, NULL);
382-    if (rc) {
383-        fprintf(stderr, "ao_macosx_open: pthread_mutex_init returned %d\n", rc);
384-       return 0;
385-    }
386-   
387-    rc = pthread_cond_init(&internal->condition, NULL);
388-    if (rc) {
389-        fprintf(stderr, "ao_macosx_open: pthread_cond_init returned %d\n", rc);
390-       return 0;
391-    }
392+  /* Set the input format of the audio unit. */
393     
394-    /* Since we don't know how big to make the buffer until we open the device
395-       we allocate the buffer here instead of ao_plugin_device_init() */
396-    internal->bufferByteCount = BUFFER_COUNT * internal->outputBufferByteCount;
397-    internal->firstValidByteOffset = 0;
398-    internal->validByteCount = 0;
399-    internal->buffer = malloc(internal->bufferByteCount);
400-    memset(internal->buffer, 0, internal->bufferByteCount);
401-    if (!internal->buffer) {
402-        fprintf(stderr, "ao_macosx_open: Unable to allocate queue buffer.\n");
403-       return 0;
404-    }
405+  result = AudioUnitSetProperty (internal->outputAudioUnit,
406+                   kAudioUnitProperty_StreamFormat,
407+                   kAudioUnitScope_Input,
408+                   0,
409+                   &requestedDesc,
410+                   sizeof(AudioStreamBasicDescription));
411 
412-    /* initialize debugging state */
413-    internal->bytesQueued = 0;
414-    internal->bytesDequeued = 0;
415-   
416-    device->driver_byte_format = AO_FMT_NATIVE;
417+  if (result) {
418+
419+    fprintf(stderr,"Output %i\n",result);
420+    return 0;
421+  }
422+
423+  rc = pthread_mutex_init(&internal->mutex, NULL);
424+  if (rc) {
425+    fprintf(stderr, "ao_macosx_open: pthread_mutex_init returned %d\n", rc);
426+    return 0;
427+  }
428+
429+  rc = pthread_cond_init(&internal->condition, NULL);
430+  if (rc) {
431+    fprintf(stderr, "ao_macosx_open: pthread_cond_init returned %d\n", rc);
432+    return 0;
433+  }
434+
435+  /* Since we don't know how big to make the buffer until we open the device
436+     we allocate the buffer here instead of ao_plugin_device_init() */
437+
438+  internal->bufferByteCount =  (internal->buffer_time * format->rate / 1000) * (format->channels * format->bits / 8);
439+
440+  internal->firstValidByteOffset = 0;
441+  internal->validByteCount = 0;
442+  internal->buffer = malloc(internal->bufferByteCount);
443+  memset(internal->buffer, 0, internal->bufferByteCount);
444+  if (!internal->buffer) {
445+    fprintf(stderr, "ao_macosx_open: Unable to allocate queue buffer.\n");
446+    return 0;
447+  }
448+
449+  /* initialize debugging state */
450+  internal->bytesQueued = 0;
451+  internal->bytesDequeued = 0;
452+
453+  device->driver_byte_format = AO_FMT_NATIVE;
454+
455+
456+  return 1;
457 
458-    return 1;
459 }
460 
461 
462 int ao_plugin_play(ao_device *device, const char *output_samples, 
463                uint_32 num_bytes)
464 {
465-    ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
466-    OSStatus status;
467+  ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
468+  OSStatus status;
469+       int err;
470+  unsigned int bytesToCopy;
471+  unsigned int firstEmptyByteOffset, emptyByteCount;
472 
473-#if DEBUG_PIPE
474-    fprintf(stderr, "Enqueue: 0x%08x %d bytes\n", output_samples, num_bytes);
475-#endif
476 
477-    while (num_bytes) {
478-        unsigned int bytesToCopy;
479-        unsigned int firstEmptyByteOffset, emptyByteCount;
480-       
481-        // Get a consistent set of data about the available space in the queue,
482-        // figure out the maximum number of bytes we can copy in this chunk,
483-        // and claim that amount of space
484-        pthread_mutex_lock(&internal->mutex);
485-
486-        // Wait until there is some empty space in the queue
487-        emptyByteCount = internal->bufferByteCount - internal->validByteCount;
488-        while (emptyByteCount == 0) {
489-            pthread_cond_wait(&internal->condition, &internal->mutex);
490-            emptyByteCount = internal->bufferByteCount - internal->validByteCount;
491-        }
492-
493-        // Compute the offset to the first empty byte and the maximum number of
494-        // bytes we can copy given the fact that the empty space might wrap
495-        // around the end of the queue.
496-        firstEmptyByteOffset = (internal->firstValidByteOffset + internal->validByteCount) % internal->bufferByteCount;
497-        if (firstEmptyByteOffset + emptyByteCount > internal->bufferByteCount)
498-            bytesToCopy = MIN(num_bytes, internal->bufferByteCount - firstEmptyByteOffset);
499-        else
500-            bytesToCopy = MIN(num_bytes, emptyByteCount);
501+  while (num_bytes) {
502+               
503+    // Get a consistent set of data about the available space in the queue,
504+    // figure out the maximum number of bytes we can copy in this chunk,
505+    // and claim that amount of space
506+    pthread_mutex_lock(&internal->mutex);
507+
508+    // Wait until there is some empty space in the queue
509+    emptyByteCount = internal->bufferByteCount - internal->validByteCount;
510+    while (emptyByteCount == 0) {
511+      err = pthread_cond_wait(&internal->condition, &internal->mutex);
512+      if (err)
513+        fprintf(stderr,"Whatever: %i\n",err);
514+      emptyByteCount = internal->bufferByteCount - internal->validByteCount;
515+    }
516+
517+    // Compute the offset to the first empty byte and the maximum number of
518+    // bytes we can copy given the fact that the empty space might wrap
519+    // around the end of the queue.
520+    firstEmptyByteOffset = (internal->firstValidByteOffset + internal->validByteCount) % internal->bufferByteCount;
521+    if (firstEmptyByteOffset + emptyByteCount > internal->bufferByteCount)
522+      bytesToCopy = MIN(num_bytes, internal->bufferByteCount - firstEmptyByteOffset);
523+    else
524+      bytesToCopy = MIN(num_bytes, emptyByteCount);
525+
526+    // Copy the bytes and get ready for the next chunk, if any
527+  #if DEBUG_PIPE
528+    fprintf(stderr, "Enqueue:\tdst = 0x%08x src=0x%08x count=%d\n",
529+        internal->buffer + firstEmptyByteOffset, output_samples, bytesToCopy);
530+  #endif
531+
532+    memcpy(internal->buffer + firstEmptyByteOffset, output_samples, bytesToCopy);
533+
534+
535+
536+    num_bytes -= bytesToCopy;
537+    output_samples += bytesToCopy;
538+    internal->validByteCount += bytesToCopy;
539+
540+    internal->bytesQueued += bytesToCopy;
541+
542+    pthread_mutex_unlock(&internal->mutex);
543+
544+    // We have to wait to start the device until we have some data queued.
545+    // It might be better to wait until we have some minimum amount of data
546+    // larger than whatever blob got enqueued here, but if we had a short
547+    // stream, we'd have to make sure that ao_macosx_close() would start
548+    // AND stop the stream when it had finished.  Yuck.  If the first
549+    // blob that is passed to us is large enough (and the caller passes
550+    // data quickly enough, this shouldn't be a problem.
551+  #if 1
552+    if (!internal->started) {
553+      internal->started = true;
554+      if(AudioOutputUnitStart(internal->outputAudioUnit)) fprintf(stderr,"asdfsadf\n");
555 
556-        // Copy the bytes and get ready for the next chunk, if any
557-#if DEBUG_PIPE
558-        fprintf(stderr, "Enqueue:\tdst = 0x%08x src=0x%08x count=%d\n",
559-                internal->buffer + firstEmptyByteOffset, output_samples, bytesToCopy);
560-#endif
561-               
562-        memcpy(internal->buffer + firstEmptyByteOffset, output_samples, bytesToCopy);
563-        /*{
564-            unsigned int i;
565-            static unsigned char bufferIndex;
566-           
567-            bufferIndex++;
568-            memset(internal->buffer + firstEmptyByteOffset, bufferIndex, bytesToCopy);
569-        }*/
570-       
571-        num_bytes -= bytesToCopy;
572-        output_samples += bytesToCopy;
573-        internal->validByteCount += bytesToCopy;
574-       
575-        internal->bytesQueued += bytesToCopy;
576-       
577-        //fprintf(stderr, "Copy: %d bytes, %d bytes left\n", bytesToCopy, internal->availableByteCount);
578-        pthread_mutex_unlock(&internal->mutex);
579-       
580-        // We have to wait to start the device until we have some data queued.
581-        // It might be better to wait until we have some minimum amount of data
582-        // larger than whatever blob got enqueued here, but if we had a short
583-        // stream, we'd have to make sure that ao_macosx_close() would start
584-        // AND stop the stream when it had finished.  Yuck.  If the first
585-        // blob that is passed to us is large enough (and the caller passes
586-        // data quickly enough, this shouldn't be a problem.
587-#if 1
588-        if (!internal->started) {
589-            internal->started = true;
590-            status = AudioDeviceStart(internal->outputDeviceID, audioDeviceIOProc);
591-            if (status) {
592-                fprintf(stderr, "ao_macosx_open: AudioDeviceStart returned %d\n", (int)status);
593-               
594-                // Can we do anything useful here?  The library doesn't expect this call
595-                // to be able to fail.
596-               return 0;
597-            }
598-        }
599-#endif
600     }
601+  #endif
602+  }
603 
604-    return 1;
605+  return 1;
606 }
607 
608 
609 int ao_plugin_close(ao_device *device)
610 {
611-    ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
612-    OSStatus status;
613+  ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
614+  OSStatus status;
615+  UInt32 running;
616+  UInt32 sizeof_running;
617 
618-    // Only stop if we ever got started
619-    if (internal->started) {
620 
621-        internal->isStopping = true;
622-       
623-        // Wait for any pending data to get flushed
624-        pthread_mutex_lock(&internal->mutex);
625-        while (internal->validByteCount)
626-            pthread_cond_wait(&internal->condition, &internal->mutex);
627-        pthread_mutex_unlock(&internal->mutex);
628-       
629-        status = AudioDeviceStop(internal->outputDeviceID, audioDeviceIOProc);
630-        if (status) {
631-            fprintf(stderr, "ao_macosx_close: AudioDeviceStop returned %d\n", (int)status);
632-            return 0;
633-        }
634+
635+  // For some rare cases (using atexit in your program) Coreaudio tears
636+  // down the HAL itself, so we do not need to do that here.
637+  // We wouldn't get an error if we did, but AO would hang waiting for the AU to flush the buffer
638+  /*sizeof_running = sizeof(UInt32);
639+  AudioUnitGetProperty(internal->outputAudioUnit, 0, false, kAudioDevicePropertyDeviceIsRunning, &sizeof_running, &running);
640+  if (!running) {
641+    return 1;
642+  }
643+  */
644+
645+  // Only stop if we ever got started
646+  if (internal->started) {
647+
648+    internal->isStopping = true;
649+
650+    // Wait for any pending data to get flushed
651+    /*pthread_mutex_lock(&internal->mutex);
652+    while (internal->validByteCount)
653+      pthread_cond_wait(&internal->condition, &internal->mutex);
654+    pthread_mutex_unlock(&internal->mutex);
655+    */
656+    status = AudioOutputUnitStop(internal->outputAudioUnit);
657+    if (status) {
658+      fprintf(stderr, "ao_macosx_close: AudioDeviceStop returned %d\n", (int)status);
659+      return 0;
660     }
661-   
662-    status = AudioDeviceRemoveIOProc(internal->outputDeviceID, audioDeviceIOProc);
663+    status = AudioUnitUninitialize(internal->outputAudioUnit);
664     if (status) {
665-        fprintf(stderr, "ao_macosx_close: AudioDeviceRemoveIOProc returned %d\n", (int)status);
666-        return 0;
667+      fprintf(stderr, "ao_macosx_close: AudioUnitUninitialize returned %d\n", (int)status);
668+      return 0;
669     }
670+  }
671 
672-    return 1;
673+  return 1;
674 }
675 
676 
677 void ao_plugin_device_clear(ao_device *device)
678 {
679-       ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
680+  ao_macosx_internal *internal = (ao_macosx_internal *) device->internal;
681 
682-       free(internal->buffer);
683-       free(internal);
684+  free(internal->buffer);
685+  free(internal);
686 }
687 
688 
689-static OSStatus audioDeviceIOProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inClientData)
690-{
691-    ao_macosx_internal *internal = (ao_macosx_internal *)inClientData;
692-    short *sample;
693-    unsigned int validByteCount;
694-    float scale = (0.5f / SHRT_MAX), *outBuffer;
695-    unsigned int bytesToCopy, samplesToCopy;
696-
697-    // Find the first valid frame and the number of valid frames
698-    pthread_mutex_lock(&internal->mutex);
699-
700-    bytesToCopy = internal->outputBufferByteCount/2;
701-    validByteCount = internal->validByteCount;
702-    outBuffer = (float *)outOutputData->mBuffers[0].mData;
703-   
704-    if (validByteCount < bytesToCopy && !internal->isStopping) {
705-        // Not enough data ... let it build up a bit more before we start copying stuff over.
706-        // If we are stopping, of course, we should just copy whatever we have.
707-        memset(outBuffer, 0, bytesToCopy);
708-        pthread_mutex_unlock(&internal->mutex);
709-        return 0;
710-    }
711-   
712-    bytesToCopy = MIN(bytesToCopy, validByteCount);
713-    sample = internal->buffer + internal->firstValidByteOffset;
714-    samplesToCopy = bytesToCopy / sizeof(*sample);
715-
716-    internal->bytesDequeued += bytesToCopy;
717-
718-#if DEBUG_PIPE
719-    fprintf(stderr, "IO: outputTime=%f firstValid=%d valid=%d toCopy=%d queued=%d dequeued=%d sample=0x%08x\n",
720-            inOutputTime->mSampleTime,
721-            internal->firstValidByteOffset, internal->validByteCount, samplesToCopy, internal->bytesQueued, internal->bytesDequeued, sample);
722-#endif
723-   
724-    internal->validByteCount -= bytesToCopy;
725-    internal->firstValidByteOffset = (internal->firstValidByteOffset + bytesToCopy) % internal->bufferByteCount;
726-   
727-    // We don't have to deal with wrapping around in the buffer since the buffer is a
728-    // multiple of the output buffer size and we only copy on buffer at a time
729-    // (except on the last buffer when we may copy only a partial output buffer).
730-#warning On the last buffer, zero out the part of the buffer that does not have valid samples
731-    while (samplesToCopy--) {
732-        short x = *sample;
733-#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.
734-/* They will go away now, I think. --- Stan */
735-/*        x = ((x & 0xff00) >> 8) | ((x & 0x00ff) << 8); */
736-        *outBuffer = x * scale;
737-        outBuffer++;
738-        sample++;
739-    }
740-   
741-    pthread_mutex_unlock(&internal->mutex);
742-    pthread_cond_signal(&internal->condition);
743-   
744-    return 0;
745-}
746-
747