sys/osxaudio/: Port of osxaudiosink to 0.10
[platform/upstream/gst-plugins-good.git] / sys / osxaudio / gstosxaudiosink.c
1 /*
2  * GStreamer
3  * Copyright 2005,2006 Zaheer Abbas Merali  <zaheerabbas at merali dot org>
4  * 
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Alternatively, the contents of this file may be used under the
24  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
25  * which case the following provisions apply instead of the ones
26  * mentioned above:
27  *
28  * This library is free software; you can redistribute it and/or
29  * modify it under the terms of the GNU Library General Public
30  * License as published by the Free Software Foundation; either
31  * version 2 of the License, or (at your option) any later version.
32  *
33  * This library is distributed in the hope that it will be useful,
34  * but WITHOUT ANY WARRANTY; without even the implied warranty of
35  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
36  * Library General Public License for more details.
37  *
38  * You should have received a copy of the GNU Library General Public
39  * License along with this library; if not, write to the
40  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
41  * Boston, MA 02111-1307, USA.
42  */
43
44 /**
45  * SECTION:element-plugin
46  *
47  * <refsect2>
48  * <title>Example launch line</title>
49  * <para>
50  * <programlisting>
51  * gst-launch -v -m audiotestsrc ! audioconvert ! osxaudiosink
52  * </programlisting>
53  * </para>
54  * </refsect2>
55  */
56
57 #ifdef HAVE_CONFIG_H
58 #  include <config.h>
59 #endif
60
61 #include <gst/gst.h>
62 #include <CoreAudio/CoreAudio.h>
63 #include "gstosxaudiosink.h"
64 #include "gstosxaudioelement.h"
65
66 GST_DEBUG_CATEGORY_STATIC (osx_audiosink_debug);
67 #define GST_CAT_DEFAULT osx_audiosink_debug
68
69 static GstElementDetails gst_osx_audio_sink_details =
70 GST_ELEMENT_DETAILS ("Audio Sink (OSX)",
71     "Sink/Audio",
72     "Output to a sound card in OS X",
73     "Zaheer Abbas Merali <zaheerabbas at merali dot org>");
74
75 /* Filter signals and args */
76 enum
77 {
78   /* FILL ME */
79   LAST_SIGNAL
80 };
81
82 enum
83 {
84   ARG_0,
85   ARG_DEVICE
86 };
87
88 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
89     GST_PAD_SINK,
90     GST_PAD_ALWAYS,
91     GST_STATIC_CAPS ("audio/x-raw-float, "
92         "endianness = (int) {" G_STRINGIFY (G_BYTE_ORDER) " }, "
93         "signed = (boolean) { TRUE }, "
94         "width = (int) 32, " "rate = (int) 44100, " "channels = (int) 2")
95     );
96
97 static void gst_osx_audio_sink_set_property (GObject * object, guint prop_id,
98     const GValue * value, GParamSpec * pspec);
99 static void gst_osx_audio_sink_get_property (GObject * object, guint prop_id,
100     GValue * value, GParamSpec * pspec);
101 static GstCaps *gst_osx_audio_sink_getcaps (GstBaseSink * sink);
102
103
104 static GstRingBuffer *gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink *
105     sink);
106 /*static GstCaps* gst_osx_audio_sink_getcaps (GstBaseSink * bsink);*/
107 static void gst_osx_audio_sink_osxelement_init (gpointer g_iface,
108     gpointer iface_data);
109 OSStatus gst_osx_audio_sink_io_proc (AudioDeviceID inDevice,
110     const AudioTimeStamp * inNow, const AudioBufferList * inInputData,
111     const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData,
112     const AudioTimeStamp * inOutputTime, void *inClientData);
113 static void
114 gst_osx_audio_sink_osxelement_do_init (GType type)
115 {
116   static const GInterfaceInfo osxelement_info = {
117     gst_osx_audio_sink_osxelement_init,
118     NULL,
119     NULL
120   };
121
122   GST_DEBUG_CATEGORY_INIT (osx_audiosink_debug, "osxaudiosink", 0,
123       "OSX Audio Sink");
124   GST_DEBUG ("Adding static interface\n");
125   g_type_add_interface_static (type, GST_OSX_AUDIO_ELEMENT_TYPE,
126       &osxelement_info);
127 }
128
129 GST_BOILERPLATE_FULL (GstOsxAudioSink, gst_osx_audio_sink, GstBaseAudioSink,
130     GST_TYPE_BASE_AUDIO_SINK, gst_osx_audio_sink_osxelement_do_init)
131
132
133      static void gst_osx_audio_sink_base_init (gpointer g_class)
134 {
135   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
136
137   gst_element_class_add_pad_template (element_class,
138       gst_static_pad_template_get (&sink_factory));
139
140   gst_element_class_set_details (element_class, &gst_osx_audio_sink_details);
141 }
142
143 /* initialize the plugin's class */
144 static void
145 gst_osx_audio_sink_class_init (GstOsxAudioSinkClass * klass)
146 {
147   GObjectClass *gobject_class;
148   GstElementClass *gstelement_class;
149   GstBaseSinkClass *gstbasesink_class;
150   GstBaseAudioSinkClass *gstbaseaudiosink_class;
151
152   gobject_class = (GObjectClass *) klass;
153   gstelement_class = (GstElementClass *) klass;
154   gstbasesink_class = (GstBaseSinkClass *) klass;
155   gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass;
156
157   parent_class = g_type_class_peek_parent (klass);
158
159   gobject_class->set_property =
160       GST_DEBUG_FUNCPTR (gst_osx_audio_sink_set_property);
161   gobject_class->get_property =
162       GST_DEBUG_FUNCPTR (gst_osx_audio_sink_get_property);
163
164   g_object_class_install_property (gobject_class, ARG_DEVICE,
165       g_param_spec_int ("device", "Device ID", "Device ID of output device",
166           0, G_MAXINT, 0, G_PARAM_READWRITE));
167
168   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_osx_audio_sink_getcaps);
169   gstbaseaudiosink_class->create_ringbuffer =
170       GST_DEBUG_FUNCPTR (gst_osx_audio_sink_create_ringbuffer);
171
172 }
173
174 /* initialize the new element
175  * instantiate pads and add them to element
176  * set functions
177  * initialize structure
178  */
179 static void
180 gst_osx_audio_sink_init (GstOsxAudioSink * sink, GstOsxAudioSinkClass * gclass)
181 {
182 /*  GstElementClass *klass = GST_ELEMENT_GET_CLASS (sink); */
183   sink->ringbuffer = NULL;
184   GST_DEBUG ("Initialising object\n");
185   gst_osx_audio_sink_create_ringbuffer (sink);
186
187 }
188
189 static void
190 gst_osx_audio_sink_set_property (GObject * object, guint prop_id,
191     const GValue * value, GParamSpec * pspec)
192 {
193   GstOsxAudioSink *sink = GST_OSX_AUDIO_SINK (object);
194
195   switch (prop_id) {
196     case ARG_DEVICE:
197       if (sink->ringbuffer)
198         sink->ringbuffer->device_id = g_value_get_int (value);
199       break;
200     default:
201       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
202       break;
203   }
204 }
205
206 static void
207 gst_osx_audio_sink_get_property (GObject * object, guint prop_id,
208     GValue * value, GParamSpec * pspec)
209 {
210   GstOsxAudioSink *sink = GST_OSX_AUDIO_SINK (object);
211   int val = 0;
212
213   switch (prop_id) {
214     case ARG_DEVICE:
215       if (sink->ringbuffer)
216         val = sink->ringbuffer->device_id;
217
218       g_value_set_int (value, val);
219       break;
220     default:
221       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
222       break;
223   }
224 }
225
226 /* GstElement vmethod implementations */
227
228 /* GstBaseSink vmethod implementations */
229 static GstCaps *
230 gst_osx_audio_sink_getcaps (GstBaseSink * sink)
231 {
232   GstCaps *caps;
233   GstOsxAudioSink *osxsink;
234   OSStatus status;
235   AudioValueRange rates[10];
236   UInt32 propertySize;
237   int i;
238
239   propertySize = sizeof (AudioValueRange) * 9;
240   osxsink = GST_OSX_AUDIO_SINK (sink);
241
242   caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD
243           (sink)));
244
245
246   status = AudioDeviceGetProperty (osxsink->ringbuffer->device_id, 0, FALSE,
247       kAudioDevicePropertyAvailableNominalSampleRates, &propertySize, &rates);
248
249   GST_DEBUG
250       ("Getting available sample rates: Status: %d number of ranges: %d\n",
251       status, propertySize / sizeof (AudioValueRange));
252
253   for (i = 0; i < propertySize / sizeof (AudioValueRange); i++) {
254     g_print ("Range from %f to %f\n", rates[i].mMinimum, rates[i].mMaximum);
255   }
256
257   return caps;
258 }
259
260 /* GstBaseAudioSink vmethod implementations */
261 static GstRingBuffer *
262 gst_osx_audio_sink_create_ringbuffer (GstBaseAudioSink * sink)
263 {
264   GstOsxAudioSink *osxsink;
265
266   osxsink = GST_OSX_AUDIO_SINK (sink);
267   if (!osxsink->ringbuffer) {
268     GST_DEBUG ("Creating ringbuffer\n");
269     osxsink->ringbuffer = g_object_new (GST_TYPE_OSX_RING_BUFFER, NULL);
270     GST_DEBUG ("osx sink 0x%x element 0x%x  ioproc 0x%x\n", osxsink,
271         GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink),
272         (void *) gst_osx_audio_sink_io_proc);
273     osxsink->ringbuffer->element =
274         GST_OSX_AUDIO_ELEMENT_GET_INTERFACE (osxsink);
275   }
276
277   return GST_RING_BUFFER (osxsink->ringbuffer);
278 }
279
280 OSStatus
281 gst_osx_audio_sink_io_proc (AudioDeviceID inDevice,
282     const AudioTimeStamp * inNow, const AudioBufferList * inInputData,
283     const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData,
284     const AudioTimeStamp * inOutputTime, void *inClientData)
285 {
286   GstOsxRingBuffer *buf = GST_OSX_RING_BUFFER (inClientData);
287
288   guint8 *readptr;
289   gint readseg;
290   gint len;
291
292   if (gst_ring_buffer_prepare_read (GST_RING_BUFFER (buf), &readseg, &readptr,
293           &len)) {
294     outOutputData->mBuffers[0].mDataByteSize = len;
295     memcpy ((char *) outOutputData->mBuffers[0].mData, readptr, len);
296
297     /* clear written samples */
298     gst_ring_buffer_clear (GST_RING_BUFFER (buf), readseg);
299
300     /* we wrote one segment */
301     gst_ring_buffer_advance (GST_RING_BUFFER (buf), 1);
302   }
303   return 0;
304 }
305
306 static void
307 gst_osx_audio_sink_osxelement_init (gpointer g_iface, gpointer iface_data)
308 {
309   GstOsxAudioElementInterface *iface = (GstOsxAudioElementInterface *) g_iface;
310
311   iface->io_proc = gst_osx_audio_sink_io_proc;
312 }
313
314 /* entry point to initialize the plug-in
315  * initialize the plug-in itself
316  * register the element factories and pad templates
317  * register the features
318  *
319  * exchange the string 'plugin' with your elemnt name
320  */
321 static gboolean
322 plugin_init (GstPlugin * plugin)
323 {
324   return gst_element_register (plugin, "osxaudiosink",
325       GST_RANK_NONE, GST_TYPE_OSX_AUDIO_SINK);
326 }
327
328 /* this is the structure that gstreamer looks for to register plugins
329  *
330  * exchange the strings 'plugin' and 'Template plugin' with you plugin name and
331  * description
332  */
333 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
334     GST_VERSION_MINOR,
335     "osxaudio",
336     "OSX Audio plugin",
337     plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")