sys/oss/gstosssrc.*: Easy to implement a mixer, eh...
[platform/upstream/gst-plugins-good.git] / sys / oss / gstosssrc.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *               2000,2005 Wim Taymans <wim@fluendo.com>
4  *
5  * gstosssrc.c: 
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 <sys/ioctl.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <sys/soundcard.h>
32
33 #include "gstosssrc.h"
34
35
36 static GstElementDetails gst_oss_src_details =
37 GST_ELEMENT_DETAILS ("Audio Source (OSS)",
38     "Source/Audio",
39     "Capture from a sound card via OSS",
40     "Erik Walthinsen <omega@cse.ogi.edu>, " "Wim Taymans <wim@fluendo.com>");
41
42
43 enum
44 {
45   PROP_0,
46   PROP_DEVICE,
47   PROP_DEVICE_NAME,
48 };
49
50 GST_BOILERPLATE_WITH_INTERFACE (GstOssSrc, gst_oss_src, GstAudioSrc,
51     GST_TYPE_AUDIO_SRC, GstMixer, GST_TYPE_MIXER, gst_oss_src_mixer);
52
53 GST_IMPLEMENT_OSS_MIXER_METHODS (GstOssSrc, gst_oss_src_mixer);
54
55 static void gst_oss_src_get_property (GObject * object, guint prop_id,
56     GValue * value, GParamSpec * pspec);
57 static void gst_oss_src_set_property (GObject * object, guint prop_id,
58     const GValue * value, GParamSpec * pspec);
59
60 static void gst_oss_src_dispose (GObject * object);
61
62 static GstCaps *gst_oss_src_getcaps (GstBaseSrc * bsrc);
63
64 static gboolean gst_oss_src_open (GstAudioSrc * asrc);
65 static gboolean gst_oss_src_close (GstAudioSrc * asrc);
66 static gboolean gst_oss_src_prepare (GstAudioSrc * asrc,
67     GstRingBufferSpec * spec);
68 static gboolean gst_oss_src_unprepare (GstAudioSrc * asrc);
69 static guint gst_oss_src_read (GstAudioSrc * asrc, gpointer data, guint length);
70 static guint gst_oss_src_delay (GstAudioSrc * asrc);
71 static void gst_oss_src_reset (GstAudioSrc * asrc);
72
73
74
75 static GstStaticPadTemplate osssrc_src_factory = GST_STATIC_PAD_TEMPLATE ("src",
76     GST_PAD_SRC,
77     GST_PAD_ALWAYS,
78     GST_STATIC_CAPS ("audio/x-raw-int, "
79         "endianness = (int) { " G_STRINGIFY (G_BYTE_ORDER) " }, "
80         "signed = (boolean) { TRUE, FALSE }, "
81         "width = (int) 16, "
82         "depth = (int) 16, "
83         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
84         "audio/x-raw-int, "
85         "signed = (boolean) { TRUE, FALSE }, "
86         "width = (int) 8, "
87         "depth = (int) 8, "
88         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]")
89     );
90
91
92 static void
93 gst_oss_src_dispose (GObject * object)
94 {
95   G_OBJECT_CLASS (parent_class)->dispose (object);
96 }
97
98 static void
99 gst_oss_src_base_init (gpointer g_class)
100 {
101   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
102
103   gst_element_class_set_details (element_class, &gst_oss_src_details);
104
105   gst_element_class_add_pad_template (element_class,
106       gst_static_pad_template_get (&osssrc_src_factory));
107 }
108 static void
109 gst_oss_src_class_init (GstOssSrcClass * klass)
110 {
111   GObjectClass *gobject_class;
112   GstElementClass *gstelement_class;
113   GstBaseSrcClass *gstbasesrc_class;
114   GstBaseAudioSrcClass *gstbaseaudiosrc_class;
115   GstAudioSrcClass *gstaudiosrc_class;
116
117   gobject_class = (GObjectClass *) klass;
118   gstelement_class = (GstElementClass *) klass;
119   gstbasesrc_class = (GstBaseSrcClass *) klass;
120   gstbaseaudiosrc_class = (GstBaseAudioSrcClass *) klass;
121   gstaudiosrc_class = (GstAudioSrcClass *) klass;
122
123   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_oss_src_dispose);
124   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_oss_src_get_property);
125   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_oss_src_set_property);
126
127   gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_oss_src_getcaps);
128
129   gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_oss_src_open);
130   gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_oss_src_prepare);
131   gstaudiosrc_class->unprepare = GST_DEBUG_FUNCPTR (gst_oss_src_unprepare);
132   gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_oss_src_close);
133   gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_oss_src_read);
134   gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_oss_src_delay);
135   gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_oss_src_reset);
136
137   g_object_class_install_property (gobject_class, PROP_DEVICE,
138       g_param_spec_string ("device", "Device",
139           "OSS device (usually /dev/dspN)", "/dev/dsp", G_PARAM_READWRITE));
140
141   g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
142       g_param_spec_string ("device-name", "Device name",
143           "Human-readable name of the sound device", "", G_PARAM_READABLE));
144 }
145
146 static void
147 gst_oss_src_set_property (GObject * object, guint prop_id,
148     const GValue * value, GParamSpec * pspec)
149 {
150   GstOssSrc *src;
151
152   src = GST_OSS_SRC (object);
153
154   switch (prop_id) {
155     case PROP_DEVICE:
156       if (src->device)
157         g_free (src->device);
158       src->device = g_strdup (g_value_get_string (value));
159       break;
160     default:
161       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
162       break;
163   }
164 }
165
166 static void
167 gst_oss_src_get_property (GObject * object, guint prop_id,
168     GValue * value, GParamSpec * pspec)
169 {
170   GstOssSrc *src;
171
172   src = GST_OSS_SRC (object);
173
174   switch (prop_id) {
175     case PROP_DEVICE:
176       g_value_set_string (value, src->device);
177       break;
178     case PROP_DEVICE_NAME:
179       g_value_set_string (value, src->device_name);
180       break;
181     default:
182       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
183       break;
184   }
185 }
186
187 static void
188 gst_oss_src_init (GstOssSrc * osssrc)
189 {
190   GST_DEBUG ("initializing osssrc");
191
192   osssrc->fd = -1;
193   osssrc->device = g_strdup ("/dev/dsp");
194 }
195
196 static GstCaps *
197 gst_oss_src_getcaps (GstBaseSrc * bsrc)
198 {
199   GstOssSrc *osssrc;
200   GstCaps *caps;
201
202   osssrc = GST_OSS_SRC (bsrc);
203
204   if (osssrc->fd == -1) {
205     caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD
206             (bsrc)));
207   } else {
208     caps = gst_oss_helper_probe_caps (osssrc->fd);
209   }
210
211   return caps;
212 }
213
214 static gint
215 ilog2 (gint x)
216 {
217   /* well... hacker's delight explains... */
218   x = x | (x >> 1);
219   x = x | (x >> 2);
220   x = x | (x >> 4);
221   x = x | (x >> 8);
222   x = x | (x >> 16);
223   x = x - ((x >> 1) & 0x55555555);
224   x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
225   x = (x + (x >> 4)) & 0x0f0f0f0f;
226   x = x + (x >> 8);
227   x = x + (x >> 16);
228   return (x & 0x0000003f) - 1;
229 }
230
231 #define SET_PARAM(_oss, _label, _name, _val)    \
232 G_STMT_START {                                  \
233   int _tmp = _val;                              \
234   if (ioctl(_oss->fd, _name, &_tmp) == -1) {    \
235     perror(_label);                             \
236     return FALSE;                               \
237   }                                             \
238   GST_DEBUG_OBJECT (_oss, _label " %d", _tmp);  \
239 } G_STMT_END
240
241 #define GET_PARAM(oss, label, name, val)        \
242 G_STMT_START {                                  \
243   if (ioctl(oss->fd, name, val) == -1) {        \
244     perror(label);                              \
245     return FALSE;                               \
246   }                                             \
247 } G_STMT_END
248
249 static gint
250 gst_oss_src_get_format (GstBufferFormat fmt)
251 {
252   gint result;
253
254   switch (fmt) {
255     case GST_MU_LAW:
256       result = AFMT_MU_LAW;
257       break;
258     case GST_A_LAW:
259       result = AFMT_A_LAW;
260       break;
261     case GST_IMA_ADPCM:
262       result = AFMT_IMA_ADPCM;
263       break;
264     case GST_U8:
265       result = AFMT_U8;
266       break;
267     case GST_S16_LE:
268       result = AFMT_S16_LE;
269       break;
270     case GST_S16_BE:
271       result = AFMT_S16_BE;
272       break;
273     case GST_S8:
274       result = AFMT_S8;
275       break;
276     case GST_U16_LE:
277       result = AFMT_U16_LE;
278       break;
279     case GST_U16_BE:
280       result = AFMT_U16_BE;
281       break;
282     case GST_MPEG:
283       result = AFMT_MPEG;
284       break;
285     default:
286       result = 0;
287       break;
288   }
289   return result;
290 }
291
292 static gboolean
293 gst_oss_src_open (GstAudioSrc * asrc)
294 {
295   GstOssSrc *oss;
296   int mode;
297
298   oss = GST_OSS_SRC (asrc);
299
300   mode = O_RDONLY;
301   mode |= O_NONBLOCK;
302
303   oss->fd = open (oss->device, mode, 0);
304   if (oss->fd == -1) {
305     perror (oss->device);
306     return FALSE;
307   }
308
309   if (!oss->mixer)
310     oss->mixer = gst_ossmixer_new ("/dev/mixer", GST_OSS_MIXER_CAPTURE);
311
312   return TRUE;
313 }
314
315 static gboolean
316 gst_oss_src_close (GstAudioSrc * asrc)
317 {
318   GstOssSrc *oss;
319
320   oss = GST_OSS_SRC (asrc);
321
322   close (oss->fd);
323
324   if (oss->mixer) {
325     gst_ossmixer_free (oss->mixer);
326     oss->mixer = NULL;
327   }
328
329   return TRUE;
330 }
331
332 static gboolean
333 gst_oss_src_prepare (GstAudioSrc * asrc, GstRingBufferSpec * spec)
334 {
335   GstOssSrc *oss;
336   struct audio_buf_info info;
337   int mode;
338   int tmp;
339
340   oss = GST_OSS_SRC (asrc);
341
342   mode = fcntl (oss->fd, F_GETFL);
343   mode &= ~O_NONBLOCK;
344   if (fcntl (oss->fd, F_SETFL, mode) == -1) {
345     perror (oss->device);
346     return FALSE;
347   }
348
349   tmp = gst_oss_src_get_format (spec->format);
350   if (tmp == 0)
351     goto wrong_format;
352
353   tmp = ilog2 (spec->segsize);
354   tmp = ((spec->segtotal & 0x7fff) << 16) | tmp;
355   GST_DEBUG ("set segsize: %d, segtotal: %d, value: %08x", spec->segsize,
356       spec->segtotal, tmp);
357
358   SET_PARAM (oss, "SETFRAGMENT", SNDCTL_DSP_SETFRAGMENT, tmp);
359
360   SET_PARAM (oss, "RESET", SNDCTL_DSP_RESET, 0);
361
362   SET_PARAM (oss, "SETFMT", SNDCTL_DSP_SETFMT, tmp);
363   if (spec->channels == 2)
364     SET_PARAM (oss, "STEREO", SNDCTL_DSP_STEREO, 1);
365   SET_PARAM (oss, "CHANNELS", SNDCTL_DSP_CHANNELS, spec->channels);
366   SET_PARAM (oss, "SPEED", SNDCTL_DSP_SPEED, spec->rate);
367
368   GET_PARAM (oss, "GETISPACE", SNDCTL_DSP_GETISPACE, &info);
369
370   spec->segsize = info.fragsize;
371   spec->segtotal = info.fragstotal;
372   spec->bytes_per_sample = 4;
373   oss->bytes_per_sample = 4;
374   memset (spec->silence_sample, 0, spec->bytes_per_sample);
375
376   GST_DEBUG ("got segsize: %d, segtotal: %d, value: %08x", spec->segsize,
377       spec->segtotal, tmp);
378
379   return TRUE;
380
381 wrong_format:
382   {
383     GST_DEBUG ("wrong format %d\n", spec->format);
384     return FALSE;
385   }
386 }
387
388 static gboolean
389 gst_oss_src_unprepare (GstAudioSrc * asrc)
390 {
391   /* could do a SNDCTL_DSP_RESET, but the OSS manual recommends a close/open */
392
393   if (!gst_oss_src_close (asrc))
394     goto couldnt_close;
395
396   if (!gst_oss_src_open (asrc))
397     goto couldnt_reopen;
398
399   return TRUE;
400
401 couldnt_close:
402   {
403     GST_DEBUG ("Could not close the audio device");
404     return FALSE;
405   }
406 couldnt_reopen:
407   {
408     GST_DEBUG ("Could not reopen the audio device");
409     return FALSE;
410   }
411 }
412
413 static guint
414 gst_oss_src_read (GstAudioSrc * asrc, gpointer data, guint length)
415 {
416   return read (GST_OSS_SRC (asrc)->fd, data, length);
417 }
418
419 static guint
420 gst_oss_src_delay (GstAudioSrc * asrc)
421 {
422   GstOssSrc *oss;
423   gint delay = 0;
424   gint ret;
425
426   oss = GST_OSS_SRC (asrc);
427
428 #ifdef SNDCTL_DSP_GETODELAY
429   ret = ioctl (oss->fd, SNDCTL_DSP_GETODELAY, &delay);
430 #else
431   ret = -1;
432 #endif
433   if (ret < 0) {
434     audio_buf_info info;
435
436     ret = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &info);
437
438     delay = (ret < 0 ? 0 : (info.fragstotal * info.fragsize) - info.bytes);
439   }
440   return delay / oss->bytes_per_sample;
441 }
442
443 static void
444 gst_oss_src_reset (GstAudioSrc * asrc)
445 {
446   GstOssSrc *oss;
447
448   //gint ret;
449
450   oss = GST_OSS_SRC (asrc);
451
452   /* deadlocks on my machine... */
453   //ret = ioctl (oss->fd, SNDCTL_DSP_RESET, 0);
454 }