3 * Copyright (C) 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
4 * Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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.
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
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.
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.
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.
45 #include <CoreAudio/CoreAudio.h>
46 #include <CoreServices/CoreServices.h>
48 #include <gst/audio/multichannel.h>
49 #include "gstosxringbuffer.h"
50 #include "gstosxaudiosink.h"
51 #include "gstosxaudiosrc.h"
53 GST_DEBUG_CATEGORY_STATIC (osx_audio_debug);
54 #define GST_CAT_DEFAULT osx_audio_debug
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);
61 static gboolean gst_osx_ring_buffer_acquire (GstRingBuffer * buf,
62 GstRingBufferSpec * spec);
63 static gboolean gst_osx_ring_buffer_release (GstRingBuffer * buf);
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;
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);
76 static AudioBufferList *buffer_list_alloc (int channels, int size);
77 static void buffer_list_free (AudioBufferList * list);
80 gst_osx_ring_buffer_do_init (GType type)
82 GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0,
83 "OSX Audio Elements");
86 GST_BOILERPLATE_FULL (GstOsxRingBuffer, gst_osx_ring_buffer, GstRingBuffer,
87 GST_TYPE_RING_BUFFER, gst_osx_ring_buffer_do_init);
90 gst_osx_ring_buffer_base_init (gpointer g_class)
92 /* Nothing to do right now */
96 gst_osx_ring_buffer_class_init (GstOsxRingBufferClass * klass)
98 GObjectClass *gobject_class;
99 GstObjectClass *gstobject_class;
100 GstRingBufferClass *gstringbuffer_class;
102 gobject_class = (GObjectClass *) klass;
103 gstobject_class = (GstObjectClass *) klass;
104 gstringbuffer_class = (GstRingBufferClass *) klass;
106 ring_parent_class = g_type_class_peek_parent (klass);
108 gobject_class->dispose = gst_osx_ring_buffer_dispose;
109 gobject_class->finalize = gst_osx_ring_buffer_finalize;
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);
124 gstringbuffer_class->delay = GST_DEBUG_FUNCPTR (gst_osx_ring_buffer_delay);
126 GST_DEBUG ("osx ring buffer class init");
130 gst_osx_ring_buffer_init (GstOsxRingBuffer * ringbuffer,
131 GstOsxRingBufferClass * g_class)
133 /* Nothing to do right now */
137 gst_osx_ring_buffer_dispose (GObject * object)
139 G_OBJECT_CLASS (ring_parent_class)->dispose (object);
143 gst_osx_ring_buffer_finalize (GObject * object)
145 G_OBJECT_CLASS (ring_parent_class)->finalize (object);
149 gst_osx_ring_buffer_create_audio_unit (GstOsxRingBuffer * osxbuf,
150 gboolean input, AudioDeviceID device_id)
152 ComponentDescription desc;
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)
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
167 desc.componentType = kAudioUnitType_Output;
168 desc.componentSubType = kAudioUnitSubType_HALOutput;
169 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
170 desc.componentFlags = 0;
171 desc.componentFlagsMask = 0;
173 comp = FindNextComponent (NULL, &desc);
175 GST_WARNING_OBJECT (osxbuf, "Couldn't find HALOutput component");
179 status = OpenAComponent (comp, &unit);
182 GST_WARNING_OBJECT (osxbuf, "Couldn't open HALOutput component");
189 status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, /* 1 = input element */
190 &enableIO, sizeof (enableIO));
193 CloseComponent (unit);
194 GST_WARNING_OBJECT (osxbuf, "Failed to enable input: %lx",
201 status = AudioUnitSetProperty (unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, /* 0 = output element */
202 &enableIO, sizeof (enableIO));
205 CloseComponent (unit);
206 GST_WARNING_OBJECT (osxbuf, "Failed to disable output: %lx",
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));
218 CloseComponent (unit);
219 GST_WARNING_OBJECT (osxbuf, "Failed to set device: %lx", (gulong) status);
223 GST_DEBUG_OBJECT (osxbuf, "Create HALOutput AudioUnit: %p", unit);
229 gst_osx_ring_buffer_open_device (GstRingBuffer * buf)
231 GstOsxRingBuffer *osxbuf;
232 GstOsxAudioSink *sink;
234 AudioStreamBasicDescription asbd_in;
238 osxbuf = GST_OSX_RING_BUFFER (buf);
242 osxbuf->audiounit = gst_osx_ring_buffer_create_audio_unit (osxbuf,
243 osxbuf->is_src, osxbuf->device_id);
245 if (osxbuf->is_src) {
246 src = GST_OSX_AUDIO_SRC (GST_OBJECT_PARENT (buf));
248 propertySize = sizeof (asbd_in);
249 status = AudioUnitGetProperty (osxbuf->audiounit,
250 kAudioUnitProperty_StreamFormat,
251 kAudioUnitScope_Input, 1, &asbd_in, &propertySize);
254 CloseComponent (osxbuf->audiounit);
255 osxbuf->audiounit = NULL;
256 GST_WARNING_OBJECT (osxbuf, "Unable to obtain device properties: %lx",
261 src->deviceChannels = asbd_in.mChannelsPerFrame;
263 sink = GST_OSX_AUDIO_SINK (GST_OBJECT_PARENT (buf));
265 /* needed for the sink's volume control */
266 sink->audiounit = osxbuf->audiounit;
273 gst_osx_ring_buffer_close_device (GstRingBuffer * buf)
275 GstOsxRingBuffer *osxbuf;
276 osxbuf = GST_OSX_RING_BUFFER (buf);
278 CloseComponent (osxbuf->audiounit);
279 osxbuf->audiounit = NULL;
284 static AudioChannelLabel
285 gst_audio_channel_position_to_coreaudio_channel_label (GstAudioChannelPosition
286 position, int channel)
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;
316 return kAudioChannelLabel_Unknown;
321 gst_osx_ring_buffer_acquire (GstRingBuffer * buf, GstRingBufferSpec * spec)
323 /* Configure the output stream and allocate ringbuffer memory */
324 GstOsxRingBuffer *osxbuf;
325 AudioStreamBasicDescription format;
326 AudioChannelLayout *layout = NULL;
332 AudioUnitScope scope;
333 gboolean ret = FALSE;
334 GstStructure *structure;
335 GstAudioChannelPosition *positions;
338 osxbuf = GST_OSX_RING_BUFFER (buf);
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;
351 /* Describe channels */
352 layoutSize = sizeof (AudioChannelLayout) +
353 spec->channels * sizeof (AudioChannelDescription);
354 layout = g_malloc (layoutSize);
356 structure = gst_caps_get_structure (spec->caps, 0);
357 positions = gst_audio_get_channel_positions (structure);
359 layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
360 layout->mChannelBitmap = 0; /* Not used */
361 layout->mNumberChannelDescriptions = spec->channels;
362 for (i = 0; i < spec->channels; i++) {
364 layout->mChannelDescriptions[i].mChannelLabel =
365 gst_audio_channel_position_to_coreaudio_channel_label (positions[i],
368 /* Discrete channel numbers are ORed into this */
369 layout->mChannelDescriptions[i].mChannelLabel =
370 kAudioChannelLabel_Discrete_0 | i;
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;
385 GST_LOG_OBJECT (osxbuf, "Format: %x, %f, %u, %x, %d, %d, %d, %d, %d",
386 (unsigned int) format.mFormatID,
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);
395 GST_DEBUG_OBJECT (osxbuf, "Setting format for AudioUnit");
397 scope = osxbuf->is_src ? kAudioUnitScope_Output : kAudioUnitScope_Input;
398 element = osxbuf->is_src ? 1 : 0;
400 propertySize = sizeof (format);
401 status = AudioUnitSetProperty (osxbuf->audiounit,
402 kAudioUnitProperty_StreamFormat, scope, element, &format, propertySize);
405 GST_WARNING_OBJECT (osxbuf, "Failed to set audio description: %lx",
410 status = AudioUnitSetProperty (osxbuf->audiounit,
411 kAudioUnitProperty_AudioChannelLayout,
412 scope, element, layout, layoutSize);
414 GST_WARNING_OBJECT (osxbuf, "Failed to set output channel layout: %lx",
420 (spec->latency_time * spec->rate / G_USEC_PER_SEC) *
421 spec->bytes_per_sample;
422 spec->segtotal = spec->buffer_time / spec->latency_time;
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);
431 GST_WARNING_OBJECT (osxbuf, "Failed to get frame size: %lx",
436 osxbuf->recBufferList = buffer_list_alloc (format.mChannelsPerFrame,
437 frameSize * format.mBytesPerFrame);
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));
443 osxbuf->segoffset = 0;
445 status = AudioUnitInitialize (osxbuf->audiounit);
447 gst_buffer_unref (buf->data);
450 if (osxbuf->recBufferList) {
451 buffer_list_free (osxbuf->recBufferList);
452 osxbuf->recBufferList = NULL;
455 GST_WARNING_OBJECT (osxbuf,
456 "Failed to initialise AudioUnit: %d", (int) status);
460 GST_DEBUG_OBJECT (osxbuf, "osx ring buffer acquired");
470 gst_osx_ring_buffer_release (GstRingBuffer * buf)
472 GstOsxRingBuffer *osxbuf;
474 osxbuf = GST_OSX_RING_BUFFER (buf);
476 AudioUnitUninitialize (osxbuf->audiounit);
478 gst_buffer_unref (buf->data);
481 if (osxbuf->recBufferList) {
482 buffer_list_free (osxbuf->recBufferList);
483 osxbuf->recBufferList = NULL;
490 gst_osx_ring_buffer_remove_render_callback (GstOsxRingBuffer * osxbuf)
492 AURenderCallbackStruct input;
495 /* Deactivate the render callback by calling SetRenderCallback with a NULL
498 input.inputProc = NULL;
499 input.inputProcRefCon = NULL;
501 status = AudioUnitSetProperty (osxbuf->audiounit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, /* N/A for global */
502 &input, sizeof (input));
505 GST_WARNING_OBJECT (osxbuf, "Failed to remove render callback");
508 /* Remove the RenderNotify too */
509 status = AudioUnitRemoveRenderNotify (osxbuf->audiounit,
510 (AURenderCallback) gst_osx_ring_buffer_render_notify, osxbuf);
513 GST_WARNING_OBJECT (osxbuf, "Failed to remove render notify callback");
516 /* We're deactivated.. */
517 osxbuf->io_proc_needs_deactivation = FALSE;
518 osxbuf->io_proc_active = FALSE;
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)
528 /* Before rendering a frame, we get the PreRender notification.
529 * Here, we detach the RenderCallback if we've been paused.
531 * This is necessary (rather than just directly detaching it) to work
532 * around some thread-safety issues in CoreAudio
534 if ((*ioActionFlags) & kAudioUnitRenderAction_PreRender) {
535 if (osxbuf->io_proc_needs_deactivation) {
536 gst_osx_ring_buffer_remove_render_callback (osxbuf);
544 gst_osx_ring_buffer_start (GstRingBuffer * buf)
547 GstOsxRingBuffer *osxbuf;
548 AURenderCallbackStruct input;
549 AudioUnitPropertyID callback_type;
551 osxbuf = GST_OSX_RING_BUFFER (buf);
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;
560 input.inputProc = (AURenderCallback) osxbuf->element->io_proc;
561 input.inputProcRefCon = osxbuf;
563 status = AudioUnitSetProperty (osxbuf->audiounit, callback_type, kAudioUnitScope_Global, 0, /* N/A for global */
564 &input, sizeof (input));
567 GST_WARNING ("AudioUnitSetProperty returned %d", (int) status);
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);
575 GST_WARNING ("AudioUnitAddRenderNotify returned %d", (int) status);
579 osxbuf->io_proc_active = TRUE;
582 osxbuf->io_proc_needs_deactivation = FALSE;
584 status = AudioOutputUnitStart (osxbuf->audiounit);
586 GST_WARNING ("AudioOutputUnitStart returned %d", (int) status);
594 gst_osx_ring_buffer_pause (GstRingBuffer * buf)
596 GstOsxRingBuffer *osxbuf = GST_OSX_RING_BUFFER (buf);
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
605 osxbuf->io_proc_needs_deactivation = TRUE;
612 gst_osx_ring_buffer_stop (GstRingBuffer * buf)
615 GstOsxRingBuffer *osxbuf;
617 osxbuf = GST_OSX_RING_BUFFER (buf);
619 GST_DEBUG ("osx ring buffer stop ioproc: 0x%p device_id %lu",
620 osxbuf->element->io_proc, (gulong) osxbuf->device_id);
622 status = AudioOutputUnitStop (osxbuf->audiounit);
624 GST_WARNING ("AudioOutputUnitStop returned %d", (int) status);
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);
634 gst_osx_ring_buffer_delay (GstRingBuffer * buf)
637 UInt32 size = sizeof (double);
638 GstOsxRingBuffer *osxbuf;
642 osxbuf = GST_OSX_RING_BUFFER (buf);
644 status = AudioUnitGetProperty (osxbuf->audiounit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, /* N/A for global */
648 GST_WARNING_OBJECT (buf, "Failed to get latency: %d", (int) status);
652 samples = latency * GST_RING_BUFFER (buf)->spec.rate;
653 GST_DEBUG_OBJECT (buf, "Got latency: %f seconds -> %d samples", latency,
658 static AudioBufferList *
659 buffer_list_alloc (int channels, int size)
661 AudioBufferList *list;
665 total_size = sizeof (AudioBufferList) + 1 * sizeof (AudioBuffer);
666 list = (AudioBufferList *) g_malloc (total_size);
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);
679 buffer_list_free (AudioBufferList * list)
683 for (n = 0; n < (int) list->mNumberBuffers; ++n) {
684 if (list->mBuffers[n].mData)
685 g_free (list->mBuffers[n].mData);