99dfff17d3f7a56dfb98f6074047f0583ac5171d
[platform/upstream/gstreamer.git] / ext / esd / esdsink.c
1 /* GStreamer
2  * Copyright (C) <2001> Richard Boulton <richard-gst@tartarus.org>
3  *
4  * Based on example.c:
5  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include "esdsink.h"
24 #include <esd.h>
25
26 /* elementfactory information */
27 static GstElementDetails esdsink_details = {
28   "Esound audio sink",
29   "Sink/Esdsink",
30   "Plays audio to an esound server",
31   VERSION,
32   "Richard Boulton <richard-gst@tartarus.org>",
33   "(C) 2001",
34 };
35
36 /* Signals and args */
37 enum {
38   /* FILL ME */
39   LAST_SIGNAL
40 };
41
42 enum {
43   ARG_0,
44   ARG_MUTE,
45   ARG_DEPTH,
46   ARG_CHANNELS,
47   ARG_RATE,
48   ARG_HOST,
49 };
50
51 GST_PADTEMPLATE_FACTORY (sink_factory,
52   "sink",                                       /* the name of the pads */
53   GST_PAD_SINK,                         /* type of the pad */
54   GST_PAD_ALWAYS,                       /* ALWAYS/SOMETIMES */
55   GST_CAPS_NEW (
56     "esdsink_sink8",                            /* the name of the caps */
57     "audio/raw",                                /* the mime type of the caps */
58       /* Properties follow: */
59       "format",       GST_PROPS_STRING ("int"),
60         "law",        GST_PROPS_INT (0),
61         "endianness", GST_PROPS_INT (G_BYTE_ORDER),
62         "width",      GST_PROPS_INT (8),
63         "depth",      GST_PROPS_INT (8),
64         "rate",       GST_PROPS_INT_RANGE (8000, 96000),
65         "channels",   GST_PROPS_LIST (GST_PROPS_INT (1), GST_PROPS_INT (2))
66   ),
67   GST_CAPS_NEW (
68     "esdsink_sink16",                           /* the name of the caps */
69     "audio/raw",                                /* the mime type of the caps */
70       /* Properties follow: */
71       "format",       GST_PROPS_STRING ("int"),
72         "law",        GST_PROPS_INT (0),
73         "endianness", GST_PROPS_INT (G_BYTE_ORDER),
74         "signed",     GST_PROPS_BOOLEAN (TRUE),
75         "width",      GST_PROPS_INT (16),
76         "depth",      GST_PROPS_INT (16),
77         "rate",       GST_PROPS_INT_RANGE (8000, 96000),
78         "channels",   GST_PROPS_LIST (GST_PROPS_INT (1), GST_PROPS_INT (2))
79   )
80 );
81
82 static void                     gst_esdsink_class_init          (GstEsdsinkClass *klass);
83 static void                     gst_esdsink_init                (GstEsdsink *esdsink);
84
85 static gboolean                 gst_esdsink_open_audio          (GstEsdsink *sink);
86 static void                     gst_esdsink_close_audio         (GstEsdsink *sink);
87 static GstElementStateReturn    gst_esdsink_change_state        (GstElement *element);
88 static gboolean                 gst_esdsink_sync_parms          (GstEsdsink *esdsink);
89 static GstPadConnectReturn      gst_esdsink_sinkconnect         (GstPad *pad, GstCaps *caps);
90
91 static void                     gst_esdsink_chain               (GstPad *pad, GstBuffer *buf);
92
93 static void                     gst_esdsink_set_property        (GObject *object, guint prop_id, 
94                                                                  const GValue *value, GParamSpec *pspec);
95 static void                     gst_esdsink_get_property        (GObject *object, guint prop_id, 
96                                                                  GValue *value, GParamSpec *pspec);
97
98 #define GST_TYPE_ESDSINK_DEPTHS (gst_esdsink_depths_get_type())
99 static GType
100 gst_esdsink_depths_get_type (void)
101 {
102   static GType esdsink_depths_type = 0;
103   static GEnumValue esdsink_depths[] = {
104     {8, "8", "8 Bits"},
105     {16, "16", "16 Bits"},
106     {0, NULL, NULL},
107   };
108   if (!esdsink_depths_type) {
109     esdsink_depths_type = g_enum_register_static("GstEsdsinkDepths", esdsink_depths);
110   }
111   return esdsink_depths_type;
112 }
113
114 #define GST_TYPE_ESDSINK_CHANNELS (gst_esdsink_channels_get_type())
115 static GType
116 gst_esdsink_channels_get_type (void)
117 {
118   static GType esdsink_channels_type = 0;
119   static GEnumValue esdsink_channels[] = {
120     {1, "1", "Mono"},
121     {2, "2", "Stereo"},
122     {0, NULL, NULL},
123   };
124   if (!esdsink_channels_type) {
125     esdsink_channels_type = g_enum_register_static("GstEsdsinkChannels", esdsink_channels);
126   }
127   return esdsink_channels_type;
128 }
129
130
131 static GstElementClass *parent_class = NULL;
132 /*static guint gst_esdsink_signals[LAST_SIGNAL] = { 0 }; */
133
134 GType
135 gst_esdsink_get_type (void)
136 {
137   static GType esdsink_type = 0;
138
139   if (!esdsink_type) {
140     static const GTypeInfo esdsink_info = {
141       sizeof(GstEsdsinkClass),      NULL,
142       NULL,
143       (GClassInitFunc)gst_esdsink_class_init,
144       NULL,
145       NULL,
146       sizeof(GstEsdsink),
147       0,
148       (GInstanceInitFunc)gst_esdsink_init,
149     };
150     esdsink_type = g_type_register_static(GST_TYPE_ELEMENT, "GstEsdsink", &esdsink_info, 0);
151   }
152   return esdsink_type;
153 }
154
155 static void
156 gst_esdsink_class_init (GstEsdsinkClass *klass)
157 {
158   GObjectClass *gobject_class;
159   GstElementClass *gstelement_class;
160
161   gobject_class = (GObjectClass*)klass;
162   gstelement_class = (GstElementClass*)klass;
163
164   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
165
166   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_MUTE,
167     g_param_spec_boolean("mute","mute","mute",
168                          TRUE,G_PARAM_READWRITE)); /* CHECKME */
169   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEPTH,
170     g_param_spec_enum("depth","depth","depth",
171                       GST_TYPE_ESDSINK_DEPTHS,16,G_PARAM_READWRITE)); /* CHECKME! */
172   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_CHANNELS,
173     g_param_spec_enum("channels","channels","channels",
174                       GST_TYPE_ESDSINK_CHANNELS,2,G_PARAM_READWRITE)); /* CHECKME! */
175   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_RATE,
176     g_param_spec_int("frequency","frequency","frequency",
177                      G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); /* CHECKME */
178   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_HOST,
179     g_param_spec_string("host","host","host",
180                         NULL, G_PARAM_READWRITE)); /* CHECKME */
181
182   gobject_class->set_property = gst_esdsink_set_property;
183   gobject_class->get_property = gst_esdsink_get_property;
184
185   gstelement_class->change_state = gst_esdsink_change_state;
186 }
187
188 static void
189 gst_esdsink_init(GstEsdsink *esdsink)
190 {
191   esdsink->sinkpad = gst_pad_new_from_template (
192                   GST_PADTEMPLATE_GET (sink_factory), "sink");
193   gst_element_add_pad(GST_ELEMENT(esdsink), esdsink->sinkpad);
194   gst_pad_set_chain_function(esdsink->sinkpad, GST_DEBUG_FUNCPTR(gst_esdsink_chain));
195   gst_pad_set_connect_function(esdsink->sinkpad, gst_esdsink_sinkconnect);
196
197   esdsink->mute = FALSE;
198   esdsink->fd = -1;
199   /* FIXME: get default from somewhere better than just putting them inline. */
200   esdsink->format = 16;
201   esdsink->depth = 16;
202   esdsink->channels = 2;
203   esdsink->frequency = 44100;
204   esdsink->host = NULL;
205 }
206
207 static gboolean
208 gst_esdsink_sync_parms (GstEsdsink *esdsink)
209 {
210   g_return_val_if_fail (esdsink != NULL, FALSE);
211   g_return_val_if_fail (GST_IS_ESDSINK (esdsink), FALSE);
212
213   if (esdsink->fd == -1) return TRUE;
214
215   /* Need to set fd to use new parameters: only way to do this is to reopen. */
216   gst_esdsink_close_audio (esdsink);
217   return gst_esdsink_open_audio (esdsink);
218 }
219
220 static GstPadConnectReturn
221 gst_esdsink_sinkconnect (GstPad *pad, GstCaps *caps)
222 {
223   GstEsdsink *esdsink;
224
225   esdsink = GST_ESDSINK (gst_pad_get_parent (pad));
226
227   if (!GST_CAPS_IS_FIXED (caps))
228     return GST_PAD_CONNECT_DELAYED;
229
230   esdsink->depth = gst_caps_get_int (caps, "depth");
231   esdsink->channels = gst_caps_get_int (caps, "channels");
232   esdsink->frequency = gst_caps_get_int (caps, "rate");
233
234   if (gst_esdsink_sync_parms (esdsink))
235     return GST_PAD_CONNECT_OK;
236
237   return GST_PAD_CONNECT_REFUSED;
238 }
239
240 static void
241 gst_esdsink_chain (GstPad *pad, GstBuffer *buf)
242 {
243   GstEsdsink *esdsink;
244
245   g_return_if_fail(pad != NULL);
246   g_return_if_fail(GST_IS_PAD(pad));
247   g_return_if_fail(buf != NULL);
248
249   esdsink = GST_ESDSINK (gst_pad_get_parent (pad));
250
251   if (GST_BUFFER_DATA (buf) != NULL) {
252     if (!esdsink->mute && esdsink->fd >= 0) {
253       GST_DEBUG (0, "esdsink: fd=%d data=%p size=%d\n",
254                  esdsink->fd, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
255       write (esdsink->fd, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
256     }
257   }
258   gst_buffer_unref (buf);
259 }
260
261 static void
262 gst_esdsink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
263 {
264   GstEsdsink *esdsink;
265
266   /* it's not null if we got it, but it might not be ours */
267   g_return_if_fail(GST_IS_ESDSINK(object));
268   esdsink = GST_ESDSINK(object);
269
270   switch (prop_id) {
271     case ARG_MUTE:
272       esdsink->mute = g_value_get_boolean (value);
273       break;
274     case ARG_DEPTH:
275       esdsink->depth = g_value_get_enum (value);
276       gst_esdsink_sync_parms (esdsink);
277       break;
278     case ARG_CHANNELS:
279       esdsink->channels = g_value_get_enum (value);
280       gst_esdsink_sync_parms (esdsink);
281       break;
282     case ARG_RATE:
283       esdsink->frequency = g_value_get_int (value);
284       gst_esdsink_sync_parms (esdsink);
285       break;
286     case ARG_HOST:
287       if (esdsink->host != NULL) g_free(esdsink->host);
288       if (g_value_get_string (value) == NULL)
289           esdsink->host = NULL;
290       else
291           esdsink->host = g_strdup (g_value_get_string (value));
292       break;
293     default:
294       break;
295   }
296 }
297
298 static void
299 gst_esdsink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
300 {
301   GstEsdsink *esdsink;
302
303   /* it's not null if we got it, but it might not be ours */
304   g_return_if_fail(GST_IS_ESDSINK(object));
305   esdsink = GST_ESDSINK(object);
306
307   switch (prop_id) {
308     case ARG_MUTE:
309       g_value_set_boolean (value, esdsink->mute);
310       break;
311     case ARG_DEPTH:
312       g_value_set_enum (value, esdsink->depth);
313       break;
314     case ARG_CHANNELS:
315       g_value_set_enum (value, esdsink->channels);
316       break;
317     case ARG_RATE:
318       g_value_set_int (value, esdsink->frequency);
319       break;
320     case ARG_HOST:
321       g_value_set_string (value, esdsink->host);
322       break;
323     default:
324       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
325       break;
326   }
327 }
328
329 static gboolean
330 plugin_init (GModule *module, GstPlugin *plugin)
331 {
332   GstElementFactory *factory;
333
334   factory = gst_elementfactory_new("esdsink", GST_TYPE_ESDSINK,
335                                    &esdsink_details);
336   g_return_val_if_fail(factory != NULL, FALSE);
337
338   gst_elementfactory_add_padtemplate(factory, GST_PADTEMPLATE_GET (sink_factory));
339
340   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
341
342   return TRUE;
343 }
344
345 GstPluginDesc plugin_desc = {
346   GST_VERSION_MAJOR,
347   GST_VERSION_MINOR,
348   "esdsink",
349   plugin_init
350 };
351
352 static gboolean
353 gst_esdsink_open_audio (GstEsdsink *sink)
354 {
355   /* Name used by esound for this connection. */
356   const char * connname = "GStreamer";
357
358   /* Bitmap describing audio format. */
359   esd_format_t esdformat = ESD_STREAM | ESD_PLAY;
360
361   g_return_val_if_fail (sink->fd == -1, FALSE);
362
363   if (sink->depth == 16) esdformat |= ESD_BITS16;
364   else if (sink->depth == 8) esdformat |= ESD_BITS8;
365   else {
366     GST_DEBUG (0, "esdsink: invalid bit depth (%d)\n", sink->depth);
367     return FALSE;
368   }
369
370   if (sink->channels == 2) esdformat |= ESD_STEREO;
371   else if (sink->channels == 1) esdformat |= ESD_MONO;
372   else {
373     GST_DEBUG (0, "esdsink: invalid number of channels (%d)\n", sink->channels);
374     return FALSE;
375   }
376
377   GST_DEBUG (0, "esdsink: attempting to open connection to esound server\n");
378   sink->fd = esd_play_stream_fallback(esdformat, sink->frequency, sink->host, connname);
379   if ( sink->fd < 0 ) {
380     GST_DEBUG (0, "esdsink: can't open connection to esound server\n");
381     return FALSE;
382   }
383
384   GST_FLAG_SET (sink, GST_ESDSINK_OPEN);
385
386   return TRUE;
387 }
388
389 static void
390 gst_esdsink_close_audio (GstEsdsink *sink)
391 {
392   if (sink->fd < 0) return;
393
394   close(sink->fd);
395   sink->fd = -1;
396
397   GST_FLAG_UNSET (sink, GST_ESDSINK_OPEN);
398
399   GST_DEBUG (0, "esdsink: closed sound device\n");
400 }
401
402 static GstElementStateReturn
403 gst_esdsink_change_state (GstElement *element)
404 {
405   g_return_val_if_fail (GST_IS_ESDSINK (element), FALSE);
406
407   /* if going down into NULL state, close the fd if it's open */
408   if (GST_STATE_PENDING (element) == GST_STATE_NULL) {
409     if (GST_FLAG_IS_SET (element, GST_ESDSINK_OPEN))
410       gst_esdsink_close_audio (GST_ESDSINK (element));
411     /* otherwise (READY or higher) we need to open the fd */
412   } else {
413     if (!GST_FLAG_IS_SET (element, GST_ESDSINK_OPEN)) {
414       if (!gst_esdsink_open_audio (GST_ESDSINK (element)))
415         return GST_STATE_FAILURE;
416     }
417   }
418
419   if (GST_ELEMENT_CLASS (parent_class)->change_state)
420     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
421   return GST_STATE_SUCCESS;
422 }
423