1/***
2  This file is part of PulseAudio.
3
4  Copyright 2005-2006 Lennart Poettering
5  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
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, see <http://www.gnu.org/licenses/>.
19***/
20
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
25#include <stdlib.h>
26#include <stdio.h>
27#include <string.h>
28
29#include <pulse/xmalloc.h>
30
31#include <pulsecore/i18n.h>
32#include <pulsecore/core-util.h>
33#include <pulsecore/macro.h>
34#include <pulsecore/bitset.h>
35#include <pulsecore/sample-util.h>
36
37#include "channelmap.h"
38
39static const char *const table[PA_CHANNEL_POSITION_MAX] = {
40    [PA_CHANNEL_POSITION_MONO] = "mono",
41
42    [PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center",
43    [PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left",
44    [PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right",
45
46    [PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center",
47    [PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left",
48    [PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right",
49
50    [PA_CHANNEL_POSITION_LFE] = "lfe",
51
52    [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center",
53    [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center",
54
55    [PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left",
56    [PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right",
57
58    [PA_CHANNEL_POSITION_AUX0] = "aux0",
59    [PA_CHANNEL_POSITION_AUX1] = "aux1",
60    [PA_CHANNEL_POSITION_AUX2] = "aux2",
61    [PA_CHANNEL_POSITION_AUX3] = "aux3",
62    [PA_CHANNEL_POSITION_AUX4] = "aux4",
63    [PA_CHANNEL_POSITION_AUX5] = "aux5",
64    [PA_CHANNEL_POSITION_AUX6] = "aux6",
65    [PA_CHANNEL_POSITION_AUX7] = "aux7",
66    [PA_CHANNEL_POSITION_AUX8] = "aux8",
67    [PA_CHANNEL_POSITION_AUX9] = "aux9",
68    [PA_CHANNEL_POSITION_AUX10] = "aux10",
69    [PA_CHANNEL_POSITION_AUX11] = "aux11",
70    [PA_CHANNEL_POSITION_AUX12] = "aux12",
71    [PA_CHANNEL_POSITION_AUX13] = "aux13",
72    [PA_CHANNEL_POSITION_AUX14] = "aux14",
73    [PA_CHANNEL_POSITION_AUX15] = "aux15",
74    [PA_CHANNEL_POSITION_AUX16] = "aux16",
75    [PA_CHANNEL_POSITION_AUX17] = "aux17",
76    [PA_CHANNEL_POSITION_AUX18] = "aux18",
77    [PA_CHANNEL_POSITION_AUX19] = "aux19",
78    [PA_CHANNEL_POSITION_AUX20] = "aux20",
79    [PA_CHANNEL_POSITION_AUX21] = "aux21",
80    [PA_CHANNEL_POSITION_AUX22] = "aux22",
81    [PA_CHANNEL_POSITION_AUX23] = "aux23",
82    [PA_CHANNEL_POSITION_AUX24] = "aux24",
83    [PA_CHANNEL_POSITION_AUX25] = "aux25",
84    [PA_CHANNEL_POSITION_AUX26] = "aux26",
85    [PA_CHANNEL_POSITION_AUX27] = "aux27",
86    [PA_CHANNEL_POSITION_AUX28] = "aux28",
87    [PA_CHANNEL_POSITION_AUX29] = "aux29",
88    [PA_CHANNEL_POSITION_AUX30] = "aux30",
89    [PA_CHANNEL_POSITION_AUX31] = "aux31",
90
91    [PA_CHANNEL_POSITION_TOP_CENTER] = "top-center",
92
93    [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center",
94    [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "top-front-left",
95    [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "top-front-right",
96
97    [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "top-rear-center",
98    [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "top-rear-left",
99    [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right"
100};
101
102static const char *const pretty_table[PA_CHANNEL_POSITION_MAX] = {
103    [PA_CHANNEL_POSITION_MONO] = N_("Mono"),
104
105    [PA_CHANNEL_POSITION_FRONT_CENTER] = N_("Front Center"),
106    [PA_CHANNEL_POSITION_FRONT_LEFT] = N_("Front Left"),
107    [PA_CHANNEL_POSITION_FRONT_RIGHT] = N_("Front Right"),
108
109    [PA_CHANNEL_POSITION_REAR_CENTER] = N_("Rear Center"),
110    [PA_CHANNEL_POSITION_REAR_LEFT] = N_("Rear Left"),
111    [PA_CHANNEL_POSITION_REAR_RIGHT] = N_("Rear Right"),
112
113    [PA_CHANNEL_POSITION_LFE] = N_("Subwoofer"),
114
115    [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = N_("Front Left-of-center"),
116    [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = N_("Front Right-of-center"),
117
118    [PA_CHANNEL_POSITION_SIDE_LEFT] = N_("Side Left"),
119    [PA_CHANNEL_POSITION_SIDE_RIGHT] = N_("Side Right"),
120
121    [PA_CHANNEL_POSITION_AUX0] = N_("Auxiliary 0"),
122    [PA_CHANNEL_POSITION_AUX1] = N_("Auxiliary 1"),
123    [PA_CHANNEL_POSITION_AUX2] = N_("Auxiliary 2"),
124    [PA_CHANNEL_POSITION_AUX3] = N_("Auxiliary 3"),
125    [PA_CHANNEL_POSITION_AUX4] = N_("Auxiliary 4"),
126    [PA_CHANNEL_POSITION_AUX5] = N_("Auxiliary 5"),
127    [PA_CHANNEL_POSITION_AUX6] = N_("Auxiliary 6"),
128    [PA_CHANNEL_POSITION_AUX7] = N_("Auxiliary 7"),
129    [PA_CHANNEL_POSITION_AUX8] = N_("Auxiliary 8"),
130    [PA_CHANNEL_POSITION_AUX9] = N_("Auxiliary 9"),
131    [PA_CHANNEL_POSITION_AUX10] = N_("Auxiliary 10"),
132    [PA_CHANNEL_POSITION_AUX11] = N_("Auxiliary 11"),
133    [PA_CHANNEL_POSITION_AUX12] = N_("Auxiliary 12"),
134    [PA_CHANNEL_POSITION_AUX13] = N_("Auxiliary 13"),
135    [PA_CHANNEL_POSITION_AUX14] = N_("Auxiliary 14"),
136    [PA_CHANNEL_POSITION_AUX15] = N_("Auxiliary 15"),
137    [PA_CHANNEL_POSITION_AUX16] = N_("Auxiliary 16"),
138    [PA_CHANNEL_POSITION_AUX17] = N_("Auxiliary 17"),
139    [PA_CHANNEL_POSITION_AUX18] = N_("Auxiliary 18"),
140    [PA_CHANNEL_POSITION_AUX19] = N_("Auxiliary 19"),
141    [PA_CHANNEL_POSITION_AUX20] = N_("Auxiliary 20"),
142    [PA_CHANNEL_POSITION_AUX21] = N_("Auxiliary 21"),
143    [PA_CHANNEL_POSITION_AUX22] = N_("Auxiliary 22"),
144    [PA_CHANNEL_POSITION_AUX23] = N_("Auxiliary 23"),
145    [PA_CHANNEL_POSITION_AUX24] = N_("Auxiliary 24"),
146    [PA_CHANNEL_POSITION_AUX25] = N_("Auxiliary 25"),
147    [PA_CHANNEL_POSITION_AUX26] = N_("Auxiliary 26"),
148    [PA_CHANNEL_POSITION_AUX27] = N_("Auxiliary 27"),
149    [PA_CHANNEL_POSITION_AUX28] = N_("Auxiliary 28"),
150    [PA_CHANNEL_POSITION_AUX29] = N_("Auxiliary 29"),
151    [PA_CHANNEL_POSITION_AUX30] = N_("Auxiliary 30"),
152    [PA_CHANNEL_POSITION_AUX31] = N_("Auxiliary 31"),
153
154    [PA_CHANNEL_POSITION_TOP_CENTER] = N_("Top Center"),
155
156    [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = N_("Top Front Center"),
157    [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = N_("Top Front Left"),
158    [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = N_("Top Front Right"),
159
160    [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = N_("Top Rear Center"),
161    [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = N_("Top Rear Left"),
162    [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = N_("Top Rear Right")
163};
164
165pa_channel_map* pa_channel_map_init(pa_channel_map *m) {
166    unsigned c;
167    pa_assert(m);
168
169    m->channels = 0;
170
171    for (c = 0; c < PA_CHANNELS_MAX; c++)
172        m->map[c] = PA_CHANNEL_POSITION_INVALID;
173
174    return m;
175}
176
177pa_channel_map* pa_channel_map_init_mono(pa_channel_map *m) {
178    pa_assert(m);
179
180    pa_channel_map_init(m);
181
182    m->channels = 1;
183    m->map[0] = PA_CHANNEL_POSITION_MONO;
184    return m;
185}
186
187pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) {
188    pa_assert(m);
189
190    pa_channel_map_init(m);
191
192    m->channels = 2;
193    m->map[0] = PA_CHANNEL_POSITION_LEFT;
194    m->map[1] = PA_CHANNEL_POSITION_RIGHT;
195    return m;
196}
197
198pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
199    pa_assert(m);
200    pa_assert(pa_channels_valid(channels));
201    pa_assert(def < PA_CHANNEL_MAP_DEF_MAX);
202
203    pa_channel_map_init(m);
204
205    m->channels = (uint8_t) channels;
206
207    switch (def) {
208        case PA_CHANNEL_MAP_AIFF:
209
210            /* This is somewhat compatible with RFC3551 */
211
212            switch (channels) {
213                case 1:
214                    m->map[0] = PA_CHANNEL_POSITION_MONO;
215                    return m;
216
217                case 6:
218                    m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
219                    m->map[1] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
220                    m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
221                    m->map[3] = PA_CHANNEL_POSITION_FRONT_RIGHT;
222                    m->map[4] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
223                    m->map[5] = PA_CHANNEL_POSITION_REAR_CENTER;
224                    return m;
225
226                case 5:
227                    m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
228                    m->map[3] = PA_CHANNEL_POSITION_REAR_LEFT;
229                    m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
230                    /* Fall through */
231
232                case 2:
233                    m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
234                    m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
235                    return m;
236
237                case 3:
238                    m->map[0] = PA_CHANNEL_POSITION_LEFT;
239                    m->map[1] = PA_CHANNEL_POSITION_RIGHT;
240                    m->map[2] = PA_CHANNEL_POSITION_CENTER;
241                    return m;
242
243                case 4:
244                    m->map[0] = PA_CHANNEL_POSITION_LEFT;
245                    m->map[1] = PA_CHANNEL_POSITION_CENTER;
246                    m->map[2] = PA_CHANNEL_POSITION_RIGHT;
247                    m->map[3] = PA_CHANNEL_POSITION_REAR_CENTER;
248                    return m;
249
250                default:
251                    return NULL;
252            }
253
254        case PA_CHANNEL_MAP_ALSA:
255
256            switch (channels) {
257                case 1:
258                    m->map[0] = PA_CHANNEL_POSITION_MONO;
259                    return m;
260
261                case 8:
262                    m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
263                    m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
264                    /* Fall through */
265
266                case 6:
267                    m->map[5] = PA_CHANNEL_POSITION_LFE;
268                    /* Fall through */
269
270                case 5:
271                    m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
272                    /* Fall through */
273
274                case 4:
275                    m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
276                    m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
277                    /* Fall through */
278
279                case 2:
280                    m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
281                    m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
282                    return m;
283
284                default:
285                    return NULL;
286            }
287
288        case PA_CHANNEL_MAP_AUX: {
289            unsigned i;
290
291            for (i = 0; i < channels; i++)
292                m->map[i] = PA_CHANNEL_POSITION_AUX0 + i;
293
294            return m;
295        }
296
297        case PA_CHANNEL_MAP_WAVEEX:
298
299            /* following: https://docs.microsoft.com/en-us/previous-versions/windows/hardware/design/dn653308(v=vs.85) */
300            switch (channels) {
301                case 1:
302                    m->map[0] = PA_CHANNEL_POSITION_MONO;
303                    return m;
304
305                case 18:
306                    m->map[15] = PA_CHANNEL_POSITION_TOP_REAR_LEFT;
307                    m->map[16] = PA_CHANNEL_POSITION_TOP_REAR_CENTER;
308                    m->map[17] = PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
309                    /* Fall through */
310
311                case 15:
312                    m->map[12] = PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
313                    m->map[13] = PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
314                    m->map[14] = PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
315                    /* Fall through */
316
317                case 12:
318                    m->map[11] = PA_CHANNEL_POSITION_TOP_CENTER;
319                    /* Fall through */
320
321                case 11:
322                    m->map[9] = PA_CHANNEL_POSITION_SIDE_LEFT;
323                    m->map[10] = PA_CHANNEL_POSITION_SIDE_RIGHT;
324                    /* Fall through */
325
326                case 9:
327                    m->map[8] = PA_CHANNEL_POSITION_REAR_CENTER;
328                    /* Fall through */
329
330                case 8:
331                    m->map[6] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
332                    m->map[7] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
333                    /* Fall through */
334
335                case 6:
336                    m->map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
337                    m->map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
338                    /* Fall through */
339
340                case 4:
341                    m->map[3] = PA_CHANNEL_POSITION_LFE;
342                    /* Fall through */
343
344                case 3:
345                    m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
346                    /* Fall through */
347
348                case 2:
349                    m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
350                    m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
351                    return m;
352
353                default:
354                    return NULL;
355            }
356
357        case PA_CHANNEL_MAP_OSS:
358
359            switch (channels) {
360                case 1:
361                    m->map[0] = PA_CHANNEL_POSITION_MONO;
362                    return m;
363
364                case 8:
365                    m->map[6] = PA_CHANNEL_POSITION_REAR_LEFT;
366                    m->map[7] = PA_CHANNEL_POSITION_REAR_RIGHT;
367                    /* Fall through */
368
369                case 6:
370                    m->map[4] = PA_CHANNEL_POSITION_SIDE_LEFT;
371                    m->map[5] = PA_CHANNEL_POSITION_SIDE_RIGHT;
372                    /* Fall through */
373
374                case 4:
375                    m->map[3] = PA_CHANNEL_POSITION_LFE;
376                    /* Fall through */
377
378                case 3:
379                    m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
380                    /* Fall through */
381
382                case 2:
383                    m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
384                    m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
385                    return m;
386
387                default:
388                    return NULL;
389            }
390
391        default:
392            pa_assert_not_reached();
393    }
394}
395
396pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
397    unsigned c;
398
399    pa_assert(m);
400    pa_assert(pa_channels_valid(channels));
401    pa_assert(def < PA_CHANNEL_MAP_DEF_MAX);
402
403    pa_channel_map_init(m);
404
405    for (c = channels; c > 0; c--) {
406
407        if (pa_channel_map_init_auto(m, c, def)) {
408            unsigned i = 0;
409
410            for (; c < channels; c++) {
411
412                m->map[c] = PA_CHANNEL_POSITION_AUX0 + i;
413                i++;
414            }
415
416            m->channels = (uint8_t) channels;
417
418            return m;
419        }
420    }
421
422    return NULL;
423}
424
425const char* pa_channel_position_to_string(pa_channel_position_t pos) {
426
427    if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
428        return NULL;
429
430    return table[pos];
431}
432
433const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) {
434
435    if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
436        return NULL;
437
438    pa_init_i18n();
439
440    return _(pretty_table[pos]);
441}
442
443int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
444    unsigned c;
445
446    pa_assert(a);
447    pa_assert(b);
448
449    pa_return_val_if_fail(pa_channel_map_valid(a), 0);
450
451    if (PA_UNLIKELY(a == b))
452        return 1;
453
454    pa_return_val_if_fail(pa_channel_map_valid(b), 0);
455
456    if (a->channels != b->channels)
457        return 0;
458
459    for (c = 0; c < a->channels; c++)
460        if (a->map[c] != b->map[c])
461            return 0;
462
463    return 1;
464}
465
466char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
467    unsigned channel;
468    bool first = true;
469    char *e;
470
471    pa_assert(s);
472    pa_assert(l > 0);
473    pa_assert(map);
474
475    pa_init_i18n();
476
477    if (!pa_channel_map_valid(map)) {
478        pa_snprintf(s, l, _("(invalid)"));
479        return s;
480    }
481
482    *(e = s) = 0;
483
484    for (channel = 0; channel < map->channels && l > 1; channel++) {
485        l -= pa_snprintf(e, l, "%s%s",
486                      first ? "" : ",",
487                      pa_channel_position_to_string(map->map[channel]));
488
489        e = strchr(e, 0);
490        first = false;
491    }
492
493    return s;
494}
495
496pa_channel_position_t pa_channel_position_from_string(const char *p) {
497    pa_channel_position_t i;
498    pa_assert(p);
499
500    /* Some special aliases */
501    if (pa_streq(p, "left"))
502        return PA_CHANNEL_POSITION_LEFT;
503    else if (pa_streq(p, "right"))
504        return PA_CHANNEL_POSITION_RIGHT;
505    else if (pa_streq(p, "center"))
506        return PA_CHANNEL_POSITION_CENTER;
507    else if (pa_streq(p, "subwoofer"))
508        return PA_CHANNEL_POSITION_SUBWOOFER;
509
510    for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++)
511        if (pa_streq(p, table[i]))
512            return i;
513
514    return PA_CHANNEL_POSITION_INVALID;
515}
516
517pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
518    const char *state;
519    pa_channel_map map;
520    char *p;
521
522    pa_assert(rmap);
523    pa_assert(s);
524
525    pa_channel_map_init(&map);
526
527    /* We don't need to match against the well known channel mapping
528     * "mono" here explicitly, because that can be understood as
529     * listing with one channel called "mono". */
530
531    if (pa_streq(s, "stereo")) {
532        map.channels = 2;
533        map.map[0] = PA_CHANNEL_POSITION_LEFT;
534        map.map[1] = PA_CHANNEL_POSITION_RIGHT;
535        goto finish;
536    } else if (pa_streq(s, "surround-21")) {
537        map.channels = 3;
538        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
539        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
540        map.map[2] = PA_CHANNEL_POSITION_LFE;
541        goto finish;
542    } else if (pa_streq(s, "surround-40")) {
543        map.channels = 4;
544        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
545        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
546        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
547        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
548        goto finish;
549    } else if (pa_streq(s, "surround-41")) {
550        map.channels = 5;
551        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
552        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
553        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
554        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
555        map.map[4] = PA_CHANNEL_POSITION_LFE;
556        goto finish;
557    } else if (pa_streq(s, "surround-50")) {
558        map.channels = 5;
559        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
560        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
561        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
562        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
563        map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
564        goto finish;
565    } else if (pa_streq(s, "surround-51")) {
566        map.channels = 6;
567        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
568        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
569        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
570        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
571        map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
572        map.map[5] = PA_CHANNEL_POSITION_LFE;
573        goto finish;
574    } else if (pa_streq(s, "surround-71")) {
575        map.channels = 8;
576        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
577        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
578        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
579        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
580        map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
581        map.map[5] = PA_CHANNEL_POSITION_LFE;
582        map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
583        map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
584        goto finish;
585    }
586
587    state = NULL;
588    map.channels = 0;
589
590    while ((p = pa_split(s, ",", &state))) {
591        pa_channel_position_t f;
592
593        if (map.channels >= PA_CHANNELS_MAX) {
594            pa_xfree(p);
595            return NULL;
596        }
597
598        if ((f = pa_channel_position_from_string(p)) == PA_CHANNEL_POSITION_INVALID) {
599            pa_xfree(p);
600            return NULL;
601        }
602
603        map.map[map.channels++] = f;
604        pa_xfree(p);
605    }
606
607finish:
608
609    if (!pa_channel_map_valid(&map))
610        return NULL;
611
612    *rmap = map;
613    return rmap;
614}
615
616int pa_channel_map_valid(const pa_channel_map *map) {
617    unsigned c;
618
619    pa_assert(map);
620
621    if (!pa_channels_valid(map->channels))
622        return 0;
623
624    for (c = 0; c < map->channels; c++)
625        if (map->map[c] < 0 || map->map[c] >= PA_CHANNEL_POSITION_MAX)
626            return 0;
627
628    return 1;
629}
630
631int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *ss) {
632    pa_assert(map);
633    pa_assert(ss);
634
635    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
636    pa_return_val_if_fail(pa_sample_spec_valid(ss), 0);
637
638    return map->channels == ss->channels;
639}
640
641int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
642    pa_channel_position_mask_t am, bm;
643
644    pa_assert(a);
645    pa_assert(b);
646
647    pa_return_val_if_fail(pa_channel_map_valid(a), 0);
648
649    if (PA_UNLIKELY(a == b))
650        return 1;
651
652    pa_return_val_if_fail(pa_channel_map_valid(b), 0);
653
654    am = pa_channel_map_mask(a);
655    bm = pa_channel_map_mask(b);
656
657    return (bm & am) == bm;
658}
659
660int pa_channel_map_can_balance(const pa_channel_map *map) {
661    pa_channel_position_mask_t m;
662
663    pa_assert(map);
664    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
665
666    m = pa_channel_map_mask(map);
667
668    return
669        (PA_CHANNEL_POSITION_MASK_LEFT & m) &&
670        (PA_CHANNEL_POSITION_MASK_RIGHT & m);
671}
672
673int pa_channel_map_can_fade(const pa_channel_map *map) {
674    pa_channel_position_mask_t m;
675
676    pa_assert(map);
677    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
678
679    m = pa_channel_map_mask(map);
680
681    return
682        (PA_CHANNEL_POSITION_MASK_FRONT & m) &&
683        (PA_CHANNEL_POSITION_MASK_REAR & m);
684}
685
686int pa_channel_map_can_lfe_balance(const pa_channel_map *map) {
687    pa_channel_position_mask_t m;
688
689    pa_assert(map);
690    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
691
692    m = pa_channel_map_mask(map);
693
694    return
695        (PA_CHANNEL_POSITION_MASK_LFE & m) &&
696        (PA_CHANNEL_POSITION_MASK_HFE & m);
697}
698
699const char* pa_channel_map_to_name(const pa_channel_map *map) {
700    pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
701    unsigned c;
702
703    pa_assert(map);
704
705    pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
706
707    memset(in_map, 0, sizeof(in_map));
708
709    for (c = 0; c < map->channels; c++)
710        pa_bitset_set(in_map, map->map[c], true);
711
712    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
713                         PA_CHANNEL_POSITION_MONO, -1))
714        return "mono";
715
716    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
717                         PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1))
718        return "stereo";
719
720    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
721                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
722                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1))
723        return "surround-40";
724
725    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
726                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
727                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
728                         PA_CHANNEL_POSITION_LFE, -1))
729        return "surround-41";
730
731    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
732                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
733                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
734                         PA_CHANNEL_POSITION_FRONT_CENTER, -1))
735        return "surround-50";
736
737    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
738                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
739                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
740                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1))
741        return "surround-51";
742
743    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
744                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
745                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
746                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
747                         PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1))
748        return "surround-71";
749
750    return NULL;
751}
752
753const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) {
754    pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
755    unsigned c;
756
757    pa_assert(map);
758
759    pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
760
761    memset(in_map, 0, sizeof(in_map));
762
763    for (c = 0; c < map->channels; c++)
764        pa_bitset_set(in_map, map->map[c], true);
765
766    pa_init_i18n();
767
768    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
769                         PA_CHANNEL_POSITION_MONO, -1))
770        return _("Mono");
771
772    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
773                         PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1))
774        return _("Stereo");
775
776    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
777                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
778                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1))
779        return _("Surround 4.0");
780
781    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
782                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
783                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
784                         PA_CHANNEL_POSITION_LFE, -1))
785        return _("Surround 4.1");
786
787    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
788                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
789                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
790                         PA_CHANNEL_POSITION_FRONT_CENTER, -1))
791        return _("Surround 5.0");
792
793    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
794                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
795                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
796                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1))
797        return _("Surround 5.1");
798
799    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
800                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
801                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
802                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
803                         PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1))
804        return _("Surround 7.1");
805
806    return NULL;
807}
808
809int pa_channel_map_has_position(const pa_channel_map *map, pa_channel_position_t p) {
810    unsigned c;
811
812    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
813    pa_return_val_if_fail(p < PA_CHANNEL_POSITION_MAX, 0);
814
815    for (c = 0; c < map->channels; c++)
816        if (map->map[c] == p)
817            return 1;
818
819    return 0;
820}
821
822pa_channel_position_mask_t pa_channel_map_mask(const pa_channel_map *map) {
823    unsigned c;
824    pa_channel_position_mask_t r = 0;
825
826    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
827
828    for (c = 0; c < map->channels; c++)
829        r |= PA_CHANNEL_POSITION_MASK(map->map[c]);
830
831    return r;
832}
833