gst-indent
[platform/upstream/gst-plugins-good.git] / ext / esd / esdmon.c
1 /* GStreamer
2  * Copyright (C) <2001,2002> 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 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include "esdmon.h"
27 #include <esd.h>
28 #include <unistd.h>
29
30
31 /* elementfactory information */
32 static GstElementDetails esdmon_details = {
33   "Esound audio monitor",
34   "Source/Audio",
35   "Monitors audio from an esound server",
36   "Richard Boulton <richard-gst@tartarus.org>",
37 };
38
39 /* Signals and args */
40 enum
41 {
42   /* FILL ME */
43   LAST_SIGNAL
44 };
45
46 enum
47 {
48   ARG_0,
49   ARG_DEPTH,
50   ARG_BYTESPERREAD,
51   ARG_CUROFFSET,
52   ARG_CHANNELS,
53   ARG_RATE,
54   ARG_HOST,
55 };
56
57 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
58     GST_PAD_SRC,
59     GST_PAD_ALWAYS,
60     GST_STATIC_CAPS ("audio/x-raw-int, "
61         "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER) ", "
62         "signed = (boolean) TRUE, "
63         "width = (int) 16, "
64         "depth = (int) 16, "
65         "rate = [ 8000, 96000 ], "
66         "channels = [ 1, 2 ]; "
67         "audio/x-raw-int, "
68         "signed = (boolean) FALSE, "
69         "width = (int) 8, "
70         "depth = (int) 8, " "rate = [ 8000, 96000 ], " "channels = [ 1, 2 ]")
71     );
72
73 static void gst_esdmon_base_init (gpointer g_class);
74 static void gst_esdmon_class_init (gpointer g_class, gpointer class_data);
75 static void gst_esdmon_init (GTypeInstance * instance, gpointer g_class);
76
77 static gboolean gst_esdmon_open_audio (GstEsdmon * src);
78 static void gst_esdmon_close_audio (GstEsdmon * src);
79 static GstElementStateReturn gst_esdmon_change_state (GstElement * element);
80 static gboolean gst_esdmon_sync_parms (GstEsdmon * esdmon);
81
82 static GstData *gst_esdmon_get (GstPad * pad);
83
84 static void gst_esdmon_set_property (GObject * object, guint prop_id,
85     const GValue * value, GParamSpec * pspec);
86 static void gst_esdmon_get_property (GObject * object, guint prop_id,
87     GValue * value, GParamSpec * pspec);
88
89 #define GST_TYPE_ESDMON_DEPTHS (gst_esdmon_depths_get_type())
90 static GType
91 gst_esdmon_depths_get_type (void)
92 {
93   static GType esdmon_depths_type = 0;
94   static GEnumValue esdmon_depths[] = {
95     {8, "8", "8 Bits"},
96     {16, "16", "16 Bits"},
97     {0, NULL, NULL},
98   };
99   if (!esdmon_depths_type) {
100     esdmon_depths_type =
101         g_enum_register_static ("GstEsdmonDepths", esdmon_depths);
102   }
103   return esdmon_depths_type;
104 }
105
106 #define GST_TYPE_ESDMON_CHANNELS (gst_esdmon_channels_get_type())
107 static GType
108 gst_esdmon_channels_get_type (void)
109 {
110   static GType esdmon_channels_type = 0;
111   static GEnumValue esdmon_channels[] = {
112     {1, "1", "Mono"},
113     {2, "2", "Stereo"},
114     {0, NULL, NULL},
115   };
116   if (!esdmon_channels_type) {
117     esdmon_channels_type =
118         g_enum_register_static ("GstEsdmonChannels", esdmon_channels);
119   }
120   return esdmon_channels_type;
121 }
122
123
124 static GstElementClass *parent_class = NULL;
125
126 /*static guint gst_esdmon_signals[LAST_SIGNAL] = { 0 }; */
127
128 GType
129 gst_esdmon_get_type (void)
130 {
131   static GType esdmon_type = 0;
132
133   if (!esdmon_type) {
134     static const GTypeInfo esdmon_info = {
135       sizeof (GstEsdmonClass),
136       gst_esdmon_base_init,
137       NULL,
138       gst_esdmon_class_init,
139       NULL,
140       NULL,
141       sizeof (GstEsdmon),
142       0,
143       gst_esdmon_init,
144     };
145     esdmon_type =
146         g_type_register_static (GST_TYPE_ELEMENT, "GstEsdmon", &esdmon_info, 0);
147   }
148   return esdmon_type;
149 }
150
151 static void
152 gst_esdmon_base_init (gpointer g_class)
153 {
154   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
155
156   gst_element_class_add_pad_template (element_class,
157       gst_static_pad_template_get (&src_factory));
158   gst_element_class_set_details (element_class, &esdmon_details);
159 }
160
161 static void
162 gst_esdmon_class_init (gpointer g_class, gpointer class_data)
163 {
164   GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
165   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
166
167   parent_class = g_type_class_peek_parent (g_class);
168
169   g_object_class_install_property (gobject_class, ARG_BYTESPERREAD, g_param_spec_ulong ("bytes_per_read", "bytes_per_read", "bytes_per_read", 0, G_MAXULONG, 0, G_PARAM_READWRITE));    /* CHECKME */
170   g_object_class_install_property (gobject_class, ARG_CUROFFSET, g_param_spec_ulong ("curoffset", "curoffset", "curoffset", 0, G_MAXULONG, 0, G_PARAM_READABLE));       /* CHECKME */
171   g_object_class_install_property (gobject_class, ARG_DEPTH, g_param_spec_enum ("depth", "depth", "depth", GST_TYPE_ESDMON_DEPTHS, 16, G_PARAM_READWRITE));     /* CHECKME! */
172   g_object_class_install_property (gobject_class, ARG_CHANNELS, g_param_spec_enum ("channels", "channels", "channels", GST_TYPE_ESDMON_CHANNELS, 2, G_PARAM_READWRITE));        /* CHECKME! */
173   g_object_class_install_property (gobject_class, ARG_RATE, g_param_spec_int ("frequency", "frequency", "frequency", G_MININT, G_MAXINT, 0, G_PARAM_READWRITE));        /* CHECKME */
174   g_object_class_install_property (gobject_class, ARG_HOST, g_param_spec_string ("host", "host", "host", NULL, G_PARAM_READWRITE));     /* CHECKME */
175
176   gobject_class->set_property = gst_esdmon_set_property;
177   gobject_class->get_property = gst_esdmon_get_property;
178
179   gstelement_class->change_state = gst_esdmon_change_state;
180 }
181
182 static void
183 gst_esdmon_init (GTypeInstance * instance, gpointer g_class)
184 {
185   GstEsdmon *esdmon = GST_ESDMON (instance);
186
187   esdmon->srcpad =
188       gst_pad_new_from_template (gst_element_class_get_pad_template
189       (GST_ELEMENT_GET_CLASS (esdmon), "src"), "src");
190   gst_pad_set_get_function (esdmon->srcpad, gst_esdmon_get);
191   gst_pad_use_explicit_caps (esdmon->srcpad);
192   gst_element_add_pad (GST_ELEMENT (esdmon), esdmon->srcpad);
193
194   esdmon->fd = -1;
195   /* FIXME: get default from somewhere better than just putting them inline. */
196   esdmon->depth = 16;
197   esdmon->channels = 2;
198   esdmon->frequency = 44100;
199   esdmon->host = NULL;
200   esdmon->bytes_per_read = 4096;
201   esdmon->curoffset = 0;
202   esdmon->basetime = 0;
203   esdmon->samples_since_basetime = 0;
204 }
205
206 static gboolean
207 gst_esdmon_sync_parms (GstEsdmon * esdmon)
208 {
209   g_return_val_if_fail (esdmon != NULL, FALSE);
210   g_return_val_if_fail (GST_IS_ESDMON (esdmon), FALSE);
211
212   if (esdmon->fd == -1)
213     return TRUE;
214
215   /* Need to set fd to use new parameters: only way to do this is to reopen. */
216   gst_esdmon_close_audio (esdmon);
217   return gst_esdmon_open_audio (esdmon);
218 }
219
220 static GstData *
221 gst_esdmon_get (GstPad * pad)
222 {
223   GstEsdmon *esdmon;
224   GstBuffer *buf;
225   glong readbytes;
226   glong readsamples;
227
228   g_return_val_if_fail (pad != NULL, NULL);
229   esdmon = GST_ESDMON (gst_pad_get_parent (pad));
230
231   GST_DEBUG ("attempting to read something from esdmon");
232
233   buf = gst_buffer_new ();
234   g_return_val_if_fail (buf, NULL);
235
236   GST_BUFFER_DATA (buf) = (gpointer) g_malloc (esdmon->bytes_per_read);
237
238   readbytes = read (esdmon->fd, GST_BUFFER_DATA (buf), esdmon->bytes_per_read);
239
240   if (readbytes == 0) {
241     gst_element_set_eos (GST_ELEMENT (esdmon));
242     return NULL;
243   }
244   if (!GST_PAD_CAPS (pad)) {
245     gint sign = (esdmon->depth == 8 ? FALSE : TRUE);
246
247     /* set caps on src pad */
248     if (gst_pad_set_explicit_caps (esdmon->srcpad,
249             gst_caps_new_simple ("audio/x-raw-int",
250                 "endianness", G_TYPE_INT, G_BYTE_ORDER,
251                 "signed", G_TYPE_BOOLEAN, sign,
252                 "width", G_TYPE_INT, esdmon->depth,
253                 "depth", G_TYPE_INT, esdmon->depth,
254                 "rate", G_TYPE_INT, esdmon->frequency,
255                 "channels", G_TYPE_INT, esdmon->channels)) <= 0) {
256       GST_ELEMENT_ERROR (esdmon, CORE, NEGOTIATION, (NULL), (NULL));
257       return NULL;
258     }
259   }
260
261   GST_BUFFER_SIZE (buf) = readbytes;
262   GST_BUFFER_OFFSET (buf) = esdmon->curoffset;
263   GST_BUFFER_TIMESTAMP (buf) = esdmon->basetime +
264       esdmon->samples_since_basetime * GST_SECOND / esdmon->frequency;
265
266   esdmon->curoffset += readbytes;
267   readsamples = readbytes / esdmon->channels;
268   if (esdmon->depth == 16)
269     readsamples /= 2;
270   esdmon->samples_since_basetime += readsamples;
271
272   GST_DEBUG ("pushed buffer from esdmon of %ld bytes, timestamp %"
273       G_GINT64_FORMAT, readbytes, GST_BUFFER_TIMESTAMP (buf));
274   return GST_DATA (buf);
275 }
276
277 static void
278 gst_esdmon_set_property (GObject * object, guint prop_id, const GValue * value,
279     GParamSpec * pspec)
280 {
281   GstEsdmon *esdmon;
282
283   /* it's not null if we got it, but it might not be ours */
284   g_return_if_fail (GST_IS_ESDMON (object));
285   esdmon = GST_ESDMON (object);
286
287   switch (prop_id) {
288     case ARG_BYTESPERREAD:
289       esdmon->bytes_per_read = g_value_get_ulong (value);
290       /* No need to sync params - will just happen on next read. */
291       break;
292     case ARG_DEPTH:
293       esdmon->depth = g_value_get_enum (value);
294       gst_esdmon_sync_parms (esdmon);
295       break;
296     case ARG_CHANNELS:
297       esdmon->channels = g_value_get_enum (value);
298       gst_esdmon_sync_parms (esdmon);
299       break;
300     case ARG_RATE:
301       /* Preserve the timestamps */
302       esdmon->basetime =
303           esdmon->samples_since_basetime * GST_SECOND / esdmon->frequency;
304       esdmon->samples_since_basetime = 0;
305
306       /* Set the new frequency */
307       esdmon->frequency = g_value_get_int (value);
308       gst_esdmon_sync_parms (esdmon);
309       break;
310     case ARG_HOST:
311       if (esdmon->host != NULL)
312         g_free (esdmon->host);
313       if (g_value_get_string (value) == NULL)
314         esdmon->host = NULL;
315       else
316         esdmon->host = g_strdup (g_value_get_string (value));
317       break;
318     default:
319       break;
320   }
321 }
322
323 static void
324 gst_esdmon_get_property (GObject * object, guint prop_id, GValue * value,
325     GParamSpec * pspec)
326 {
327   GstEsdmon *esdmon;
328
329   /* it's not null if we got it, but it might not be ours */
330   g_return_if_fail (GST_IS_ESDMON (object));
331   esdmon = GST_ESDMON (object);
332
333   switch (prop_id) {
334     case ARG_BYTESPERREAD:
335       g_value_set_ulong (value, esdmon->bytes_per_read);
336       break;
337     case ARG_CUROFFSET:
338       g_value_set_ulong (value, esdmon->curoffset);
339       break;
340     case ARG_DEPTH:
341       g_value_set_enum (value, esdmon->depth);
342       break;
343     case ARG_CHANNELS:
344       g_value_set_enum (value, esdmon->channels);
345       break;
346     case ARG_RATE:
347       g_value_set_int (value, esdmon->frequency);
348       break;
349     case ARG_HOST:
350       g_value_set_string (value, esdmon->host);
351       break;
352     default:
353       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
354       break;
355   }
356 }
357
358 gboolean
359 gst_esdmon_factory_init (GstPlugin * plugin)
360 {
361   if (!gst_element_register (plugin, "esdmon", GST_RANK_NONE, GST_TYPE_ESDMON))
362     return FALSE;
363
364   return TRUE;
365 }
366
367 static gboolean
368 gst_esdmon_open_audio (GstEsdmon * src)
369 {
370   /* Name used by esound for this connection. */
371   const char *connname = "GStreamer";
372
373   /* Bitmap describing audio format. */
374   esd_format_t esdformat = ESD_STREAM | ESD_PLAY;
375
376   g_return_val_if_fail (src->fd == -1, FALSE);
377
378   if (src->depth == 16)
379     esdformat |= ESD_BITS16;
380   else if (src->depth == 8)
381     esdformat |= ESD_BITS8;
382   else {
383     GST_DEBUG ("esdmon: invalid bit depth (%d)", src->depth);
384     return FALSE;
385   }
386
387   if (src->channels == 2)
388     esdformat |= ESD_STEREO;
389   else if (src->channels == 1)
390     esdformat |= ESD_MONO;
391   else {
392     GST_DEBUG ("esdmon: invalid number of channels (%d)", src->channels);
393     return FALSE;
394   }
395
396   GST_DEBUG ("esdmon: attempting to open connection to esound server");
397   src->fd = esd_monitor_stream (esdformat, src->frequency, src->host, connname);
398   if (src->fd < 0) {
399     GST_DEBUG ("esdmon: can't open connection to esound server");
400     return FALSE;
401   }
402
403   GST_FLAG_SET (src, GST_ESDMON_OPEN);
404
405   return TRUE;
406 }
407
408 static void
409 gst_esdmon_close_audio (GstEsdmon * src)
410 {
411   if (src->fd < 0)
412     return;
413
414   close (src->fd);
415   src->fd = -1;
416
417   GST_FLAG_UNSET (src, GST_ESDMON_OPEN);
418
419   GST_DEBUG ("esdmon: closed sound device");
420 }
421
422 static GstElementStateReturn
423 gst_esdmon_change_state (GstElement * element)
424 {
425   g_return_val_if_fail (GST_IS_ESDMON (element), FALSE);
426
427   /* if going down into NULL state, close the fd if it's open */
428   if (GST_STATE_PENDING (element) == GST_STATE_NULL) {
429     if (GST_FLAG_IS_SET (element, GST_ESDMON_OPEN))
430       gst_esdmon_close_audio (GST_ESDMON (element));
431     /* otherwise (READY or higher) we need to open the fd */
432   } else {
433     if (!GST_FLAG_IS_SET (element, GST_ESDMON_OPEN)) {
434       if (!gst_esdmon_open_audio (GST_ESDMON (element)))
435         return GST_STATE_FAILURE;
436     }
437   }
438
439   if (GST_ELEMENT_CLASS (parent_class)->change_state)
440     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
441   return GST_STATE_SUCCESS;
442 }