aec-manager: Add audio AEC manager
[platform/core/multimedia/pulseaudio-modules-tizen.git] / src / aec-manager.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2021 Jaechul Lee <jcsing.lee@samsung.com>
5
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published
8   by the Free Software Foundation; either version 2.1 of the License,
9   or (at your option) any later version.
10
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with PulseAudio; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <pulsecore/sink.h>
27 #include <pulsecore/source.h>
28 #include <pulsecore/core-util.h>
29 #include <pulsecore/core-error.h>
30 #include <pulsecore/macro.h>
31
32 #include <pulsecore/dbus-shared.h>
33 #include <pulsecore/protocol-dbus.h>
34 #include <pulsecore/dbus-util.h>
35
36 #include "aec-manager.h"
37
38 #define AEC_MANAGER_OBJECT_PATH                         "/org/pulseaudio/AecManager"
39 #define AEC_MANAGER_INTERFACE                           "org.pulseaudio.AecManager"
40
41 #define AEC_MANAGER_METHOD_NAME_ON                      "On"
42 #define AEC_MANAGER_METHOD_NAME_OFF                     "Off"
43 #define AEC_MANAGER_METHOD_NAME_GET_SINK_PARAMS         "GetSinkParam"
44 #define AEC_MANAGER_METHOD_NAME_GET_SOURCE_PARAMS       "GetSourceParam"
45
46 #define AEC_UNIX_SOCKET_PATH "/tmp/.aec.socket"
47
48 static struct aec_manager {
49     pa_dbus_connection *dbus_conn;
50     pa_source *source; /* builtin-mic */
51     pa_sink *sink; /* builtin-spk */
52 } am;
53
54 enum {
55     SINK_MESSAGE_SET_AEC_STATE = PA_SINK_MESSAGE_MAX,
56     SINK_MESSAGE_GET_AEC_PARAMS,
57 };
58
59 enum {
60     SOURCE_MESSAGE_SET_AEC_STATE = PA_SOURCE_MESSAGE_MAX,
61     SOURCE_MESSAGE_GET_AEC_PARAMS,
62 };
63
64 #define AEC_MANAGER_INTROSPECT_XML                                      \
65     DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
66     "<node>"                                                            \
67     " <interface name=\"AEC_MANAGER_INTERFACE\">"                       \
68     "  <method name=\"AEC_MANAGER_METHOD_NAME_ON\">"                    \
69     "  </method>"                                                       \
70     "  <method name=\"AEC_MANAGER_METHOD_NAME_OFF\">"                   \
71     "  </method>"                                                       \
72     "  <method name=\"AEC_MANAGER_METHOD_NAME_GET_SINK_PARAMS\">"       \
73     "   <arg name=\"rate\" direction=\"out\" type=\"i\"/>"              \
74     "   <arg name=\"channels\" direction=\"out\" type=\"i\"/>"          \
75     "   <arg name=\"format\" direction=\"out\" type=\"i\"/>"            \
76     "   <arg name=\"frag_size\" direction=\"out\" type=\"i\"/>"         \
77     "   <arg name=\"nfrags\" direction=\"out\" type=\"i\"/>"            \
78     "   <arg name=\"card\" direction=\"out\" type=\"s\"/>"              \
79     "   <arg name=\"device\" direction=\"out\" type=\"s\"/>"            \
80     "  </method>"                                                       \
81     "  <method name=\"AEC_MANAGER_METHOD_NAME_GET_SOURCE_PARAMS\">"     \
82     "   <arg name=\"rate\" direction=\"out\" type=\"i\"/>"              \
83     "   <arg name=\"channels\" direction=\"out\" type=\"i\"/>"          \
84     "   <arg name=\"format\" direction=\"out\" type=\"i\"/>"            \
85     "   <arg name=\"frag_size\" direction=\"out\" type=\"i\"/>"         \
86     "   <arg name=\"nfrags\" direction=\"out\" type=\"i\"/>"            \
87     "   <arg name=\"card\" direction=\"out\" type=\"s\"/>"              \
88     "   <arg name=\"device\" direction=\"out\" type=\"s\"/>"            \
89     "  </method>"                                                       \
90     " </interface>"                                                     \
91     " <interface name=\"org.freedesktop.DBus.Introspectable\">"         \
92     "  <method name=\"Introspect\">"                                    \
93     "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"              \
94     "  </method>"                                                       \
95     " </interface>"                                                     \
96     "</node>"
97
98 enum method_handler_index {
99     METHOD_HANDLER_ON,
100     METHOD_HANDLER_OFF,
101     METHOD_HANDLER_GET_SINK_PARAMS,
102     METHOD_HANDLER_GET_SOURCE_PARAMS,
103     METHOD_HANDLER_MAX
104 };
105
106 static void handle_method_on(DBusConnection *conn, DBusMessage *msg, void *userdata);
107 static void handle_method_off(DBusConnection *conn, DBusMessage *msg, void *userdata);
108 static void handle_method_get_sink_params(DBusConnection *conn, DBusMessage *msg, void *userdata);
109 static void handle_method_get_source_params(DBusConnection *conn, DBusMessage *msg, void *userdata);
110
111 static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
112     [METHOD_HANDLER_ON] = {
113         .method_name = AEC_MANAGER_METHOD_NAME_ON,
114         .receive_cb = handle_method_on },
115     [METHOD_HANDLER_OFF] = {
116         .method_name = AEC_MANAGER_METHOD_NAME_OFF,
117         .receive_cb = handle_method_off },
118     [METHOD_HANDLER_GET_SINK_PARAMS] = {
119         .method_name = AEC_MANAGER_METHOD_NAME_GET_SINK_PARAMS,
120         .receive_cb = handle_method_get_sink_params },
121     [METHOD_HANDLER_GET_SOURCE_PARAMS] = {
122         .method_name = AEC_MANAGER_METHOD_NAME_GET_SOURCE_PARAMS,
123         .receive_cb = handle_method_get_source_params },
124 };
125
126 static void send_get_param_reply(DBusConnection *conn, DBusMessage *msg, aec_params_t* param) {
127     DBusMessage *reply = NULL;
128     DBusMessageIter msg_iter;
129
130     const char *ptr1 = param->card;
131     const char *ptr2 = param->device;
132
133     reply = dbus_message_new_method_return(msg);
134     if (!reply) {
135         pa_log_error("Failed to alloc reply");
136         return;
137     }
138
139     dbus_message_iter_init_append(reply, &msg_iter);
140     dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, &param->rate);
141     dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, &param->channels);
142     dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, &param->format);
143     dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, &param->frag_size);
144     dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, &param->nfrags);
145     dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &ptr1);
146     dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &ptr2);
147     dbus_connection_send(conn, reply, NULL);
148
149     dbus_message_unref(reply);
150 }
151
152 static void handle_method_on(DBusConnection *conn, DBusMessage *msg, void *userdata) {
153     pa_asyncmsgq_post(am.sink->asyncmsgq, PA_MSGOBJECT(am.sink),
154                         SINK_MESSAGE_SET_AEC_STATE, (void *)TRUE, 0, NULL, NULL);
155     pa_asyncmsgq_post(am.source->asyncmsgq, PA_MSGOBJECT(am.source),
156                         SOURCE_MESSAGE_SET_AEC_STATE, (void *)TRUE, 0, NULL, NULL);
157
158     pa_dbus_send_empty_reply(conn, msg);
159 }
160
161 static void handle_method_off(DBusConnection *conn, DBusMessage *msg, void *userdata) {
162     pa_asyncmsgq_post(am.sink->asyncmsgq, PA_MSGOBJECT(am.sink),
163                         SINK_MESSAGE_SET_AEC_STATE, (void *)FALSE, 0, NULL, NULL);
164     pa_asyncmsgq_post(am.source->asyncmsgq, PA_MSGOBJECT(am.source),
165                         SOURCE_MESSAGE_SET_AEC_STATE, (void *)FALSE, 0, NULL, NULL);
166
167     pa_dbus_send_empty_reply(conn, msg);
168 }
169
170 static void handle_method_get_sink_params(DBusConnection *conn, DBusMessage *msg, void *userdata) {
171     aec_params_t param;
172
173     pa_asyncmsgq_send(am.sink->asyncmsgq, PA_MSGOBJECT(am.sink),
174                         SINK_MESSAGE_GET_AEC_PARAMS, &param, 0, NULL);
175
176     send_get_param_reply(conn, msg, &param);
177 }
178
179 static void handle_method_get_source_params(DBusConnection *conn, DBusMessage *msg, void *userdata) {
180     aec_params_t param;
181
182     pa_asyncmsgq_send(am.source->asyncmsgq, PA_MSGOBJECT(am.source),
183                         SOURCE_MESSAGE_GET_AEC_PARAMS, &param, 0, NULL);
184
185     send_get_param_reply(conn, msg, &param);
186 }
187
188 static DBusHandlerResult method_handler_for_vt(DBusConnection *conn, DBusMessage *m, void *userdata) {
189     const char *path, *interface, *member;
190     DBusMessage *r = NULL;
191
192     pa_assert(conn);
193     pa_assert(m);
194
195     path = dbus_message_get_path(m);
196     interface = dbus_message_get_interface(m);
197     member = dbus_message_get_member(m);
198
199     pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
200
201     if (!pa_safe_streq(path, AEC_MANAGER_OBJECT_PATH))
202         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
203
204     if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
205         r = dbus_message_new_method_return(m);
206         if (r) {
207             const char *xml = AEC_MANAGER_INTROSPECT_XML;
208             dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID);
209             dbus_connection_send((conn), r, NULL);
210             dbus_message_unref(r);
211         }
212     } else {
213         int i;
214         for (i = 0; i < METHOD_HANDLER_MAX; i++) {
215             if (dbus_message_is_method_call(m, AEC_MANAGER_INTERFACE,
216                                             method_handlers[i].method_name))
217                 method_handlers[i].receive_cb(conn, m, NULL);
218         }
219     }
220
221     return DBUS_HANDLER_RESULT_HANDLED;
222 }
223
224 int aec_manager_init(pa_core *core, pa_source *source, pa_sink *sink) {
225     DBusError err;
226     pa_dbus_connection *conn = NULL;
227     static const DBusObjectPathVTable vtable = {
228         .message_function = method_handler_for_vt,
229     };
230
231     if (!source || !sink) {
232         pa_log_error("AEC init failed. source(%p)/sink(%p) is null.", source, sink);
233         return -1;
234     }
235
236     dbus_error_init(&err);
237     if (!(conn = pa_dbus_bus_get(core, DBUS_BUS_SYSTEM, &err)) || dbus_error_is_set(&err)) {
238         if (conn)
239             pa_dbus_connection_unref(conn);
240
241         dbus_error_free(&err);
242         pa_log_error("Unable to contact D-Bus system bus: %s: %s", err.name, err.message);
243         return -1;
244     }
245
246     am.dbus_conn = conn;
247     if (!dbus_connection_register_object_path(pa_dbus_connection_get(conn),
248                                                 AEC_MANAGER_OBJECT_PATH, &vtable, NULL)) {
249         pa_dbus_connection_unref(conn);
250         pa_log_error("Failed to register object path");
251         return -1;
252     }
253
254     am.source = source;
255     am.sink = sink;
256
257     pa_log_info("AEC init success");
258
259     return 0;
260 }
261
262 void aec_manager_deinit(void) {
263     if (am.dbus_conn)
264         pa_dbus_connection_unref(am.dbus_conn);
265
266     pa_log_info("AEC deinit success");
267 }