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