From e8e6c1d8bc297c88bc02a9e267d226b5011a271d Mon Sep 17 00:00:00 2001
From: George Kiagiadakis <george.kiagiadakis@collabora.com>
Date: Mon, 11 May 2020 11:19:01 +0300
Subject: [PATCH] algorithms: allow specifying a preferred number of channels
 in the format choosing algorithm

---
 modules/module-pipewire/algorithms.c          |  15 +-
 modules/module-pipewire/algorithms.h          |   2 +-
 .../audio-softdsp-endpoint/adapter.c          |   2 +-
 modules/module-si-adapter.c                   |   2 +-
 tests/modules/algorithms.c                    | 146 ++++++++++--------
 5 files changed, 97 insertions(+), 70 deletions(-)

diff --git a/modules/module-pipewire/algorithms.c b/modules/module-pipewire/algorithms.c
index fd5e522e..53d3ebd2 100644
--- a/modules/module-pipewire/algorithms.c
+++ b/modules/module-pipewire/algorithms.c
@@ -284,7 +284,7 @@ select_rate (WpSpaPod *value)
 }
 
 static gint
-select_channels (WpSpaPod *value)
+select_channels (WpSpaPod *value, gint preference)
 {
   gint ret = 0;
 
@@ -307,10 +307,13 @@ select_channels (WpSpaPod *value)
     /* choose the most channels */
     g_autoptr (WpIterator) it = wp_spa_pod_iterator_new (value);
     GValue next = G_VALUE_INIT;
+    gint diff = SPA_AUDIO_MAX_CHANNELS;
     while (wp_iterator_next (it, &next)) {
       gint *channel = (gint *) g_value_get_pointer (&next);
-      if (*channel > ret)
+      if (abs (*channel - preference) < diff) {
+        diff = abs (*channel - preference);
         ret = *channel;
+      }
       g_value_unset (&next);
     }
   }
@@ -329,8 +332,8 @@ select_channels (WpSpaPod *value)
       g_value_unset (&next);
       i++;
     }
-    ret = SPA_MAX (vals[1], vals[2]);
-    ret = SPA_MIN (ret, SPA_AUDIO_MAX_CHANNELS);
+    ret = SPA_MAX (vals[1], preference);
+    ret = SPA_MIN (ret, vals[2]);
   }
 
   return ret;
@@ -338,7 +341,7 @@ select_channels (WpSpaPod *value)
 
 gboolean
 choose_sensible_raw_audio_format (GPtrArray *formats,
-    struct spa_audio_info_raw *result)
+    gint channels_preference, struct spa_audio_info_raw *result)
 {
   guint i, most_channels = 0;
   struct spa_audio_info_raw *raw;
@@ -392,7 +395,7 @@ choose_sensible_raw_audio_format (GPtrArray *formats,
 
       /* channels */
       else if (g_strcmp0 (key, "channels") == 0) {
-        raw[i].channels = select_channels (value);
+        raw[i].channels = select_channels (value, channels_preference);
       }
 
       /* position */
diff --git a/modules/module-pipewire/algorithms.h b/modules/module-pipewire/algorithms.h
index 4d76d5a6..8c3b1d9b 100644
--- a/modules/module-pipewire/algorithms.h
+++ b/modules/module-pipewire/algorithms.h
@@ -12,4 +12,4 @@ gboolean multiport_link_create (GVariant * src_data, GVariant * sink_data,
 
 struct spa_audio_info_raw;
 gboolean choose_sensible_raw_audio_format (GPtrArray *formats,
-    struct spa_audio_info_raw *result);
+    gint channels_preference, struct spa_audio_info_raw *result);
diff --git a/modules/module-pipewire/audio-softdsp-endpoint/adapter.c b/modules/module-pipewire/audio-softdsp-endpoint/adapter.c
index 206301e3..8ef138d0 100644
--- a/modules/module-pipewire/audio-softdsp-endpoint/adapter.c
+++ b/modules/module-pipewire/audio-softdsp-endpoint/adapter.c
@@ -87,7 +87,7 @@ on_proxy_enum_format_done (WpProxy *proxy, GAsyncResult *res,
     return;
   }
 
-  if (!choose_sensible_raw_audio_format (formats, &self->format)) {
+  if (!choose_sensible_raw_audio_format (formats, 34, &self->format)) {
     uint32_t media_type, media_subtype;
 
     g_warning ("WpAudioAdapter:%p failed to choose a sensible audio format",
diff --git a/modules/module-si-adapter.c b/modules/module-si-adapter.c
index 1d93d7e8..2d112886 100644
--- a/modules/module-si-adapter.c
+++ b/modules/module-si-adapter.c
@@ -207,7 +207,7 @@ on_node_enum_format_done (WpProxy *proxy, GAsyncResult *res,
     return;
   }
 
-  if (!choose_sensible_raw_audio_format (formats, &self->format)) {
+  if (!choose_sensible_raw_audio_format (formats, 34, &self->format)) {
     wp_warning_object (self, "failed to choose a sensible audio format");
     wp_transition_return_error (transition,
         g_error_new (WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
diff --git a/tests/modules/algorithms.c b/tests/modules/algorithms.c
index 9a7908f3..1c554c00 100644
--- a/tests/modules/algorithms.c
+++ b/tests/modules/algorithms.c
@@ -28,68 +28,92 @@ test_choose_sensible_raw_audio_format (void)
   g_autoptr (GPtrArray) formats =
       g_ptr_array_new_with_free_func ((GDestroyNotify) wp_spa_pod_unref);
 
-  /* test 1 */
-  g_ptr_array_remove_range (formats, 0, formats->len);
-  g_autoptr (WpSpaPod) param1 = wp_spa_pod_new_object (
-      "Format", "Format",
-      "mediaType",    SPA_POD_Id(SPA_MEDIA_TYPE_audio),
-      "mediaSubtype", SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
-      "format",       SPA_POD_CHOICE_ENUM_Id(3,
-                          SPA_AUDIO_FORMAT_F32_OE,
-                          SPA_AUDIO_FORMAT_S16,
-                          SPA_AUDIO_FORMAT_S20),
-      "rate",         SPA_POD_CHOICE_RANGE_Int(22000, 44100, 8000),
-      "channels",     SPA_POD_CHOICE_RANGE_Int(2, 1, 8),
-      NULL);
-  g_assert_nonnull (param1);
-  g_ptr_array_add (formats, g_steal_pointer (&param1));
-  g_assert_true (choose_sensible_raw_audio_format (formats, &info));
-  g_assert_cmpint (info.format, ==, SPA_AUDIO_FORMAT_S16);
-  g_assert_cmpint (info.rate, ==, 44100);
-  g_assert_cmpint (info.channels, ==, 8);
-  g_assert_cmpint (info.flags, ==, SPA_AUDIO_FLAG_UNPOSITIONED);
+  {
+    g_ptr_array_remove_range (formats, 0, formats->len);
+    g_autoptr (WpSpaPod) param1 = wp_spa_pod_new_object (
+        "Format", "Format",
+        "mediaType",    SPA_POD_Id(SPA_MEDIA_TYPE_audio),
+        "mediaSubtype", SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
+        "format",       SPA_POD_CHOICE_ENUM_Id(3,
+                            SPA_AUDIO_FORMAT_F32_OE,
+                            SPA_AUDIO_FORMAT_S16,
+                            SPA_AUDIO_FORMAT_S20),
+        "rate",         SPA_POD_CHOICE_RANGE_Int(22000, 44100, 8000),
+        "channels",     SPA_POD_CHOICE_RANGE_Int(2, 1, 8),
+        NULL);
+    g_assert_nonnull (param1);
+    g_ptr_array_add (formats, g_steal_pointer (&param1));
+    g_assert_true (choose_sensible_raw_audio_format (formats, 34, &info));
+    g_assert_cmpint (info.format, ==, SPA_AUDIO_FORMAT_S16);
+    g_assert_cmpint (info.rate, ==, 44100);
+    g_assert_cmpint (info.channels, ==, 8);
+    g_assert_cmpint (info.flags, ==, SPA_AUDIO_FLAG_UNPOSITIONED);
+  }
 
-  /* test 2 */
-  g_ptr_array_remove_range (formats, 0, formats->len);
-  g_autoptr (WpSpaPod) param2 = wp_spa_pod_new_object (
-      "Format", "Format",
-      "mediaType",    SPA_POD_Id(SPA_MEDIA_TYPE_audio),
-      "mediaSubtype", SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
-      "format",       SPA_POD_CHOICE_ENUM_Id(3,
-                          SPA_AUDIO_FORMAT_S32,
-                          SPA_AUDIO_FORMAT_U8,
-                          SPA_AUDIO_FORMAT_F32),
-      "rate",         SPA_POD_CHOICE_RANGE_Int(56000, 44100, 96000),
-      "channels",     SPA_POD_Int(2),
-      "position",     SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, 2, layout),
-      NULL);
-  g_assert_nonnull (param2);
-  g_ptr_array_add (formats, g_steal_pointer (&param2));
-  g_autoptr (WpSpaPod) param3 = wp_spa_pod_new_object (
-      "Format", "Format",
-      "mediaType",    SPA_POD_Id(SPA_MEDIA_TYPE_audio),
-      "mediaSubtype", SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
-      "format",       SPA_POD_CHOICE_ENUM_Id(3,
-                          SPA_AUDIO_FORMAT_S32,
-                          SPA_AUDIO_FORMAT_U8,
-                          SPA_AUDIO_FORMAT_F32),
-      "rate",         SPA_POD_CHOICE_RANGE_Int(56000, 44100, 96000),
-      "channels",     SPA_POD_Int(5),
-      "position",     SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, 5, layout),
-      NULL);
-  g_assert_nonnull (param3);
-  g_ptr_array_add (formats, g_steal_pointer (&param3));
-  g_assert_true (choose_sensible_raw_audio_format (formats, &info));
-  g_assert_cmpint (info.format, ==, SPA_AUDIO_FORMAT_F32);
-  g_assert_cmpint (info.rate, ==, 48000);
-  g_assert_cmpint (info.channels, ==, 5);
-  g_assert_cmpint (info.flags, ==, SPA_AUDIO_FLAG_NONE);
-  g_assert_cmpint (info.position[0], ==, layout[0]);
-  g_assert_cmpint (info.position[1], ==, layout[1]);
-  g_assert_cmpint (info.position[2], ==, layout[2]);
-  g_assert_cmpint (info.position[3], ==, layout[3]);
-  g_assert_cmpint (info.position[4], ==, layout[4]);
-  g_assert_cmpint (info.position[5], ==, 0);
+  {
+    g_ptr_array_remove_range (formats, 0, formats->len);
+    g_autoptr (WpSpaPod) param1 = wp_spa_pod_new_object (
+        "Format", "Format",
+        "mediaType",    SPA_POD_Id(SPA_MEDIA_TYPE_audio),
+        "mediaSubtype", SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
+        "format",       SPA_POD_CHOICE_ENUM_Id(3,
+                            SPA_AUDIO_FORMAT_F32_OE,
+                            SPA_AUDIO_FORMAT_S16,
+                            SPA_AUDIO_FORMAT_S20),
+        "rate",         SPA_POD_CHOICE_RANGE_Int(22000, 44100, 8000),
+        "channels",     SPA_POD_CHOICE_RANGE_Int(2, 1, 8),
+        NULL);
+    g_assert_nonnull (param1);
+    g_ptr_array_add (formats, g_steal_pointer (&param1));
+    g_assert_true (choose_sensible_raw_audio_format (formats, 2, &info));
+    g_assert_cmpint (info.format, ==, SPA_AUDIO_FORMAT_S16);
+    g_assert_cmpint (info.rate, ==, 44100);
+    g_assert_cmpint (info.channels, ==, 2);
+    g_assert_cmpint (info.flags, ==, SPA_AUDIO_FLAG_UNPOSITIONED);
+  }
+
+  {
+    g_ptr_array_remove_range (formats, 0, formats->len);
+    g_autoptr (WpSpaPod) param2 = wp_spa_pod_new_object (
+        "Format", "Format",
+        "mediaType",    SPA_POD_Id(SPA_MEDIA_TYPE_audio),
+        "mediaSubtype", SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
+        "format",       SPA_POD_CHOICE_ENUM_Id(3,
+                            SPA_AUDIO_FORMAT_S32,
+                            SPA_AUDIO_FORMAT_U8,
+                            SPA_AUDIO_FORMAT_F32),
+        "rate",         SPA_POD_CHOICE_RANGE_Int(56000, 44100, 96000),
+        "channels",     SPA_POD_Int(2),
+        "position",     SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, 2, layout),
+        NULL);
+    g_assert_nonnull (param2);
+    g_ptr_array_add (formats, g_steal_pointer (&param2));
+    g_autoptr (WpSpaPod) param3 = wp_spa_pod_new_object (
+        "Format", "Format",
+        "mediaType",    SPA_POD_Id(SPA_MEDIA_TYPE_audio),
+        "mediaSubtype", SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
+        "format",       SPA_POD_CHOICE_ENUM_Id(3,
+                            SPA_AUDIO_FORMAT_S32,
+                            SPA_AUDIO_FORMAT_U8,
+                            SPA_AUDIO_FORMAT_F32),
+        "rate",         SPA_POD_CHOICE_RANGE_Int(56000, 44100, 96000),
+        "channels",     SPA_POD_Int(5),
+        "position",     SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, 5, layout),
+        NULL);
+    g_assert_nonnull (param3);
+    g_ptr_array_add (formats, g_steal_pointer (&param3));
+    g_assert_true (choose_sensible_raw_audio_format (formats, 34, &info));
+    g_assert_cmpint (info.format, ==, SPA_AUDIO_FORMAT_F32);
+    g_assert_cmpint (info.rate, ==, 48000);
+    g_assert_cmpint (info.channels, ==, 5);
+    g_assert_cmpint (info.flags, ==, SPA_AUDIO_FLAG_NONE);
+    g_assert_cmpint (info.position[0], ==, layout[0]);
+    g_assert_cmpint (info.position[1], ==, layout[1]);
+    g_assert_cmpint (info.position[2], ==, layout[2]);
+    g_assert_cmpint (info.position[3], ==, layout[3]);
+    g_assert_cmpint (info.position[4], ==, layout[4]);
+    g_assert_cmpint (info.position[5], ==, 0);
+  }
 
   wp_spa_type_deinit ();
 }
-- 
GitLab