aec-manager: Add audio AEC manager 53/257053/11 accepted/tizen/unified/20210426.002345 submit/tizen/20210423.033332 submit/tizen/20210423.060829 submit/tizen/20210423.064540
authorJaechul Lee <jcsing.lee@samsung.com>
Mon, 29 Mar 2021 05:17:04 +0000 (14:17 +0900)
committerJaechul Lee <jcsing.lee@samsung.com>
Fri, 23 Apr 2021 02:37:07 +0000 (11:37 +0900)
added new feature

[Version] 13.0.61
[Issue Type] New feature

Change-Id: I401390836ee34ea4444180e9725febd1720ac4a4
Signed-off-by: Jaechul Lee <jcsing.lee@samsung.com>
Makefile.am
configure.ac
packaging/pulseaudio-modules-tizen.spec
src/aec-manager.c [new file with mode: 0644]
src/aec-manager.h [new file with mode: 0644]
src/device-manager.c
src/module-tizenaudio-sink.c
src/module-tizenaudio-source.c

index e66660b..495578e 100644 (file)
@@ -27,6 +27,10 @@ AM_LIBADD = $(PTHREAD_LIBS) $(INTLLIBS)
 AM_LDFLAGS = $(NODELETE_LDFLAGS)
 
 MODULE_CFLAGS = $(AM_CFLAGS) $(PACORE_CFLAGS) $(PA_CFLAGS) -D__TIZEN__
+if ENABLE_AEC
+MODULE_CFLAGS += -DSUPPORT_AEC
+endif
+
 MODULE_LDFLAGS = $(AM_LDFLAGS) $(PACORE_LDFLAGS) $(PA_LDFLAGS) -module -disable-static -avoid-version
 MODULE_LIBADD = $(AM_LIBADD) $(PACORE_LIBS) $(PA_LIBS)
 
@@ -94,6 +98,11 @@ module_tizenaudio_policy_la_SOURCES = \
           src/device-manager-db.c   src/device-manager-db-priv.h  \
           src/tizen-device.c   src/tizen-device.h   src/tizen-device-def.c  src/tizen-device-def.h  \
           src/subscribe-observer.c   src/subscribe-observer.h
+
+if ENABLE_AEC
+module_tizenaudio_policy_la_SOURCES += src/aec-manager.c src/aec-manager.h
+endif
+
 module_tizenaudio_policy_la_LDFLAGS = $(MODULE_LDFLAGS) -L$(pulsemodlibexecdir)
 module_tizenaudio_policy_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS) $(VCONF_LIBS) $(INIPARSER_LIBS) $(LIBJSON_LIBS) libhal-interface.la libcommunicator.la
 module_tizenaudio_policy_la_CFLAGS = $(MODULE_CFLAGS) $(DBUS_CFLAGS) $(VCONF_CFLAGS) $(INIPARSER_CFLAGS) $(LIBJSON_CFLAGS) -DPA_MODULE_NAME=module_tizenaudio_policy
index ac631fa..d1720a3 100644 (file)
@@ -393,6 +393,18 @@ AC_ARG_ENABLE(acm, AC_HELP_STRING([--enable-acm], [using acm]),
 AM_CONDITIONAL(ENABLE_ACM, test "x$ENABLE_ACM" = "xyes")
 dnl end --------------------------------------------------------------------
 
+dnl use aec ----------------------------------------------------------------
+AC_ARG_ENABLE(aec, AC_HELP_STRING([--enable-aec], [using aec]),
+[
+ case "${enableval}" in
+        yes) ENABLE_AEC=yes ;;
+        no)  ENABLE_AEC=no ;;
+        *)   AC_MSG_ERROR(bad value ${enableval} for --enable-aec) ;;
+ esac
+ ],[USE_AEC=no])
+AM_CONDITIONAL(ENABLE_AEC, test "x$ENABLE_AEC" = "xyes")
+dnl end --------------------------------------------------------------------
+
 #### D-Bus support (optional) ####
 
 AC_ARG_ENABLE([dbus],
index 3310887..13add09 100644 (file)
@@ -1,6 +1,6 @@
 Name:             pulseaudio-modules-tizen
 Summary:          Pulseaudio modules for Tizen
-Version:          13.0.60
+Version:          13.0.61
 Release:          0
 Group:            Multimedia/Audio
 License:          LGPL-2.1+
@@ -46,7 +46,9 @@ export LD_AS_NEEDED=0
 %reconfigure --prefix=%{_prefix} \
         --disable-static \
         --enable-acm \
-%if "%{tizen_profile_name}" == "tv"
+%if "%{tizen_profile_name}" != "tv"
+        --enable-aec
+%else
         --enable-vconf-helper
 %endif
 #        --enable-haltc
diff --git a/src/aec-manager.c b/src/aec-manager.c
new file mode 100644 (file)
index 0000000..8ed96b5
--- /dev/null
@@ -0,0 +1,267 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2021 Jaechul Lee <jcsing.lee@samsung.com>
+
+  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.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/macro.h>
+
+#include <pulsecore/dbus-shared.h>
+#include <pulsecore/protocol-dbus.h>
+#include <pulsecore/dbus-util.h>
+
+#include "aec-manager.h"
+
+#define AEC_MANAGER_OBJECT_PATH                         "/org/pulseaudio/AecManager"
+#define AEC_MANAGER_INTERFACE                           "org.pulseaudio.AecManager"
+
+#define AEC_MANAGER_METHOD_NAME_ON                      "On"
+#define AEC_MANAGER_METHOD_NAME_OFF                     "Off"
+#define AEC_MANAGER_METHOD_NAME_GET_SINK_PARAMS         "GetSinkParam"
+#define AEC_MANAGER_METHOD_NAME_GET_SOURCE_PARAMS       "GetSourceParam"
+
+#define AEC_UNIX_SOCKET_PATH "/tmp/.aec.socket"
+
+static struct aec_manager {
+    pa_dbus_connection *dbus_conn;
+    pa_source *source; /* builtin-mic */
+    pa_sink *sink; /* builtin-spk */
+} am;
+
+enum {
+    SINK_MESSAGE_SET_AEC_STATE = PA_SINK_MESSAGE_MAX,
+    SINK_MESSAGE_GET_AEC_PARAMS,
+};
+
+enum {
+    SOURCE_MESSAGE_SET_AEC_STATE = PA_SOURCE_MESSAGE_MAX,
+    SOURCE_MESSAGE_GET_AEC_PARAMS,
+};
+
+#define AEC_MANAGER_INTROSPECT_XML                                      \
+    DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
+    "<node>"                                                            \
+    " <interface name=\"AEC_MANAGER_INTERFACE\">"                       \
+    "  <method name=\"AEC_MANAGER_METHOD_NAME_ON\">"                    \
+    "  </method>"                                                       \
+    "  <method name=\"AEC_MANAGER_METHOD_NAME_OFF\">"                   \
+    "  </method>"                                                       \
+    "  <method name=\"AEC_MANAGER_METHOD_NAME_GET_SINK_PARAMS\">"       \
+    "   <arg name=\"rate\" direction=\"out\" type=\"i\"/>"              \
+    "   <arg name=\"channels\" direction=\"out\" type=\"i\"/>"          \
+    "   <arg name=\"format\" direction=\"out\" type=\"i\"/>"            \
+    "   <arg name=\"frag_size\" direction=\"out\" type=\"i\"/>"         \
+    "   <arg name=\"nfrags\" direction=\"out\" type=\"i\"/>"            \
+    "   <arg name=\"card\" direction=\"out\" type=\"s\"/>"              \
+    "   <arg name=\"device\" direction=\"out\" type=\"s\"/>"            \
+    "  </method>"                                                       \
+    "  <method name=\"AEC_MANAGER_METHOD_NAME_GET_SOURCE_PARAMS\">"     \
+    "   <arg name=\"rate\" direction=\"out\" type=\"i\"/>"              \
+    "   <arg name=\"channels\" direction=\"out\" type=\"i\"/>"          \
+    "   <arg name=\"format\" direction=\"out\" type=\"i\"/>"            \
+    "   <arg name=\"frag_size\" direction=\"out\" type=\"i\"/>"         \
+    "   <arg name=\"nfrags\" direction=\"out\" type=\"i\"/>"            \
+    "   <arg name=\"card\" direction=\"out\" type=\"s\"/>"              \
+    "   <arg name=\"device\" direction=\"out\" type=\"s\"/>"            \
+    "  </method>"                                                       \
+    " </interface>"                                                     \
+    " <interface name=\"org.freedesktop.DBus.Introspectable\">"         \
+    "  <method name=\"Introspect\">"                                    \
+    "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"              \
+    "  </method>"                                                       \
+    " </interface>"                                                     \
+    "</node>"
+
+enum method_handler_index {
+    METHOD_HANDLER_ON,
+    METHOD_HANDLER_OFF,
+    METHOD_HANDLER_GET_SINK_PARAMS,
+    METHOD_HANDLER_GET_SOURCE_PARAMS,
+    METHOD_HANDLER_MAX
+};
+
+static void handle_method_on(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_method_off(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_method_get_sink_params(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_method_get_source_params(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_ON] = {
+        .method_name = AEC_MANAGER_METHOD_NAME_ON,
+        .receive_cb = handle_method_on },
+    [METHOD_HANDLER_OFF] = {
+        .method_name = AEC_MANAGER_METHOD_NAME_OFF,
+        .receive_cb = handle_method_off },
+    [METHOD_HANDLER_GET_SINK_PARAMS] = {
+        .method_name = AEC_MANAGER_METHOD_NAME_GET_SINK_PARAMS,
+        .receive_cb = handle_method_get_sink_params },
+    [METHOD_HANDLER_GET_SOURCE_PARAMS] = {
+        .method_name = AEC_MANAGER_METHOD_NAME_GET_SOURCE_PARAMS,
+        .receive_cb = handle_method_get_source_params },
+};
+
+static void send_get_param_reply(DBusConnection *conn, DBusMessage *msg, aec_params_t* param) {
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+
+    const char *ptr1 = param->card;
+    const char *ptr2 = param->device;
+
+    reply = dbus_message_new_method_return(msg);
+    if (!reply) {
+        pa_log_error("Failed to alloc reply");
+        return;
+    }
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, &param->rate);
+    dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, &param->channels);
+    dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, &param->format);
+    dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, &param->frag_size);
+    dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, &param->nfrags);
+    dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &ptr1);
+    dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &ptr2);
+    dbus_connection_send(conn, reply, NULL);
+
+    dbus_message_unref(reply);
+}
+
+static void handle_method_on(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_asyncmsgq_post(am.sink->asyncmsgq, PA_MSGOBJECT(am.sink),
+                        SINK_MESSAGE_SET_AEC_STATE, (void *)TRUE, 0, NULL, NULL);
+    pa_asyncmsgq_post(am.source->asyncmsgq, PA_MSGOBJECT(am.source),
+                        SOURCE_MESSAGE_SET_AEC_STATE, (void *)TRUE, 0, NULL, NULL);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_method_off(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_asyncmsgq_post(am.sink->asyncmsgq, PA_MSGOBJECT(am.sink),
+                        SINK_MESSAGE_SET_AEC_STATE, (void *)FALSE, 0, NULL, NULL);
+    pa_asyncmsgq_post(am.source->asyncmsgq, PA_MSGOBJECT(am.source),
+                        SOURCE_MESSAGE_SET_AEC_STATE, (void *)FALSE, 0, NULL, NULL);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_method_get_sink_params(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    aec_params_t param;
+
+    pa_asyncmsgq_send(am.sink->asyncmsgq, PA_MSGOBJECT(am.sink),
+                        SINK_MESSAGE_GET_AEC_PARAMS, &param, 0, NULL);
+
+    send_get_param_reply(conn, msg, &param);
+}
+
+static void handle_method_get_source_params(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    aec_params_t param;
+
+    pa_asyncmsgq_send(am.source->asyncmsgq, PA_MSGOBJECT(am.source),
+                        SOURCE_MESSAGE_GET_AEC_PARAMS, &param, 0, NULL);
+
+    send_get_param_reply(conn, msg, &param);
+}
+
+static DBusHandlerResult method_handler_for_vt(DBusConnection *conn, DBusMessage *m, void *userdata) {
+    const char *path, *interface, *member;
+    DBusMessage *r = NULL;
+
+    pa_assert(conn);
+    pa_assert(m);
+
+    path = dbus_message_get_path(m);
+    interface = dbus_message_get_interface(m);
+    member = dbus_message_get_member(m);
+
+    pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
+
+    if (!pa_safe_streq(path, AEC_MANAGER_OBJECT_PATH))
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+        r = dbus_message_new_method_return(m);
+        if (r) {
+            const char *xml = AEC_MANAGER_INTROSPECT_XML;
+            dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID);
+            dbus_connection_send((conn), r, NULL);
+            dbus_message_unref(r);
+        }
+    } else {
+        int i;
+        for (i = 0; i < METHOD_HANDLER_MAX; i++) {
+            if (dbus_message_is_method_call(m, AEC_MANAGER_INTERFACE,
+                                            method_handlers[i].method_name))
+                method_handlers[i].receive_cb(conn, m, NULL);
+        }
+    }
+
+    return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+int aec_manager_init(pa_core *core, pa_source *source, pa_sink *sink) {
+    DBusError err;
+    pa_dbus_connection *conn = NULL;
+    static const DBusObjectPathVTable vtable = {
+        .message_function = method_handler_for_vt,
+    };
+
+    if (!source || !sink) {
+        pa_log_error("AEC init failed. source(%p)/sink(%p) is null.", source, sink);
+        return -1;
+    }
+
+    dbus_error_init(&err);
+    if (!(conn = pa_dbus_bus_get(core, DBUS_BUS_SYSTEM, &err)) || dbus_error_is_set(&err)) {
+        if (conn)
+            pa_dbus_connection_unref(conn);
+
+        dbus_error_free(&err);
+        pa_log_error("Unable to contact D-Bus system bus: %s: %s", err.name, err.message);
+        return -1;
+    }
+
+    am.dbus_conn = conn;
+    if (!dbus_connection_register_object_path(pa_dbus_connection_get(conn),
+                                                AEC_MANAGER_OBJECT_PATH, &vtable, NULL)) {
+        pa_dbus_connection_unref(conn);
+        pa_log_error("Failed to register object path");
+        return -1;
+    }
+
+    am.source = source;
+    am.sink = sink;
+
+    pa_log_info("AEC init success");
+
+    return 0;
+}
+
+void aec_manager_deinit(void) {
+    if (am.dbus_conn)
+        pa_dbus_connection_unref(am.dbus_conn);
+
+    pa_log_info("AEC deinit success");
+}
diff --git a/src/aec-manager.h b/src/aec-manager.h
new file mode 100644 (file)
index 0000000..d30f788
--- /dev/null
@@ -0,0 +1,38 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2021 Jaechul Lee <jcsing.lee@samsung.com>
+
+  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.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifndef footizenaecmanagerfoo
+#define footizenaecmanagerfoo
+
+typedef struct aec_params {
+    int rate;
+    int channels;
+    int format;
+    int frag_size;
+    int nfrags;
+    char card[32];
+    char device[32];
+} aec_params_t;
+
+int aec_manager_init(pa_core *core, pa_source *source, pa_sink *sink);
+void aec_manager_deinit();
+
+#endif
index 14b2b89..aef1eee 100644 (file)
 #include "device-manager-dbus-priv.h"
 #include "device-manager-db-priv.h"
 
+#ifdef SUPPORT_AEC
+#include "aec-manager.h"
+#endif
+
 #define SHARED_DEVICE_MANAGER "tizen-device-manager"
 
 #define DEVICE_MAP_FILE                    "/etc/pulse/device-map.json"
@@ -2905,6 +2909,8 @@ static pa_hook_result_t device_running_changed_hook_cb(pa_core *c, pa_tz_device_
 
 pa_device_manager* pa_device_manager_get(pa_core *c) {
     pa_device_manager *dm;
+    pa_source *source;
+    pa_sink *sink;
 
     pa_assert(c);
 
@@ -2961,12 +2967,17 @@ pa_device_manager* pa_device_manager_get(pa_core *c) {
     }
 
     /* Just for convenience when test*/
-    if (!_device_manager_set_default_sink(dm,  DEVICE_TYPE_SPEAKER, "normal")) {
+    sink = _device_manager_set_default_sink(dm,  DEVICE_TYPE_SPEAKER, "normal");
+    if (!sink)
         pa_log_warn("Set default sink with speaker(normal) failed");
-    }
-    if (!_device_manager_set_default_source(dm,  DEVICE_TYPE_MIC, "normal")) {
+
+    source = _device_manager_set_default_source(dm,  DEVICE_TYPE_MIC, "normal");
+    if (!source)
         pa_log_warn("Set default source with mic(normal) failed");
-    }
+
+#ifdef SUPPORT_AEC
+    aec_manager_init(dm->core, source, sink);
+#endif
 
     pa_shared_set(c, SHARED_DEVICE_MANAGER, dm);
 
@@ -3044,4 +3055,9 @@ void pa_device_manager_unref(pa_device_manager *dm) {
         pa_shared_remove(dm->core, SHARED_DEVICE_MANAGER);
 
     pa_xfree(dm);
+
+#ifdef SUPPORT_AEC
+    aec_manager_deinit();
+#endif
+
 }
index 03f1e4d..c8f1d35 100644 (file)
 
 #include "hal-interface.h"
 
+#ifdef SUPPORT_AEC
+#include "aec-manager.h"
+#endif
+
 PA_MODULE_AUTHOR("Tizen");
 PA_MODULE_DESCRIPTION("Tizen Audio Sink");
 PA_MODULE_VERSION(PACKAGE_VERSION);
@@ -74,6 +78,13 @@ PA_MODULE_USAGE(
 
 #define DEVICE_NAME_MAX                     30
 
+#ifdef SUPPORT_AEC
+enum {
+    PA_SINK_MESSAGE_SET_AEC_STATE = PA_SINK_MESSAGE_MAX,
+    PA_SINK_MESSAGE_GET_AEC_PARAMS,
+};
+#endif
+
 struct userdata {
     pa_core *core;
     pa_module *module;
@@ -100,6 +111,11 @@ struct userdata {
 
     uint64_t write_count;
     pa_hal_interface *hal_interface;
+
+#ifdef SUPPORT_AEC
+    bool aec_enable;
+    pa_sample_spec ss;
+#endif
 };
 
 static const char* const valid_modargs[] = {
@@ -159,7 +175,7 @@ static int suspend(struct userdata *u) {
         u->rtpoll_item = NULL;
     }
 
-    pa_log_info("Device suspended...[%s,%s]", u->card, u->device);
+    pa_log_info("Device suspended...");
 
     return 0;
 }
@@ -170,6 +186,9 @@ static int unsuspend(struct userdata *u) {
     int32_t ret;
     size_t frame_size;
 
+    char *card = u->card;
+    char *device = u->device;
+
     pa_assert(u);
     pa_assert(!u->pcm_handle);
 
@@ -182,9 +201,16 @@ static int unsuspend(struct userdata *u) {
         goto fail;
     }
 
+#ifdef SUPPORT_AEC
+    if (u->aec_enable) {
+        card = "Loopback";
+        device = "0,0";
+    }
+#endif
+
     ret = pa_hal_interface_pcm_open(u->hal_interface,
-              u->card,
-              u->device,
+              card,
+              device,
               DIRECTION_OUT,
               &sample_spec,
               u->frag_size / frame_size,
@@ -201,7 +227,7 @@ static int unsuspend(struct userdata *u) {
     u->write_count = 0;
     u->first = true;
 
-    pa_log_info("Resumed successfully...");
+    pa_log_info("Resumed successfully...device(%s:%s)", card, device);
 
     return 0;
 
@@ -278,6 +304,36 @@ static int sink_process_msg(
             *((pa_usec_t*)data) = latency;
             return 0;
         }
+#ifdef SUPPORT_AEC
+        case PA_SINK_MESSAGE_SET_AEC_STATE: {
+            pa_sink *s = PA_SINK(o);
+            bool enable = (bool)data;
+
+            if (u->aec_enable == enable)
+                return 0;
+
+            pa_log_info("AEC enable(%d)", enable);
+
+            u->aec_enable = enable;
+            if (s->thread_info.state == PA_SINK_RUNNING) {
+                suspend(u);
+                unsuspend(u);
+            }
+
+            return 0;
+        }
+        case PA_SINK_MESSAGE_GET_AEC_PARAMS: {
+            aec_params_t *params = (aec_params_t *)data;
+            params->rate = u->ss.rate;
+            params->channels = u->ss.channels;
+            params->format = u->ss.format;
+            params->frag_size = u->frag_size;
+            params->nfrags = u->nfrags;
+            snprintf(params->card, DEVICE_NAME_MAX, "%s", u->card);
+            snprintf(params->device, DEVICE_NAME_MAX, "%s", u->device);
+            return 0;
+        }
+#endif
     }
 
     return pa_sink_process_msg(o, code, data, offset, chunk);
@@ -519,6 +575,7 @@ int pa__init(pa_module*m) {
     const char *modarg_device;
     char card[DEVICE_NAME_MAX];
     char device[DEVICE_NAME_MAX];
+    size_t frame_size, buffer_size, period_frames, buffer_frames;
 
     pa_assert(m);
 
@@ -546,6 +603,9 @@ int pa__init(pa_module*m) {
     u->first = true;
     u->hal_interface = pa_hal_interface_get(u->core);
     u->rtpoll = pa_rtpoll_new();
+#ifdef SUPPORT_AEC
+    u->ss = ss;
+#endif
     pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
 
     if (!(modarg_device = pa_modargs_get_value(ma, "device", NULL))) {
@@ -595,6 +655,13 @@ int pa__init(pa_module*m) {
     pa_proplist_sets(data.proplist, "tizen.device", u->device);
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "tizen");
 
+    frame_size = pa_frame_size(&ss);
+    buffer_size = u->frag_size * u->nfrags;
+    buffer_frames = buffer_size / frame_size;
+    period_frames = u->frag_size / frame_size;
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%zu", buffer_frames * frame_size);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%zu", period_frames * frame_size);
+
     if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
         pa_log_error("Invalid properties.");
         pa_sink_new_data_done(&data);
index c0337e0..4ec1330 100644 (file)
 
 #include "hal-interface.h"
 
+#ifdef SUPPORT_AEC
+#include "aec-manager.h"
+#endif
+
 PA_MODULE_AUTHOR("Tizen");
 PA_MODULE_DESCRIPTION("Tizen Audio Source");
 PA_MODULE_VERSION(PACKAGE_VERSION);
@@ -70,6 +74,13 @@ PA_MODULE_USAGE(
 
 #define DEVICE_NAME_MAX                     30
 
+#ifdef SUPPORT_AEC
+enum {
+    PA_SOURCE_MESSAGE_SET_AEC_STATE = PA_SOURCE_MESSAGE_MAX,
+    PA_SOURCE_MESSAGE_GET_AEC_PARAMS,
+};
+#endif
+
 struct userdata {
     pa_core *core;
     pa_module *module;
@@ -95,6 +106,11 @@ struct userdata {
     uint64_t read_count;
     pa_usec_t latency_time;
     pa_hal_interface *hal_interface;
+
+#ifdef SUPPORT_AEC
+    bool aec_enable;
+    pa_sample_spec ss;
+#endif
 };
 
 static const char* const valid_modargs[] = {
@@ -152,7 +168,7 @@ static int suspend(struct userdata *u) {
         u->rtpoll_item = NULL;
     }
 
-    pa_log_info("Device suspended...[%s,%s]", u->card, u->device);
+    pa_log_info("Device suspended...");
 
     return 0;
 }
@@ -163,6 +179,9 @@ static int unsuspend(struct userdata *u) {
     int32_t ret;
     size_t frame_size;
 
+    char *card = u->card;
+    char *device = u->device;
+
     pa_assert(u);
     pa_assert(!u->pcm_handle);
 
@@ -175,9 +194,16 @@ static int unsuspend(struct userdata *u) {
         goto fail;
     }
 
+#ifdef SUPPORT_AEC
+    if (u->aec_enable) {
+        card = "Loopback";
+        device = "1,1";
+    }
+#endif
+
     ret = pa_hal_interface_pcm_open(u->hal_interface,
-              u->card,
-              u->device,
+              card,
+              device,
               DIRECTION_IN,
               &sample_spec,
               u->frag_size / frame_size,
@@ -194,7 +220,7 @@ static int unsuspend(struct userdata *u) {
     u->read_count = 0;
     u->first = true;
 
-    pa_log_info("Resumed successfully...");
+    pa_log_info("Resumed successfully...device(%s:%s)", card, device);
 
     return 0;
 
@@ -265,6 +291,36 @@ static int source_process_msg(
             *((pa_usec_t*)data) = u->timestamp > now ? 0ULL : now - u->timestamp;
             return 0;
         }
+#ifdef SUPPORT_AEC
+        case PA_SOURCE_MESSAGE_SET_AEC_STATE: {
+            pa_source *s = PA_SOURCE(o);
+            bool enable = (bool)data;
+
+            if (u->aec_enable == enable)
+                return 0;
+
+            pa_log_info("AEC enable(%d)", enable);
+
+            u->aec_enable = enable;
+            if (s->thread_info.state == PA_SOURCE_RUNNING) {
+                suspend(u);
+                unsuspend(u);
+            }
+
+            return 0;
+        }
+        case PA_SOURCE_MESSAGE_GET_AEC_PARAMS: {
+            aec_params_t *params = (aec_params_t *)data;
+            params->rate = u->ss.rate;
+            params->channels = u->ss.channels;
+            params->format = u->ss.format;
+            params->frag_size = u->frag_size;
+            params->nfrags = u->nfrags;
+            snprintf(params->card, DEVICE_NAME_MAX, "%s", u->card);
+            snprintf(params->device, DEVICE_NAME_MAX, "%s", u->device);
+            return 0;
+        }
+#endif
     }
 
     return pa_source_process_msg(o, code, data, offset, chunk);
@@ -463,6 +519,7 @@ int pa__init(pa_module*m) {
     const char *modarg_device;
     char card[DEVICE_NAME_MAX];
     char device[DEVICE_NAME_MAX];
+    size_t frame_size, buffer_size, period_frames, buffer_frames;
 
     pa_assert(m);
 
@@ -490,6 +547,9 @@ int pa__init(pa_module*m) {
     u->first = true;
     u->hal_interface = pa_hal_interface_get(u->core);
     u->rtpoll = pa_rtpoll_new();
+#ifdef SUPPORT_AEC
+    u->ss = ss;
+#endif
     pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
 
     if (!(modarg_device = pa_modargs_get_value(ma, "device", NULL))) {
@@ -526,6 +586,13 @@ int pa__init(pa_module*m) {
     pa_proplist_sets(data.proplist, "tizen.device", u->device);
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "tizen");
 
+    frame_size = pa_frame_size(&ss);
+    buffer_size = u->frag_size * u->nfrags;
+    buffer_frames = buffer_size / frame_size;
+    period_frames = u->frag_size / frame_size;
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%zu", buffer_frames * frame_size);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%zu", period_frames * frame_size);
+
     if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
         pa_log_error("Invalid properties.");
         pa_source_new_data_done(&data);