2 * Copyright © 2014 Nick Bowler
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This file incorporates work covered by the following copyright and
12 * Copyright (c) 2009 by Takashi Iwai <tiwai@suse.de>
14 * This library is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License as
16 * published by the Free Software Foundation; either version 2.1 of
17 * the License, or (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include <alsa/asoundlib.h>
28 #include <alsa/pcm_external.h>
29 #include <alsa/pcm_plugin.h>
30 #include <libavresample/avresample.h>
31 #include <libavutil/samplefmt.h>
32 #include <libavutil/opt.h>
35 snd_pcm_extplug_t ext;
36 AVAudioResampleContext *avr;
39 static inline void *area_addr(const snd_pcm_channel_area_t *area,
40 snd_pcm_uframes_t offset)
42 unsigned int bitofs = area->first + area->step * offset;
43 return (char *) area->addr + bitofs / 8;
46 static snd_pcm_sframes_t
47 dplii_transfer(snd_pcm_extplug_t *ext,
48 const snd_pcm_channel_area_t *dst_areas,
49 snd_pcm_uframes_t dst_offset,
50 const snd_pcm_channel_area_t *src_areas,
51 snd_pcm_uframes_t src_offset,
52 snd_pcm_uframes_t size)
54 struct dplii_state *dplii = (void *)ext;
55 void *src = area_addr(src_areas, src_offset);
56 void *dst = area_addr(dst_areas, dst_offset);
58 return avresample_convert(dplii->avr, &dst, 2*size, size,
62 static int dplii_init(snd_pcm_extplug_t *ext)
64 struct dplii_state *dplii = (void *)ext;
68 dplii->avr = avresample_alloc_context();
72 av_opt_set_int(dplii->avr, "in_channel_layout", AV_CH_LAYOUT_5POINT0, 0);
73 av_opt_set_int(dplii->avr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);
74 av_opt_set_int(dplii->avr, "in_sample_rate", ext->rate, 0);
75 av_opt_set_int(dplii->avr, "out_sample_rate", ext->rate, 0);
76 av_opt_set_int(dplii->avr, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
77 av_opt_set_int(dplii->avr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
78 av_opt_set_int(dplii->avr, "matrix_encoding", AV_MATRIX_ENCODING_DPLII, 0);
80 rc = avresample_open(dplii->avr);
82 avresample_free(&dplii->avr);
89 static int dplii_close(snd_pcm_extplug_t *ext)
91 struct dplii_state *dplii = (void *)ext;
93 avresample_close(dplii->avr);
94 avresample_free(&dplii->avr);
99 static const unsigned chmap5[] = {
100 SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_FC, SND_CHMAP_RL, SND_CHMAP_RR,
103 static snd_pcm_chmap_query_t **dplii_query_chmaps(snd_pcm_extplug_t *ext)
105 snd_pcm_chmap_query_t **maps;
106 snd_pcm_chmap_query_t *p;
108 maps = malloc(2 * sizeof *maps);
112 p = malloc(sizeof *p + 5 * sizeof *p->map.pos);
118 p->type = SND_CHMAP_TYPE_FIXED;
120 memcpy(p->map.pos, chmap5, 5 * sizeof p->map.pos[0]);
128 static snd_pcm_chmap_t *dplii_get_chmap(snd_pcm_extplug_t *ext)
130 snd_pcm_chmap_t *map;
132 map = malloc(sizeof *map + 5 * sizeof map->pos[0]);
135 memcpy(map->pos, chmap5, 5 * sizeof map->pos[0]);
141 static const snd_pcm_extplug_callback_t dplii_callback = {
143 .transfer = dplii_transfer,
144 .close = dplii_close,
145 .query_chmaps = dplii_query_chmaps,
146 .get_chmap = dplii_get_chmap,
149 SND_PCM_PLUGIN_DEFINE_FUNC(dplii)
151 snd_config_iterator_t i, next;
152 snd_config_t *slave = NULL;
153 struct dplii_state *dplii;
156 if (stream != SND_PCM_STREAM_PLAYBACK) {
157 SNDERR("dplii is only for playback");
161 snd_config_for_each(i, next, conf) {
162 snd_config_t *n = snd_config_iterator_entry(i);
165 if (snd_config_get_id(n, &id) < 0)
167 if (!strcmp(id, "comment") || !strcmp(id, "type") || !strcmp(id, "hint"))
170 if (!strcmp(id, "slave")) {
175 SNDERR("Unknown field %s", id);
180 SNDERR("no slave configuration for %s", name);
184 dplii = malloc(sizeof *dplii);
188 *dplii = (struct dplii_state) {
190 .version = SND_PCM_EXTPLUG_VERSION,
191 .name = "Dolby Pro-Logic II Encoder Plugin",
192 .callback = &dplii_callback,
193 .private_data = dplii,
197 rc = snd_pcm_extplug_create(&dplii->ext, name, root,
198 slave, stream, mode);
204 snd_pcm_extplug_set_param(&dplii->ext, SND_PCM_EXTPLUG_HW_CHANNELS, 5);
205 snd_pcm_extplug_set_slave_param(&dplii->ext, SND_PCM_EXTPLUG_HW_CHANNELS, 2);
206 snd_pcm_extplug_set_param(&dplii->ext, SND_PCM_EXTPLUG_HW_FORMAT, SND_PCM_FORMAT_S16);
207 snd_pcm_extplug_set_slave_param(&dplii->ext, SND_PCM_EXTPLUG_HW_FORMAT, SND_PCM_FORMAT_S16);
209 *pcmp = dplii->ext.pcm;
212 SND_PCM_PLUGIN_SYMBOL(dplii);