]> code.delx.au - pulseaudio/blob - src/pulsecore/remap.c
remap: Add stereo to mono and 4-channel special case remapping
[pulseaudio] / src / pulsecore / remap.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk.com>
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 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 Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h>
28
29 #include <pulse/xmalloc.h>
30 #include <pulse/sample.h>
31 #include <pulse/volume.h>
32 #include <pulsecore/log.h>
33 #include <pulsecore/macro.h>
34
35 #include "remap.h"
36
37 static void remap_mono_to_stereo_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
38 unsigned i;
39
40 for (i = n >> 2; i; i--) {
41 dst[0] = dst[1] = src[0];
42 dst[2] = dst[3] = src[1];
43 dst[4] = dst[5] = src[2];
44 dst[6] = dst[7] = src[3];
45 src += 4;
46 dst += 8;
47 }
48 for (i = n & 3; i; i--) {
49 dst[0] = dst[1] = src[0];
50 src++;
51 dst += 2;
52 }
53 }
54
55 static void remap_mono_to_stereo_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
56 unsigned i;
57
58 for (i = n >> 2; i; i--) {
59 dst[0] = dst[1] = src[0];
60 dst[2] = dst[3] = src[1];
61 dst[4] = dst[5] = src[2];
62 dst[6] = dst[7] = src[3];
63 src += 4;
64 dst += 8;
65 }
66 for (i = n & 3; i; i--) {
67 dst[0] = dst[1] = src[0];
68 src++;
69 dst += 2;
70 }
71 }
72
73 static void remap_stereo_to_mono_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
74 unsigned i;
75
76 for (i = n >> 2; i > 0; i--) {
77 dst[0] = (src[0] + src[1])/2;
78 dst[1] = (src[2] + src[3])/2;
79 dst[2] = (src[4] + src[5])/2;
80 dst[3] = (src[6] + src[7])/2;
81 src += 8;
82 dst += 4;
83 }
84 for (i = n & 3; i; i--) {
85 dst[0] = (src[0] + src[1])/2;
86 src += 2;
87 dst += 1;
88 }
89 }
90
91 static void remap_stereo_to_mono_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
92 unsigned i;
93
94 for (i = n >> 2; i > 0; i--) {
95 dst[0] = (src[0] + src[1])*0.5f;
96 dst[1] = (src[2] + src[3])*0.5f;
97 dst[2] = (src[4] + src[5])*0.5f;
98 dst[3] = (src[6] + src[7])*0.5f;
99 src += 8;
100 dst += 4;
101 }
102 for (i = n & 3; i; i--) {
103 dst[0] = (src[0] + src[1])*0.5f;
104 src += 2;
105 dst += 1;
106 }
107 }
108
109 static void remap_mono_to_ch4_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
110 unsigned i;
111
112 for (i = n >> 2; i; i--) {
113 dst[0] = dst[1] = dst[2] = dst[3] = src[0];
114 dst[4] = dst[5] = dst[6] = dst[7] = src[1];
115 dst[8] = dst[9] = dst[10] = dst[11] = src[2];
116 dst[12] = dst[13] = dst[14] = dst[15] = src[3];
117 src += 4;
118 dst += 16;
119 }
120 for (i = n & 3; i; i--) {
121 dst[0] = dst[1] = dst[2] = dst[3] = src[0];
122 src++;
123 dst += 4;
124 }
125 }
126
127 static void remap_mono_to_ch4_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
128 unsigned i;
129
130 for (i = n >> 2; i; i--) {
131 dst[0] = dst[1] = dst[2] = dst[3] = src[0];
132 dst[4] = dst[5] = dst[6] = dst[7] = src[1];
133 dst[8] = dst[9] = dst[10] = dst[11] = src[2];
134 dst[12] = dst[13] = dst[14] = dst[15] = src[3];
135 src += 4;
136 dst += 16;
137 }
138 for (i = n & 3; i; i--) {
139 dst[0] = dst[1] = dst[2] = dst[3] = src[0];
140 src++;
141 dst += 4;
142 }
143 }
144
145 static void remap_ch4_to_mono_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
146 unsigned i;
147
148 for (i = n >> 2; i > 0; i--) {
149 dst[0] = (src[0] + src[1] + src[2] + src[3])/4;
150 dst[1] = (src[4] + src[5] + src[6] + src[7])/4;
151 dst[2] = (src[8] + src[9] + src[10] + src[11])/4;
152 dst[3] = (src[12] + src[13] + src[14] + src[15])/4;
153 src += 16;
154 dst += 4;
155 }
156 for (i = n & 3; i; i--) {
157 dst[0] = (src[0] + src[1] + src[2] + src[3])/4;
158 src += 4;
159 dst += 1;
160 }
161 }
162
163 static void remap_ch4_to_mono_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
164 unsigned i;
165
166 for (i = n >> 2; i > 0; i--) {
167 dst[0] = (src[0] + src[1] + src[2] + src[3])*0.25f;
168 dst[1] = (src[4] + src[5] + src[6] + src[7])*0.25f;
169 dst[2] = (src[8] + src[9] + src[10] + src[11])*0.25f;
170 dst[3] = (src[12] + src[13] + src[14] + src[15])*0.25f;
171 src += 16;
172 dst += 4;
173 }
174 for (i = n & 3; i; i--) {
175 dst[0] = (src[0] + src[1] + src[2] + src[3])*0.25f;
176 src += 4;
177 dst += 1;
178 }
179 }
180
181 static void remap_channels_matrix_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
182
183 unsigned oc, ic, i;
184 unsigned n_ic, n_oc;
185
186 n_ic = m->i_ss.channels;
187 n_oc = m->o_ss.channels;
188
189 memset(dst, 0, n * sizeof(int16_t) * n_oc);
190
191 for (oc = 0; oc < n_oc; oc++) {
192
193 for (ic = 0; ic < n_ic; ic++) {
194 int16_t *d = dst + oc;
195 const int16_t *s = src + ic;
196 int32_t vol = m->map_table_i[oc][ic];
197
198 if (vol <= 0)
199 continue;
200
201 if (vol >= 0x10000) {
202 for (i = n; i > 0; i--, s += n_ic, d += n_oc)
203 *d += *s;
204 } else {
205 for (i = n; i > 0; i--, s += n_ic, d += n_oc)
206 *d += (int16_t) (((int32_t)*s * vol) >> 16);
207 }
208 }
209 }
210 }
211
212 static void remap_channels_matrix_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
213 unsigned oc, ic, i;
214 unsigned n_ic, n_oc;
215
216 n_ic = m->i_ss.channels;
217 n_oc = m->o_ss.channels;
218
219 memset(dst, 0, n * sizeof(float) * n_oc);
220
221 for (oc = 0; oc < n_oc; oc++) {
222
223 for (ic = 0; ic < n_ic; ic++) {
224 float *d = dst + oc;
225 const float *s = src + ic;
226 float vol = m->map_table_f[oc][ic];
227
228 if (vol <= 0.0f)
229 continue;
230
231 if (vol >= 1.0f) {
232 for (i = n; i > 0; i--, s += n_ic, d += n_oc)
233 *d += *s;
234 } else {
235 for (i = n; i > 0; i--, s += n_ic, d += n_oc)
236 *d += *s * vol;
237 }
238 }
239 }
240 }
241
242 bool pa_setup_remap_arrange(const pa_remap_t *m, int8_t arrange[PA_CHANNELS_MAX]) {
243 unsigned ic, oc;
244 unsigned n_ic, n_oc;
245
246 pa_assert(m);
247
248 n_ic = m->i_ss.channels;
249 n_oc = m->o_ss.channels;
250
251 for (oc = 0; oc < n_oc; oc++) {
252 arrange[oc] = -1;
253 for (ic = 0; ic < n_ic; ic++) {
254 int32_t vol = m->map_table_i[oc][ic];
255
256 /* input channel is not used */
257 if (vol == 0)
258 continue;
259
260 /* if mixing this channel, we cannot just rearrange */
261 if (vol != 0x10000 || arrange[oc] >= 0)
262 return false;
263
264 arrange[oc] = ic;
265 }
266 }
267
268 return true;
269 }
270
271 static void remap_arrange_stereo_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
272 const unsigned n_ic = m->i_ss.channels;
273 const int8_t *arrange = m->state;
274 const int8_t ic0 = arrange[0], ic1 = arrange[1];
275
276 for (; n > 0; n--) {
277 *dst++ = (ic0 >= 0) ? *(src + ic0) : 0;
278 *dst++ = (ic1 >= 0) ? *(src + ic1) : 0;
279 src += n_ic;
280 }
281 }
282
283 static void remap_arrange_ch4_s16ne_c(pa_remap_t *m, int16_t *dst, const int16_t *src, unsigned n) {
284 const unsigned n_ic = m->i_ss.channels;
285 const int8_t *arrange = m->state;
286 const int8_t ic0 = arrange[0], ic1 = arrange[1],
287 ic2 = arrange[2], ic3 = arrange[3];
288
289 for (; n > 0; n--) {
290 *dst++ = (ic0 >= 0) ? *(src + ic0) : 0;
291 *dst++ = (ic1 >= 0) ? *(src + ic1) : 0;
292 *dst++ = (ic2 >= 0) ? *(src + ic2) : 0;
293 *dst++ = (ic3 >= 0) ? *(src + ic3) : 0;
294 src += n_ic;
295 }
296 }
297
298 static void remap_arrange_stereo_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
299 const unsigned n_ic = m->i_ss.channels;
300 const int8_t *arrange = m->state;
301 const int ic0 = arrange[0], ic1 = arrange[1];
302
303 for (; n > 0; n--) {
304 *dst++ = (ic0 >= 0) ? *(src + ic0) : 0.0f;
305 *dst++ = (ic1 >= 0) ? *(src + ic1) : 0.0f;
306 src += n_ic;
307 }
308 }
309
310 static void remap_arrange_ch4_float32ne_c(pa_remap_t *m, float *dst, const float *src, unsigned n) {
311 const unsigned n_ic = m->i_ss.channels;
312 const int8_t *arrange = m->state;
313 const int ic0 = arrange[0], ic1 = arrange[1],
314 ic2 = arrange[2], ic3 = arrange[3];
315
316 for (; n > 0; n--) {
317 *dst++ = (ic0 >= 0) ? *(src + ic0) : 0.0f;
318 *dst++ = (ic1 >= 0) ? *(src + ic1) : 0.0f;
319 *dst++ = (ic2 >= 0) ? *(src + ic2) : 0.0f;
320 *dst++ = (ic3 >= 0) ? *(src + ic3) : 0.0f;
321 src += n_ic;
322 }
323 }
324
325 void pa_set_remap_func(pa_remap_t *m, pa_do_remap_func_t func_s16,
326 pa_do_remap_func_t func_float) {
327
328 pa_assert(m);
329
330 if (m->format == PA_SAMPLE_S16NE)
331 m->do_remap = func_s16;
332 else if (m->format == PA_SAMPLE_FLOAT32NE)
333 m->do_remap = func_float;
334 else
335 pa_assert_not_reached();
336 }
337
338 /* set the function that will execute the remapping based on the matrices */
339 static void init_remap_c(pa_remap_t *m) {
340 unsigned n_oc, n_ic;
341 int8_t arrange[PA_CHANNELS_MAX];
342
343 n_oc = m->o_ss.channels;
344 n_ic = m->i_ss.channels;
345
346 /* find some common channel remappings, fall back to full matrix operation. */
347 if (n_ic == 1 && n_oc == 2 &&
348 m->map_table_i[0][0] == 0x10000 && m->map_table_i[1][0] == 0x10000) {
349
350 pa_log_info("Using mono to stereo remapping");
351 pa_set_remap_func(m, (pa_do_remap_func_t) remap_mono_to_stereo_s16ne_c,
352 (pa_do_remap_func_t) remap_mono_to_stereo_float32ne_c);
353 } else if (n_ic == 2 && n_oc == 1 &&
354 m->map_table_i[0][0] == 0x8000 && m->map_table_i[0][1] == 0x8000) {
355
356 pa_log_info("Using stereo to mono remapping");
357 pa_set_remap_func(m, (pa_do_remap_func_t) remap_stereo_to_mono_s16ne_c,
358 (pa_do_remap_func_t) remap_stereo_to_mono_float32ne_c);
359 } else if (n_ic == 1 && n_oc == 4 &&
360 m->map_table_i[0][0] == 0x10000 && m->map_table_i[1][0] == 0x10000 &&
361 m->map_table_i[2][0] == 0x10000 && m->map_table_i[3][0] == 0x10000) {
362
363 pa_log_info("Using mono to 4-channel remapping");
364 pa_set_remap_func(m, (pa_do_remap_func_t)remap_mono_to_ch4_s16ne_c,
365 (pa_do_remap_func_t) remap_mono_to_ch4_float32ne_c);
366 } else if (n_ic == 4 && n_oc == 1 &&
367 m->map_table_i[0][0] == 0x4000 && m->map_table_i[0][1] == 0x4000 &&
368 m->map_table_i[0][2] == 0x4000 && m->map_table_i[0][3] == 0x4000) {
369
370 pa_log_info("Using 4-channel to mono remapping");
371 pa_set_remap_func(m, (pa_do_remap_func_t) remap_ch4_to_mono_s16ne_c,
372 (pa_do_remap_func_t) remap_ch4_to_mono_float32ne_c);
373 } else if (pa_setup_remap_arrange(m, arrange) && n_oc == 2) {
374
375 pa_log_info("Using stereo arrange remapping");
376 pa_set_remap_func(m, (pa_do_remap_func_t) remap_arrange_stereo_s16ne_c,
377 (pa_do_remap_func_t) remap_arrange_stereo_float32ne_c);
378
379 /* setup state */
380 m->state = pa_xnewdup(int8_t, arrange, PA_CHANNELS_MAX);
381 } else if (pa_setup_remap_arrange(m, arrange) && n_oc == 4) {
382
383 pa_log_info("Using 4-channel arrange remapping");
384 pa_set_remap_func(m, (pa_do_remap_func_t) remap_arrange_ch4_s16ne_c,
385 (pa_do_remap_func_t) remap_arrange_ch4_float32ne_c);
386
387 /* setup state */
388 m->state = pa_xnewdup(int8_t, arrange, PA_CHANNELS_MAX);
389 } else {
390
391 pa_log_info("Using generic matrix remapping");
392 pa_set_remap_func(m, (pa_do_remap_func_t) remap_channels_matrix_s16ne_c,
393 (pa_do_remap_func_t) remap_channels_matrix_float32ne_c);
394 }
395 }
396
397 /* default C implementation */
398 static pa_init_remap_func_t init_remap_func = init_remap_c;
399
400 void pa_init_remap_func(pa_remap_t *m) {
401 pa_assert(init_remap_func);
402
403 m->do_remap = NULL;
404
405 /* call the installed remap init function */
406 init_remap_func(m);
407
408 if (m->do_remap == NULL) {
409 /* nothing was installed, fallback to C version */
410 init_remap_c(m);
411 }
412 }
413
414 pa_init_remap_func_t pa_get_init_remap_func(void) {
415 return init_remap_func;
416 }
417
418 void pa_set_init_remap_func(pa_init_remap_func_t func) {
419 init_remap_func = func;
420 }