merge from release branch
[platform/upstream/gstreamer.git] / ext / alsa / gstalsa.c
1 /* -*- c-basic-offset: 4 -*- */
2 /*
3     Copyright (C) 2001 CodeFactory AB
4     Copyright (C) 2001 Thomas Nyberg <thomas@codefactory.se>
5     Copyright (C) 2001-2002 Andy Wingo <apwingo@eos.ncsu.edu>
6
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU 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     General Public License for more details.
16
17     You should have received a copy of the GNU General Public
18     License along with this library; if not, write to the Free
19     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include <stdlib.h>
23 #include <sys/time.h>
24 #include "gstalsa.h"
25
26 static GstElementDetails gst_alsa_sink_details = {  
27     "Alsa Sink",
28     "Sink/Audio",
29     "Output to a sound card via ALSA",
30     VERSION,
31     "Thomas Nyberg <thomas@codefactory.se>, "
32     "Andy Wingo <apwingo@eos.ncsu.edu>",
33     "(C) 2001 "
34 };
35
36 static GstElementDetails gst_alsa_src_details = {  
37     "Alsa Src",
38     "Source/Audio",
39     "Read from a sound card via ALSA",
40     VERSION,
41     "Thomas Nyberg <thomas@codefactory.se>, "
42     "Andy Wingo <apwingo@eos.ncsu.edu>",
43     "(C) 2001"
44 };
45
46 static GstElement *parent_class = NULL;
47
48 static void gst_alsa_init(GstAlsa *this);
49 static void gst_alsa_class_init(GstAlsaClass *klass);
50
51 static GstPadTemplate *gst_alsa_src_pad_factory();
52 static GstPadTemplate *gst_alsa_src_request_pad_factory();
53 static GstPadTemplate *gst_alsa_sink_pad_factory();
54 static GstPadTemplate *gst_alsa_sink_request_pad_factory();
55
56 static GstPad* gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const
57                                          gchar *name);
58
59 static void gst_alsa_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
60 static void gst_alsa_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
61 static GstElementStateReturn gst_alsa_change_state(GstElement *element);
62 static GstPadConnectReturn gst_alsa_connect(GstPad *pad, GstCaps *caps);
63
64 static GstCaps* gst_alsa_caps (GstAlsa *this);
65
66 static gboolean gst_alsa_open_audio(GstAlsa *this);
67 static gboolean gst_alsa_start_audio(GstAlsa *this);
68 static void gst_alsa_stop_audio(GstAlsa *this);
69 static void gst_alsa_close_audio(GstAlsa *this);
70
71 static gboolean gst_alsa_set_params(GstAlsa *this);
72 static void gst_alsa_loop (GstElement *element);
73 static void gst_alsa_xrun_recovery (GstAlsa *this);
74 static gboolean gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames);
75 static gboolean gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames);
76
77 static gboolean gst_alsa_get_channel_addresses (GstAlsa *this);
78 static void gst_alsa_release_channel_addresses (GstAlsa *this);
79
80 static void gst_alsa_sink_silence_on_channel (GstAlsa *this, guint32 chn, guint32 nframes);
81 static void memset_interleave (char *dst, char val, unsigned int bytes, 
82                                unsigned int unit_bytes, 
83                                unsigned int skip_bytes);
84
85 /* #define _DEBUG */
86 #ifdef _DEBUG
87 #define DEBUG(text, args...) g_message(text, ##args)
88 #else
89 #define DEBUG(text, args...)
90 #endif
91
92
93 enum {
94     ARG_0,
95     ARG_DEVICE,
96     ARG_FORMAT,
97     ARG_CHANNELS,
98     ARG_RATE,
99     ARG_PERIODCOUNT,
100     ARG_PERIODFRAMES,
101     ARG_DEBUG
102 };
103
104 #define GST_TYPE_ALSA_FORMAT (gst_alsa_format_get_type())
105 static GType
106 gst_alsa_format_get_type (void) 
107 {
108     static GType type = 0;
109     static GEnumValue *values = NULL;
110     gint i, len = SND_PCM_FORMAT_GSM + 3;
111
112     if (values == NULL) {
113         /* the three: for -1, 0, and the terminating NULL */
114         values = g_new0 (GEnumValue, len);
115     
116         for (i=0; i<len-1; i++) {
117             values[i].value = i-1; /* UNKNOWN is -1 */
118             values[i].value_name = g_strdup_printf ("%d", i-1);
119             values[i].value_nick = g_strdup (snd_pcm_format_name ((snd_pcm_format_t)i-1));
120         }
121     }
122
123     if (!type) {
124         type = g_enum_register_static ("GstAlsaFormat", values);
125     }
126     return type;
127 }
128
129 GType
130 gst_alsa_get_type (void) 
131 {
132     static GType alsa_type = 0;
133     
134     if (!alsa_type) {
135         static const GTypeInfo alsa_info = {
136             sizeof(GstAlsaClass),
137             NULL,
138             NULL,
139             NULL,
140             NULL,
141             NULL,
142             sizeof(GstAlsa),
143             0,
144             NULL,
145         };
146         alsa_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAlsa", &alsa_info, 0);
147     }
148     return alsa_type;
149 }
150
151 GType
152 gst_alsa_sink_get_type (void) 
153 {
154     static GType alsa_type = 0;
155
156     if (!alsa_type) {
157         static const GTypeInfo alsa_info = {
158             sizeof(GstAlsaClass),
159             NULL,
160             NULL,
161             (GClassInitFunc)gst_alsa_class_init,
162             NULL,
163             NULL,
164             sizeof(GstAlsa),
165             0,
166             (GInstanceInitFunc)gst_alsa_init,
167         };
168         alsa_type = g_type_register_static (GST_TYPE_ALSA, "GstAlsaSink", &alsa_info, 0);
169     }
170     return alsa_type;
171 }
172
173 GType
174 gst_alsa_src_get_type (void) 
175 {
176     static GType alsa_type = 0;
177     
178     if (!alsa_type) {
179         static const GTypeInfo alsa_info = {
180             sizeof(GstAlsaClass),
181             NULL,
182             NULL,
183             (GClassInitFunc)gst_alsa_class_init,
184             NULL,
185             NULL,
186             sizeof(GstAlsa),
187             0,
188             (GInstanceInitFunc)gst_alsa_init,
189         };
190         alsa_type = g_type_register_static (GST_TYPE_ALSA, "GstAlsaSrc", &alsa_info, 0);
191     }
192     return alsa_type;
193 }
194
195 static GstPadTemplate*
196 gst_alsa_src_pad_factory(void)
197 {
198     static GstPadTemplate *template = NULL;
199     
200     if (!template)
201         template = gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_SOMETIMES, 
202                                         gst_caps_new("src", "audio/raw", NULL),
203                                         NULL);
204     
205     return template;
206 }
207
208 static GstPadTemplate*
209 gst_alsa_src_request_pad_factory(void)
210 {
211     static GstPadTemplate *template = NULL;
212     
213     if (!template)
214         template = gst_pad_template_new("src%d", GST_PAD_SRC, GST_PAD_REQUEST, 
215                                         gst_caps_new("src-request", "audio/raw",
216                                                      gst_props_new("channels", GST_PROPS_INT(1), NULL)),
217                                         NULL);
218     
219     return template;
220 }
221
222 static GstPadTemplate*
223 gst_alsa_sink_pad_factory(void)
224 {
225     static GstPadTemplate *template = NULL;
226     
227     if (!template)
228         template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_SOMETIMES, 
229                                         gst_caps_new("sink", "audio/raw", NULL),
230                                         NULL);
231     
232     return template;
233 }
234
235 static GstPadTemplate*
236 gst_alsa_sink_request_pad_factory(void)
237 {
238     static GstPadTemplate *template = NULL;
239     
240     if (!template)
241         template = gst_pad_template_new("sink%d", GST_PAD_SINK, GST_PAD_REQUEST, 
242                                         gst_caps_new("sink-request", "audio/raw",
243                                                      gst_props_new("channels", GST_PROPS_INT(1), NULL)),
244                                         NULL);
245     
246     return template;
247 }
248
249 static void
250 gst_alsa_class_init(GstAlsaClass *klass)
251 {
252     GObjectClass *object_class;
253     GstElementClass *element_class;
254     
255     object_class = (GObjectClass *)klass;
256     element_class = (GstElementClass *)klass;
257     
258     if (parent_class == NULL)
259         parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
260     
261     object_class->get_property = gst_alsa_get_property;
262     object_class->set_property = gst_alsa_set_property;
263     
264     g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEVICE,
265                                     g_param_spec_string("device","Device","Alsa device, as defined in an asoundrc",
266                                                      "default",
267                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
268     g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FORMAT,
269                                     g_param_spec_enum("format","Format","PCM audio format",
270                                                      GST_TYPE_ALSA_FORMAT, -1,
271                                                      G_PARAM_READWRITE));
272     g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_CHANNELS,
273                                     g_param_spec_int("channels","Channels","Number of channels",
274                                                      1, 64, 2,
275                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
276     g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_RATE,
277                                     g_param_spec_int("rate","Rate","Sample rate, in Hz",
278                                                      8000, 192000, 44100,
279                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
280     g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_PERIODCOUNT,
281                                     g_param_spec_int("period-count","Period count","Number of hardware buffers to use",
282                                                      2, 64, 2,
283                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
284     g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_PERIODFRAMES,
285                                     g_param_spec_int("period-frames","Period frames","Number of frames (samples on each channel) in one hardware period",
286                                                      64, 8192, 8192,
287                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
288     g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEBUG,
289                                     g_param_spec_boolean("debug","Debug","Set to TRUE to output PCM state info",
290                                                      FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
291     
292     element_class->change_state = gst_alsa_change_state;
293     
294     element_class->request_new_pad = gst_alsa_request_new_pad;
295 }
296
297 static void
298 gst_alsa_init(GstAlsa *this)
299 {
300     /* init values */
301     this->handle = NULL;
302     
303     GST_FLAG_SET(this, GST_ELEMENT_THREAD_SUGGESTED);
304     
305     if (G_OBJECT_TYPE(this) == GST_TYPE_ALSA_SRC) {
306         this->stream = SND_PCM_STREAM_CAPTURE;
307         this->pads = g_list_append(NULL, g_new0(GstAlsaPad, 1));
308         GST_ALSA_PAD(this->pads)->pad = gst_pad_new_from_template(gst_alsa_src_pad_factory(), "src");
309         this->process = gst_alsa_src_process;
310         this->format = SND_PCM_FORMAT_S16; /* native endian */
311     } else if (G_OBJECT_TYPE(this) == GST_TYPE_ALSA_SINK) {
312         this->stream = SND_PCM_STREAM_PLAYBACK;
313         this->pads = g_list_append(NULL, g_new0(GstAlsaPad, 1));
314         GST_ALSA_PAD(this->pads)->pad = gst_pad_new_from_template(gst_alsa_sink_pad_factory(), "sink");
315         this->process = gst_alsa_sink_process;
316         this->format = SND_PCM_FORMAT_UNKNOWN; /* we don't know until caps are
317                                                 * set */
318     }
319     
320     GST_ALSA_PAD(this->pads)->channel = -1;
321     
322     this->data_interleaved = TRUE;
323     
324     gst_element_add_pad(GST_ELEMENT(this), GST_ALSA_PAD(this->pads)->pad);
325     
326     gst_pad_set_connect_function(GST_ALSA_PAD(this->pads)->pad, gst_alsa_connect);
327     gst_element_set_loop_function(GST_ELEMENT(this), gst_alsa_loop);
328 }
329
330 static GstPad*
331 gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name) 
332 {
333     GstAlsa *this;
334     gint channel;
335     gchar *newname;
336     GList *l;
337     GstAlsaPad *pad;
338     
339     g_return_val_if_fail ((this = GST_ALSA(element)), NULL);
340     
341     /* you can't request a pad if the non-request pad is connected */
342     g_return_val_if_fail (this->data_interleaved == FALSE ||
343                           this->pads == NULL || 
344                           GST_ALSA_PAD(this->pads) == NULL ||
345                           GST_ALSA_PAD(this->pads)->pad == NULL ||
346                           GST_PAD_PEER(GST_ALSA_PAD(this->pads)->pad) == NULL, 
347                           NULL);
348     
349     if (name) {
350         channel = atoi (name + (strchr (templ->name_template, '%') - templ->name_template));
351         
352         l = this->pads;
353         while (l) {
354             if (GST_ALSA_PAD(l)->channel == channel) {
355                 g_warning("requested channel %d already in use.", channel);
356                 return NULL;
357             }
358             l = l->next;
359         }
360     } else {
361         l = this->pads;
362         channel = 0;
363         while (l) {
364             if (GST_ALSA_PAD(l)->channel > channel)
365                 channel = GST_ALSA_PAD(l)->channel;
366             l = l->next;
367         }
368     }
369     
370     newname = g_strdup (name);
371     
372     pad = g_new0(GstAlsaPad, 1);
373     pad->channel = channel;
374     pad->pad = gst_pad_new_from_template (templ, newname);
375     gst_element_add_pad (GST_ELEMENT (this), pad->pad);
376     gst_pad_set_connect_function(pad->pad, gst_alsa_connect);
377     
378     if (this->data_interleaved && this->pads) {
379         gst_element_remove_pad (GST_ELEMENT (this), GST_ALSA_PAD(this->pads)->pad);
380         g_free (GST_ALSA_PAD(this->pads));
381         g_list_free (this->pads);
382         this->pads = NULL;
383     }
384     
385     this->pads = g_list_append(this->pads, pad);
386     
387     /* FIXME: allow interleaved access (for hw:N,M access on consumer hardware) */
388
389     if (this->data_interleaved) {
390         this->channels = pad->channel + 1;
391         this->data_interleaved = FALSE;
392     } else {
393         this->channels = MAX(this->channels, pad->channel + 1);
394     }
395     
396     return pad->pad;
397 }
398
399 static void
400 gst_alsa_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
401 {
402     GstAlsa *this;
403     
404     this = (GstAlsa *)object;
405     switch (prop_id) {
406     case ARG_DEVICE:
407         if (this->device)
408             g_free (this->device);
409         this->device = g_strdup(g_value_get_string (value));
410         break;
411     case ARG_FORMAT:
412         this->format = g_value_get_enum (value);
413         break;
414     case ARG_CHANNELS:
415         this->channels = g_value_get_int (value);
416         break;
417     case ARG_RATE:
418         this->rate = g_value_get_int (value);
419         break;
420     case ARG_PERIODCOUNT:
421         this->period_count = g_value_get_int (value);
422         this->buffer_frames = this->period_count * this->period_frames;
423         break;
424     case ARG_PERIODFRAMES:
425         this->period_frames = g_value_get_int (value);
426         this->buffer_frames = this->period_count * this->period_frames;
427         break;
428     case ARG_DEBUG:
429         this->debug = g_value_get_boolean (value);
430         return;
431     default:
432         GST_DEBUG(0, "Unknown arg");
433         return;
434     }
435     
436     if (GST_STATE(this) == GST_STATE_NULL)
437         return;
438     
439     if (GST_FLAG_IS_SET(this, GST_ALSA_RUNNING)) {
440         gst_alsa_stop_audio(this);
441         gst_alsa_set_params(this);
442         gst_alsa_start_audio(this);
443     } else {
444         gst_alsa_set_params(this);
445     }
446 }
447
448 static void
449 gst_alsa_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
450 {
451     GstAlsa *this;
452
453     this = (GstAlsa *)object;
454
455     switch (prop_id) {
456     case ARG_DEVICE:
457         g_value_set_string (value, this->device);
458         break;
459     case ARG_FORMAT:
460         g_value_set_enum (value, this->format);
461         break;
462     case ARG_CHANNELS:
463         g_value_set_int (value, this->channels);
464         break;
465     case ARG_RATE:
466         g_value_set_int (value, this->rate);
467         break;
468     case ARG_PERIODCOUNT:
469         g_value_set_int (value, this->period_count);
470         break;
471     case ARG_PERIODFRAMES:
472         g_value_set_int (value, this->period_frames);
473         break;
474     case ARG_DEBUG:
475         g_value_set_boolean (value, this->debug);
476         break;
477     default:
478         GST_DEBUG(0, "Unknown arg");
479         break;
480     }
481 }
482
483 static GstElementStateReturn
484 gst_alsa_change_state(GstElement *element)
485 {
486     GstAlsa *this;
487     guint chn;
488     
489     g_return_val_if_fail(element != NULL, FALSE);
490     this = GST_ALSA (element);
491     
492     switch (GST_STATE_PENDING(element)) {
493     case GST_STATE_NULL:
494         if (GST_FLAG_IS_SET(element, GST_ALSA_RUNNING))
495             gst_alsa_stop_audio((GstAlsa *)element);
496         if (GST_FLAG_IS_SET(element, GST_ALSA_OPEN))
497             gst_alsa_close_audio((GstAlsa *)element);
498         /* FIXME: clean up bytestreams, etc */
499         break;
500         
501     case GST_STATE_READY:
502         break;
503         
504     case GST_STATE_PAUSED:
505         if (GST_FLAG_IS_SET(element, GST_ALSA_OPEN) == FALSE)
506             if (gst_alsa_open_audio((GstAlsa *)element) == FALSE)
507                 return GST_STATE_FAILURE;
508         if (GST_FLAG_IS_SET(element, GST_ALSA_RUNNING)) {
509             if (this->stream == SND_PCM_STREAM_PLAYBACK) {
510                 for (chn = 0; chn < this->channels; chn++) {
511                     gst_alsa_sink_silence_on_channel (this, chn, this->avail);
512                 }
513             }
514             gst_alsa_stop_audio((GstAlsa *)element);
515         }
516         break;
517         
518     case GST_STATE_PLAYING:
519         if (GST_FLAG_IS_SET(element, GST_ALSA_RUNNING) == FALSE)
520             if (gst_alsa_start_audio((GstAlsa *)element) == FALSE)
521                 return GST_STATE_FAILURE;
522         break;
523     }
524     
525     if (GST_ELEMENT_CLASS(parent_class)->change_state)
526         return GST_ELEMENT_CLASS(parent_class)->change_state(element);
527
528     return GST_STATE_SUCCESS;
529 }
530
531 static gboolean
532 gst_alsa_parse_caps (GstAlsa *this, GstCaps *caps)
533 {
534     gint law, endianness, width, depth, channels;
535     gboolean sign;
536     gint format = -1;
537     const gchar* format_name;
538
539     if (!gst_caps_get_string (caps, "format", &format_name))
540         return FALSE;
541     
542     if (format_name == NULL) {
543         return FALSE;
544     } else if (strcmp(format_name, "int")==0) {
545         if (!gst_caps_get (caps,
546                            "width", &width,
547                            "depth", &depth,
548                            "law", &law,
549                            "endianness", &endianness,
550                            "signed", &sign,
551                            NULL))
552             return FALSE;
553         
554         if (law == 0) {
555             if (width == 8) {
556                 if (sign == TRUE) {
557                     format = SND_PCM_FORMAT_S8;
558                 } else {
559                     format = SND_PCM_FORMAT_U8;
560                 }
561             } else if (width == 16) {
562                 if (sign == TRUE) {
563                     if (endianness == G_LITTLE_ENDIAN)
564                         format = SND_PCM_FORMAT_S16_LE;
565                     else if (endianness == G_BIG_ENDIAN)
566                         format = SND_PCM_FORMAT_S16_BE;
567                 } else {
568                     if (endianness == G_LITTLE_ENDIAN)
569                         format = SND_PCM_FORMAT_U16_LE;
570                     else if (endianness == G_BIG_ENDIAN)
571                         format = SND_PCM_FORMAT_U16_BE;
572                 }
573             } else if (width == 24) {
574                 if (sign == TRUE) {
575                     if (endianness == G_LITTLE_ENDIAN)
576                         format = SND_PCM_FORMAT_S24_LE;
577                     else if (endianness == G_BIG_ENDIAN)
578                         format = SND_PCM_FORMAT_S24_BE;
579                 } else {
580                     if (endianness == G_LITTLE_ENDIAN)
581                         format = SND_PCM_FORMAT_U24_LE;
582                     else if (endianness == G_BIG_ENDIAN)
583                         format = SND_PCM_FORMAT_U24_BE;
584                 }
585             } else if (width == 32) {
586                 if (sign == TRUE) {
587                     if (endianness == G_LITTLE_ENDIAN)
588                         format = SND_PCM_FORMAT_S32_LE;
589                     else if (endianness == G_BIG_ENDIAN)
590                         format = SND_PCM_FORMAT_S32_BE;
591                 } else {
592                     if (endianness == G_LITTLE_ENDIAN)
593                         format = SND_PCM_FORMAT_U32_LE;
594                     else if (endianness == G_BIG_ENDIAN)
595                         format = SND_PCM_FORMAT_U32_BE;
596                 }
597             }
598         } else if (law == 1) { /* mu law */
599             if (width == depth && width == 8 && sign == FALSE) {
600                 format = SND_PCM_FORMAT_MU_LAW;
601             } else {
602                 return FALSE;
603             }
604         } else if (law == 2) { /* a law, ug. */
605             if (width == depth && width == 8 && sign == FALSE) {
606                 format = SND_PCM_FORMAT_A_LAW;
607             }
608         } else {
609             return FALSE;
610         }
611     } else if (strcmp(format_name, "float")==0) {
612         const gchar *layout;
613
614         if (!gst_caps_get_string (caps, "layout", &layout))
615             return FALSE;
616         
617         if (strcmp(layout, "gfloat")==0) {
618             format = SND_PCM_FORMAT_FLOAT;
619         } else {
620             return FALSE;
621             /* you need doubles? jeez... */
622         }
623     } else {
624         return FALSE;
625     }
626     
627     this->format = format;
628     if (!gst_caps_get (caps,
629                        "rate", &this->rate,
630                        "channels", &channels,
631                        NULL))
632         return FALSE;
633
634     if (this->data_interleaved)
635         this->channels = channels;
636     else if (channels != 1)
637         return FALSE;
638     
639     return TRUE;
640 }
641
642 /* caps are so painful sometimes. */
643 static GstCaps*
644 gst_alsa_caps (GstAlsa *this)
645 {
646     gint law, endianness, width, depth;
647     gboolean sign;
648     GstProps *props;
649     
650     g_return_val_if_fail (this != NULL && this->handle != NULL, NULL);
651     
652     if (this->format == SND_PCM_FORMAT_FLOAT) {
653         props = gst_props_new ("format", GST_PROPS_STRING ("float"),
654                                "layout", GST_PROPS_STRING ("gfloat"),
655                                "rate",       GST_PROPS_INT (this->rate),
656                                "channels",   GST_PROPS_INT ((this->data_interleaved ? this->channels : 1)),
657                                NULL);
658     } else {
659         /* we'll just have to assume int, i don't feel like checking */
660         if (this->format == SND_PCM_FORMAT_MU_LAW) {
661             law = 1;
662             width = 8;
663             sign = FALSE;
664             endianness = 0;
665         } else if (this->format == SND_PCM_FORMAT_A_LAW) {
666             law = 2;
667             width = 8;
668             sign = FALSE;
669             endianness = 0;
670         } else {
671             law = 0;
672             if (this->format == SND_PCM_FORMAT_S8) {
673                 width = 8;
674                 sign = TRUE;
675                 endianness = 0;
676             } else if (this->format == SND_PCM_FORMAT_U8) {
677                 width = 8;
678                 sign = FALSE;
679                 endianness = 0;
680             } else if (this->format == SND_PCM_FORMAT_S16_LE) {
681                 width = 16;
682                 sign = TRUE;
683                 endianness = G_LITTLE_ENDIAN;
684             } else if (this->format == SND_PCM_FORMAT_S16_BE) {
685                 width = 16;
686                 sign = TRUE;
687                 endianness = G_BIG_ENDIAN;
688             } else if (this->format == SND_PCM_FORMAT_U16_LE) {
689                 width = 16;
690                 sign = FALSE;
691                 endianness = G_LITTLE_ENDIAN;
692             } else if (this->format == SND_PCM_FORMAT_U16_BE) {
693                 width = 16;
694                 sign = FALSE;
695                 endianness = G_BIG_ENDIAN;
696             } else if (this->format == SND_PCM_FORMAT_S24_LE) {
697                 width = 24;
698                 sign = TRUE;
699                 endianness = G_LITTLE_ENDIAN;
700             } else if (this->format == SND_PCM_FORMAT_S24_BE) {
701                 width = 24;
702                 sign = TRUE;
703                 endianness = G_BIG_ENDIAN;
704             } else if (this->format == SND_PCM_FORMAT_U24_LE) {
705                 width = 24;
706                 sign = FALSE;
707                 endianness = G_LITTLE_ENDIAN;
708             } else if (this->format == SND_PCM_FORMAT_U24_BE) {
709                 width = 24;
710                 sign = FALSE;
711                 endianness = G_BIG_ENDIAN;
712             } else if (this->format == SND_PCM_FORMAT_S32_LE) {
713                 width = 32;
714                 sign = TRUE;
715                 endianness = G_LITTLE_ENDIAN;
716             } else if (this->format == SND_PCM_FORMAT_S32_BE) {
717                 width = 32;
718                 sign = TRUE;
719                 endianness = G_BIG_ENDIAN;
720             } else if (this->format == SND_PCM_FORMAT_U32_LE) {
721                 width = 32;
722                 sign = FALSE;
723                 endianness = G_LITTLE_ENDIAN;
724             } else if (this->format == SND_PCM_FORMAT_U32_BE) {
725                 width = 32;
726                 sign = FALSE;
727                 endianness = G_BIG_ENDIAN;
728             } else {
729                 g_error ("what is going on here?");
730                 return NULL;
731             }
732         }
733         depth = width;
734         props = gst_props_new ("format", GST_PROPS_STRING ("int"),
735                                "rate",       GST_PROPS_INT (this->rate),
736                                "channels",   GST_PROPS_INT ((this->data_interleaved ? this->channels : 1)),
737                                "law",        GST_PROPS_INT (law),
738                                "endianness", GST_PROPS_INT (endianness),
739                                "signed",     GST_PROPS_BOOLEAN (sign),
740                                "width",      GST_PROPS_INT (width),
741                                "depth",      GST_PROPS_INT (depth),
742                                NULL);
743     }
744     
745     return gst_caps_new ("alsasrc", "audio/raw", props);
746 }
747
748 /*
749  * Negotiates the caps, "borrowed" from gstosssink.c
750  */
751 GstPadConnectReturn
752 gst_alsa_connect(GstPad *pad, GstCaps *caps)
753 {
754     GstAlsa *this;
755     gboolean need_mmap;
756
757     g_return_val_if_fail (caps != NULL, GST_PAD_CONNECT_REFUSED);
758     g_return_val_if_fail (pad  != NULL, GST_PAD_CONNECT_REFUSED);
759
760     this = GST_ALSA(gst_pad_get_parent(pad));
761     
762     if (GST_CAPS_IS_FIXED (caps)) {
763         if (this->handle == NULL)
764             if (!gst_alsa_open_audio(this))
765                 return GST_PAD_CONNECT_REFUSED;
766         
767         if (gst_alsa_parse_caps(this, caps)) {
768             need_mmap = this->mmap_open;
769             
770             /* sync the params */
771             if (GST_FLAG_IS_SET(this, GST_ALSA_RUNNING))
772                 gst_alsa_stop_audio(this);
773             
774             if (GST_FLAG_IS_SET(this, GST_ALSA_OPEN))
775                 gst_alsa_close_audio(this);
776             
777             /* FIXME send out another caps if nego fails */
778             
779             if (!gst_alsa_open_audio(this))
780                 return GST_PAD_CONNECT_REFUSED;
781             
782             if (!gst_alsa_start_audio(this))
783                 return GST_PAD_CONNECT_REFUSED;
784             
785             if (need_mmap && !gst_alsa_get_channel_addresses(this))
786                 return GST_PAD_CONNECT_REFUSED;
787             
788             return GST_PAD_CONNECT_OK;
789         }
790         
791         return GST_PAD_CONNECT_REFUSED;
792     }
793     
794     return GST_PAD_CONNECT_DELAYED;
795 }
796
797 /* shamelessly stolen from pbd's audioengine and jack alsa_driver. thanks, paul! */
798 static void
799 gst_alsa_loop (GstElement *element)
800 {
801     struct pollfd pfd;
802     gboolean xrun_detected;
803     guint32 i;
804     GstAlsa *this = GST_ALSA(element);
805     
806     g_return_if_fail(this != NULL);
807     
808     snd_pcm_poll_descriptors (this->handle, &pfd, 1);
809     
810     if (this->stream == SND_PCM_STREAM_PLAYBACK) {
811         pfd.events = POLLOUT | POLLERR;
812     } else {
813         pfd.events = POLLIN | POLLERR;
814     }
815     
816     do {
817         if (poll (&pfd, 1, 1000) < 0) {
818             if (errno == EINTR) {
819                 /* this happens mostly when run
820                  * under gdb, or when exiting due to a signal */
821                 g_print ("EINTR\n");
822                 continue;
823             }
824             
825             g_warning("poll call failed (%s)", strerror(errno));
826             return;
827         }
828         
829         if (pfd.revents & POLLERR) {
830             g_warning("alsa: poll reports error.");
831             return;
832         }
833         
834         if (pfd.revents == 0) {
835             g_print ("poll on alsa %s device \"%s\" timed out\n",
836                      this->stream==SND_PCM_STREAM_CAPTURE ? "capture" : "playback",
837                      this->device);
838             /* timed out, such as when the device is paused */
839             continue;
840         }
841         
842         xrun_detected = FALSE;
843         
844         this->avail = snd_pcm_avail_update (this->handle);
845         DEBUG ("snd_pcm_avail_update() = %d", (int)this->avail);
846         
847         if (this->avail < 0) {
848             if (this->avail == -EPIPE) {
849                 gst_alsa_xrun_recovery (this);
850                 this->avail = 0;
851             } else {
852                 g_warning("unknown ALSA avail_update return value (%d)",
853                           (int)this->avail);
854                 return;
855             }
856         }
857         
858         /* round down to nearest period_frames avail */
859         this->avail -= this->avail % this->period_frames;
860
861         DEBUG ("snd_pcm_avail_update(), rounded down = %d", (int)this->avail);
862
863         /* we need to loop here because the available bytes might not be
864          * contiguous */
865         while (this->avail) {
866             /* changes this->avail, as a side effect */
867             if (!gst_alsa_get_channel_addresses (this) < 0) {
868                 g_error("could not get channels");
869                 return;
870             }
871             
872             if (this->mute && this->stream == SND_PCM_STREAM_PLAYBACK) {
873                 for (i = 0; i < this->channels; i++) {
874                     if (this->mute & (1<<i)) {
875                         gst_alsa_sink_silence_on_channel (this, i, this->buffer_frames);
876                     }
877                 }
878             }
879             
880             if (!this->process(this, this->avail)) {
881                 g_warning("alsa: something happened while processing audio");
882                 return;
883             }
884             
885             /* we could have released the mmap regions on a state change */
886             if (this->mmap_open)
887                 gst_alsa_release_channel_addresses(this);
888         }
889         gst_element_yield (element);
890     } while (TRUE);
891 }
892
893 static gboolean
894 gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames)
895 {
896     GstBuffer *buf;
897     GList *l;
898     GstAlsaPad *pad = NULL;
899     GstCaps *caps;
900     gint unit;
901 /*    gint i=0; */
902     
903     static gboolean caps_set = FALSE;
904     
905     if (!caps_set) {
906         /* let's get on the caps-setting merry-go-round! */
907         caps = gst_alsa_caps(this);
908         l = this->pads;
909         while (l) {
910             if (!gst_pad_try_set_caps (GST_ALSA_PAD(l)->pad, caps)) {
911                 g_print ("DANGER WILL ROBINSON!\n");
912                 sleep(1);
913                 return FALSE;
914             }
915             
916             l = l->next;
917         }
918         caps_set = TRUE;
919     }
920     
921     unit = this->sample_bytes * (this->data_interleaved ? this->channels : 1);
922     
923     while (frames) {
924 /*        g_print ("(%d) frames to process: %d\n", i++, frames); */
925         l = this->pads;
926         while (l) {
927             pad = GST_ALSA_PAD(l);
928             
929             if (!pad->buf) {
930                 pad->buf = g_malloc(this->period_frames * unit);
931                 /*  g_print ("created buffer %p of size %d\n", pad->buf, this->period_frames * unit); */
932             }
933             /*
934             g_print ("pad->buf = %p, offset = %d\n", pad->buf, pad->offset);
935             g_print ("about to memcpy(%p, %p, %d)\n", 
936                      pad->buf + pad->offset * unit,
937                      pad->access_addr,  
938                      MIN(frames, this->period_frames - pad->offset) * unit);
939             */
940             memcpy(pad->buf + pad->offset * unit,
941                    pad->access_addr, 
942                    MIN(frames, this->period_frames - pad->offset) * unit);
943             
944             pad->offset += MIN(frames, this->period_frames - pad->offset);
945             
946             if (pad->offset >= this->period_frames) {
947                 g_assert(pad->offset <= this->period_frames);
948                 buf = gst_buffer_new();
949                 GST_BUFFER_DATA(buf)    = pad->buf;
950                 GST_BUFFER_SIZE(buf)    = this->period_frames * unit;
951                 GST_BUFFER_MAXSIZE(buf) = this->period_frames * unit;
952                 gst_pad_push(pad->pad, buf);
953                 pad->buf = NULL;
954                 pad->offset = 0;
955             }
956             l = l->next;
957         }
958         frames -= MIN(frames, this->period_frames - pad->offset); /* shouldn't */
959         /* matter which pad, in theory (tm) */
960     }
961     
962     return TRUE;
963 }
964
965 static gboolean
966 gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames)
967 {
968     guint8 *peeked;
969     guint32 len, avail;
970     GstEvent *event = NULL;
971     GList *l;
972     
973     /* this is necessary because the sample_bytes will change, probably, when
974      * caps are set, which will occur after the first bytestream_peek. we
975      * underestimate the amount of data we will need by peeking 'frames' only.
976      * */
977     
978     /* FIXME: if 0 < peek_bytes < len, play the peek_bytes */
979
980     if (!this->sample_bytes) {
981         if (!GST_ALSA_PAD(this->pads)->bs)
982             GST_ALSA_PAD(this->pads)->bs = gst_bytestream_new(GST_ALSA_PAD(this->pads)->pad);
983         
984         if (gst_bytestream_peek_bytes (GST_ALSA_PAD (this->pads)->bs, &peeked, frames) != frames) {
985             g_warning("could not make initial pull of %d bytes on pad %s:%s", (int)frames, GST_DEBUG_PAD_NAME(GST_ALSA_PAD(this->pads)->pad));
986             gst_element_set_eos (GST_ELEMENT(this));
987             return FALSE;
988         }
989         
990         if (!this->sample_bytes) {
991             g_critical ("alsa plugin requires a pipeline that can adequately set caps.");
992             return FALSE;
993         }
994     }
995     
996     len = frames * this->channels * this->sample_bytes;
997     
998     l = this->pads;
999     while (l) {
1000         if (!GST_ALSA_PAD(this->pads)->bs)
1001             GST_ALSA_PAD(this->pads)->bs = gst_bytestream_new(GST_ALSA_PAD(this->pads)->pad);
1002         
1003         if (gst_bytestream_peek_bytes(GST_ALSA_PAD(this->pads)->bs, &peeked, len) != len) {
1004             gst_bytestream_get_status(GST_ALSA_PAD(this->pads)->bs, &avail, &event);
1005             if (event) {
1006                 g_warning("got an event on alsasink");
1007                 if (GST_EVENT_TYPE(event) == GST_EVENT_EOS) {
1008                     /* really, we should just cut this pad out of the graph. let
1009                      * me know when this is needed ;)
1010                      * also, for sample accuracy etc, we should play avail
1011                      * bytes, but hey. */
1012                     gst_element_set_eos(GST_ELEMENT(this));
1013                     gst_event_free(event);
1014                     return TRUE;
1015                 }
1016             } else {
1017                 /* the element at the top of the chain did not emit an eos
1018                  * event. this is a Bug(tm) */
1019                 g_assert_not_reached();
1020             }
1021         }
1022         
1023         memcpy(GST_ALSA_PAD(this->pads)->access_addr, peeked, len);
1024         gst_bytestream_flush(GST_ALSA_PAD(this->pads)->bs, len);
1025         
1026         l=l->next;
1027     }
1028     
1029     return TRUE;
1030 }
1031
1032 static void
1033 gst_alsa_xrun_recovery (GstAlsa *this)
1034 {
1035     snd_pcm_status_t *status;
1036     int res;
1037
1038     snd_pcm_status_alloca(&status);
1039
1040     if (this->stream == SND_PCM_STREAM_CAPTURE) {
1041         if ((res = snd_pcm_status(this->handle, status)) < 0) {
1042             g_warning ("status error: %s", snd_strerror(res));
1043         }
1044     } else {
1045         if ((res = snd_pcm_status(this->handle, status)) < 0) {
1046             g_warning ("status error: %s", snd_strerror(res));
1047         }
1048     }
1049     
1050     if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
1051         struct timeval now, diff, tstamp;
1052         gettimeofday(&now, 0);
1053         snd_pcm_status_get_trigger_tstamp(status, &tstamp);
1054         timersub(&now, &tstamp, &diff);
1055         g_warning("alsa: xrun of at least %.3f msecs", diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
1056     }
1057     
1058     gst_alsa_stop_audio (this);
1059     gst_alsa_start_audio (this);
1060 }       
1061
1062 /* taken more or less from pbd's audioengine code */
1063 static gboolean
1064 gst_alsa_set_params (GstAlsa *this)
1065 {
1066     snd_pcm_sw_params_t *sw_param;
1067     snd_pcm_hw_params_t *hw_param;
1068     snd_pcm_access_mask_t *mask;
1069     gint ret;
1070     
1071     g_return_val_if_fail(this != NULL, FALSE);
1072     g_return_val_if_fail(this->handle != NULL, FALSE);
1073     
1074     g_print("Preparing channel: %s %dHz, %d channels\n", 
1075             snd_pcm_format_name(this->format), 
1076             this->rate, this->channels);
1077         
1078     snd_pcm_hw_params_alloca(&hw_param);
1079     snd_pcm_sw_params_alloca(&sw_param);
1080         
1081     ret = snd_pcm_hw_params_any(this->handle, hw_param);
1082     if (ret < 0) {
1083         g_warning("Broken configuration for this PCM: no configurations available");
1084         return FALSE;
1085     }
1086     
1087     if ((ret = snd_pcm_hw_params_set_periods_integer (this->handle, hw_param)) < 0) {
1088         g_warning("cannot restrict period size to integral value.");
1089         return FALSE;
1090     }
1091
1092     mask = alloca(snd_pcm_access_mask_sizeof());
1093     snd_pcm_access_mask_none(mask);
1094     
1095     if (this->data_interleaved)
1096         snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
1097     
1098     snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
1099     ret = snd_pcm_hw_params_set_access_mask(this->handle, hw_param, mask);
1100     if (ret < 0) {
1101         g_warning("the gstreamer alsa plugin does not support your hardware.");
1102         return FALSE;
1103     }
1104     
1105     if (this->format != SND_PCM_FORMAT_UNKNOWN) {
1106         ret = snd_pcm_hw_params_set_format(this->handle, hw_param, this->format);
1107         if (ret < 0) {
1108             g_warning("Sample format (%s) not available: %s", snd_pcm_format_name(this->format), snd_strerror(ret));
1109             return FALSE;
1110         }
1111         this->sample_bytes = snd_pcm_format_physical_width(this->format) / 8;
1112     }
1113     
1114     ret = snd_pcm_hw_params_set_channels(this->handle, hw_param, this->channels);
1115     if (ret < 0) {
1116         g_warning("Channels count (%d) not available: %s", this->channels, snd_strerror(ret));
1117         return FALSE;
1118     }
1119     this->channels = snd_pcm_hw_params_get_channels(hw_param);
1120     
1121     if (this->rate) {
1122         ret = snd_pcm_hw_params_set_rate(this->handle, hw_param, this->rate, 0);
1123         if (ret < 0) {
1124             g_warning("error setting rate (%d): %s", this->rate, snd_strerror(ret));
1125             return FALSE;
1126         }
1127     }
1128     
1129     if (this->period_count) {
1130         ret = snd_pcm_hw_params_set_periods (this->handle, hw_param, this->period_count, 0);
1131         if (ret < 0) {
1132             g_warning("error setting period count minimum (%d): %s", this->period_count, snd_strerror(ret));
1133             return FALSE;
1134         }
1135     }
1136     
1137     if (this->period_frames) {
1138         ret = snd_pcm_hw_params_set_period_size (this->handle, hw_param, this->period_frames, 0);
1139         if (ret < 0) {
1140             g_warning("error setting period in frames (%d): %s", this->period_frames, snd_strerror(ret));
1141             return FALSE;
1142         }
1143     }
1144     
1145     if (this->buffer_frames) {
1146         ret = snd_pcm_hw_params_set_buffer_size (this->handle, hw_param, this->buffer_frames);
1147         if (ret < 0) {
1148             g_warning("error setting buffer size (%d): %s", this->buffer_frames, snd_strerror(ret));
1149             return FALSE;
1150         }
1151     }
1152     
1153     ret = snd_pcm_hw_params(this->handle, hw_param);
1154     if (ret < 0) {
1155         g_warning("could not set hw params: %s", snd_strerror(ret));
1156         snd_pcm_hw_params_dump(hw_param, this->out);
1157         return FALSE;
1158     }
1159     
1160     if (!this->rate)
1161         this->rate = snd_pcm_hw_params_get_rate(hw_param, 0);
1162     if (!this->format)
1163         this->format = snd_pcm_hw_params_get_format(hw_param);
1164     if (!this->period_count)
1165         this->period_count = snd_pcm_hw_params_get_periods(hw_param, 0);
1166     if (!this->period_frames)
1167         this->period_frames = snd_pcm_hw_params_get_period_size(hw_param, 0);
1168     if (!this->buffer_frames)
1169         this->buffer_frames = snd_pcm_hw_params_get_buffer_size(hw_param);
1170     if (this->buffer_frames != this->period_count * this->period_frames)
1171         g_critical ("buffer size != period size * number of periods, unexpected things may happen!");
1172     
1173     snd_pcm_sw_params_current (this->handle, sw_param);
1174
1175     ret = snd_pcm_sw_params_set_start_threshold (this->handle, sw_param, ~0U);
1176     if (ret < 0) {
1177         g_warning("could not set start mode: %s", snd_strerror(ret));
1178         return FALSE;
1179     }
1180     
1181     ret = snd_pcm_sw_params_set_stop_threshold (this->handle, sw_param, this->buffer_frames);
1182     if (ret < 0) {
1183         g_warning("could not set stop mode: %s", snd_strerror(ret));
1184         return FALSE;
1185     }
1186     
1187     ret = snd_pcm_sw_params_set_silence_threshold (this->handle, sw_param, 0);
1188     if (ret < 0) {
1189         g_warning("could not set silence threshold: %s", snd_strerror(ret));
1190         return FALSE;
1191     }
1192     
1193     ret = snd_pcm_sw_params_set_silence_size (this->handle, sw_param, this->buffer_frames);
1194     if (ret < 0) {
1195         g_warning("could not set silence size: %s", snd_strerror(ret));
1196         return FALSE;
1197     }
1198     
1199     ret = snd_pcm_sw_params_set_avail_min (this->handle, sw_param, this->period_frames);
1200     if (ret < 0) {
1201         g_warning("could not set avail min: %s", snd_strerror(ret));
1202         return FALSE;
1203     }
1204     
1205     ret = snd_pcm_sw_params (this->handle, sw_param);
1206     if (ret < 0) {
1207         g_warning("could not set sw_params: %s", snd_strerror(ret));
1208         return FALSE;
1209     }
1210
1211     if (this->debug)
1212         snd_pcm_dump(this->handle, this->out);
1213     
1214     this->access_interleaved = !(snd_pcm_hw_params_get_access (hw_param) ==
1215                                  SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
1216     
1217     if (this->access_interleaved) {
1218         this->interleave_unit = this->sample_bytes;
1219         this->interleave_skip = this->interleave_unit * this->channels;
1220     } else {
1221         this->interleave_unit = 0; /* not used */
1222         this->interleave_skip = this->sample_bytes;
1223     }
1224     
1225     if (this->access_addr)
1226         g_free (this->access_addr);
1227     this->access_addr = g_new0 (char*, this->channels);
1228     
1229     return TRUE;
1230 }
1231
1232 static gboolean
1233 gst_alsa_open_audio(GstAlsa *this)
1234 {
1235     gint ret;
1236     g_assert(this != NULL);
1237
1238     if (this->handle)
1239         gst_alsa_close_audio(this);
1240     
1241     g_print("Opening alsa device \"%s\" for %s...\n", this->device,
1242             this->stream==SND_PCM_STREAM_PLAYBACK ? "playback" : "capture");
1243     
1244     ret = snd_output_stdio_attach(&this->out, stdout, 0);
1245     if (ret < 0) {
1246         g_print("error opening log output: %s", snd_strerror(ret));
1247         return FALSE;
1248     }
1249     
1250     /* blocking i/o */
1251     if ((ret = snd_pcm_open(&this->handle, this->device, this->stream, 0))) {
1252         g_print("error opening pcm device: %s", snd_strerror(ret));
1253         return FALSE;
1254     }
1255     
1256     if (gst_alsa_set_params(this) == FALSE) {
1257         gst_alsa_close_audio(this);
1258         return FALSE;
1259     }
1260     
1261     GST_FLAG_SET(this, GST_ALSA_OPEN);
1262     return TRUE;
1263 }
1264
1265
1266 static gboolean
1267 gst_alsa_start_audio(GstAlsa *this)
1268 {
1269     gint err;
1270     guint32 chn;
1271     
1272     g_return_val_if_fail(this != NULL, FALSE);
1273     g_return_val_if_fail(this->handle != NULL, FALSE);
1274     
1275     if ((err = snd_pcm_prepare (this->handle)) < 0) {
1276         g_warning("channel prepare failed: %s", snd_strerror (err));
1277         return FALSE;
1278     }
1279         
1280     this->avail = snd_pcm_avail_update (this->handle);
1281     
1282     if (this->stream == SND_PCM_STREAM_PLAYBACK &&
1283         this->avail != this->buffer_frames) {
1284         g_warning ("full buffer not available at start");
1285         return FALSE;
1286     }
1287     
1288     if (!gst_alsa_get_channel_addresses (this)) {
1289         return FALSE;
1290     }
1291     
1292     if (this->stream == SND_PCM_STREAM_PLAYBACK) {
1293         for (chn = 0; chn < this->channels; chn++) {
1294             gst_alsa_sink_silence_on_channel (this, chn, this->buffer_frames);
1295         }
1296     }
1297     
1298     gst_alsa_release_channel_addresses (this);
1299     
1300     if ((err = snd_pcm_start (this->handle)) < 0) {
1301         g_warning("could not start audio: %s", snd_strerror (err));
1302         return FALSE;
1303     }
1304     
1305     GST_FLAG_SET(this, GST_ALSA_RUNNING);
1306     return TRUE;
1307 }
1308
1309 static void
1310 gst_alsa_stop_audio(GstAlsa *this)
1311 {
1312     gint err;
1313     g_assert(this != NULL);
1314     
1315     g_return_if_fail(this != NULL);
1316     g_return_if_fail(this->handle != NULL);
1317
1318     if (this->mmap_open)
1319         gst_alsa_release_channel_addresses (this);
1320     
1321     if (this->stream == SND_PCM_STREAM_PLAYBACK && 
1322         (err = snd_pcm_drop (this->handle)) < 0) {
1323         g_warning("channel flush failed: %s", snd_strerror (err));
1324         return;
1325     }
1326     
1327     GST_FLAG_UNSET(this, GST_ALSA_RUNNING);
1328 }
1329
1330 static void
1331 gst_alsa_close_audio(GstAlsa *this)
1332 {
1333 /*    gint err; */
1334     g_return_if_fail(this != NULL);
1335     g_return_if_fail(this->handle != NULL);
1336
1337 /*    if ((err = snd_pcm_drop (this->handle)) < 0) {
1338         g_warning("channel flush for failed: %s", snd_strerror (err));
1339         return;
1340         } */
1341     
1342     snd_pcm_close(this->handle);
1343     
1344     this->handle = NULL;
1345
1346     GST_FLAG_UNSET(this, GST_ALSA_OPEN);
1347 }
1348
1349 static gboolean
1350 gst_alsa_get_channel_addresses (GstAlsa *this)
1351 {
1352     guint32 err, i;
1353     const snd_pcm_channel_area_t *a;
1354     GList *l;
1355     
1356     g_return_val_if_fail (this->mmap_open == FALSE, FALSE);
1357     
1358     if ((err = snd_pcm_mmap_begin (this->handle, &this->mmap_areas, &this->offset, &this->avail)) < 0) {
1359         g_warning("gstalsa: mmap failed: %s", snd_strerror(err));
1360         return FALSE;
1361     }
1362     
1363     GST_DEBUG(0, "got %d mmap'd frames", (int)this->avail);
1364     
1365 /*    g_print ("snd_pcm_mmap_begin() sets avail = %d\n", this->avail); */
1366     
1367     l = this->pads;
1368     while (l) {
1369         a = &this->mmap_areas[GST_ALSA_PAD(l)->channel > 0 ?
1370                              GST_ALSA_PAD(l)->channel-1 : 0];
1371         GST_ALSA_PAD(l)->access_addr = (char *) a->addr + ((a->first + a->step *
1372                                                             this->offset) / 8);
1373         l = l->next;
1374     }
1375     
1376     for (i=0; i<this->channels; i++) {
1377         a = &this->mmap_areas[i];
1378         this->access_addr[i] = (char *) a->addr + ((a->first + a->step * this->offset) / 8);
1379     }
1380     
1381     this->mmap_open = TRUE;
1382     
1383     return TRUE;
1384 }
1385
1386 static void
1387 gst_alsa_release_channel_addresses (GstAlsa *this)
1388 {
1389     guint32 err, i;
1390     GList *l;
1391     
1392     g_return_if_fail (this->mmap_open == TRUE);
1393     
1394     GST_DEBUG(0, "releasing mmap'd data region: %d frames", (int)this->avail);
1395     
1396     if ((err = snd_pcm_mmap_commit (this->handle, this->offset, this->avail)) < 0) {
1397         g_warning("gstalsa: mmap commit failed: %s", snd_strerror(err));
1398         return;
1399     }
1400     
1401     l = this->pads;
1402     while (l) {
1403         GST_ALSA_PAD(l)->access_addr = NULL;
1404         l = l->next;
1405     }
1406     
1407     for (i=0; i<this->channels; i++) {
1408         this->access_addr[i] = NULL;
1409     }
1410     
1411     this->mmap_open = FALSE;
1412     this->avail=0;
1413 }
1414
1415 static void 
1416 gst_alsa_sink_silence_on_channel (GstAlsa *this, guint32 chn, guint32 nframes) 
1417 {
1418     if (this->access_interleaved) {
1419         memset_interleave 
1420             (this->access_addr[chn],
1421              0, nframes * this->sample_bytes,
1422              this->interleave_unit,
1423              this->interleave_skip);
1424     } else {
1425         memset (this->access_addr[chn], 0, nframes * this->sample_bytes);
1426     }
1427 /*    mark_channel_done (chn); */
1428 }
1429
1430 /* taken directly from paul davis' memops.cc */
1431 static void
1432 memset_interleave (char *dst, char val, unsigned int bytes, 
1433                    unsigned int unit_bytes, 
1434                    unsigned int skip_bytes) 
1435 {
1436     switch (unit_bytes) {
1437     case 1:
1438         while (bytes--) {
1439             *dst = val;
1440             dst += skip_bytes;
1441         }
1442         break;
1443     case 2:
1444         while (bytes) {
1445             *((short *) dst) = (short) val;
1446             dst += skip_bytes;
1447             bytes -= 2;
1448         }
1449         break;
1450     case 4:                 
1451         while (bytes) {
1452             *((int *) dst) = (int) val;
1453             dst += skip_bytes;
1454             bytes -= 4;
1455         }
1456         break;
1457     }
1458 }
1459
1460 static gboolean
1461 plugin_init (GModule *module, GstPlugin *plugin)
1462 {
1463     GstElementFactory *factory;
1464     
1465     if (!gst_library_load ("gstbytestream")) {
1466         gst_info("alsa: could not load support library: 'gstbytestream'\n");
1467         return FALSE;
1468     }
1469     
1470     factory = gst_element_factory_new ("alsasrc", GST_TYPE_ALSA_SRC, &gst_alsa_src_details);
1471     g_return_val_if_fail (factory != NULL, FALSE);
1472     gst_element_factory_add_pad_template (factory, gst_alsa_src_pad_factory());
1473     gst_element_factory_add_pad_template (factory, gst_alsa_src_request_pad_factory());
1474     gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
1475     
1476     factory = gst_element_factory_new ("alsasink", GST_TYPE_ALSA_SINK, &gst_alsa_sink_details);
1477     g_return_val_if_fail (factory != NULL, FALSE);
1478     gst_element_factory_add_pad_template (factory, gst_alsa_sink_pad_factory());
1479     gst_element_factory_add_pad_template (factory, gst_alsa_sink_request_pad_factory());
1480     gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
1481     
1482     gst_plugin_set_longname(plugin, "ALSA plugin library");
1483     
1484     return TRUE;
1485 }
1486
1487 GstPluginDesc plugin_desc = {
1488     GST_VERSION_MAJOR,
1489     GST_VERSION_MINOR,
1490     "alsa",
1491     plugin_init
1492 };
1493
1494