From f49985f02d8d1e49a4e2075f3b9f2d651c571770 Mon Sep 17 00:00:00 2001 From: Jaechul Lee Date: Mon, 29 Mar 2021 14:17:04 +0900 Subject: [PATCH] aec-manager: Add audio AEC manager added new feature [Version] 13.0.61 [Issue Type] New feature Change-Id: I401390836ee34ea4444180e9725febd1720ac4a4 Signed-off-by: Jaechul Lee --- Makefile.am | 9 ++ configure.ac | 12 ++ packaging/pulseaudio-modules-tizen.spec | 6 +- src/aec-manager.c | 267 ++++++++++++++++++++++++++++++++ src/aec-manager.h | 38 +++++ src/device-manager.c | 24 ++- src/module-tizenaudio-sink.c | 75 ++++++++- src/module-tizenaudio-source.c | 75 ++++++++- 8 files changed, 492 insertions(+), 14 deletions(-) create mode 100644 src/aec-manager.c create mode 100644 src/aec-manager.h diff --git a/Makefile.am b/Makefile.am index e66660b..495578e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/configure.ac b/configure.ac index ac631fa..d1720a3 100644 --- a/configure.ac +++ b/configure.ac @@ -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], diff --git a/packaging/pulseaudio-modules-tizen.spec b/packaging/pulseaudio-modules-tizen.spec index 3310887..13add09 100644 --- a/packaging/pulseaudio-modules-tizen.spec +++ b/packaging/pulseaudio-modules-tizen.spec @@ -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 index 0000000..8ed96b5 --- /dev/null +++ b/src/aec-manager.c @@ -0,0 +1,267 @@ +/*** + This file is part of PulseAudio. + + Copyright 2021 Jaechul Lee + + 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 +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 \ + "" \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + "" + +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, ¶m->rate); + dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, ¶m->channels); + dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, ¶m->format); + dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, ¶m->frag_size); + dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, ¶m->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, ¶m, 0, NULL); + + send_get_param_reply(conn, msg, ¶m); +} + +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, ¶m, 0, NULL); + + send_get_param_reply(conn, msg, ¶m); +} + +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 index 0000000..d30f788 --- /dev/null +++ b/src/aec-manager.h @@ -0,0 +1,38 @@ +/*** + This file is part of PulseAudio. + + Copyright 2021 Jaechul Lee + + 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 diff --git a/src/device-manager.c b/src/device-manager.c index 14b2b89..aef1eee 100644 --- a/src/device-manager.c +++ b/src/device-manager.c @@ -49,6 +49,10 @@ #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 + } diff --git a/src/module-tizenaudio-sink.c b/src/module-tizenaudio-sink.c index 03f1e4d..c8f1d35 100644 --- a/src/module-tizenaudio-sink.c +++ b/src/module-tizenaudio-sink.c @@ -47,6 +47,10 @@ #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); diff --git a/src/module-tizenaudio-source.c b/src/module-tizenaudio-source.c index c0337e0..4ec1330 100644 --- a/src/module-tizenaudio-source.c +++ b/src/module-tizenaudio-source.c @@ -47,6 +47,10 @@ #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); -- 2.7.4