]> git.alsa-project.org Git - alsa-plugins.git/commitdiff
Add a52 output plugin
authorTakashi Iwai <tiwai@suse.de>
Thu, 6 Apr 2006 15:53:07 +0000 (17:53 +0200)
committerTakashi Iwai <tiwai@suse.de>
Thu, 6 Apr 2006 15:53:07 +0000 (17:53 +0200)
Added (experimental) a52 output plugin.
The plugin requires libavcodec as the audio encoding engine.

See doc/a52.txt for the usage.

Makefile.am
a52/Makefile.am [new file with mode: 0644]
a52/pcm_a52.c [new file with mode: 0644]
configure.in
doc/a52.txt [new file with mode: 0644]

index 6b9e37aa9c36aa7e10bbe43c1cc60b63378bc5e7..f33191d25667711068fe693f9f360151ef961a08 100644 (file)
@@ -7,8 +7,11 @@ endif
 if HAVE_SAMPLERATE
 SAMPLERATEDIR = rate
 endif
+if HAVE_AVCODEC
+A52DIR = a52
+endif
 
-SUBDIRS = oss mix $(JACKDIR) $(POLYPDIR) $(SAMPLERATEDIR) doc
+SUBDIRS = oss mix $(JACKDIR) $(POLYPDIR) $(SAMPLERATEDIR) $(A52DIR) doc
 EXTRA_DIST = cvscompile version COPYING.GPL
 AUTOMAKE_OPTIONS = foreign
 
diff --git a/a52/Makefile.am b/a52/Makefile.am
new file mode 100644 (file)
index 0000000..4021496
--- /dev/null
@@ -0,0 +1,9 @@
+asound_module_pcm_a52_LTLIBRARIES = libasound_module_pcm_a52.la
+
+asound_module_pcm_a52dir = $(libdir)/alsa-lib
+
+AM_CFLAGS = -Wall -g @ALSA_CFLAGS@ @AVCODEC_CFLAGS@
+AM_LDFLAGS = -module -avoid-version -export-dynamic
+
+libasound_module_pcm_a52_la_SOURCES = pcm_a52.c
+libasound_module_pcm_a52_la_LIBADD = @ALSA_LIBS@ @AVCODEC_LIBS@
diff --git a/a52/pcm_a52.c b/a52/pcm_a52.c
new file mode 100644 (file)
index 0000000..3d376e2
--- /dev/null
@@ -0,0 +1,638 @@
+/*
+ * A52 Output Plugin
+ *
+ * Copyright (c) 2006 by Takashi Iwai <tiwai@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#define __USE_XOPEN
+#include <unistd.h>
+#include <alsa/asoundlib.h>
+#include <alsa/pcm_external.h>
+#include <ffmpeg/avcodec.h>
+
+struct a52_ctx {
+       snd_pcm_ioplug_t io;
+       snd_pcm_t *slave;
+       AVCodec *codec;
+       AVCodecContext *avctx;
+       snd_pcm_format_t format;
+       unsigned int channels;
+       unsigned int rate;
+       unsigned int bitrate;
+       short *inbuf;
+       unsigned char *outbuf;
+       int outbuf_size;
+       snd_pcm_uframes_t transfer;
+       int remain;
+       int filled;
+       unsigned int slave_period_size;
+       unsigned int slave_buffer_size;
+       snd_pcm_hw_params_t *hw_params;
+};
+
+/* convert to SPDIF output */
+static void convert_data(struct a52_ctx *rec)
+{
+       int out_bytes;
+
+       out_bytes = avcodec_encode_audio(rec->avctx, rec->outbuf + 8,
+                                        rec->outbuf_size - 8,
+                                        rec->inbuf);
+       rec->outbuf[0] = 0x72;
+       rec->outbuf[1] = 0xf8;
+       rec->outbuf[2] = 0x1f;
+       rec->outbuf[3] = 0x4e;
+       rec->outbuf[4] = 0x01;
+       rec->outbuf[5] = rec->outbuf[5] & 7;
+       rec->outbuf[6] = (out_bytes * 8) & 0xff;
+       rec->outbuf[7] = ((out_bytes * 8) >> 8) & 0xff;
+       if (rec->format == SND_PCM_FORMAT_S16_LE)
+               swab(rec->outbuf + 8, rec->outbuf + 8, out_bytes);
+       memset(rec->outbuf +  8 + out_bytes, 0,
+              rec->outbuf_size - 8 - out_bytes);
+       rec->remain = rec->outbuf_size / 4;
+       rec->filled = 0;
+}
+
+static int write_out_pending(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED,
+                            struct a52_ctx *rec)
+{
+       int err, ofs = 0;
+
+       if (! rec->remain)
+               return 0;
+
+       while (rec->remain) {
+               err = snd_pcm_writei(rec->slave, rec->outbuf + ofs, rec->remain);
+               if (err < 0)
+                       return err;
+               else if (! err)
+                       break;
+               if (err < rec->remain)
+                       ofs += (rec->remain - err) * 4;
+               rec->remain -= err;
+       }
+       if (rec->remain && ofs)
+               memmove(rec->outbuf, rec->outbuf + ofs, rec->remain * 4);
+       return 0;
+}
+
+static int a52_drain(snd_pcm_ioplug_t *io)
+{
+       struct a52_ctx *rec = io->private_data;
+       int err;
+
+       if (rec->filled) {
+               memset(rec->inbuf + rec->filled * io->channels, 0,
+                      (rec->avctx->frame_size - rec->filled) * io->channels * 2);
+               convert_data(rec);
+       }
+       err = write_out_pending(io, rec);
+       if (err < 0)
+               return err;
+       snd_pcm_drain(rec->slave);
+       return 0;
+}
+
+static int check_interleaved(const snd_pcm_channel_area_t *areas,
+                            unsigned int channels)
+{
+       unsigned int ch;
+
+       if (channels > 4) /* we need re-routing for 6 channels */
+               return 0;
+
+       for (ch = 0; ch < channels; ch++) {
+               if (areas[ch].addr != areas[0].addr ||
+                   areas[ch].first != ch * 16 ||
+                   areas[ch].step != channels * 16)
+                       return 0;
+       }
+       return 1;
+}
+
+static int fill_data(snd_pcm_ioplug_t *io,
+                    const snd_pcm_channel_area_t *areas,
+                    unsigned int offset, unsigned int size,
+                    int interleaved)
+{
+       struct a52_ctx *rec = io->private_data;
+       unsigned int len = rec->avctx->frame_size - rec->filled;
+       short *src, *dst;
+       unsigned int src_step;
+       int err;
+
+       if ((err = write_out_pending(io, rec)) < 0)
+               return err;
+
+       if (size > len)
+               size = len;
+
+       dst = rec->inbuf + rec->filled * io->channels;
+       if (interleaved) {
+               memcpy(dst, areas->addr + offset * io->channels * 2,
+                      size * io->channels * 2);
+       } else {
+               unsigned int i, ch, dst_step;
+               short *dst1;
+               static unsigned int ch_index[3][6] = {
+                       { 0, 1 },
+                       { 0, 1, 2, 3 },
+                       { 0, 4, 1, 2, 3, 5 },
+               };
+               /* flatten copy to n-channel interleaved */
+               dst_step = io->channels;
+               for (ch = 0; ch < io->channels; ch++, dst++) {
+                       const snd_pcm_channel_area_t *ap;
+                       ap = &areas[ch_index[io->channels / 2 - 1][ch]];
+                       dst1 = dst;
+                       src = (short *)(ap->addr +
+                                       (ap->first + offset * ap->step) / 8);
+                       src_step = ap->step / 16; /* in word */
+                       for (i = 0; i < size; i++) {
+                               *dst1 = *src;
+                               src += src_step;
+                               dst1 += dst_step;
+                       }
+               }
+       }
+       rec->filled += size;
+       if (rec->filled == rec->avctx->frame_size) {
+               convert_data(rec);
+               write_out_pending(io, rec);
+       }
+       return (int)size;
+}
+
+static snd_pcm_sframes_t a52_transfer(snd_pcm_ioplug_t *io,
+                                     const snd_pcm_channel_area_t *areas,
+                                     snd_pcm_uframes_t offset,
+                                     snd_pcm_uframes_t size)
+{
+       struct a52_ctx *rec = io->private_data;
+       snd_pcm_sframes_t result = 0;
+       int err = 0;
+       int interleaved = check_interleaved(areas, io->channels);
+
+       do {
+               err = fill_data(io, areas, offset, size, interleaved);
+               if (err < 0)
+                       break;
+               offset += (unsigned int)err;
+               size -= (unsigned int)err;
+               result += err;
+               rec->transfer += err;
+       } while (size);
+       return result > 0 ? result : err;
+}
+
+static snd_pcm_sframes_t a52_pointer(snd_pcm_ioplug_t *io)
+{
+       struct a52_ctx *rec = io->private_data;
+       snd_pcm_sframes_t delay;
+       snd_pcm_state_t state;
+       int err;
+
+       state = snd_pcm_state(rec->slave);
+       if (state == SND_PCM_STATE_RUNNING ||
+           state == SND_PCM_STATE_DRAINING) {
+               if ((err = snd_pcm_delay(rec->slave, &delay)) < 0)
+                       return err;
+       } else
+               return 0;
+
+       if (delay < 0 || delay >= (snd_pcm_sframes_t)rec->slave_buffer_size)
+               delay = 0;
+       delay = (snd_pcm_sframes_t)io->appl_ptr - delay;
+       if (delay < 0) {
+               delay += io->buffer_size;
+               if (delay < 0)
+                       delay = 0;
+       }
+       delay %= io->buffer_size;
+       return delay;
+}
+
+static int a52_slave_hw_params_half(struct a52_ctx *rec)
+{
+       int err;
+
+       if ((err = snd_pcm_hw_params_malloc(&rec->hw_params)) < 0)
+               return err;
+
+       if ((err = snd_pcm_hw_params_any(rec->slave, rec->hw_params)) < 0) {
+               SNDERR("Cannot get slave hw_params");
+               goto out;
+       }
+       if ((err = snd_pcm_hw_params_set_access(rec->slave, rec->hw_params,
+                                               SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
+               SNDERR("Cannot set slave access RW_INTERLEAVED");
+               goto out;
+       }
+       if ((err = snd_pcm_hw_params_set_channels(rec->slave, rec->hw_params, 2)) < 0) {
+               SNDERR("Cannot set slave channels 2");
+               goto out;
+       }
+       if ((err = snd_pcm_hw_params_set_format(rec->slave, rec->hw_params,
+                                               rec->format)) < 0) {
+               SNDERR("Cannot set slave format");
+               goto out;
+       }
+       if ((err = snd_pcm_hw_params_set_rate(rec->slave, rec->hw_params, rec->rate, 0)) < 0) {
+               SNDERR("Cannot set slave rate %d", rec->rate);
+               goto out;
+       }
+       return 0;
+
+ out:
+       free(rec->hw_params);
+       rec->hw_params = NULL;
+       return err;
+}
+
+static int a52_hw_params(snd_pcm_ioplug_t *io,
+                        snd_pcm_hw_params_t *params ATTRIBUTE_UNUSED)
+{
+       struct a52_ctx *rec = io->private_data;
+       snd_pcm_uframes_t period_size;
+       snd_pcm_uframes_t buffer_size;
+       int err;
+
+       if (! rec->hw_params) {
+               err = a52_slave_hw_params_half(rec);
+               if (err < 0)
+                       return err;
+       }
+       period_size = io->period_size;
+       if ((err = snd_pcm_hw_params_set_period_size_near(rec->slave, rec->hw_params,
+                                                         &period_size, NULL)) < 0) {
+               SNDERR("Cannot set slave period size %ld", period_size);
+               return err;
+       }
+       buffer_size = io->buffer_size;
+       if ((err = snd_pcm_hw_params_set_buffer_size_near(rec->slave, rec->hw_params,
+                                                         &buffer_size)) < 0) {
+               SNDERR("Cannot set slave buffer size %ld", buffer_size);
+               return err;
+       }
+       if ((err = snd_pcm_hw_params(rec->slave, rec->hw_params)) < 0) {
+               SNDERR("Cannot set slave hw_params");
+               return err;
+       }
+       rec->slave_period_size = period_size;
+       rec->slave_buffer_size = buffer_size;
+
+       return 0;
+}
+
+static int a52_hw_free(snd_pcm_ioplug_t *io)
+{
+       struct a52_ctx *rec = io->private_data;
+
+       free(rec->hw_params);
+       rec->hw_params = NULL;
+       return snd_pcm_hw_free(rec->slave);
+}
+
+static int a52_sw_params(snd_pcm_ioplug_t *io, snd_pcm_sw_params_t *params)
+{
+       struct a52_ctx *rec = io->private_data;
+       snd_pcm_sw_params_t *sparams;
+       snd_pcm_uframes_t avail_min, start_threshold;
+       int len;
+
+       snd_pcm_sw_params_get_avail_min(params, &avail_min);
+       snd_pcm_sw_params_get_start_threshold(params, &start_threshold);
+
+       len = avail_min;
+       len += (int)rec->slave_buffer_size - (int)io->buffer_size;
+       if (len < 0)
+               avail_min = 1;
+       else
+               avail_min = len;
+       snd_pcm_sw_params_alloca(&sparams);
+       snd_pcm_sw_params_current(rec->slave, sparams);
+       snd_pcm_sw_params_set_avail_min(rec->slave, sparams, avail_min);
+       snd_pcm_sw_params_set_start_threshold(rec->slave, sparams,
+                                             start_threshold);
+
+       return snd_pcm_sw_params(rec->slave, sparams);
+}
+
+static int a52_start(snd_pcm_ioplug_t *io)
+{
+       struct a52_ctx *rec = io->private_data;
+
+       snd_pcm_start(rec->slave);
+       return 0;
+}
+
+static int a52_stop(snd_pcm_ioplug_t *io)
+{
+       struct a52_ctx *rec = io->private_data;
+
+       snd_pcm_drop(rec->slave);
+       return 0;
+}
+
+static void a52_free(struct a52_ctx *rec)
+{
+       if (rec->avctx) {
+               avcodec_close(rec->avctx);
+               av_free(rec->avctx);
+               rec->avctx = NULL;
+       }
+       free(rec->inbuf);
+       rec->inbuf = NULL;
+       free(rec->outbuf);
+       rec->outbuf = NULL;
+}
+
+static int a52_prepare(snd_pcm_ioplug_t *io)
+{
+       struct a52_ctx *rec = io->private_data;
+
+       a52_free(rec);
+
+       rec->avctx = avcodec_alloc_context();
+       if (! rec->avctx)
+               return -ENOMEM;
+
+       rec->avctx->bit_rate = rec->bitrate * 1000;
+       rec->avctx->sample_rate = io->rate;
+       rec->avctx->channels = io->channels;
+
+       if (avcodec_open(rec->avctx, rec->codec) < 0)
+               return -EINVAL;
+
+       rec->inbuf = malloc(rec->avctx->frame_size * 2 * io->channels);
+       if (! rec->inbuf)
+               return -ENOMEM;
+       rec->outbuf_size = rec->avctx->frame_size * 4;
+       rec->outbuf = malloc(rec->outbuf_size);
+       if (! rec->outbuf)
+               return -ENOMEM;
+
+       rec->transfer = 0;
+       rec->remain = 0;
+       rec->filled = 0;
+
+       return snd_pcm_prepare(rec->slave);
+}
+
+static int a52_poll_descriptors_count(snd_pcm_ioplug_t *io)
+{
+       struct a52_ctx *rec = io->private_data;
+       return snd_pcm_poll_descriptors_count(rec->slave);
+}
+
+static int a52_poll_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfd,
+                               unsigned int space)
+{
+       struct a52_ctx *rec = io->private_data;
+       return snd_pcm_poll_descriptors(rec->slave, pfd, space);
+}
+
+static int a52_poll_revents(snd_pcm_ioplug_t *io, struct pollfd *pfd,
+                           unsigned int nfds, unsigned short *revents)
+{
+       struct a52_ctx *rec = io->private_data;
+       return snd_pcm_poll_descriptors_revents(rec->slave, pfd, nfds, revents);
+}
+
+static int a52_close(snd_pcm_ioplug_t *io)
+{
+       struct a52_ctx *rec = io->private_data;
+
+       a52_free(rec);
+       if (rec->slave)
+               snd_pcm_close(rec->slave);
+       return 0;
+}
+                             
+static snd_pcm_ioplug_callback_t a52_ops = {
+       .start = a52_start,
+       .stop = a52_stop,
+       .pointer = a52_pointer,
+       .transfer = a52_transfer,
+       .close = a52_close,
+       .hw_params = a52_hw_params,
+       .hw_free = a52_hw_free,
+       .sw_params = a52_sw_params,
+       .prepare = a52_prepare,
+       .drain = a52_drain,
+       .poll_descriptors_count = a52_poll_descriptors_count,
+       .poll_descriptors = a52_poll_descriptors,
+       .poll_revents = a52_poll_revents,
+};
+
+#define A52_FRAME_SIZE 1536
+
+#define ARRAY_SIZE(ary)        (sizeof(ary)/sizeof(ary[0]))
+
+static int a52_set_hw_constraint(struct a52_ctx *rec)
+{
+       unsigned int accesses[] = {
+               SND_PCM_ACCESS_MMAP_INTERLEAVED,
+               SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
+               SND_PCM_ACCESS_RW_INTERLEAVED,
+               SND_PCM_ACCESS_RW_NONINTERLEAVED
+       };
+       unsigned int formats[] = { SND_PCM_FORMAT_S16 };
+       int err;
+       snd_pcm_uframes_t buffer_max;
+       unsigned int period_bytes, max_periods;
+
+       if ((err = snd_pcm_ioplug_set_param_list(&rec->io, SND_PCM_IOPLUG_HW_ACCESS,
+                                                ARRAY_SIZE(accesses), accesses)) < 0 ||
+           (err = snd_pcm_ioplug_set_param_list(&rec->io, SND_PCM_IOPLUG_HW_FORMAT,
+                                                ARRAY_SIZE(formats), formats)) < 0 ||
+           (err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_CHANNELS,
+                                                  rec->channels, rec->channels)) < 0 ||
+           (err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_RATE,
+                                                  rec->rate, rec->rate)) < 0)
+               return err;
+
+       if ((err = a52_slave_hw_params_half(rec)) < 0)
+               return err;
+
+       snd_pcm_hw_params_get_buffer_size_max(rec->hw_params, &buffer_max);
+       period_bytes = A52_FRAME_SIZE * 2 * rec->channels;
+       max_periods = buffer_max / A52_FRAME_SIZE;
+
+       if ((err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
+                                                  period_bytes, period_bytes)) < 0 ||
+           (err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_PERIODS,
+                                                  2, max_periods)) < 0)
+               return err;
+
+       return 0;
+}
+
+SND_PCM_PLUGIN_DEFINE_FUNC(a52)
+{
+       snd_config_iterator_t i, next;
+       int err;
+       const char *card = NULL;
+       unsigned int rate = 48000;
+       unsigned int bitrate = 448;
+       unsigned int channels = 6;
+       snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
+       char devstr[128];
+       struct a52_ctx *rec;
+       
+       if (stream != SND_PCM_STREAM_PLAYBACK) {
+               SNDERR("a52 is only for playback");
+               return -EINVAL;
+       }
+
+       snd_config_for_each(i, next, conf) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *id;
+               if (snd_config_get_id(n, &id) < 0)
+                       continue;
+               if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
+                       continue;
+               if (strcmp(id, "card") == 0) {
+                       if (snd_config_get_string(n, &card) < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+                       continue;
+               }
+               if (strcmp(id, "rate") == 0) {
+                       long val;
+                       if (snd_config_get_integer(n, &val) < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+                       rate = val;
+                       if (rate != 44100 && rate != 48000) {
+                               SNDERR("rate must be 44100 or 48000");
+                               return -EINVAL;
+                       }
+                       continue;
+               }
+               if (strcmp(id, "bitrate") == 0) {
+                       long val;
+                       if (snd_config_get_integer(n, &val) < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+                       bitrate = val;
+                       if (bitrate < 128 || bitrate > 1000) {
+                               SNDERR("Invalid bitrate value %d", bitrate);
+                               return -EINVAL;
+                       }
+                       continue;
+               }
+               if (strcmp(id, "channels") == 0) {
+                       long val;
+                       if (snd_config_get_integer(n, &val) < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+                       channels = val;
+                       if (channels != 2 && channels != 4 && channels != 6) {
+                               SNDERR("channels must be 2, 4 or 6");
+                               return -EINVAL;
+                       }
+                       continue;
+               }
+               if (strcmp(id, "format") == 0) {
+                       const char *str;
+                       err = snd_config_get_string(n, &str);
+                       if (err < 0) {
+                               SNDERR("invalid type for %s", id);
+                               return -EINVAL;
+                       }
+                       format = snd_pcm_format_value(str);
+                       if (format == SND_PCM_FORMAT_UNKNOWN) {
+                               SNDERR("unknown format %s", str);
+                               return -EINVAL;
+                       }
+                       if (format != SND_PCM_FORMAT_S16_LE &&
+                           format != SND_PCM_FORMAT_S16_BE) {
+                               SNDERR("Only S16_LE/BE formats are allowed");
+                               return -EINVAL;
+                       }
+                       continue;
+               }
+               SNDERR("Unknown field %s", id);
+               return -EINVAL;
+       }
+
+       rec = calloc(1, sizeof(*rec));
+       if (! rec) {
+               SNDERR("cannot allocate");
+               return -ENOMEM;
+       }
+
+       rec->rate = rate;
+       rec->bitrate = bitrate;
+       rec->channels = channels;
+       rec->format = format;
+
+       avcodec_register_all();
+       rec->codec = avcodec_find_encoder(CODEC_ID_AC3);
+       if (! rec->codec) {
+               SNDERR("Cannot find codec engine");
+               err = -EINVAL;
+               goto error;
+       }
+
+       snprintf(devstr, sizeof(devstr),
+                "plug:iec958:{AES0 0x%x AES1 0x%x AES2 0x%x AES3 0x%x %s%s}",
+                IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO |
+                IEC958_AES0_CON_NOT_COPYRIGHT,
+                IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
+                0, rate == 48000 ? IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100,
+                card ? " CARD " : "",
+                card ? card : "");
+
+       err = snd_pcm_open(&rec->slave, devstr, stream, mode);
+       if (err < 0)
+               goto error;
+
+       rec->io.version = SND_PCM_IOPLUG_VERSION;
+       rec->io.name = "A52 Output Plugin";
+       rec->io.mmap_rw = 0;
+       rec->io.callback = &a52_ops;
+       rec->io.private_data = rec;
+
+       err = snd_pcm_ioplug_create(&rec->io, name, stream, mode);
+       if (err < 0)
+               goto error;
+
+       if ((err = a52_set_hw_constraint(rec)) < 0) {
+               snd_pcm_ioplug_delete(&rec->io);
+               return err;
+       }
+
+       *pcmp = rec->io.pcm;
+       return 0;
+
+ error:
+       if (rec->slave)
+               snd_pcm_close(rec->slave);
+       free(rec);
+       return err;
+}
+
+SND_PCM_PLUGIN_SYMBOL(a52);
index 7691770b966ac00a882c16913501247991f0eac1..b45ee56c4ff53ecc90a3f723c430e3aaa76f3929 100644 (file)
@@ -22,6 +22,27 @@ AM_CONDITIONAL(HAVE_POLYP, test x$HAVE_POLYP = xyes)
 PKG_CHECK_MODULES(samplerate, [samplerate], [HAVE_SAMPLERATE=yes], [HAVE_SAMPLERATE=no])
 AM_CONDITIONAL(HAVE_SAMPLERATE, test x$HAVE_SAMPLERATE = xyes)
 
+AC_ARG_WITH([avcodec-includedir],
+       [--with-avcodec-includedir=dir    AVcodec include directory],
+       [AVCODEC_CFLAGS="-I$withval"], [AVCODEC_CFLAGS=""])
+AC_ARG_WITH([avcodec-libdir],
+       [--with-avcodec-libdir=dir        AVcodec library directory],
+       [AVCODEC_LIBS="-L$withval"], [AVCODEC_LIBS=""])
+CFLAGS_saved="$CFLAGS"
+LDFLAGS_saved="$LDFLAGS"
+CFLAGS="$CFLAGS $AVCODEC_CFLAGS"
+LDFLAGS="$LDFLAGS $AVCODEC_LIBS"
+AC_SUBST(AVCODEC_CFLAGS)
+AVCODEC_LIBS="$AVCODEC_LIBS -lavcodec"
+AC_SUBST(AVCODEC_LIBS)
+AC_CHECK_LIB([avcodec], [avcodec_open], [HAVE_AVCODEC=yes], [HAVE_AVCODEC=no])
+if test x$HAVE_AVCODEC = xyes; then
+  AC_CHECK_HEADER([ffmpeg/avcodec.h], [], [HAVE_AVCODEC=no])
+fi
+AM_CONDITIONAL(HAVE_AVCODEC, test x$HAVE_AVCODEC = xyes)
+CFLAGS="$CFLAGS_saved"
+LDFLAGS="$LDFLAGS_saved"
+
 SAVE_PLUGINS_VERSION
 
 AC_OUTPUT([
@@ -31,5 +52,6 @@ AC_OUTPUT([
        polyp/Makefile
        mix/Makefile
        rate/Makefile
+       a52/Makefile
        doc/Makefile
 ])
diff --git a/doc/a52.txt b/doc/a52.txt
new file mode 100644 (file)
index 0000000..236e64a
--- /dev/null
@@ -0,0 +1,48 @@
+A52 OUTPUT PLUGIN
+=================
+
+This plugin converts S16 linear format to A52 compressed stream and
+send to an SPDIF output.  It requires libavcodec for encoding the
+audio stream.
+
+A PCM using this plugin can be defined like below:
+
+       pcm.myout {
+               type a52
+       }
+
+In addition, the following options are available:
+
+- The "card" option specifies the card ID or number of the SPDIF.
+  The output PCM becomes "iec958:{CARD=$CARD}" with extra AESx
+  settings.  When omitted, the default card is used.
+
+- The "rate" option specifies the input/output sample rate in HZ.
+  The accepted rate is either 44100 or 48000.
+  When omitted, 48000 is used.
+
+- The "channels" option specifies the number of _input_ channels.
+  It must be either 2, 4 or 6.  The default value is 6.
+
+- The "bitrate" option specifies the bit-rate of the compressed
+  stream in kbps.  Too small or too big value may not be accepted by
+  the encoder.  When omitted, 448 is used.
+
+- The "format" option specifies the output format type.  It's either
+  S16_LE or S16_BE.  As default, S16_LE is used.
+
+An example using the secondary card, 44.1kHz, 4 channels, output
+bitrate 256kbps and output format S16_BE looks like below: 
+
+       pcm.myout {
+               type a52
+               card 1
+               rate 44100
+               channels 4
+               bitrate 256
+               format S16_BE
+       }
+
+
+The plugin reads always S16 format (i.e. native-endian) as input, so
+you'd need plug layer appropriately to covert it.