]> code.delx.au - pulseaudio/blobdiff - src/pulse/channelmap.c
core: Fix uninit pointer read in protocol-native
[pulseaudio] / src / pulse / channelmap.c
index 2b35ee752d7a11cf6c69fab65cddccc8c67c22f7..72e4130eeffcae0afee44f02cf4e93c6bf2bf3f9 100644 (file)
@@ -1,5 +1,3 @@
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
 /***
   This file is part of PulseAudio.
 
@@ -8,7 +6,7 @@
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
 
   PulseAudio 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 of the License,
+  by the Free Software Foundation; either version 2.1 of the License,
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
 #include <string.h>
 
 #include <pulse/xmalloc.h>
 #include <string.h>
 
 #include <pulse/xmalloc.h>
+
+#include <pulsecore/i18n.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
+#include <pulsecore/bitset.h>
+#include <pulsecore/sample-util.h>
 
 #include "channelmap.h"
 
 
 #include "channelmap.h"
 
@@ -100,66 +102,66 @@ const char *const table[PA_CHANNEL_POSITION_MAX] = {
 };
 
 const char *const pretty_table[PA_CHANNEL_POSITION_MAX] = {
 };
 
 const char *const pretty_table[PA_CHANNEL_POSITION_MAX] = {
-    [PA_CHANNEL_POSITION_MONO] = "Mono",
-
-    [PA_CHANNEL_POSITION_FRONT_CENTER] = "Front Center",
-    [PA_CHANNEL_POSITION_FRONT_LEFT] = "Front Left",
-    [PA_CHANNEL_POSITION_FRONT_RIGHT] = "Front Right",
-
-    [PA_CHANNEL_POSITION_REAR_CENTER] = "Rear Center",
-    [PA_CHANNEL_POSITION_REAR_LEFT] = "Rear Left",
-    [PA_CHANNEL_POSITION_REAR_RIGHT] = "Rear Right",
-
-    [PA_CHANNEL_POSITION_LFE] = "Low Frequency Emmiter",
-
-    [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "Front Left-of-center",
-    [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "Front Right-of-center",
-
-    [PA_CHANNEL_POSITION_SIDE_LEFT] = "Side Left",
-    [PA_CHANNEL_POSITION_SIDE_RIGHT] = "Side Right",
-
-    [PA_CHANNEL_POSITION_AUX0] = "Auxiliary 0",
-    [PA_CHANNEL_POSITION_AUX1] = "Auxiliary 1",
-    [PA_CHANNEL_POSITION_AUX2] = "Auxiliary 2",
-    [PA_CHANNEL_POSITION_AUX3] = "Auxiliary 3",
-    [PA_CHANNEL_POSITION_AUX4] = "Auxiliary 4",
-    [PA_CHANNEL_POSITION_AUX5] = "Auxiliary 5",
-    [PA_CHANNEL_POSITION_AUX6] = "Auxiliary 6",
-    [PA_CHANNEL_POSITION_AUX7] = "Auxiliary 7",
-    [PA_CHANNEL_POSITION_AUX8] = "Auxiliary 8",
-    [PA_CHANNEL_POSITION_AUX9] = "Auxiliary 9",
-    [PA_CHANNEL_POSITION_AUX10] = "Auxiliary 10",
-    [PA_CHANNEL_POSITION_AUX11] = "Auxiliary 11",
-    [PA_CHANNEL_POSITION_AUX12] = "Auxiliary 12",
-    [PA_CHANNEL_POSITION_AUX13] = "Auxiliary 13",
-    [PA_CHANNEL_POSITION_AUX14] = "Auxiliary 14",
-    [PA_CHANNEL_POSITION_AUX15] = "Auxiliary 15",
-    [PA_CHANNEL_POSITION_AUX16] = "Auxiliary 16",
-    [PA_CHANNEL_POSITION_AUX17] = "Auxiliary 17",
-    [PA_CHANNEL_POSITION_AUX18] = "Auxiliary 18",
-    [PA_CHANNEL_POSITION_AUX19] = "Auxiliary 19",
-    [PA_CHANNEL_POSITION_AUX20] = "Auxiliary 20",
-    [PA_CHANNEL_POSITION_AUX21] = "Auxiliary 21",
-    [PA_CHANNEL_POSITION_AUX22] = "Auxiliary 22",
-    [PA_CHANNEL_POSITION_AUX23] = "Auxiliary 23",
-    [PA_CHANNEL_POSITION_AUX24] = "Auxiliary 24",
-    [PA_CHANNEL_POSITION_AUX25] = "Auxiliary 25",
-    [PA_CHANNEL_POSITION_AUX26] = "Auxiliary 26",
-    [PA_CHANNEL_POSITION_AUX27] = "Auxiliary 27",
-    [PA_CHANNEL_POSITION_AUX28] = "Auxiliary 28",
-    [PA_CHANNEL_POSITION_AUX29] = "Auxiliary 29",
-    [PA_CHANNEL_POSITION_AUX30] = "Auxiliary 30",
-    [PA_CHANNEL_POSITION_AUX31] = "Auxiliary 31",
-
-    [PA_CHANNEL_POSITION_TOP_CENTER] = "Top Center",
-
-    [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "Top Front Center",
-    [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "Top Front Left",
-    [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "Top Front Right",
-
-    [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "Top Rear Center",
-    [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "Top Rear left",
-    [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "Top Rear Right"
+    [PA_CHANNEL_POSITION_MONO] = N_("Mono"),
+
+    [PA_CHANNEL_POSITION_FRONT_CENTER] = N_("Front Center"),
+    [PA_CHANNEL_POSITION_FRONT_LEFT] = N_("Front Left"),
+    [PA_CHANNEL_POSITION_FRONT_RIGHT] = N_("Front Right"),
+
+    [PA_CHANNEL_POSITION_REAR_CENTER] = N_("Rear Center"),
+    [PA_CHANNEL_POSITION_REAR_LEFT] = N_("Rear Left"),
+    [PA_CHANNEL_POSITION_REAR_RIGHT] = N_("Rear Right"),
+
+    [PA_CHANNEL_POSITION_LFE] = N_("Subwoofer"),
+
+    [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = N_("Front Left-of-center"),
+    [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = N_("Front Right-of-center"),
+
+    [PA_CHANNEL_POSITION_SIDE_LEFT] = N_("Side Left"),
+    [PA_CHANNEL_POSITION_SIDE_RIGHT] = N_("Side Right"),
+
+    [PA_CHANNEL_POSITION_AUX0] = N_("Auxiliary 0"),
+    [PA_CHANNEL_POSITION_AUX1] = N_("Auxiliary 1"),
+    [PA_CHANNEL_POSITION_AUX2] = N_("Auxiliary 2"),
+    [PA_CHANNEL_POSITION_AUX3] = N_("Auxiliary 3"),
+    [PA_CHANNEL_POSITION_AUX4] = N_("Auxiliary 4"),
+    [PA_CHANNEL_POSITION_AUX5] = N_("Auxiliary 5"),
+    [PA_CHANNEL_POSITION_AUX6] = N_("Auxiliary 6"),
+    [PA_CHANNEL_POSITION_AUX7] = N_("Auxiliary 7"),
+    [PA_CHANNEL_POSITION_AUX8] = N_("Auxiliary 8"),
+    [PA_CHANNEL_POSITION_AUX9] = N_("Auxiliary 9"),
+    [PA_CHANNEL_POSITION_AUX10] = N_("Auxiliary 10"),
+    [PA_CHANNEL_POSITION_AUX11] = N_("Auxiliary 11"),
+    [PA_CHANNEL_POSITION_AUX12] = N_("Auxiliary 12"),
+    [PA_CHANNEL_POSITION_AUX13] = N_("Auxiliary 13"),
+    [PA_CHANNEL_POSITION_AUX14] = N_("Auxiliary 14"),
+    [PA_CHANNEL_POSITION_AUX15] = N_("Auxiliary 15"),
+    [PA_CHANNEL_POSITION_AUX16] = N_("Auxiliary 16"),
+    [PA_CHANNEL_POSITION_AUX17] = N_("Auxiliary 17"),
+    [PA_CHANNEL_POSITION_AUX18] = N_("Auxiliary 18"),
+    [PA_CHANNEL_POSITION_AUX19] = N_("Auxiliary 19"),
+    [PA_CHANNEL_POSITION_AUX20] = N_("Auxiliary 20"),
+    [PA_CHANNEL_POSITION_AUX21] = N_("Auxiliary 21"),
+    [PA_CHANNEL_POSITION_AUX22] = N_("Auxiliary 22"),
+    [PA_CHANNEL_POSITION_AUX23] = N_("Auxiliary 23"),
+    [PA_CHANNEL_POSITION_AUX24] = N_("Auxiliary 24"),
+    [PA_CHANNEL_POSITION_AUX25] = N_("Auxiliary 25"),
+    [PA_CHANNEL_POSITION_AUX26] = N_("Auxiliary 26"),
+    [PA_CHANNEL_POSITION_AUX27] = N_("Auxiliary 27"),
+    [PA_CHANNEL_POSITION_AUX28] = N_("Auxiliary 28"),
+    [PA_CHANNEL_POSITION_AUX29] = N_("Auxiliary 29"),
+    [PA_CHANNEL_POSITION_AUX30] = N_("Auxiliary 30"),
+    [PA_CHANNEL_POSITION_AUX31] = N_("Auxiliary 31"),
+
+    [PA_CHANNEL_POSITION_TOP_CENTER] = N_("Top Center"),
+
+    [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = N_("Top Front Center"),
+    [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = N_("Top Front Left"),
+    [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = N_("Top Front Right"),
+
+    [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = N_("Top Rear Center"),
+    [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = N_("Top Rear Left"),
+    [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = N_("Top Rear Right")
 };
 
 pa_channel_map* pa_channel_map_init(pa_channel_map *m) {
 };
 
 pa_channel_map* pa_channel_map_init(pa_channel_map *m) {
@@ -197,12 +199,12 @@ pa_channel_map* pa_channel_map_init_stereo(pa_channel_map *m) {
 
 pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
     pa_assert(m);
 
 pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
     pa_assert(m);
-    pa_assert(channels > 0);
-    pa_assert(channels <= PA_CHANNELS_MAX);
+    pa_assert(pa_channels_valid(channels));
+    pa_assert(def < PA_CHANNEL_MAP_DEF_MAX);
 
     pa_channel_map_init(m);
 
 
     pa_channel_map_init(m);
 
-    m->channels = channels;
+    m->channels = (uint8_t) channels;
 
     switch (def) {
         case PA_CHANNEL_MAP_AIFF:
 
     switch (def) {
         case PA_CHANNEL_MAP_AIFF:
@@ -216,11 +218,11 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
 
                 case 6:
                     m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
 
                 case 6:
                     m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
-                    m->map[1] = PA_CHANNEL_POSITION_SIDE_LEFT;
+                    m->map[1] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
                     m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
                     m->map[3] = PA_CHANNEL_POSITION_FRONT_RIGHT;
                     m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
                     m->map[3] = PA_CHANNEL_POSITION_FRONT_RIGHT;
-                    m->map[4] = PA_CHANNEL_POSITION_SIDE_RIGHT;
-                    m->map[5] = PA_CHANNEL_POSITION_LFE;
+                    m->map[4] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+                    m->map[5] = PA_CHANNEL_POSITION_REAR_CENTER;
                     return m;
 
                 case 5:
                     return m;
 
                 case 5:
@@ -244,7 +246,7 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
                     m->map[0] = PA_CHANNEL_POSITION_LEFT;
                     m->map[1] = PA_CHANNEL_POSITION_CENTER;
                     m->map[2] = PA_CHANNEL_POSITION_RIGHT;
                     m->map[0] = PA_CHANNEL_POSITION_LEFT;
                     m->map[1] = PA_CHANNEL_POSITION_CENTER;
                     m->map[2] = PA_CHANNEL_POSITION_RIGHT;
-                    m->map[3] = PA_CHANNEL_POSITION_LFE;
+                    m->map[3] = PA_CHANNEL_POSITION_REAR_CENTER;
                     return m;
 
                 default:
                     return m;
 
                 default:
@@ -288,9 +290,6 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
         case PA_CHANNEL_MAP_AUX: {
             unsigned i;
 
         case PA_CHANNEL_MAP_AUX: {
             unsigned i;
 
-            if (channels >= PA_CHANNELS_MAX)
-                return NULL;
-
             for (i = 0; i < channels; i++)
                 m->map[i] = PA_CHANNEL_POSITION_AUX0 + i;
 
             for (i = 0; i < channels; i++)
                 m->map[i] = PA_CHANNEL_POSITION_AUX0 + i;
 
@@ -299,6 +298,8 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
 
         case PA_CHANNEL_MAP_WAVEEX:
 
 
         case PA_CHANNEL_MAP_WAVEEX:
 
+            /* Following http://www.microsoft.com/whdc/device/audio/multichaud.mspx#EKLAC */
+
             switch (channels) {
                 case 1:
                     m->map[0] = PA_CHANNEL_POSITION_MONO;
             switch (channels) {
                 case 1:
                     m->map[0] = PA_CHANNEL_POSITION_MONO;
@@ -390,12 +391,39 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
                     return NULL;
             }
 
                     return NULL;
             }
 
-
         default:
         default:
-            return NULL;
+            pa_assert_not_reached();
     }
 }
 
     }
 }
 
+pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def) {
+    unsigned c;
+
+    pa_assert(m);
+    pa_assert(pa_channels_valid(channels));
+    pa_assert(def < PA_CHANNEL_MAP_DEF_MAX);
+
+    pa_channel_map_init(m);
+
+    for (c = channels; c > 0; c--) {
+
+        if (pa_channel_map_init_auto(m, c, def)) {
+            unsigned i = 0;
+
+            for (; c < channels; c++) {
+
+                m->map[c] = PA_CHANNEL_POSITION_AUX0 + i;
+                i++;
+            }
+
+            m->channels = (uint8_t) channels;
+
+            return m;
+        }
+    }
+
+    return NULL;
+}
 
 const char* pa_channel_position_to_string(pa_channel_position_t pos) {
 
 
 const char* pa_channel_position_to_string(pa_channel_position_t pos) {
 
@@ -406,10 +434,13 @@ const char* pa_channel_position_to_string(pa_channel_position_t pos) {
 }
 
 const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) {
 }
 
 const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) {
+
     if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
         return NULL;
 
     if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
         return NULL;
 
-    return pretty_table[pos];
+    pa_init_i18n();
+
+    return _(pretty_table[pos]);
 }
 
 int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
 }
 
 int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
@@ -418,6 +449,13 @@ int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
     pa_assert(a);
     pa_assert(b);
 
     pa_assert(a);
     pa_assert(b);
 
+    pa_return_val_if_fail(pa_channel_map_valid(a), 0);
+
+    if (PA_UNLIKELY(a == b))
+        return 1;
+
+    pa_return_val_if_fail(pa_channel_map_valid(b), 0);
+
     if (a->channels != b->channels)
         return 0;
 
     if (a->channels != b->channels)
         return 0;
 
@@ -430,13 +468,20 @@ int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
 
 char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
     unsigned channel;
 
 char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
     unsigned channel;
-    int first = 1;
+    bool first = true;
     char *e;
 
     pa_assert(s);
     pa_assert(l > 0);
     pa_assert(map);
 
     char *e;
 
     pa_assert(s);
     pa_assert(l > 0);
     pa_assert(map);
 
+    pa_init_i18n();
+
+    if (!pa_channel_map_valid(map)) {
+        pa_snprintf(s, l, _("(invalid)"));
+        return s;
+    }
+
     *(e = s) = 0;
 
     for (channel = 0; channel < map->channels && l > 1; channel++) {
     *(e = s) = 0;
 
     for (channel = 0; channel < map->channels && l > 1; channel++) {
@@ -445,12 +490,33 @@ char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
                       pa_channel_position_to_string(map->map[channel]));
 
         e = strchr(e, 0);
                       pa_channel_position_to_string(map->map[channel]));
 
         e = strchr(e, 0);
-        first = 0;
+        first = false;
     }
 
     return s;
 }
 
     }
 
     return s;
 }
 
+pa_channel_position_t pa_channel_position_from_string(const char *p) {
+    pa_channel_position_t i;
+    pa_assert(p);
+
+    /* Some special aliases */
+    if (pa_streq(p, "left"))
+        return PA_CHANNEL_POSITION_LEFT;
+    else if (pa_streq(p, "right"))
+        return PA_CHANNEL_POSITION_RIGHT;
+    else if (pa_streq(p, "center"))
+        return PA_CHANNEL_POSITION_CENTER;
+    else if (pa_streq(p, "subwoofer"))
+        return PA_CHANNEL_POSITION_SUBWOOFER;
+
+    for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++)
+        if (pa_streq(p, table[i]))
+            return i;
+
+    return PA_CHANNEL_POSITION_INVALID;
+}
+
 pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
     const char *state;
     pa_channel_map map;
 pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
     const char *state;
     pa_channel_map map;
@@ -459,49 +525,79 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
     pa_assert(rmap);
     pa_assert(s);
 
     pa_assert(rmap);
     pa_assert(s);
 
-    memset(&map, 0, sizeof(map));
+    pa_channel_map_init(&map);
+
+    /* We don't need to match against the well known channel mapping
+     * "mono" here explicitly, because that can be understood as
+     * listing with one channel called "mono". */
 
 
-    if (strcmp(s, "stereo") == 0) {
+    if (pa_streq(s, "stereo")) {
         map.channels = 2;
         map.map[0] = PA_CHANNEL_POSITION_LEFT;
         map.map[1] = PA_CHANNEL_POSITION_RIGHT;
         goto finish;
         map.channels = 2;
         map.map[0] = PA_CHANNEL_POSITION_LEFT;
         map.map[1] = PA_CHANNEL_POSITION_RIGHT;
         goto finish;
+    } else if (pa_streq(s, "surround-40")) {
+        map.channels = 4;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+        goto finish;
+    } else if (pa_streq(s, "surround-41")) {
+        map.channels = 5;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+        map.map[4] = PA_CHANNEL_POSITION_LFE;
+        goto finish;
+    } else if (pa_streq(s, "surround-50")) {
+        map.channels = 5;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+        map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+        goto finish;
+    } else if (pa_streq(s, "surround-51")) {
+        map.channels = 6;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+        map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+        map.map[5] = PA_CHANNEL_POSITION_LFE;
+        goto finish;
+    } else if (pa_streq(s, "surround-71")) {
+        map.channels = 8;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+        map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+        map.map[5] = PA_CHANNEL_POSITION_LFE;
+        map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
+        map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+        goto finish;
     }
 
     state = NULL;
     map.channels = 0;
 
     while ((p = pa_split(s, ",", &state))) {
     }
 
     state = NULL;
     map.channels = 0;
 
     while ((p = pa_split(s, ",", &state))) {
+        pa_channel_position_t f;
 
         if (map.channels >= PA_CHANNELS_MAX) {
             pa_xfree(p);
             return NULL;
         }
 
 
         if (map.channels >= PA_CHANNELS_MAX) {
             pa_xfree(p);
             return NULL;
         }
 
-        /* Some special aliases */
-        if (strcmp(p, "left") == 0)
-            map.map[map.channels++] = PA_CHANNEL_POSITION_LEFT;
-        else if (strcmp(p, "right") == 0)
-            map.map[map.channels++] = PA_CHANNEL_POSITION_RIGHT;
-        else if (strcmp(p, "center") == 0)
-            map.map[map.channels++] = PA_CHANNEL_POSITION_CENTER;
-        else if (strcmp(p, "subwoofer") == 0)
-            map.map[map.channels++] = PA_CHANNEL_POSITION_SUBWOOFER;
-        else {
-            pa_channel_position_t i;
-
-            for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++)
-                if (strcmp(p, table[i]) == 0) {
-                    map.map[map.channels++] = i;
-                    break;
-                }
-
-            if (i >= PA_CHANNEL_POSITION_MAX) {
-                pa_xfree(p);
-                return NULL;
-            }
+        if ((f = pa_channel_position_from_string(p)) == PA_CHANNEL_POSITION_INVALID) {
+            pa_xfree(p);
+            return NULL;
         }
 
         }
 
+        map.map[map.channels++] = f;
         pa_xfree(p);
     }
 
         pa_xfree(p);
     }
 
@@ -519,15 +615,202 @@ int pa_channel_map_valid(const pa_channel_map *map) {
 
     pa_assert(map);
 
 
     pa_assert(map);
 
-    if (map->channels <= 0 || map->channels > PA_CHANNELS_MAX)
+    if (!pa_channels_valid(map->channels))
         return 0;
 
         return 0;
 
-    for (c = 0; c < map->channels; c++) {
-
-        if (map->map[c] < 0 ||map->map[c] >= PA_CHANNEL_POSITION_MAX)
+    for (c = 0; c < map->channels; c++)
+        if (map->map[c] < 0 || map->map[c] >= PA_CHANNEL_POSITION_MAX)
             return 0;
 
             return 0;
 
-    }
-
     return 1;
 }
     return 1;
 }
+
+int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *ss) {
+    pa_assert(map);
+    pa_assert(ss);
+
+    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+    pa_return_val_if_fail(pa_sample_spec_valid(ss), 0);
+
+    return map->channels == ss->channels;
+}
+
+int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
+    pa_channel_position_mask_t am, bm;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    pa_return_val_if_fail(pa_channel_map_valid(a), 0);
+
+    if (PA_UNLIKELY(a == b))
+        return 1;
+
+    pa_return_val_if_fail(pa_channel_map_valid(b), 0);
+
+    am = pa_channel_map_mask(a);
+    bm = pa_channel_map_mask(b);
+
+    return (bm & am) == bm;
+}
+
+int pa_channel_map_can_balance(const pa_channel_map *map) {
+    pa_channel_position_mask_t m;
+
+    pa_assert(map);
+    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+
+    m = pa_channel_map_mask(map);
+
+    return
+        (PA_CHANNEL_POSITION_MASK_LEFT & m) &&
+        (PA_CHANNEL_POSITION_MASK_RIGHT & m);
+}
+
+int pa_channel_map_can_fade(const pa_channel_map *map) {
+    pa_channel_position_mask_t m;
+
+    pa_assert(map);
+    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+
+    m = pa_channel_map_mask(map);
+
+    return
+        (PA_CHANNEL_POSITION_MASK_FRONT & m) &&
+        (PA_CHANNEL_POSITION_MASK_REAR & m);
+}
+
+const char* pa_channel_map_to_name(const pa_channel_map *map) {
+    pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
+    unsigned c;
+
+    pa_assert(map);
+
+    pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
+
+    memset(in_map, 0, sizeof(in_map));
+
+    for (c = 0; c < map->channels; c++)
+        pa_bitset_set(in_map, map->map[c], true);
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_MONO, -1))
+        return "mono";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1))
+        return "stereo";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1))
+        return "surround-40";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_LFE, -1))
+        return "surround-41";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, -1))
+        return "surround-50";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1))
+        return "surround-51";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
+                         PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1))
+        return "surround-71";
+
+    return NULL;
+}
+
+const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) {
+    pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
+    unsigned c;
+
+    pa_assert(map);
+
+    pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
+
+    memset(in_map, 0, sizeof(in_map));
+
+    for (c = 0; c < map->channels; c++)
+        pa_bitset_set(in_map, map->map[c], true);
+
+    pa_init_i18n();
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_MONO, -1))
+        return _("Mono");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1))
+        return _("Stereo");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1))
+        return _("Surround 4.0");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_LFE, -1))
+        return _("Surround 4.1");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, -1))
+        return _("Surround 5.0");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1))
+        return _("Surround 5.1");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
+                         PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1))
+        return _("Surround 7.1");
+
+    return NULL;
+}
+
+int pa_channel_map_has_position(const pa_channel_map *map, pa_channel_position_t p) {
+    unsigned c;
+
+    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+    pa_return_val_if_fail(p < PA_CHANNEL_POSITION_MAX, 0);
+
+    for (c = 0; c < map->channels; c++)
+        if (map->map[c] == p)
+            return 1;
+
+    return 0;
+}
+
+pa_channel_position_mask_t pa_channel_map_mask(const pa_channel_map *map) {
+    unsigned c;
+    pa_channel_position_mask_t r = 0;
+
+    pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+
+    for (c = 0; c < map->channels; c++)
+        r |= PA_CHANNEL_POSITION_MASK(map->map[c]);
+
+    return r;
+}