]> git.draconx.ca Git - pcm-dplii.git/blob - src/dplii.c
Initial commit
[pcm-dplii.git] / src / dplii.c
1 /*
2  * Copyright © 2014 Nick Bowler
3  *
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.
8  *
9  * This file incorporates work covered by the following copyright and
10  * permission notice:
11  *
12  *   Copyright (c) 2009 by Takashi Iwai <tiwai@suse.de>
13  *
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.
18  *
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.
23  *
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/>.
26  */
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>
33
34 struct dplii_state {
35         snd_pcm_extplug_t ext;
36         AVAudioResampleContext *avr;
37 };
38
39 static inline void *area_addr(const snd_pcm_channel_area_t *area,
40                               snd_pcm_uframes_t offset)
41 {
42         unsigned int bitofs = area->first + area->step * offset;
43         return (char *) area->addr + bitofs / 8;
44 }
45
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)
53 {
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);
57
58         return avresample_convert(dplii->avr, &dst, 2*size, size,
59                                               &src, 2*size, size);
60 }
61
62 static int dplii_init(snd_pcm_extplug_t *ext)
63 {
64         struct dplii_state *dplii = (void *)ext;
65         unsigned rate;
66         int rc;
67
68         dplii->avr = avresample_alloc_context();
69         if (!dplii->avr)
70                 return -ENOMEM;
71
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);
79
80         rc = avresample_open(dplii->avr);
81         if (rc < 0) {
82                 avresample_free(&dplii->avr);
83                 return -ENODEV;
84         }
85
86         return 0;
87 }
88
89 static int dplii_close(snd_pcm_extplug_t *ext)
90 {
91         struct dplii_state *dplii = (void *)ext;
92
93         avresample_close(dplii->avr);
94         avresample_free(&dplii->avr);
95
96         return 0;
97 }
98
99 static const unsigned chmap5[] = {
100         SND_CHMAP_FL, SND_CHMAP_FR, SND_CHMAP_FC, SND_CHMAP_RL, SND_CHMAP_RR,
101 };
102
103 static snd_pcm_chmap_query_t **dplii_query_chmaps(snd_pcm_extplug_t *ext)
104 {
105         snd_pcm_chmap_query_t **maps;
106         snd_pcm_chmap_query_t *p;
107
108         maps = malloc(2 * sizeof *maps);
109         if (!maps)
110                 return NULL;
111
112         p = malloc(sizeof *p + 5 * sizeof *p->map.pos);
113         if (!p) {
114                 free(maps);
115                 return NULL;
116         }
117
118         p->type = SND_CHMAP_TYPE_FIXED;
119         p->map.channels = 5;
120         memcpy(p->map.pos, chmap5, 5 * sizeof p->map.pos[0]);
121
122         maps[0] = p;
123         maps[1] = NULL;
124
125         return maps;
126 }
127
128 static snd_pcm_chmap_t *dplii_get_chmap(snd_pcm_extplug_t *ext)
129 {
130         snd_pcm_chmap_t *map;
131
132         map = malloc(sizeof *map + 5 * sizeof map->pos[0]);
133         if (map) {
134                 map->channels = 5;
135                 memcpy(map->pos, chmap5, 5 * sizeof map->pos[0]);
136         }
137
138         return map;
139 }
140
141 static const snd_pcm_extplug_callback_t dplii_callback = {
142         .init         = dplii_init,
143         .transfer     = dplii_transfer,
144         .close        = dplii_close,
145         .query_chmaps = dplii_query_chmaps,
146         .get_chmap    = dplii_get_chmap,
147 };
148
149 SND_PCM_PLUGIN_DEFINE_FUNC(dplii)
150 {
151         snd_config_iterator_t i, next;
152         snd_config_t *slave = NULL;
153         struct dplii_state *dplii;
154         int rc;
155
156         if (stream != SND_PCM_STREAM_PLAYBACK) {
157                 SNDERR("dplii is only for playback");
158                 return -EINVAL;
159         }
160
161         snd_config_for_each(i, next, conf) {
162                 snd_config_t *n = snd_config_iterator_entry(i);
163                 const char *id;
164
165                 if (snd_config_get_id(n, &id) < 0)
166                         continue;
167                 if (!strcmp(id, "comment") || !strcmp(id, "type") || !strcmp(id, "hint"))
168                         continue;
169
170                 if (!strcmp(id, "slave")) {
171                         slave = n;
172                         continue;
173                 }
174
175                 SNDERR("Unknown field %s", id);
176                 return -EINVAL;
177         }
178
179         if (!slave) {
180                 SNDERR("no slave configuration for %s", name);
181                 return -EINVAL;
182         }
183
184         dplii = malloc(sizeof *dplii);
185         if (!dplii)
186                 return -ENOMEM;
187
188         *dplii = (struct dplii_state) {
189                 .ext = {
190                         .version      = SND_PCM_EXTPLUG_VERSION,
191                         .name         = "Dolby Pro-Logic II Encoder Plugin",
192                         .callback     = &dplii_callback,
193                         .private_data = dplii,
194                 },
195         };
196
197         rc = snd_pcm_extplug_create(&dplii->ext, name, root,
198                                     slave, stream, mode);
199         if (rc < 0) {
200                 free(dplii);
201                 return rc;
202         }
203
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);
208
209         *pcmp = dplii->ext.pcm;
210         return 0;
211 }
212 SND_PCM_PLUGIN_SYMBOL(dplii);