Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.git] / sys / osxaudio / gstosxringbuffer.c
1 /*
2  * GStreamer
3  * Copyright (C) 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
4  * Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  *
24  * Alternatively, the contents of this file may be used under the
25  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
26  * which case the following provisions apply instead of the ones
27  * mentioned above:
28  *
29  * This library is free software; you can redistribute it and/or
30  * modify it under the terms of the GNU Library General Public
31  * License as published by the Free Software Foundation; either
32  * version 2 of the License, or (at your option) any later version.
33  *
34  * This library is distributed in the hope that it will be useful,
35  * but WITHOUT ANY WARRANTY; without even the implied warranty of
36  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
37  * Library General Public License for more details.
38  *
39  * You should have received a copy of the GNU Library General Public
40  * License along with this library; if not, write to the
41  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
42  * Boston, MA 02111-1307, USA.
43  */
44
45 #include <CoreAudio/CoreAudio.h>
46 #include <CoreServices/CoreServices.h>
47 #include <gst/gst.h>
48 #include <gst/audio/multichannel.h>
49 #include "gstosxringbuffer.h"
50 #include "gstosxaudiosink.h"
51 #include "gstosxaudiosrc.h"
52
53 GST_DEBUG_CATEGORY_STATIC (osx_audio_debug);
54 #define GST_CAT_DEFAULT osx_audio_debug
55
56 static void gst_osx_ring_buffer_dispose (GObject * object);
57 static void gst_osx_ring_buffer_finalize (GObject * object);
58 static gboolean gst_osx_ring_buffer_open_device (GstRingBuffer * buf);
59 static gboolean gst_osx_ring_buffer_close_device (GstRingBuffer * buf);
60
61 static gboolean gst_osx_ring_buffer_acquire (GstRingBuffer * buf,
62     GstRingBufferSpec * spec);
63 static gboolean gst_osx_ring_buffer_release (GstRingBuffer * buf);
64
65 static gboolean gst_osx_ring_buffer_start (GstRingBuffer * buf);
66 static gboolean gst_osx_ring_buffer_pause (GstRingBuffer * buf);
67 static gboolean gst_osx_ring_buffer_stop (GstRingBuffer * buf);
68 static guint gst_osx_ring_buffer_delay (GstRingBuffer * buf);
69 static GstRingBufferClass *ring_parent_class = NULL;
70
71 static OSStatus gst_osx_ring_buffer_render_notify (GstOsxRingBuffer * osxbuf,
72     AudioUnitRenderActionFlags * ioActionFlags,
73     const AudioTimeStamp * inTimeStamp, unsigned int inBusNumber,
74     unsigned int inNumberFrames, AudioBufferList * ioData);
75
76 static AudioBufferList *buffer_list_alloc (int channels, int size);
77 static void buffer_list_free (AudioBufferList * list);
78
79 static void
80 gst_osx_ring_buffer_do_init (GType type)
81 {
82   GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0,
83       "OSX Audio Elements");
84 }
85
86 GST_BOILERPLATE_FULL (GstOsxRingBuffer, gst_osx_ring_buffer, GstRingBuffer,
87     GST_TYPE_RING_BUFFER, gst_osx_ring_buffer_do_init);
88
89 static void
90 gst_osx_ring_buffer_base_init (gpointer g_class)
91 {
92   /* Nothing to do right now */
93 }
94
95 static void
96 gst_osx_ring_buffer_class_init (GstOsxRingBufferClass * klass)
97 {
98   GObjectClass *gobject_class;
99   GstObjectClass *gstobject_class;
100   GstRingBufferClass *gstringbuffer_class;
101
102   gobject_class = (GObjectClass *) klass;
103   gstobject_class = (GstObjectClass *) klass;
104   gstringbuffer_class = (GstRingBufferClass *) klass;
105
106   ring_parent_class = g_type_class_peek_parent (klass);
107
108   gobject_class->dispose = gst_osx_ring_buffer_dispose;
109   gobject_class->finalize = gst_osx_ring_buffer_finalize;
110
111   gstringbuffer_class->open_device =
112       GST_DEBUG_FUNCPTR (gst_osx_ring_buffer_open_device);
113   gstringbuffer_class->close_device =
114       GST_DEBUG_FUNCPTR (gst_osx_ring_buffer_close_device);
115   gstringbuffer_class->acquire =
116       GST_DEBUG_FUNCPTR (gst_osx_ring_buffer_acquire);
117   gstringbuffer_class->release =
118       GST_DEBUG_FUNCPTR (gst_osx_ring_buffer_release);
119   gstringbuffer_class->start = GST_DEBUG_FUNCPTR (gst_osx_ring_buffer_start);
120   gstringbuffer_class->pause = GST_DEBUG_FUNCPTR (gst_osx_ring_buffer_pause);
121   gstringbuffer_class->resume = GST_DEBUG_FUNCPTR (gst_osx_ring_buffer_start);
122   gstringbuffer_class->stop = GST_DEBUG_FUNCPTR (gst_osx_ring_buffer_stop);
123
124   gstringbuffer_class->delay = GST_DEBUG_FUNCPTR (gst_osx_ring_buffer_delay);
125
126   GST_DEBUG ("osx ring buffer class init");
127 }
128
129 static void
130 gst_osx_ring_buffer_init (GstOsxRingBuffer * ringbuffer,
131     GstOsxRingBufferClass * g_class)
132 {
133   /* Nothing to do right now */
134 }
135
136 static void
137 gst_osx_ring_buffer_dispose (GObject * object)
138 {
139   G_OBJECT_CLASS (ring_parent_class)->dispose (object);
140 }
141
142 static void
143 gst_osx_ring_buffer_finalize (GObject * object)
144 {
145   G_OBJECT_CLASS (ring_parent_class)->finalize (object);
146 }
147
148 static AudioUnit
149 gst_osx_ring_buffer_create_audio_unit (GstOsxRingBuffer * osxbuf,
150     gboolean input, AudioDeviceID device_id)
151 {
152   ComponentDescription desc;
153   Component comp;
154   OSStatus status;
155   AudioUnit unit;
156   UInt32 enableIO;
157
158   /* Create a HALOutput AudioUnit.
159    * This is the lowest-level output API that is actually sensibly usable
160    * (the lower level ones require that you do channel-remapping yourself,
161    * and the CoreAudio channel mapping is sufficiently complex that doing
162    * so would be very difficult)
163    *
164    * Note that for input we request an output unit even though we will do
165    * input with it: http://developer.apple.com/technotes/tn2002/tn2091.html
166    */
167   desc.componentType = kAudioUnitType_Output;
168   desc.componentSubType = kAudioUnitSubType_HALOutput;
169   desc.componentManufacturer = kAudioUnitManufacturer_Apple;
170   desc.componentFlags = 0;
171   desc.componentFlagsMask = 0;
172
173   comp = FindNextComponent (NULL, &desc);
174   if (comp == NULL) {
175     GST_WARNING_OBJECT (osxbuf, "Couldn't find HALOutput component");
176     return NULL;
177   }
178
179   status = OpenAComponent (comp, &unit);
180
181   if (status) {
182     GST_WARNING_OBJECT (osxbuf, "Couldn't open HALOutput component");
183     return NULL;
184   }
185
186   if (input) {
187     /* enable input */
188     enableIO = 1;
189     status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1,   /* 1 = input element */
190         &enableIO, sizeof (enableIO));
191
192     if (status) {
193       CloseComponent (unit);
194       GST_WARNING_OBJECT (osxbuf, "Failed to enable input: %lx",
195           (gulong) status);
196       return NULL;
197     }
198
199     /* disable output */
200     enableIO = 0;
201     status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0,  /* 0 = output element */
202         &enableIO, sizeof (enableIO));
203
204     if (status) {
205       CloseComponent (unit);
206       GST_WARNING_OBJECT (osxbuf, "Failed to disable output: %lx",
207           (gulong) status);
208       return NULL;
209     }
210   }
211
212   /* Specify which device we're using. */
213   GST_DEBUG_OBJECT (osxbuf, "Setting device to %d", (int) device_id);
214   status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0,       /* N/A for global */
215       &device_id, sizeof (AudioDeviceID));
216
217   if (status) {
218     CloseComponent (unit);
219     GST_WARNING_OBJECT (osxbuf, "Failed to set device: %lx", (gulong) status);
220     return NULL;
221   }
222
223   GST_DEBUG_OBJECT (osxbuf, "Create HALOutput AudioUnit: %p", unit);
224
225   return unit;
226 }
227
228 static gboolean
229 gst_osx_ring_buffer_open_device (GstRingBuffer * buf)
230 {
231   GstOsxRingBuffer *osxbuf;
232   GstOsxAudioSink *sink;
233   GstOsxAudioSrc *src;
234   AudioStreamBasicDescription asbd_in;
235   OSStatus status;
236   UInt32 propertySize;
237
238   osxbuf = GST_OSX_RING_BUFFER (buf);
239   sink = NULL;
240   src = NULL;
241
242   osxbuf->audiounit = gst_osx_ring_buffer_create_audio_unit (osxbuf,
243       osxbuf->is_src, osxbuf->device_id);
244
245   if (osxbuf->is_src) {
246     src = GST_OSX_AUDIO_SRC (GST_OBJECT_PARENT (buf));
247
248     propertySize = sizeof (asbd_in);
249     status = AudioUnitGetProperty (osxbuf->audiounit,
250         kAudioUnitProperty_StreamFormat,
251         kAudioUnitScope_Input, 1, &asbd_in, &propertySize);
252
253     if (status) {
254       CloseComponent (osxbuf->audiounit);
255       osxbuf->audiounit = NULL;
256       GST_WARNING_OBJECT (osxbuf, "Unable to obtain device properties: %lx",
257           (gulong) status);
258       return FALSE;
259     }
260
261     src->deviceChannels = asbd_in.mChannelsPerFrame;
262   } else {
263     sink = GST_OSX_AUDIO_SINK (GST_OBJECT_PARENT (buf));
264
265     /* needed for the sink's volume control */
266     sink->audiounit = osxbuf->audiounit;
267   }
268
269   return TRUE;
270 }
271
272 static gboolean
273 gst_osx_ring_buffer_close_device (GstRingBuffer * buf)
274 {
275   GstOsxRingBuffer *osxbuf;
276   osxbuf = GST_OSX_RING_BUFFER (buf);
277
278   CloseComponent (osxbuf->audiounit);
279   osxbuf->audiounit = NULL;
280
281   return TRUE;
282 }
283
284 static AudioChannelLabel
285 gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition
286     position, int channel)
287 {
288   switch (position) {
289     case GST_AUDIO_CHANNEL_POSITION_NONE:
290       return kAudioChannelLabel_Discrete_0 | channel;
291     case GST_AUDIO_CHANNEL_POSITION_FRONT_MONO:
292       return kAudioChannelLabel_Mono;
293     case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT:
294       return kAudioChannelLabel_Left;
295     case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT:
296       return kAudioChannelLabel_Right;
297     case GST_AUDIO_CHANNEL_POSITION_REAR_CENTER:
298       return kAudioChannelLabel_CenterSurround;
299     case GST_AUDIO_CHANNEL_POSITION_REAR_LEFT:
300       return kAudioChannelLabel_LeftSurround;
301     case GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT:
302       return kAudioChannelLabel_RightSurround;
303     case GST_AUDIO_CHANNEL_POSITION_LFE:
304       return kAudioChannelLabel_LFEScreen;
305     case GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER:
306       return kAudioChannelLabel_Center;
307     case GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
308       return kAudioChannelLabel_Center; // ???
309     case GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
310       return kAudioChannelLabel_Center; // ???
311     case GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT:
312       return kAudioChannelLabel_LeftSurroundDirect;
313     case GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT:
314       return kAudioChannelLabel_RightSurroundDirect;
315     default:
316       return kAudioChannelLabel_Unknown;
317   }
318 }
319
320 static gboolean
321 gst_osx_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
322 {
323   /* Configure the output stream and allocate ringbuffer memory */
324   GstOsxRingBuffer *osxbuf;
325   AudioStreamBasicDescription format;
326   AudioChannelLayout *layout = NULL;
327   OSStatus status;
328   UInt32 propertySize;
329   int layoutSize;
330   int element;
331   int i;
332   AudioUnitScope scope;
333   gboolean ret = FALSE;
334   GstStructure *structure;
335   GstAudioChannelPosition *positions;
336   UInt32 frameSize;
337
338   osxbuf = GST_OSX_RING_BUFFER (buf);
339
340   /* Fill out the audio description we're going to be using */
341   format.mFormatID = kAudioFormatLinearPCM;
342   format.mSampleRate = (double) spec->rate;
343   format.mChannelsPerFrame = spec->channels;
344   format.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
345   format.mBytesPerFrame = spec->channels * sizeof (float);
346   format.mBitsPerChannel = sizeof (float) * 8;
347   format.mBytesPerPacket = spec->channels * sizeof (float);
348   format.mFramesPerPacket = 1;
349   format.mReserved = 0;
350
351   /* Describe channels */
352   layoutSize = sizeof (AudioChannelLayout) +
353       spec->channels * sizeof (AudioChannelDescription);
354   layout = g_malloc (layoutSize);
355
356   structure = gst_caps_get_structure (spec->caps, 0);
357   positions = gst_audio_get_channel_positions (structure);
358
359   layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
360   layout->mChannelBitmap = 0;   /* Not used */
361   layout->mNumberChannelDescriptions = spec->channels;
362   for (i = 0; i < spec->channels; i++) {
363     if (positions) {
364       layout->mChannelDescriptions[i].mChannelLabel =
365           gst_audio_channel_position_to_coreaudio_channel_label (positions[i],
366           i);
367     } else {
368       /* Discrete channel numbers are ORed into this */
369       layout->mChannelDescriptions[i].mChannelLabel =
370           kAudioChannelLabel_Discrete_0 | i;
371     }
372
373     /* Others unused */
374     layout->mChannelDescriptions[i].mChannelFlags = 0;
375     layout->mChannelDescriptions[i].mCoordinates[0] = 0.f;
376     layout->mChannelDescriptions[i].mCoordinates[1] = 0.f;
377     layout->mChannelDescriptions[i].mCoordinates[2] = 0.f;
378   }
379
380   if (positions) {
381     g_free (positions);
382     positions = NULL;
383   }
384
385   GST_LOG_OBJECT (osxbuf, "Format: %x, %f, %u, %x, %d, %d, %d, %d, %d",
386       (unsigned int) format.mFormatID,
387       format.mSampleRate,
388       (unsigned int) format.mChannelsPerFrame,
389       (unsigned int) format.mFormatFlags,
390       (unsigned int) format.mBytesPerFrame,
391       (unsigned int) format.mBitsPerChannel,
392       (unsigned int) format.mBytesPerPacket,
393       (unsigned int) format.mFramesPerPacket, (unsigned int) format.mReserved);
394
395   GST_DEBUG_OBJECT (osxbuf, "Setting format for AudioUnit");
396
397   scope = osxbuf->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input;
398   element = osxbuf->is_src ? 1 : 0;
399
400   propertySize = sizeof (format);
401   status = AudioUnitSetProperty (osxbuf->audiounit,
402       kAudioUnitProperty_StreamFormat, scope, element, &format, propertySize);
403
404   if (status) {
405     GST_WARNING_OBJECT (osxbuf, "Failed to set audio description: %lx",
406         (gulong) status);
407     goto done;
408   }
409
410   status = AudioUnitSetProperty (osxbuf->audiounit,
411       kAudioUnitProperty_AudioChannelLayout,
412       scope, element, layout, layoutSize);
413   if (status) {
414     GST_WARNING_OBJECT (osxbuf, "Failed to set output channel layout: %lx",
415         (gulong) status);
416     goto done;
417   }
418
419   spec->segsize =
420       (spec->latency_time * spec->rate / G_USEC_PER_SEC) *
421       spec->bytes_per_sample;
422   spec->segtotal = spec->buffer_time / spec->latency_time;
423
424   /* create AudioBufferList needed for recording */
425   if (osxbuf->is_src) {
426     propertySize = sizeof (frameSize);
427     status = AudioUnitGetProperty (osxbuf->audiounit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0,   /* N/A for global */
428         &frameSize, &propertySize);
429
430     if (status) {
431       GST_WARNING_OBJECT (osxbuf, "Failed to get frame size: %lx",
432           (gulong) status);
433       goto done;
434     }
435
436     osxbuf->recBufferList = buffer_list_alloc (format.mChannelsPerFrame,
437         frameSize * format.mBytesPerFrame);
438   }
439
440   buf->data = gst_buffer_new_and_alloc (spec->segtotal * spec->segsize);
441   memset (GST_BUFFER_DATA (buf->data), 0, GST_BUFFER_SIZE (buf->data));
442
443   osxbuf->segoffset = 0;
444
445   status = AudioUnitInitialize (osxbuf->audiounit);
446   if (status) {
447     gst_buffer_unref (buf->data);
448     buf->data = NULL;
449
450     if (osxbuf->recBufferList) {
451       buffer_list_free (osxbuf->recBufferList);
452       osxbuf->recBufferList = NULL;
453     }
454
455     GST_WARNING_OBJECT (osxbuf,
456         "Failed to initialise AudioUnit: %d", (int) status);
457     goto done;
458   }
459
460   GST_DEBUG_OBJECT (osxbuf, "osx ring buffer acquired");
461
462   ret = TRUE;
463
464 done:
465   g_free (layout);
466   return ret;
467 }
468
469 static gboolean
470 gst_osx_ring_buffer_release (GstRingBuffer * buf)
471 {
472   GstOsxRingBuffer *osxbuf;
473
474   osxbuf = GST_OSX_RING_BUFFER (buf);
475
476   AudioUnitUninitialize (osxbuf->audiounit);
477
478   gst_buffer_unref (buf->data);
479   buf->data = NULL;
480
481   if (osxbuf->recBufferList) {
482     buffer_list_free (osxbuf->recBufferList);
483     osxbuf->recBufferList = NULL;
484   }
485
486   return TRUE;
487 }
488
489 static void
490 gst_osx_ring_buffer_remove_render_callback (GstOsxRingBuffer * osxbuf)
491 {
492   AURenderCallbackStruct input;
493   OSStatus status;
494
495   /* Deactivate the render callback by calling SetRenderCallback with a NULL
496    * inputProc.
497    */
498   input.inputProc = NULL;
499   input.inputProcRefCon = NULL;
500
501   status = AudioUnitSetProperty (osxbuf->audiounit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0,    /* N/A for global */
502       &input, sizeof (input));
503
504   if (status) {
505     GST_WARNING_OBJECT (osxbuf, "Failed to remove render callback");
506   }
507
508   /* Remove the RenderNotify too */
509   status = AudioUnitRemoveRenderNotify (osxbuf->audiounit,
510       (AURenderCallback) gst_osx_ring_buffer_render_notify, osxbuf);
511
512   if (status) {
513     GST_WARNING_OBJECT (osxbuf, "Failed to remove render notify callback");
514   }
515
516   /* We're deactivated.. */
517   osxbuf->io_proc_needs_deactivation = FALSE;
518   osxbuf->io_proc_active = FALSE;
519 }
520
521 static OSStatus
522 gst_osx_ring_buffer_render_notify (GstOsxRingBuffer * osxbuf,
523     AudioUnitRenderActionFlags * ioActionFlags,
524     const AudioTimeStamp * inTimeStamp,
525     unsigned int inBusNumber,
526     unsigned int inNumberFrames, AudioBufferList * ioData)
527 {
528   /* Before rendering a frame, we get the PreRender notification.
529    * Here, we detach the RenderCallback if we've been paused.
530    *
531    * This is necessary (rather than just directly detaching it) to work
532    * around some thread-safety issues in CoreAudio
533    */
534   if ((*ioActionFlags) & kAudioUnitRenderAction_PreRender) {
535     if (osxbuf->io_proc_needs_deactivation) {
536       gst_osx_ring_buffer_remove_render_callback (osxbuf);
537     }
538   }
539
540   return noErr;
541 }
542
543 static gboolean
544 gst_osx_ring_buffer_start (GstRingBuffer * buf)
545 {
546   OSStatus status;
547   GstOsxRingBuffer *osxbuf;
548   AURenderCallbackStruct input;
549   AudioUnitPropertyID callback_type;
550
551   osxbuf = GST_OSX_RING_BUFFER (buf);
552
553   GST_DEBUG ("osx ring buffer start ioproc: 0x%p device_id %lu",
554       osxbuf->element->io_proc, (gulong) osxbuf->device_id);
555   if (!osxbuf->io_proc_active) {
556     callback_type = osxbuf->is_src ?
557         kAudioOutputUnitProperty_SetInputCallback :
558         kAudioUnitProperty_SetRenderCallback;
559
560     input.inputProc = (AURenderCallback) osxbuf->element->io_proc;
561     input.inputProcRefCon = osxbuf;
562
563     status = AudioUnitSetProperty (osxbuf->audiounit, callback_type, kAudioUnitScope_Global, 0, /* N/A for global */
564         &input, sizeof (input));
565
566     if (status) {
567       GST_WARNING ("AudioUnitSetProperty returned %d", (int) status);
568       return FALSE;
569     }
570     // ### does it make sense to do this notify stuff for input mode?
571     status = AudioUnitAddRenderNotify (osxbuf->audiounit,
572         (AURenderCallback) gst_osx_ring_buffer_render_notify, osxbuf);
573
574     if (status) {
575       GST_WARNING ("AudioUnitAddRenderNotify returned %d", (int) status);
576       return FALSE;
577     }
578
579     osxbuf->io_proc_active = TRUE;
580   }
581
582   osxbuf->io_proc_needs_deactivation = FALSE;
583
584   status = AudioOutputUnitStart (osxbuf->audiounit);
585   if (status) {
586     GST_WARNING ("AudioOutputUnitStart returned %d", (int) status);
587     return FALSE;
588   }
589   return TRUE;
590 }
591
592 // ###
593 static gboolean
594 gst_osx_ring_buffer_pause (GstRingBuffer * buf)
595 {
596   GstOsxRingBuffer *osxbuf = GST_OSX_RING_BUFFER (buf);
597
598   GST_DEBUG ("osx ring buffer pause ioproc: 0x%p device_id %lu",
599       osxbuf->element->io_proc, (gulong) osxbuf->device_id);
600   if (osxbuf->io_proc_active) {
601     /* CoreAudio isn't threadsafe enough to do this here; we must deactivate
602      * the render callback elsewhere. See:
603      *   http://lists.apple.com/archives/Coreaudio-api/2006/Mar/msg00010.html
604      */
605     osxbuf->io_proc_needs_deactivation = TRUE;
606   }
607   return TRUE;
608 }
609
610 // ###
611 static gboolean
612 gst_osx_ring_buffer_stop (GstRingBuffer * buf)
613 {
614   OSErr status;
615   GstOsxRingBuffer *osxbuf;
616
617   osxbuf = GST_OSX_RING_BUFFER (buf);
618
619   GST_DEBUG ("osx ring buffer stop ioproc: 0x%p device_id %lu",
620       osxbuf->element->io_proc, (gulong) osxbuf->device_id);
621
622   status = AudioOutputUnitStop (osxbuf->audiounit);
623   if (status)
624     GST_WARNING ("AudioOutputUnitStop returned %d", (int) status);
625
626   // ###: why is it okay to directly remove from here but not from pause() ?
627   if (osxbuf->io_proc_active) {
628     gst_osx_ring_buffer_remove_render_callback (osxbuf);
629   }
630   return TRUE;
631 }
632
633 static guint
634 gst_osx_ring_buffer_delay (GstRingBuffer * buf)
635 {
636   double latency;
637   UInt32 size = sizeof (double);
638   GstOsxRingBuffer *osxbuf;
639   OSStatus status;
640   guint samples;
641
642   osxbuf = GST_OSX_RING_BUFFER (buf);
643
644   status = AudioUnitGetProperty (osxbuf->audiounit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0,      /* N/A for global */
645       &latency, &size);
646
647   if (status) {
648     GST_WARNING_OBJECT (buf, "Failed to get latency: %d", (int) status);
649     return 0;
650   }
651
652   samples = latency * GST_RING_BUFFER (buf)->spec.rate;
653   GST_DEBUG_OBJECT (buf, "Got latency: %f seconds -> %d samples", latency,
654       samples);
655   return samples;
656 }
657
658 static AudioBufferList *
659 buffer_list_alloc (int channels, int size)
660 {
661   AudioBufferList *list;
662   int total_size;
663   int n;
664
665   total_size = sizeof (AudioBufferList) + 1 * sizeof (AudioBuffer);
666   list = (AudioBufferList *) g_malloc (total_size);
667
668   list->mNumberBuffers = 1;
669   for (n = 0; n < (int) list->mNumberBuffers; ++n) {
670     list->mBuffers[n].mNumberChannels = channels;
671     list->mBuffers[n].mDataByteSize = size;
672     list->mBuffers[n].mData = g_malloc (size);
673   }
674
675   return list;
676 }
677
678 static void
679 buffer_list_free (AudioBufferList * list)
680 {
681   int n;
682
683   for (n = 0; n < (int) list->mNumberBuffers; ++n) {
684     if (list->mBuffers[n].mData)
685       g_free (list->mBuffers[n].mData);
686   }
687
688   g_free (list);
689 }