1 /* -*- c-basic-offset: 4 -*- */
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>
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.
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.
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.
26 static GstElementDetails gst_alsa_sink_details = {
29 "Output to a sound card via ALSA",
31 "Thomas Nyberg <thomas@codefactory.se>, "
32 "Andy Wingo <apwingo@eos.ncsu.edu>",
36 static GstElementDetails gst_alsa_src_details = {
39 "Read from a sound card via ALSA",
41 "Thomas Nyberg <thomas@codefactory.se>, "
42 "Andy Wingo <apwingo@eos.ncsu.edu>",
46 static GstElement *parent_class = NULL;
48 static void gst_alsa_init(GstAlsa *this);
49 static void gst_alsa_class_init(GstAlsaClass *klass);
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();
56 static GstPad* gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const
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);
64 static GstCaps* gst_alsa_caps (GstAlsa *this);
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);
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);
77 static gboolean gst_alsa_get_channel_addresses (GstAlsa *this);
78 static void gst_alsa_release_channel_addresses (GstAlsa *this);
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);
87 #define DEBUG(text, args...) g_message(text, ##args)
89 #define DEBUG(text, args...)
104 #define GST_TYPE_ALSA_FORMAT (gst_alsa_format_get_type())
106 gst_alsa_format_get_type (void)
108 static GType type = 0;
109 static GEnumValue *values = NULL;
110 gint i, len = SND_PCM_FORMAT_GSM + 3;
112 if (values == NULL) {
113 /* the three: for -1, 0, and the terminating NULL */
114 values = g_new0 (GEnumValue, len);
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));
124 type = g_enum_register_static ("GstAlsaFormat", values);
130 gst_alsa_get_type (void)
132 static GType alsa_type = 0;
135 static const GTypeInfo alsa_info = {
136 sizeof(GstAlsaClass),
146 alsa_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAlsa", &alsa_info, 0);
152 gst_alsa_sink_get_type (void)
154 static GType alsa_type = 0;
157 static const GTypeInfo alsa_info = {
158 sizeof(GstAlsaClass),
161 (GClassInitFunc)gst_alsa_class_init,
166 (GInstanceInitFunc)gst_alsa_init,
168 alsa_type = g_type_register_static (GST_TYPE_ALSA, "GstAlsaSink", &alsa_info, 0);
174 gst_alsa_src_get_type (void)
176 static GType alsa_type = 0;
179 static const GTypeInfo alsa_info = {
180 sizeof(GstAlsaClass),
183 (GClassInitFunc)gst_alsa_class_init,
188 (GInstanceInitFunc)gst_alsa_init,
190 alsa_type = g_type_register_static (GST_TYPE_ALSA, "GstAlsaSrc", &alsa_info, 0);
195 static GstPadTemplate*
196 gst_alsa_src_pad_factory(void)
198 static GstPadTemplate *template = NULL;
201 template = gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_SOMETIMES,
202 gst_caps_new("src", "audio/raw", NULL),
208 static GstPadTemplate*
209 gst_alsa_src_request_pad_factory(void)
211 static GstPadTemplate *template = NULL;
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)),
222 static GstPadTemplate*
223 gst_alsa_sink_pad_factory(void)
225 static GstPadTemplate *template = NULL;
228 template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_SOMETIMES,
229 gst_caps_new("sink", "audio/raw", NULL),
235 static GstPadTemplate*
236 gst_alsa_sink_request_pad_factory(void)
238 static GstPadTemplate *template = NULL;
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)),
250 gst_alsa_class_init(GstAlsaClass *klass)
252 GObjectClass *object_class;
253 GstElementClass *element_class;
255 object_class = (GObjectClass *)klass;
256 element_class = (GstElementClass *)klass;
258 if (parent_class == NULL)
259 parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
261 object_class->get_property = gst_alsa_get_property;
262 object_class->set_property = gst_alsa_set_property;
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",
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,
272 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_CHANNELS,
273 g_param_spec_int("channels","Channels","Number of channels",
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",
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",
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",
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));
292 element_class->change_state = gst_alsa_change_state;
294 element_class->request_new_pad = gst_alsa_request_new_pad;
298 gst_alsa_init(GstAlsa *this)
303 GST_FLAG_SET(this, GST_ELEMENT_THREAD_SUGGESTED);
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
320 GST_ALSA_PAD(this->pads)->channel = -1;
322 this->data_interleaved = TRUE;
324 gst_element_add_pad(GST_ELEMENT(this), GST_ALSA_PAD(this->pads)->pad);
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);
331 gst_alsa_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *name)
339 g_return_val_if_fail ((this = GST_ALSA(element)), NULL);
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,
350 channel = atoi (name + (strchr (templ->name_template, '%') - templ->name_template));
354 if (GST_ALSA_PAD(l)->channel == channel) {
355 g_warning("requested channel %d already in use.", channel);
364 if (GST_ALSA_PAD(l)->channel > channel)
365 channel = GST_ALSA_PAD(l)->channel;
370 newname = g_strdup (name);
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);
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);
385 this->pads = g_list_append(this->pads, pad);
387 /* FIXME: allow interleaved access (for hw:N,M access on consumer hardware) */
389 if (this->data_interleaved) {
390 this->channels = pad->channel + 1;
391 this->data_interleaved = FALSE;
393 this->channels = MAX(this->channels, pad->channel + 1);
400 gst_alsa_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
404 this = (GstAlsa *)object;
408 g_free (this->device);
409 this->device = g_strdup(g_value_get_string (value));
412 this->format = g_value_get_enum (value);
415 this->channels = g_value_get_int (value);
418 this->rate = g_value_get_int (value);
420 case ARG_PERIODCOUNT:
421 this->period_count = g_value_get_int (value);
422 this->buffer_frames = this->period_count * this->period_frames;
424 case ARG_PERIODFRAMES:
425 this->period_frames = g_value_get_int (value);
426 this->buffer_frames = this->period_count * this->period_frames;
429 this->debug = g_value_get_boolean (value);
432 GST_DEBUG(0, "Unknown arg");
436 if (GST_STATE(this) == GST_STATE_NULL)
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);
444 gst_alsa_set_params(this);
449 gst_alsa_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
453 this = (GstAlsa *)object;
457 g_value_set_string (value, this->device);
460 g_value_set_enum (value, this->format);
463 g_value_set_int (value, this->channels);
466 g_value_set_int (value, this->rate);
468 case ARG_PERIODCOUNT:
469 g_value_set_int (value, this->period_count);
471 case ARG_PERIODFRAMES:
472 g_value_set_int (value, this->period_frames);
475 g_value_set_boolean (value, this->debug);
478 GST_DEBUG(0, "Unknown arg");
483 static GstElementStateReturn
484 gst_alsa_change_state(GstElement *element)
489 g_return_val_if_fail(element != NULL, FALSE);
490 this = GST_ALSA (element);
492 switch (GST_STATE_PENDING(element)) {
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 */
501 case GST_STATE_READY:
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);
514 gst_alsa_stop_audio((GstAlsa *)element);
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;
525 if (GST_ELEMENT_CLASS(parent_class)->change_state)
526 return GST_ELEMENT_CLASS(parent_class)->change_state(element);
528 return GST_STATE_SUCCESS;
532 gst_alsa_parse_caps (GstAlsa *this, GstCaps *caps)
534 gint law, endianness, width, depth, channels;
537 const gchar* format_name;
539 if (!gst_caps_get_string (caps, "format", &format_name))
542 if (format_name == NULL) {
544 } else if (strcmp(format_name, "int")==0) {
545 if (!gst_caps_get (caps,
549 "endianness", &endianness,
557 format = SND_PCM_FORMAT_S8;
559 format = SND_PCM_FORMAT_U8;
561 } else if (width == 16) {
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;
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;
573 } else if (width == 24) {
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;
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;
585 } else if (width == 32) {
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;
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;
598 } else if (law == 1) { /* mu law */
599 if (width == depth && width == 8 && sign == FALSE) {
600 format = SND_PCM_FORMAT_MU_LAW;
604 } else if (law == 2) { /* a law, ug. */
605 if (width == depth && width == 8 && sign == FALSE) {
606 format = SND_PCM_FORMAT_A_LAW;
611 } else if (strcmp(format_name, "float")==0) {
614 if (!gst_caps_get_string (caps, "layout", &layout))
617 if (strcmp(layout, "gfloat")==0) {
618 format = SND_PCM_FORMAT_FLOAT;
621 /* you need doubles? jeez... */
627 this->format = format;
628 if (!gst_caps_get (caps,
630 "channels", &channels,
634 if (this->data_interleaved)
635 this->channels = channels;
636 else if (channels != 1)
642 /* caps are so painful sometimes. */
644 gst_alsa_caps (GstAlsa *this)
646 gint law, endianness, width, depth;
650 g_return_val_if_fail (this != NULL && this->handle != NULL, NULL);
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)),
659 /* we'll just have to assume int, i don't feel like checking */
660 if (this->format == SND_PCM_FORMAT_MU_LAW) {
665 } else if (this->format == SND_PCM_FORMAT_A_LAW) {
672 if (this->format == SND_PCM_FORMAT_S8) {
676 } else if (this->format == SND_PCM_FORMAT_U8) {
680 } else if (this->format == SND_PCM_FORMAT_S16_LE) {
683 endianness = G_LITTLE_ENDIAN;
684 } else if (this->format == SND_PCM_FORMAT_S16_BE) {
687 endianness = G_BIG_ENDIAN;
688 } else if (this->format == SND_PCM_FORMAT_U16_LE) {
691 endianness = G_LITTLE_ENDIAN;
692 } else if (this->format == SND_PCM_FORMAT_U16_BE) {
695 endianness = G_BIG_ENDIAN;
696 } else if (this->format == SND_PCM_FORMAT_S24_LE) {
699 endianness = G_LITTLE_ENDIAN;
700 } else if (this->format == SND_PCM_FORMAT_S24_BE) {
703 endianness = G_BIG_ENDIAN;
704 } else if (this->format == SND_PCM_FORMAT_U24_LE) {
707 endianness = G_LITTLE_ENDIAN;
708 } else if (this->format == SND_PCM_FORMAT_U24_BE) {
711 endianness = G_BIG_ENDIAN;
712 } else if (this->format == SND_PCM_FORMAT_S32_LE) {
715 endianness = G_LITTLE_ENDIAN;
716 } else if (this->format == SND_PCM_FORMAT_S32_BE) {
719 endianness = G_BIG_ENDIAN;
720 } else if (this->format == SND_PCM_FORMAT_U32_LE) {
723 endianness = G_LITTLE_ENDIAN;
724 } else if (this->format == SND_PCM_FORMAT_U32_BE) {
727 endianness = G_BIG_ENDIAN;
729 g_error ("what is going on here?");
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),
745 return gst_caps_new ("alsasrc", "audio/raw", props);
749 * Negotiates the caps, "borrowed" from gstosssink.c
752 gst_alsa_connect(GstPad *pad, GstCaps *caps)
757 g_return_val_if_fail (caps != NULL, GST_PAD_CONNECT_REFUSED);
758 g_return_val_if_fail (pad != NULL, GST_PAD_CONNECT_REFUSED);
760 this = GST_ALSA(gst_pad_get_parent(pad));
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;
767 if (gst_alsa_parse_caps(this, caps)) {
768 need_mmap = this->mmap_open;
770 /* sync the params */
771 if (GST_FLAG_IS_SET(this, GST_ALSA_RUNNING))
772 gst_alsa_stop_audio(this);
774 if (GST_FLAG_IS_SET(this, GST_ALSA_OPEN))
775 gst_alsa_close_audio(this);
777 /* FIXME send out another caps if nego fails */
779 if (!gst_alsa_open_audio(this))
780 return GST_PAD_CONNECT_REFUSED;
782 if (!gst_alsa_start_audio(this))
783 return GST_PAD_CONNECT_REFUSED;
785 if (need_mmap && !gst_alsa_get_channel_addresses(this))
786 return GST_PAD_CONNECT_REFUSED;
788 return GST_PAD_CONNECT_OK;
791 return GST_PAD_CONNECT_REFUSED;
794 return GST_PAD_CONNECT_DELAYED;
797 /* shamelessly stolen from pbd's audioengine and jack alsa_driver. thanks, paul! */
799 gst_alsa_loop (GstElement *element)
802 gboolean xrun_detected;
804 GstAlsa *this = GST_ALSA(element);
806 g_return_if_fail(this != NULL);
808 snd_pcm_poll_descriptors (this->handle, &pfd, 1);
810 if (this->stream == SND_PCM_STREAM_PLAYBACK) {
811 pfd.events = POLLOUT | POLLERR;
813 pfd.events = POLLIN | POLLERR;
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 */
825 g_warning("poll call failed (%s)", strerror(errno));
829 if (pfd.revents & POLLERR) {
830 g_warning("alsa: poll reports error.");
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",
838 /* timed out, such as when the device is paused */
842 xrun_detected = FALSE;
844 this->avail = snd_pcm_avail_update (this->handle);
845 DEBUG ("snd_pcm_avail_update() = %d", (int)this->avail);
847 if (this->avail < 0) {
848 if (this->avail == -EPIPE) {
849 gst_alsa_xrun_recovery (this);
852 g_warning("unknown ALSA avail_update return value (%d)",
858 /* round down to nearest period_frames avail */
859 this->avail -= this->avail % this->period_frames;
861 DEBUG ("snd_pcm_avail_update(), rounded down = %d", (int)this->avail);
863 /* we need to loop here because the available bytes might not be
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");
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);
880 if (!this->process(this, this->avail)) {
881 g_warning("alsa: something happened while processing audio");
885 /* we could have released the mmap regions on a state change */
887 gst_alsa_release_channel_addresses(this);
889 gst_element_yield (element);
894 gst_alsa_src_process (GstAlsa *this, snd_pcm_uframes_t frames)
898 GstAlsaPad *pad = NULL;
903 static gboolean caps_set = FALSE;
906 /* let's get on the caps-setting merry-go-round! */
907 caps = gst_alsa_caps(this);
910 if (!gst_pad_try_set_caps (GST_ALSA_PAD(l)->pad, caps)) {
911 g_print ("DANGER WILL ROBINSON!\n");
921 unit = this->sample_bytes * (this->data_interleaved ? this->channels : 1);
924 /* g_print ("(%d) frames to process: %d\n", i++, frames); */
927 pad = GST_ALSA_PAD(l);
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); */
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,
938 MIN(frames, this->period_frames - pad->offset) * unit);
940 memcpy(pad->buf + pad->offset * unit,
942 MIN(frames, this->period_frames - pad->offset) * unit);
944 pad->offset += MIN(frames, this->period_frames - pad->offset);
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);
958 frames -= MIN(frames, this->period_frames - pad->offset); /* shouldn't */
959 /* matter which pad, in theory (tm) */
966 gst_alsa_sink_process (GstAlsa *this, snd_pcm_uframes_t frames)
970 GstEvent *event = NULL;
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.
978 /* FIXME: if 0 < peek_bytes < len, play the peek_bytes */
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);
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));
990 if (!this->sample_bytes) {
991 g_critical ("alsa plugin requires a pipeline that can adequately set caps.");
996 len = frames * this->channels * this->sample_bytes;
1000 if (!GST_ALSA_PAD(this->pads)->bs)
1001 GST_ALSA_PAD(this->pads)->bs = gst_bytestream_new(GST_ALSA_PAD(this->pads)->pad);
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);
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);
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();
1023 memcpy(GST_ALSA_PAD(this->pads)->access_addr, peeked, len);
1024 gst_bytestream_flush(GST_ALSA_PAD(this->pads)->bs, len);
1033 gst_alsa_xrun_recovery (GstAlsa *this)
1035 snd_pcm_status_t *status;
1038 snd_pcm_status_alloca(&status);
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));
1045 if ((res = snd_pcm_status(this->handle, status)) < 0) {
1046 g_warning ("status error: %s", snd_strerror(res));
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);
1058 gst_alsa_stop_audio (this);
1059 gst_alsa_start_audio (this);
1062 /* taken more or less from pbd's audioengine code */
1064 gst_alsa_set_params (GstAlsa *this)
1066 snd_pcm_sw_params_t *sw_param;
1067 snd_pcm_hw_params_t *hw_param;
1068 snd_pcm_access_mask_t *mask;
1071 g_return_val_if_fail(this != NULL, FALSE);
1072 g_return_val_if_fail(this->handle != NULL, FALSE);
1074 g_print("Preparing channel: %s %dHz, %d channels\n",
1075 snd_pcm_format_name(this->format),
1076 this->rate, this->channels);
1078 snd_pcm_hw_params_alloca(&hw_param);
1079 snd_pcm_sw_params_alloca(&sw_param);
1081 ret = snd_pcm_hw_params_any(this->handle, hw_param);
1083 g_warning("Broken configuration for this PCM: no configurations available");
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.");
1092 mask = alloca(snd_pcm_access_mask_sizeof());
1093 snd_pcm_access_mask_none(mask);
1095 if (this->data_interleaved)
1096 snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
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);
1101 g_warning("the gstreamer alsa plugin does not support your hardware.");
1105 if (this->format != SND_PCM_FORMAT_UNKNOWN) {
1106 ret = snd_pcm_hw_params_set_format(this->handle, hw_param, this->format);
1108 g_warning("Sample format (%s) not available: %s", snd_pcm_format_name(this->format), snd_strerror(ret));
1111 this->sample_bytes = snd_pcm_format_physical_width(this->format) / 8;
1114 ret = snd_pcm_hw_params_set_channels(this->handle, hw_param, this->channels);
1116 g_warning("Channels count (%d) not available: %s", this->channels, snd_strerror(ret));
1119 this->channels = snd_pcm_hw_params_get_channels(hw_param);
1122 ret = snd_pcm_hw_params_set_rate(this->handle, hw_param, this->rate, 0);
1124 g_warning("error setting rate (%d): %s", this->rate, snd_strerror(ret));
1129 if (this->period_count) {
1130 ret = snd_pcm_hw_params_set_periods (this->handle, hw_param, this->period_count, 0);
1132 g_warning("error setting period count minimum (%d): %s", this->period_count, snd_strerror(ret));
1137 if (this->period_frames) {
1138 ret = snd_pcm_hw_params_set_period_size (this->handle, hw_param, this->period_frames, 0);
1140 g_warning("error setting period in frames (%d): %s", this->period_frames, snd_strerror(ret));
1145 if (this->buffer_frames) {
1146 ret = snd_pcm_hw_params_set_buffer_size (this->handle, hw_param, this->buffer_frames);
1148 g_warning("error setting buffer size (%d): %s", this->buffer_frames, snd_strerror(ret));
1153 ret = snd_pcm_hw_params(this->handle, hw_param);
1155 g_warning("could not set hw params: %s", snd_strerror(ret));
1156 snd_pcm_hw_params_dump(hw_param, this->out);
1161 this->rate = snd_pcm_hw_params_get_rate(hw_param, 0);
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!");
1173 snd_pcm_sw_params_current (this->handle, sw_param);
1175 ret = snd_pcm_sw_params_set_start_threshold (this->handle, sw_param, ~0U);
1177 g_warning("could not set start mode: %s", snd_strerror(ret));
1181 ret = snd_pcm_sw_params_set_stop_threshold (this->handle, sw_param, this->buffer_frames);
1183 g_warning("could not set stop mode: %s", snd_strerror(ret));
1187 ret = snd_pcm_sw_params_set_silence_threshold (this->handle, sw_param, 0);
1189 g_warning("could not set silence threshold: %s", snd_strerror(ret));
1193 ret = snd_pcm_sw_params_set_silence_size (this->handle, sw_param, this->buffer_frames);
1195 g_warning("could not set silence size: %s", snd_strerror(ret));
1199 ret = snd_pcm_sw_params_set_avail_min (this->handle, sw_param, this->period_frames);
1201 g_warning("could not set avail min: %s", snd_strerror(ret));
1205 ret = snd_pcm_sw_params (this->handle, sw_param);
1207 g_warning("could not set sw_params: %s", snd_strerror(ret));
1212 snd_pcm_dump(this->handle, this->out);
1214 this->access_interleaved = !(snd_pcm_hw_params_get_access (hw_param) ==
1215 SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
1217 if (this->access_interleaved) {
1218 this->interleave_unit = this->sample_bytes;
1219 this->interleave_skip = this->interleave_unit * this->channels;
1221 this->interleave_unit = 0; /* not used */
1222 this->interleave_skip = this->sample_bytes;
1225 if (this->access_addr)
1226 g_free (this->access_addr);
1227 this->access_addr = g_new0 (char*, this->channels);
1233 gst_alsa_open_audio(GstAlsa *this)
1236 g_assert(this != NULL);
1239 gst_alsa_close_audio(this);
1241 g_print("Opening alsa device \"%s\" for %s...\n", this->device,
1242 this->stream==SND_PCM_STREAM_PLAYBACK ? "playback" : "capture");
1244 ret = snd_output_stdio_attach(&this->out, stdout, 0);
1246 g_print("error opening log output: %s", snd_strerror(ret));
1251 if ((ret = snd_pcm_open(&this->handle, this->device, this->stream, 0))) {
1252 g_print("error opening pcm device: %s", snd_strerror(ret));
1256 if (gst_alsa_set_params(this) == FALSE) {
1257 gst_alsa_close_audio(this);
1261 GST_FLAG_SET(this, GST_ALSA_OPEN);
1267 gst_alsa_start_audio(GstAlsa *this)
1272 g_return_val_if_fail(this != NULL, FALSE);
1273 g_return_val_if_fail(this->handle != NULL, FALSE);
1275 if ((err = snd_pcm_prepare (this->handle)) < 0) {
1276 g_warning("channel prepare failed: %s", snd_strerror (err));
1280 this->avail = snd_pcm_avail_update (this->handle);
1282 if (this->stream == SND_PCM_STREAM_PLAYBACK &&
1283 this->avail != this->buffer_frames) {
1284 g_warning ("full buffer not available at start");
1288 if (!gst_alsa_get_channel_addresses (this)) {
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);
1298 gst_alsa_release_channel_addresses (this);
1300 if ((err = snd_pcm_start (this->handle)) < 0) {
1301 g_warning("could not start audio: %s", snd_strerror (err));
1305 GST_FLAG_SET(this, GST_ALSA_RUNNING);
1310 gst_alsa_stop_audio(GstAlsa *this)
1313 g_assert(this != NULL);
1315 g_return_if_fail(this != NULL);
1316 g_return_if_fail(this->handle != NULL);
1318 if (this->mmap_open)
1319 gst_alsa_release_channel_addresses (this);
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));
1327 GST_FLAG_UNSET(this, GST_ALSA_RUNNING);
1331 gst_alsa_close_audio(GstAlsa *this)
1334 g_return_if_fail(this != NULL);
1335 g_return_if_fail(this->handle != NULL);
1337 /* if ((err = snd_pcm_drop (this->handle)) < 0) {
1338 g_warning("channel flush for failed: %s", snd_strerror (err));
1342 snd_pcm_close(this->handle);
1344 this->handle = NULL;
1346 GST_FLAG_UNSET(this, GST_ALSA_OPEN);
1350 gst_alsa_get_channel_addresses (GstAlsa *this)
1353 const snd_pcm_channel_area_t *a;
1356 g_return_val_if_fail (this->mmap_open == FALSE, FALSE);
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));
1363 GST_DEBUG(0, "got %d mmap'd frames", (int)this->avail);
1365 /* g_print ("snd_pcm_mmap_begin() sets avail = %d\n", this->avail); */
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 *
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);
1381 this->mmap_open = TRUE;
1387 gst_alsa_release_channel_addresses (GstAlsa *this)
1392 g_return_if_fail (this->mmap_open == TRUE);
1394 GST_DEBUG(0, "releasing mmap'd data region: %d frames", (int)this->avail);
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));
1403 GST_ALSA_PAD(l)->access_addr = NULL;
1407 for (i=0; i<this->channels; i++) {
1408 this->access_addr[i] = NULL;
1411 this->mmap_open = FALSE;
1416 gst_alsa_sink_silence_on_channel (GstAlsa *this, guint32 chn, guint32 nframes)
1418 if (this->access_interleaved) {
1420 (this->access_addr[chn],
1421 0, nframes * this->sample_bytes,
1422 this->interleave_unit,
1423 this->interleave_skip);
1425 memset (this->access_addr[chn], 0, nframes * this->sample_bytes);
1427 /* mark_channel_done (chn); */
1430 /* taken directly from paul davis' memops.cc */
1432 memset_interleave (char *dst, char val, unsigned int bytes,
1433 unsigned int unit_bytes,
1434 unsigned int skip_bytes)
1436 switch (unit_bytes) {
1445 *((short *) dst) = (short) val;
1452 *((int *) dst) = (int) val;
1461 plugin_init (GModule *module, GstPlugin *plugin)
1463 GstElementFactory *factory;
1465 if (!gst_library_load ("gstbytestream")) {
1466 gst_info("alsa: could not load support library: 'gstbytestream'\n");
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));
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));
1482 gst_plugin_set_longname(plugin, "ALSA plugin library");
1487 GstPluginDesc plugin_desc = {