diff options
Diffstat (limited to 'sys/osxaudio/gstosxaudiosink.c')
-rw-r--r-- | sys/osxaudio/gstosxaudiosink.c | 273 |
1 files changed, 85 insertions, 188 deletions
diff --git a/sys/osxaudio/gstosxaudiosink.c b/sys/osxaudio/gstosxaudiosink.c index 46d229b5..aec8874d 100644 --- a/sys/osxaudio/gstosxaudiosink.c +++ b/sys/osxaudio/gstosxaudiosink.c @@ -1,8 +1,8 @@ /* * GStreamer - * Copyright 2005,2006 Zaheer Abbas Merali <zaheerabbas at merali dot org> - * Copyright 2007 Pioneers of the Inevitable <songbird@songbirdnest.com> - * + * Copyright (C) 2005,2006 Zaheer Abbas Merali <zaheerabbas at merali dot org> + * Copyright (C) 2007,2008 Pioneers of the Inevitable <songbird@songbirdnest.com> + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation @@ -41,8 +41,8 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * - * The development of this code was made possible due to the involvement of - * Pioneers of the Inevitable, the creators of the Songbird Music player. + * The development of this code was made possible due to the involvement of + * Pioneers of the Inevitable, the creators of the Songbird Music player * */ @@ -67,8 +67,6 @@ #include <CoreAudio/CoreAudio.h> #include <CoreAudio/AudioHardware.h> #include "gstosxaudiosink.h" -#include "gstosxaudiosrc.h" - #include "gstosxaudioelement.h" GST_DEBUG_CATEGORY_STATIC (osx_audiosink_debug); @@ -90,9 +88,12 @@ enum enum { ARG_0, - ARG_DEVICE + ARG_DEVICE, + ARG_VOLUME }; +#define DEFAULT_VOLUME 1.0 + static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -101,27 +102,28 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", "signed = (boolean) { TRUE }, " "width = (int) 32, " "depth = (int) 32, " - "rate = (int) [1, MAX], " "channels = (int) [1, 2]") + "rate = (int) [1, MAX], " "channels = (int) [1, MAX]") ); static void gst_osx_audio_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_osx_audio_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static GstCaps *gst_osx_audio_sink_getcaps (GstBaseSink * sink); static GstRingBuffer *gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * sink); static void gst_osx_audio_sink_osxelement_init (gpointer g_iface, gpointer iface_data); -OSStatus gst_osx_audio_sink_io_proc (AudioDeviceID inDevice, - const AudioTimeStamp * inNow, const AudioBufferList * inInputData, - const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData, - const AudioTimeStamp * inOutputTime, void *inClientData); static void gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink); +static void gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink); + +static OSStatus gst_osx_audio_sink_io_proc (GstOsxRingBuffer * buf, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * bufferList); static void -gst_osx_audio_sink_osxelement_do_init (GType type) +gst_osx_audio_sink_do_init (GType type) { static const GInterfaceInfo osxelement_info = { gst_osx_audio_sink_osxelement_init, @@ -137,8 +139,7 @@ gst_osx_audio_sink_osxelement_do_init (GType type) } GST_BOILERPLATE_FULL (GstOsxAudioSink, gst_osx_audio_sink, GstBaseAudioSink, - GST_TYPE_BASE_AUDIO_SINK, gst_osx_audio_sink_osxelement_do_init); - + GST_TYPE_BASE_AUDIO_SINK, gst_osx_audio_sink_do_init); static void gst_osx_audio_sink_base_init (gpointer g_class) @@ -151,7 +152,6 @@ gst_osx_audio_sink_base_init (gpointer g_class) gst_element_class_set_details (element_class, &gst_osx_audio_sink_details); } -/* initialize the plugin's class */ static void gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass) { @@ -176,25 +176,21 @@ gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass) g_param_spec_int ("device", "Device ID", "Device ID of output device", 0, G_MAXINT, 0, G_PARAM_READWRITE)); - gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_osx_audio_sink_getcaps); + g_object_class_install_property (gobject_class, ARG_VOLUME, + g_param_spec_double ("volume", "Volume", "Volume of this stream", + 0, 1.0, 1.0, G_PARAM_READWRITE)); + gstbaseaudiosink_class->create_ringbuffer = GST_DEBUG_FUNCPTR (gst_osx_audio_sink_create_ringbuffer); - } -/* initialize the new element - * instantiate pads and add them to element - * set functions - * initialize structure - */ static void gst_osx_audio_sink_init (GstOsxAudioSink * sink, GstOsxAudioSinkClass * gclass) { -/* GstElementClass *klass = GST_ELEMENT_GET_CLASS (sink); */ GST_DEBUG ("Initialising object"); sink->device_id = kAudioDeviceUnknown; - sink->stream_id = kAudioStreamUnknown; + sink->volume = DEFAULT_VOLUME; } static void @@ -207,6 +203,10 @@ gst_osx_audio_sink_set_property (GObject * object, guint prop_id, case ARG_DEVICE: sink->device_id = g_value_get_int (value); break; + case ARG_VOLUME: + sink->volume = g_value_get_double (value); + gst_osx_audio_sink_set_volume (sink); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -222,109 +222,15 @@ gst_osx_audio_sink_get_property (GObject * object, guint prop_id, case ARG_DEVICE: g_value_set_int (value, sink->device_id); break; + case ARG_VOLUME: + g_value_set_double (value, sink->volume); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } -/* GstElement vmethod implementations */ - -/* GstBaseSink vmethod implementations */ -static GstCaps * -gst_osx_audio_sink_getcaps (GstBaseSink * sink) -{ - GstCaps *caps = NULL; - GstOsxAudioSink *osxsink; - OSStatus status; - AudioValueRange *rates = NULL; - UInt32 propertySize; - int i; - gboolean foundFixedRate = FALSE; - GstStructure *structure; - GValue rate_v = { 0 }; - GValue rates_v = { 0 }; - - osxsink = GST_OSX_AUDIO_SINK (sink); - - gst_osx_audio_sink_select_device (osxsink); - - GST_DEBUG_OBJECT (osxsink, "Using device_id %d", (int) osxsink->device_id); - - status = AudioDeviceGetPropertyInfo (osxsink->device_id, 0, /* Master channel */ - FALSE, /* isInput */ - kAudioDevicePropertyAvailableNominalSampleRates, &propertySize, NULL); - - if (status) { - GST_WARNING_OBJECT (osxsink, "Failed to get sample rates size: %ld", - status); - goto done; - } - - GST_DEBUG_OBJECT (osxsink, "Allocating %d bytes for sizes", - (int) propertySize); - rates = g_malloc (propertySize); - - status = AudioDeviceGetProperty (osxsink->device_id, 0, /* Master channel */ - FALSE, /* isInput */ - kAudioDevicePropertyAvailableNominalSampleRates, &propertySize, rates); - - if (status) { - GST_WARNING_OBJECT (osxsink, "Failed to get sample rates: %ld", status); - goto done; - } - - GST_DEBUG_OBJECT (osxsink, "Used %d bytes for sizes", (int) propertySize); - - if (propertySize < sizeof (AudioValueRange)) { - GST_WARNING_OBJECT (osxsink, "Zero sample rates available"); - goto done; - } - - /* Create base caps object, then modify to suit. */ - caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD - (sink))); - structure = gst_caps_get_structure (caps, 0); - - GST_DEBUG - ("Getting available sample rates: Status: %ld number of ranges: %lu", - status, propertySize / sizeof (AudioValueRange)); - - g_value_init (&rates_v, GST_TYPE_LIST); - g_value_init (&rate_v, G_TYPE_INT); - - for (i = 0; i < propertySize / sizeof (AudioValueRange); i++) { - GST_LOG_OBJECT (osxsink, "Range from %f to %f", rates[i].mMinimum, - rates[i].mMaximum); - if (rates[i].mMinimum == rates[i].mMaximum) { - /* For now, we only support these in this form. If there are none - * in this form, we use the first (only) as a range. */ - foundFixedRate = TRUE; - - g_value_set_int (&rate_v, rates[i].mMinimum); - gst_value_list_append_value (&rates_v, &rate_v); - } - } - - g_value_unset (&rate_v); - - if (foundFixedRate) { - gst_structure_set_value (structure, "rate", &rates_v); - } else { - gst_structure_set (structure, "rate", GST_TYPE_INT_RANGE, - rates[0].mMinimum, rates[0].mMaximum, NULL); - } - - g_value_unset (&rates_v); - -done: - if (rates) - g_free (rates); - - return caps; -} - -/* GstBaseAudioSink vmethod implementations */ static GstRingBuffer * gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * sink) { @@ -340,35 +246,57 @@ gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * sink) GST_DEBUG ("osx sink 0x%p element 0x%p ioproc 0x%p", osxsink, GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink), (void *) gst_osx_audio_sink_io_proc); + + gst_osx_audio_sink_set_volume (osxsink); + ringbuffer->element = GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink); ringbuffer->device_id = osxsink->device_id; - ringbuffer->stream_id = osxsink->stream_id; return GST_RING_BUFFER (ringbuffer); } -OSStatus -gst_osx_audio_sink_io_proc (AudioDeviceID inDevice, - const AudioTimeStamp * inNow, const AudioBufferList * inInputData, - const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData, - const AudioTimeStamp * inOutputTime, void *inClientData) +/* HALOutput AudioUnit will request fairly arbitrarily-sized chunks of data, + * not of a fixed size. So, we keep track of where in the current ringbuffer + * segment we are, and only advance the segment once we've read the whole + * thing */ +static OSStatus +gst_osx_audio_sink_io_proc (GstOsxRingBuffer * buf, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * bufferList) { - GstOsxRingBuffer *buf = GST_OSX_RING_BUFFER (inClientData); - guint8 *readptr; gint readseg; gint len; + gint remaining = bufferList->mBuffers[0].mDataByteSize; + gint offset = 0; + + while (remaining) { + if (!gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf), + &readseg, &readptr, &len)) + return 0; - if (gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf), &readseg, &readptr, - &len)) { - outOutputData->mBuffers[0].mDataByteSize = len; - memcpy ((char *) outOutputData->mBuffers[0].mData, readptr, len); + len -= buf->segoffset; - /* clear written samples */ - gst_ring_buffer_clear (GST_RING_BUFFER (buf), readseg); + if (len > remaining) + len = remaining; - /* we wrote one segment */ - gst_ring_buffer_advance (GST_RING_BUFFER (buf), 1); + memcpy ((char *) bufferList->mBuffers[0].mData + offset, + readptr + buf->segoffset, len); + + buf->segoffset += len; + offset += len; + remaining -= len; + + if ((gint) buf->segoffset == GST_RING_BUFFER (buf)->spec.segsize) { + /* clear written samples */ + gst_ring_buffer_clear (GST_RING_BUFFER (buf), readseg); + + /* we wrote one segment */ + gst_ring_buffer_advance (GST_RING_BUFFER (buf), 1); + + buf->segoffset = 0; + } } return 0; } @@ -378,9 +306,18 @@ gst_osx_audio_sink_osxelement_init (gpointer g_iface, gpointer iface_data) { GstOsxAudioElementInterface *iface = (GstOsxAudioElementInterface *) g_iface; - iface->io_proc = gst_osx_audio_sink_io_proc; + iface->io_proc = (AURenderCallback) gst_osx_audio_sink_io_proc; } +static void +gst_osx_audio_sink_set_volume (GstOsxAudioSink * sink) +{ + if (!sink->audiounit) + return; + + AudioUnitSetParameter (sink->audiounit, kHALOutputParam_Volume, + kAudioUnitScope_Global, 0, (float) sink->volume, 0); +} static void gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink) @@ -389,67 +326,27 @@ gst_osx_audio_sink_select_device (GstOsxAudioSink * osxsink) UInt32 propertySize; if (osxsink->device_id == kAudioDeviceUnknown) { + /* If no specific device has been selected by the user, then pick the + * default device */ GST_DEBUG_OBJECT (osxsink, "Selecting device for OSXAudioSink"); propertySize = sizeof (osxsink->device_id); status = AudioHardwareGetProperty (kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &osxsink->device_id); - if (status) + if (status) { GST_WARNING_OBJECT (osxsink, "AudioHardwareGetProperty returned %d", (int) status); - else + } else { GST_DEBUG_OBJECT (osxsink, "AudioHardwareGetProperty returned 0"); + } - if (osxsink->device_id == kAudioDeviceUnknown) + if (osxsink->device_id == kAudioDeviceUnknown) { GST_WARNING_OBJECT (osxsink, "AudioHardwareGetProperty: device_id is kAudioDeviceUnknown"); + } GST_DEBUG_OBJECT (osxsink, "AudioHardwareGetProperty: device_id is %lu", (long) osxsink->device_id); } - - if (osxsink->stream_id == kAudioStreamUnknown) { - AudioStreamID *streams; - - GST_DEBUG_OBJECT (osxsink, "Getting streamid"); - status = AudioDeviceGetPropertyInfo (osxsink->device_id, 0, /* Master channel */ - FALSE, /* isInput */ - kAudioDevicePropertyStreams, &propertySize, NULL); - - if (status) { - GST_WARNING_OBJECT (osxsink, - "AudioDeviceGetProperty returned %d", (int) status); - return; - } - - GST_DEBUG_OBJECT (osxsink, - "Getting available streamids from %d (%d bytes)", - (int) (propertySize / sizeof (AudioStreamID)), - (unsigned int) propertySize); - streams = g_malloc (propertySize); - status = AudioDeviceGetProperty (osxsink->device_id, 0, /* Master channel */ - FALSE, /* isInput */ - kAudioDevicePropertyStreams, &propertySize, streams); - - if (status) { - GST_WARNING_OBJECT (osxsink, - "AudioDeviceGetProperty returned %d", (int) status); - g_free (streams); - return; - } - - GST_DEBUG_OBJECT (osxsink, "Getting streamid from %d (%d bytes)", - (int) (propertySize / sizeof (AudioStreamID)), - (unsigned int) propertySize); - - if (propertySize >= sizeof (AudioStreamID)) { - osxsink->stream_id = streams[0]; - GST_DEBUG_OBJECT (osxsink, "Selected stream %d of %d: %d", 0, - (int) (propertySize / sizeof (AudioStreamID)), - (int) osxsink->stream_id); - } - - g_free (streams); - } } |