device-manager: Add Dbus interfaces related to sample rate of usb device 09/178909/3
authorSangchul Lee <sc11.lee@samsung.com>
Fri, 11 May 2018 01:44:57 +0000 (10:44 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Fri, 1 Jun 2018 08:34:44 +0000 (17:34 +0900)
Methods are added as below
 - GetSupportedSampleRates
    : arg#1 (in)  int32 for device_id
      arg#2 (out) uint32 array for sample_rates
 - SetSampleRate
    : arg#1 (in)  int32 for device_id
      arg#2 (in)  uint32 for sample_rate
 - GetSampleRate
    : arg#1 (in)  int32 for device_id
      arg#2 (out) uint32 for sample_rate

1. Those APIs are only for usb output device.
2. Here's description about setting sample rate to '0'.
 If sample rate is set to '0', it will work to use stream's original sample
 rate if the usb device supports the sample rate. In this case, if the
 stream's sample rate is lower than default and alternate sample rate of
 pulseaudio configuration, it will use the default or the alternate sample
 rate. When mixing case happens in the same usb device, the sample rate of
 the prior stream will be maintained.

[Version] 11.1.7
[Issue Type] New feature

Change-Id: I7f82d5aefbc84e0641fc05dab86bc89def897c66
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
packaging/pulseaudio-modules-tizen.spec
src/device-manager.c

index 03a3546..99bb142 100644 (file)
@@ -1,6 +1,6 @@
 Name:             pulseaudio-modules-tizen
 Summary:          Pulseaudio modules for Tizen
-Version:          11.1.6
+Version:          11.1.7
 Release:          0
 Group:            Multimedia/Audio
 License:          LGPL-2.1+
index bcc97e0..275c439 100644 (file)
     "   <arg name=\"device_type\" direction=\"in\" type=\"s\"/>\n"                          \
     "   <arg name=\"role\" direction=\"in\" type=\"s\"/>\n"                                 \
     "  </method>\n"                                                                         \
+    "  <method name=\"GetSupportedSampleRates\">\n"                                         \
+    "   <arg name=\"device_id\" direction=\"in\" type=\"i\"/>\n"                            \
+    "   <arg name=\"sample_rates\" direction=\"out\" type=\"a(u)\"/>\n"                     \
+    "  </method>\n"                                                                         \
+    "  <method name=\"SetSampleRate\">\n"                                                   \
+    "   <arg name=\"device_id\" direction=\"in\" type=\"i\"/>\n"                            \
+    "   <arg name=\"sample_rate\" direction=\"in\" type=\"u\"/>\n"                          \
+    "  </method>\n"                                                                         \
+    "  <method name=\"GetSampleRate\">\n"                                                   \
+    "   <arg name=\"device_id\" direction=\"in\" type=\"i\"/>\n"                            \
+    "   <arg name=\"sample_rate\" direction=\"out\" type=\"u\"/>\n"                         \
+    "  </method>\n"                                                                         \
     "  <method name=\"DumpDeviceList\">\n"                                                  \
     "  </method>\n"                                                                         \
     "  <method name=\"TestStatusChange\">\n"                                                \
     " </interface>\n"                                                                       \
     "</node>\n"
 
-
 #define FILTER_DEVICED_SYSNOTI                             \
     "type='signal',"                                       \
     " interface='" DBUS_INTERFACE_DEVICED_SYSNOTI "'"
@@ -406,6 +417,9 @@ static void handle_get_connected_device_list(DBusConnection *conn, DBusMessage *
 static void handle_get_device_by_id(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_is_stream_on_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_bt_a2dp_status(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_supported_sample_rates(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_load_sink(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_unload_sink(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_unload_sink_with_device_string(DBusConnection *conn, DBusMessage *msg, void *userdata);
@@ -424,6 +438,9 @@ enum method_handler_index {
     METHOD_HANDLER_UNLOAD_SINK,
     METHOD_HANDLER_UNLOAD_SINK_WITH_DEVICE_STRING,
     METHOD_HANDLER_GET_DEVICE_STRING,
+    METHOD_HANDLER_GET_SUPPORTED_SAMPLE_RATES,
+    METHOD_HANDLER_SET_SAMPLE_RATE,
+    METHOD_HANDLER_GET_SAMPLE_RATE,
     METHOD_HANDLER_DUMP_DEVICE_LIST,
     METHOD_HANDLER_STATUS_TEST,
     METHOD_HANDLER_MAX
@@ -454,6 +471,15 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
     [METHOD_HANDLER_GET_DEVICE_STRING] = {
         .method_name = "GetDeviceString",
         .receive_cb = handle_get_device_string },
+    [METHOD_HANDLER_GET_SUPPORTED_SAMPLE_RATES] = {
+        .method_name = "GetSupportedSampleRates",
+        .receive_cb = handle_get_supported_sample_rates },
+    [METHOD_HANDLER_SET_SAMPLE_RATE] = {
+        .method_name = "SetSampleRate",
+        .receive_cb = handle_set_sample_rate },
+    [METHOD_HANDLER_GET_SAMPLE_RATE] = {
+        .method_name = "GetSampleRate",
+        .receive_cb = handle_get_sample_rate },
     [METHOD_HANDLER_DUMP_DEVICE_LIST] = {
         .method_name = "DumpDeviceList",
         .receive_cb = handle_dump_device_list },
@@ -3221,6 +3247,212 @@ static void handle_dump_device_list(DBusConnection *conn, DBusMessage *msg, void
     dbus_message_unref(reply);
 }
 
+static bool is_usb_output_device(pa_tz_device *device) {
+    char *type;
+    dm_device_direction_t direction;
+    pa_sink *sink;
+
+    pa_assert(device);
+
+    type = pa_tz_device_get_type(device);
+    if (!pa_streq(type, DEVICE_TYPE_USB_AUDIO)) {
+        pa_log_error("device(id:%d, %s) is not USB AUDIO type", pa_tz_device_get_id(device), type);
+        return false;
+    }
+    direction = pa_tz_device_get_direction(device);
+    if (direction & DM_DEVICE_DIRECTION_OUT) {
+        if (!(sink = pa_tz_device_get_sink(device, DEVICE_ROLE_NORMAL))) {
+            pa_log_error("sink is null");
+            return false;
+        }
+    } else {
+        pa_log_error("this device is not for output");
+        return false;
+    }
+
+    return true;
+}
+
+static bool is_supported_sample_rate(uint32_t *supported_sample_rates, uint32_t sample_rate) {
+    int i;
+
+    pa_assert(supported_sample_rates);
+
+    /* use a supported sample rate selected by user */
+    for (i = 0; supported_sample_rates[i]; i++) {
+        if (supported_sample_rates[i] == sample_rate) {
+            pa_log_info("%u is supported", sample_rate);
+            return true;
+        }
+    }
+    pa_log_error("%u is not supported", sample_rate);
+    return false;
+}
+
+static void handle_get_supported_sample_rates(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_device_manager *dm;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter, array_iter, item_iter;
+    dbus_int32_t device_id;
+    pa_tz_device *device;
+    pa_sink *sink;
+    int i;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(userdata);
+
+    dm = (pa_device_manager *)userdata;
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    pa_assert_se(dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_INT32, &device_id,
+                                       DBUS_TYPE_INVALID));
+
+    pa_log_info("Get supported sample rates of the device(id:%d)", device_id);
+
+    if (!(device = _device_list_get_device_by_id(dm, device_id))) {
+        pa_log_error("could not find any device with id:%d", device_id);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", "org.tizen.multimedia.audio.InvalidArgument");
+        return;
+    }
+    if (!is_usb_output_device(device)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_NOT_SUPPORTED, "%s", "org.tizen.multimedia.audio.PolicyInternal");
+        return;
+    }
+    sink = pa_tz_device_get_sink(device, DEVICE_ROLE_NORMAL);
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "(u)", &array_iter));
+    for (i = 0; sink->supported_sample_rates[i]; i++) {
+        pa_log_info("%u is supported", sink->supported_sample_rates[i]);
+        pa_assert_se(dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &item_iter));
+        dbus_message_iter_append_basic(&item_iter, DBUS_TYPE_UINT32, &sink->supported_sample_rates[i]);
+        pa_assert_se(dbus_message_iter_close_container(&array_iter, &item_iter));
+    }
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &array_iter));
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+static void handle_set_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_device_manager *dm;
+    DBusMessage *reply = NULL;
+    dbus_int32_t device_id;
+    dbus_uint32_t sample_rate;
+    uint32_t prev_selected_sample_rate;
+    pa_tz_device *device;
+    pa_sink *sink;
+    pa_sink_input *si;
+    uint32_t idx;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(userdata);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+    dm = (pa_device_manager *)userdata;
+    pa_assert_se(dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_INT32, &device_id,
+                                       DBUS_TYPE_UINT32, &sample_rate,
+                                       DBUS_TYPE_INVALID));
+
+    pa_log_info("Set sample rate(%u) of the device(id:%d)", sample_rate, device_id);
+
+    if (!(device = _device_list_get_device_by_id(dm, device_id))) {
+        pa_log_error("could not find any device with id:%d", device_id);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", "org.tizen.multimedia.audio.InvalidArgument");
+        return;
+    }
+    if (!is_usb_output_device(device)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_NOT_SUPPORTED, "%s", "org.tizen.multimedia.audio.PolicyInternal");
+        return;
+    }
+    sink = pa_tz_device_get_sink(device, DEVICE_ROLE_NORMAL);
+
+    /* use stream original sample rate if possible */
+    if (sample_rate == 0) {
+        sink->avoid_resampling = true;
+        PA_IDXSET_FOREACH(si, sink->inputs, idx) {
+            if (pa_sink_reconfigure(sink, &si->sample_spec, pa_sink_input_is_passthrough(si)) == -1) {
+                pa_log_error("failed to reconfigure");
+                pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "%s", "org.tizen.multimedia.audio.Internal");
+                sink->avoid_resampling = sink->origin_avoid_resampling;
+                return;
+            }
+        }
+        sink->selected_sample_rate = 0;
+
+        pa_log_info("Set sample rate to avoid resampling for the device(%u) successfully", device_id);
+        pa_assert_se(dbus_connection_send(conn, reply, NULL));
+        dbus_message_unref(reply);
+        return;
+    }
+
+    /* use a supported sample rate selected by user */
+    if (!is_supported_sample_rate(sink->supported_sample_rates, sample_rate)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", "org.tizen.multimedia.audio.InvalidArgument");
+        return;
+    }
+    prev_selected_sample_rate = sink->selected_sample_rate;
+    sink->selected_sample_rate = sample_rate;
+
+    PA_IDXSET_FOREACH(si, sink->inputs, idx) {
+        if (pa_sink_reconfigure(sink, &si->sample_spec, pa_sink_input_is_passthrough(si)) == -1) {
+            pa_log_error("failed to reconfigure");
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "%s", "org.tizen.multimedia.audio.Internal");
+            sink->selected_sample_rate = prev_selected_sample_rate;
+            return;
+        }
+        break;
+    }
+    sink->avoid_resampling = sink->origin_avoid_resampling;
+
+    pa_log_info("Set sample rate(%u) of the device(id:%d) successfully", sample_rate, device_id);
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_device_manager *dm;
+    DBusMessage *reply = NULL;
+    dbus_int32_t device_id;
+    pa_tz_device *device;
+    pa_sink *sink;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(userdata);
+
+    dm = (pa_device_manager *)userdata;
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    pa_assert_se(dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_INT32, &device_id,
+                                       DBUS_TYPE_INVALID));
+
+    pa_log_info("Get sample rate of the device(id:%d)", device_id);
+
+    if (!(device = _device_list_get_device_by_id(dm, device_id))) {
+        pa_log_error("could not find any device with id:%d", device_id);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", "org.tizen.multimedia.audio.InvalidArgument");
+        return;
+    }
+    if (!is_usb_output_device(device)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_NOT_SUPPORTED, "%s", "org.tizen.multimedia.audio.PolicyInternal");
+        return;
+    }
+    sink = pa_tz_device_get_sink(device, DEVICE_ROLE_NORMAL);
+
+    pa_log_info("Get sample rate(%u) of the device(id:%d) successfully", sink->selected_sample_rate, device_id);
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+    pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_UINT32, &sink->selected_sample_rate, DBUS_TYPE_INVALID));
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
 static void handle_test_device_status_change(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     pa_device_manager *dm = (pa_device_manager *)userdata;
     char *type;