Tizen 2.0 Release
[framework/multimedia/gst-plugins-bad0.10.git] / ext / sdl / sdlaudiosink.c
1 /* GStreamer
2  * Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more 
13  */
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif
18
19 #include "sdlaudiosink.h"
20
21 #include <SDL_byteorder.h>
22 #include <string.h>
23
24 #include <unistd.h>
25
26 GST_DEBUG_CATEGORY_EXTERN (sdl_debug);
27 #define GST_CAT_DEFAULT sdl_debug
28
29 static void gst_sdlaudio_sink_dispose (GObject * object);
30
31 static GstCaps *gst_sdlaudio_sink_getcaps (GstBaseSink * bsink);
32
33 static gboolean gst_sdlaudio_sink_open (GstAudioSink * asink);
34 static gboolean gst_sdlaudio_sink_close (GstAudioSink * asink);
35 static gboolean gst_sdlaudio_sink_prepare (GstAudioSink * asink,
36     GstRingBufferSpec * spec);
37 static gboolean gst_sdlaudio_sink_unprepare (GstAudioSink * asink);
38 static guint gst_sdlaudio_sink_write (GstAudioSink * asink, gpointer data,
39     guint length);
40
41 #if 0
42 static guint gst_sdlaudio_sink_delay (GstAudioSink * asink);
43 static void gst_sdlaudio_sink_reset (GstAudioSink * asink);
44 #endif
45
46
47 /* SdlaudioSink signals and args */
48 enum
49 {
50   LAST_SIGNAL
51 };
52
53 #define  SEMAPHORE_INIT(s,f)                    \
54   do {                                          \
55     s.cond = g_cond_new();                      \
56     s.mutex = g_mutex_new();                    \
57     s.mutexflag = f;                            \
58   } while(0)
59
60 #define  SEMAPHORE_CLOSE(s)                     \
61   do {                                          \
62     if ( s.cond ) {                             \
63       g_cond_free(s.cond);                      \
64       s.cond = NULL;                            \
65     }                                           \
66     if ( s.mutex ) {                            \
67       g_mutex_free(s.mutex);                    \
68       s.mutex = NULL;                           \
69     }                                           \
70   } while(0)
71
72 #define SEMAPHORE_UP(s)                                 \
73   do                                                    \
74     {                                                   \
75       g_mutex_lock(s.mutex);                            \
76       s.mutexflag = TRUE;                               \
77       g_mutex_unlock(s.mutex);                          \
78       g_cond_signal(s.cond);                            \
79     } while(0)
80
81 #define SEMAPHORE_DOWN(s, e)                            \
82   do                                                    \
83     {                                                   \
84       while (1) {                                       \
85         g_mutex_lock(s.mutex);                          \
86         if (!s.mutexflag) {                             \
87           if ( e ) {                                    \
88             g_mutex_unlock(s.mutex);                    \
89             break;                                      \
90           }                                             \
91           g_cond_wait(s.cond,s.mutex);                  \
92         }                                               \
93         else {                                          \
94           s.mutexflag = FALSE;                          \
95           g_mutex_unlock(s.mutex);                      \
96           break;                                        \
97         }                                               \
98         g_mutex_unlock(s.mutex);                        \
99       }                                                 \
100     } while(0)
101
102
103 static GstStaticPadTemplate sdlaudiosink_sink_factory =
104     GST_STATIC_PAD_TEMPLATE ("sink",
105     GST_PAD_SINK,
106     GST_PAD_ALWAYS,
107     GST_STATIC_CAPS ("audio/x-raw-int, "
108         "endianness = (int) { " G_STRINGIFY (G_BYTE_ORDER) " }, "
109         "signed = (boolean) { TRUE, FALSE }, "
110         "width = (int) 16, "
111         "depth = (int) 16, "
112         "rate = (int) [ 1, MAX ], "
113         "channels = (int) [ 1, 2 ]; "
114         "audio/x-raw-int, "
115         "endianness = (int) { " G_STRINGIFY (G_BYTE_ORDER) " }, "
116         "signed = (boolean) { TRUE, FALSE }, "
117         "width = (int) 8, "
118         "depth = (int) 8, "
119         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]")
120     );
121
122 GST_BOILERPLATE (GstSDLAudioSink, gst_sdlaudio_sink, GstAudioSink,
123     GST_TYPE_AUDIO_SINK);
124
125 static void
126 gst_sdlaudio_sink_dispose (GObject * object)
127 {
128   GstSDLAudioSink *sdlaudiosink = GST_SDLAUDIOSINK (object);
129
130   SEMAPHORE_CLOSE (sdlaudiosink->semB);
131
132   SEMAPHORE_CLOSE (sdlaudiosink->semA);
133
134   if (sdlaudiosink->buffer) {
135     g_free (sdlaudiosink->buffer);
136   }
137
138   G_OBJECT_CLASS (parent_class)->dispose (object);
139
140 }
141
142 static void
143 gst_sdlaudio_sink_base_init (gpointer g_class)
144 {
145   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
146
147   gst_element_class_set_details_simple (element_class, "SDL audio sink",
148       "Sink/Audio",
149       "Output to a sound card via SDLAUDIO",
150       "Edgard Lima <edgard.lima@indt.org.br>");
151
152   gst_element_class_add_static_pad_template (element_class,
153       &sdlaudiosink_sink_factory);
154 }
155
156 static void
157 gst_sdlaudio_sink_class_init (GstSDLAudioSinkClass * klass)
158 {
159   GObjectClass *gobject_class;
160   GstBaseSinkClass *gstbasesink_class;
161   GstAudioSinkClass *gstaudiosink_class;
162
163   gobject_class = (GObjectClass *) klass;
164   gstbasesink_class = (GstBaseSinkClass *) klass;
165   gstaudiosink_class = (GstAudioSinkClass *) klass;
166
167   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_dispose);
168
169   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_getcaps);
170
171   gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_open);
172   gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_close);
173   gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_prepare);
174   gstaudiosink_class->unprepare =
175       GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_unprepare);
176   gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_write);
177
178 #if 0
179   gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_delay);
180   gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_sdlaudio_sink_reset);
181 #endif
182
183 }
184
185 static void
186 gst_sdlaudio_sink_init (GstSDLAudioSink * sdlaudiosink,
187     GstSDLAudioSinkClass * g_class)
188 {
189   GST_DEBUG ("initializing sdlaudiosink");
190
191   memset (&sdlaudiosink->fmt, 0, sizeof (SDL_AudioSpec));
192
193   sdlaudiosink->buffer = NULL;
194   sdlaudiosink->eos = FALSE;
195
196   SEMAPHORE_INIT (sdlaudiosink->semA, TRUE);
197
198   SEMAPHORE_INIT (sdlaudiosink->semB, FALSE);
199
200 }
201
202 static GstCaps *
203 gst_sdlaudio_sink_getcaps (GstBaseSink * bsink)
204 {
205   return gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD
206           (bsink)));
207 }
208
209 static gint
210 gst_sdlaudio_sink_get_format (GstBufferFormat fmt)
211 {
212   gint result = GST_UNKNOWN;
213
214   switch (fmt) {
215     case GST_U8:
216       result = AUDIO_U8;
217       break;
218     case GST_S8:
219       result = AUDIO_S8;
220       break;
221     case GST_S16_LE:
222       result = AUDIO_S16LSB;
223       break;
224     case GST_S16_BE:
225       result = AUDIO_S16MSB;
226       break;
227     case GST_U16_LE:
228       result = AUDIO_U16LSB;
229       break;
230     case GST_U16_BE:
231       result = AUDIO_U16MSB;
232       break;
233     default:
234       break;
235   }
236   return result;
237 }
238
239 static gboolean
240 gst_sdlaudio_sink_open (GstAudioSink * asink)
241 {
242   GstSDLAudioSink *sdlaudio;
243
244   sdlaudio = GST_SDLAUDIOSINK (asink);
245
246   if (SDL_Init (SDL_INIT_AUDIO) < 0) {
247     goto open_failed;
248   }
249
250   return TRUE;
251
252 open_failed:
253   {
254     GST_ELEMENT_ERROR (sdlaudio, LIBRARY, INIT,
255         ("Unable to init SDL: %s\n", SDL_GetError ()), (NULL));
256     return FALSE;
257   }
258 }
259
260 static gboolean
261 gst_sdlaudio_sink_close (GstAudioSink * asink)
262 {
263   GstSDLAudioSink *sdlaudio = GST_SDLAUDIOSINK (asink);
264
265   sdlaudio->eos = TRUE;
266   SEMAPHORE_UP (sdlaudio->semA);
267   SEMAPHORE_UP (sdlaudio->semB);
268   SDL_QuitSubSystem (SDL_INIT_AUDIO);
269   return TRUE;
270 }
271
272 static guint
273 gst_sdlaudio_sink_write (GstAudioSink * asink, gpointer data, guint length)
274 {
275   GstSDLAudioSink *sdlaudio = GST_SDLAUDIOSINK (asink);
276
277   if (sdlaudio->fmt.size != length) {
278     GST_ERROR ("ring buffer segment length (%u) != sdl buffer len (%u)", length,
279         sdlaudio->fmt.size);
280   }
281
282   SEMAPHORE_DOWN (sdlaudio->semA, sdlaudio->eos);
283
284   if (!sdlaudio->eos)
285     memcpy (sdlaudio->buffer, data, length);
286
287   SEMAPHORE_UP (sdlaudio->semB);
288
289   return sdlaudio->fmt.size;
290 }
291
292
293 static void
294 mixaudio (void *unused, Uint8 * stream, int len)
295 {
296   GstSDLAudioSink *sdlaudio;
297
298   sdlaudio = GST_SDLAUDIOSINK (unused);
299
300   if (sdlaudio->fmt.size != len) {
301     GST_ERROR ("fmt buffer len (%u) != sdl callback len (%d)",
302         sdlaudio->fmt.size, len);
303   }
304
305   SEMAPHORE_DOWN (sdlaudio->semB, sdlaudio->eos);
306
307   if (!sdlaudio->eos)
308     SDL_MixAudio (stream, sdlaudio->buffer, sdlaudio->fmt.size,
309         SDL_MIX_MAXVOLUME);
310
311   SEMAPHORE_UP (sdlaudio->semA);
312
313 }
314
315 static gboolean
316 gst_sdlaudio_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
317 {
318   GstSDLAudioSink *sdlaudio;
319   gint power2 = -1;
320
321   sdlaudio = GST_SDLAUDIOSINK (asink);
322
323   sdlaudio->fmt.format = gst_sdlaudio_sink_get_format (spec->format);
324   if (sdlaudio->fmt.format == 0)
325     goto wrong_format;
326
327   if (spec->width != 16 && spec->width != 8)
328     goto dodgy_width;
329
330   sdlaudio->fmt.freq = spec->rate;
331   sdlaudio->fmt.channels = spec->channels;
332   sdlaudio->fmt.samples =
333       spec->segsize / (spec->channels * ((sdlaudio->fmt.format & 0xFF) >> 3));
334   sdlaudio->fmt.callback = mixaudio;
335   sdlaudio->fmt.userdata = sdlaudio;
336
337   GST_DEBUG ("set segsize: %d, segtotal: %d, samples: %d", spec->segsize,
338       spec->segtotal, sdlaudio->fmt.samples);
339
340   while (sdlaudio->fmt.samples) {
341     sdlaudio->fmt.samples >>= 1;
342     ++power2;
343   }
344
345   sdlaudio->fmt.samples = 1;
346   sdlaudio->fmt.samples <<= power2;
347
348   GST_DEBUG ("set segsize: %d, segtotal: %d, samples: %d", spec->segsize,
349       spec->segtotal, sdlaudio->fmt.samples);
350
351   if (SDL_OpenAudio (&sdlaudio->fmt, NULL) < 0) {
352     goto unable_open;
353   }
354
355   spec->segsize = sdlaudio->fmt.size;
356
357   sdlaudio->buffer = g_malloc (sdlaudio->fmt.size);
358   memset (sdlaudio->buffer, sdlaudio->fmt.silence, sdlaudio->fmt.size);
359
360   GST_DEBUG ("set segsize: %d, segtotal: %d, samples: %d", spec->segsize,
361       spec->segtotal, sdlaudio->fmt.samples);
362
363   spec->bytes_per_sample =
364       spec->channels * ((sdlaudio->fmt.format & 0xFF) >> 3);
365   memset (spec->silence_sample, sdlaudio->fmt.silence, spec->bytes_per_sample);
366
367   SDL_PauseAudio (0);
368
369   return TRUE;
370
371 unable_open:
372   {
373     GST_ELEMENT_ERROR (sdlaudio, RESOURCE, OPEN_READ,
374         ("Unable to open audio: %s", SDL_GetError ()), (NULL));
375     return FALSE;
376   }
377 wrong_format:
378   {
379     GST_ELEMENT_ERROR (sdlaudio, RESOURCE, OPEN_READ,
380         ("Unable to get format %d", spec->format), (NULL));
381     return FALSE;
382   }
383 dodgy_width:
384   {
385     GST_ELEMENT_ERROR (sdlaudio, RESOURCE, OPEN_READ,
386         ("unexpected width %d", spec->width), (NULL));
387     return FALSE;
388   }
389 }
390
391 static gboolean
392 gst_sdlaudio_sink_unprepare (GstAudioSink * asink)
393 {
394
395   SDL_CloseAudio ();
396
397   return TRUE;
398
399 #if 0
400   if (!gst_sdlaudio_sink_close (asink))
401     goto couldnt_close;
402
403   if (!gst_sdlaudio_sink_open (asink))
404     goto couldnt_reopen;
405
406   return TRUE;
407
408 couldnt_close:
409   {
410     GST_DEBUG ("Could not close the audio device");
411     return FALSE;
412   }
413 couldnt_reopen:
414   {
415     GST_DEBUG ("Could not reopen the audio device");
416     return FALSE;
417   }
418 #endif
419
420 }
421
422 #if 0
423 static guint
424 gst_sdlaudio_sink_delay (GstAudioSink * asink)
425 {
426   GstSDLAudioSink *sdlaudio;
427
428   sdlaudio = GST_SDLAUDIOSINK (asink);
429
430   return 0;
431 }
432
433 static void
434 gst_sdlaudio_sink_reset (GstAudioSink * asink)
435 {
436 }
437 #endif