1c1182824179a85d3fba8531f4054a0da830e353
[profile/ivi/speech-recognition.git] / src / daemon / client.c
1 /*
2  * Copyright (c) 2012, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *   * Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *   * Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *   * Neither the name of Intel Corporation nor the names of its contributors
14  *     may be used to endorse or promote products derived from this software
15  *     without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <murphy/common/mm.h>
31 #include <murphy/common/log.h>
32
33 #include "srs/daemon/resctl.h"
34 #include "srs/daemon/recognizer.h"
35 #include "srs/daemon/client.h"
36
37 typedef struct {
38     mrp_list_hook_t  hook;               /* to list of voice requests */
39     srs_client_t    *c;                  /* client for this request */
40     uint32_t         id;                 /* voice request id */
41     int              notify_events;      /* event mask */
42 } voice_req_t;
43
44
45 static void resource_event(srs_resctl_event_t *e, void *user_data);
46
47 void client_reset_resources(srs_context_t *srs)
48 {
49     mrp_list_hook_t *p, *n;
50     srs_client_t    *c;
51
52     mrp_list_foreach(&srs->clients, p, n) {
53         c = mrp_list_entry(p, typeof(*c), hook);
54         srs_resctl_offline(c->rset);
55     }
56 }
57
58
59 void client_create_resources(srs_context_t *srs)
60 {
61     mrp_list_hook_t   *p, *n;
62     srs_client_t      *c;
63     srs_voice_focus_t  f;
64
65     mrp_list_foreach(&srs->clients, p, n) {
66         c = mrp_list_entry(p, typeof(*c), hook);
67
68         if (c->rset == NULL)
69             c->rset = srs_resctl_create(srs, c->appclass, resource_event, c);
70         else
71             srs_resctl_online(srs, c->rset);
72
73         if (c->rset != NULL) {
74             f = c->requested;
75             c->requested = SRS_VOICE_FOCUS_NONE;
76             client_request_focus(c, f);
77         }
78     }
79 }
80
81
82 static void free_commands(srs_command_t *cmds, int ncmd)
83 {
84     srs_command_t *c;
85     int            i, j;
86
87     if (cmds != NULL) {
88         for (i = 0, c = cmds; i < ncmd; i++, c++) {
89             if (c->tokens != NULL) {
90                 for (j = 0; j < c->ntoken; j++)
91                     mrp_free(c->tokens[j]);
92
93                 mrp_free(c->tokens);
94             }
95         }
96
97         mrp_free(cmds);
98     }
99 }
100
101
102 static int parse_command(srs_command_t *cmd, char *command)
103 {
104     char **tokens, *p, *b, *e;
105     int    ntoken, len, osize, nsize, i;
106
107     tokens = NULL;
108     ntoken = 0;
109
110     p = command;
111
112     while (*p) {
113         b = p;
114         while (*b == ' ' || *b == '\t')
115             b++;
116
117         e = b;
118         while (*e != ' ' && *e != '\t' && *e != '\0')
119             e++;
120
121         len   = e - b;
122         osize = sizeof(*tokens) *  ntoken;
123         nsize = sizeof(*tokens) * (ntoken + 1);
124
125         if (mrp_reallocz(tokens, osize, nsize) == NULL)
126             goto fail;
127
128         tokens[ntoken] = mrp_datadup(b, len + 1);
129
130         if (tokens[ntoken] == NULL)
131             goto fail;
132
133         tokens[ntoken][len] = '\0';
134         ntoken++;
135
136         p = e;
137     }
138
139     cmd->tokens = tokens;
140     cmd->ntoken = ntoken;
141
142     return TRUE;
143
144  fail:
145     for (i = 0; i < ntoken; i++)
146         mrp_free(tokens[i]);
147     mrp_free(tokens);
148
149     return FALSE;
150 }
151
152
153 static srs_command_t *parse_commands(char **commands, int ncommand)
154 {
155     srs_command_t *cmds;
156     int            i;
157
158     cmds = mrp_allocz_array(typeof(*cmds), ncommand);
159
160     if (cmds != NULL) {
161         for (i = 0; i < ncommand; i++)
162             if (!parse_command(cmds + i, commands[i]))
163                 goto fail;
164     }
165
166     return cmds;
167
168  fail:
169     free_commands(cmds, ncommand);
170     return NULL;
171 }
172
173
174 srs_client_t *client_create(srs_context_t *srs, srs_client_type_t type,
175                             const char *name, const char *appclass,
176                             char **commands, int ncommand, const char *id,
177                             srs_client_ops_t *ops, void *user_data)
178 {
179     srs_client_t *c;
180
181     c = mrp_allocz(sizeof(*c));
182
183     if (c == NULL)
184         return NULL;
185
186     mrp_list_init(&c->hook);
187     mrp_list_init(&c->voices);
188     c->srs       = srs;
189     c->type      = type;
190     c->ops       = *ops;
191     c->user_data = user_data;
192     c->name      = mrp_strdup(name);
193     c->appclass  = mrp_strdup(appclass);
194     c->id        = mrp_strdup(id);
195
196     if (c->name == NULL || c->appclass == NULL || c->id == NULL) {
197         client_destroy(c);
198         return NULL;
199     }
200
201     c->commands = parse_commands(commands, ncommand);
202     c->ncommand = ncommand;
203
204     if (c->commands == NULL || srs_srec_add_client(srs, c) != 0) {
205         client_destroy(c);
206         return NULL;
207     }
208
209     c->rset = srs_resctl_create(srs, c->appclass, resource_event, c);
210
211     mrp_list_append(&srs->clients, &c->hook);
212
213     mrp_log_info("created client %s (%s:%s)", c->id, c->appclass, c->name);
214
215     return c;
216 }
217
218
219 static void purge_voice_requests(srs_client_t *c)
220 {
221     mrp_list_hook_t *p, *n;
222     voice_req_t     *req;
223
224     mrp_list_foreach(&c->voices, p, n) {
225         req = mrp_list_entry(p, typeof(*req), hook);
226         client_cancel_voice(c, req->id);
227     }
228 }
229
230
231 void client_destroy(srs_client_t *c)
232 {
233     if (c != NULL) {
234         mrp_log_info("destroying client %s (%s:%s)", c->id,
235                      c->appclass, c->name);
236
237         srs_srec_del_client(c->srs, c);
238         srs_resctl_destroy(c->rset);
239         c->rset = NULL;
240
241         mrp_list_delete(&c->hook);
242
243         mrp_free(c->name);
244         mrp_free(c->appclass);
245         mrp_free(c->id);
246
247         free_commands(c->commands, c->ncommand);
248         purge_voice_requests(c);
249     }
250 }
251
252
253 srs_client_t *client_lookup_by_id(srs_context_t *srs, const char *id)
254 {
255     srs_client_t    *c;
256     mrp_list_hook_t *p, *n;
257
258     /* for now we just do a linear search... */
259
260     mrp_list_foreach(&srs->clients, p, n) {
261         c = mrp_list_entry(p, typeof(*c), hook);
262
263         if (!strcmp(c->id, id))
264             return c;
265     }
266
267     return NULL;
268 }
269
270
271 static const char *focus_string(srs_voice_focus_t focus)
272 {
273     switch (focus) {
274     case SRS_VOICE_FOCUS_NONE:      return "none";
275     case SRS_VOICE_FOCUS_SHARED:    return "shared";
276     case SRS_VOICE_FOCUS_EXCLUSIVE: return "exclusive";
277     default:                        return "<invalid>";
278     }
279 }
280
281
282 int client_request_focus(srs_client_t *c, srs_voice_focus_t focus)
283 {
284     mrp_debug("client %s requested %s focus", c->id, focus_string(focus));
285
286     if (c->requested != focus) {
287         c->requested = focus;
288
289         if (c->requested != SRS_VOICE_FOCUS_NONE) {
290             c->enabled = TRUE;
291             c->shared  = focus == SRS_VOICE_FOCUS_SHARED;
292             return srs_resctl_acquire(c->rset, c->shared);
293         }
294         else
295             return srs_resctl_release(c->rset);
296     }
297     else
298         mrp_debug("client %s has already the requested %s focus", c->id,
299                   focus_string(focus));
300
301     return TRUE;
302 }
303
304
305 static void notify_focus(srs_client_t *c, int granted)
306 {
307     srs_voice_focus_t focus;
308
309     if (!c->enabled)
310         return;
311
312     if (granted & SRS_RESCTL_MASK_SREC) {
313         if (c->shared)
314             focus = SRS_VOICE_FOCUS_SHARED;
315         else
316             focus = SRS_VOICE_FOCUS_EXCLUSIVE;
317     }
318     else
319         focus = SRS_VOICE_FOCUS_NONE;
320
321     mrp_log_info("Client %s has %s %svoice focus.", c->id,
322                  focus ? "gained" : "lost",
323                  focus ? (c->shared ? "shared " : "exclusive ") : "");
324
325     c->ops.notify_focus(c, focus);
326
327     c->granted = granted;
328 }
329
330
331 static void resource_event(srs_resctl_event_t *e, void *user_data)
332 {
333     srs_client_t *c = (srs_client_t *)user_data;
334
335     if (e->type != SRS_RESCTL_EVENT_RESOURCE)
336         return;
337
338     notify_focus(c, e->resource.granted);
339
340     c->granted = e->resource.granted;
341 }
342
343
344 void client_notify_command(srs_client_t *c, int index,
345                            int ntoken, const char **tokens,
346                            uint32_t *start, uint32_t *end,
347                            srs_audiobuf_t *audio)
348 {
349     if (!c->enabled)
350         return;
351
352     if (c->rset != NULL && !(c->granted & SRS_RESCTL_MASK_SREC))
353         return;
354
355     if (0 <= index && index < c->ncommand) {
356         c->ops.notify_command(c, index, ntoken, (char **)tokens,
357                               start, end, audio);
358     }
359 }
360
361
362 static void client_voice_event(srs_voice_event_t *event, void *notify_data)
363 {
364     voice_req_t *req  = (void *)notify_data;
365     int          mask = (1 << event->type);
366     int          done;
367
368     if (mask & SRS_VOICE_MASK_DONE) {
369         mrp_list_delete(&req->hook);
370         done = TRUE;
371     }
372     else
373         done = FALSE;
374
375     if (req->notify_events & mask)
376         req->c->ops.notify_render(req->c, event);
377
378     if (done)
379         mrp_free(req);
380 }
381
382
383 uint32_t client_render_voice(srs_client_t *c, const char *msg,
384                              const char *voice, double rate, double pitch,
385                              int timeout, int notify_events)
386 {
387     srs_context_t *srs    = c->srs;
388     const char    *tags[] = { "media.role=speech", NULL };
389     int            forced = SRS_VOICE_MASK_DONE;
390     voice_req_t   *req;
391
392     if ((req = mrp_allocz(sizeof(*req))) == NULL)
393         return SRS_VOICE_INVALID;
394
395     mrp_list_init(&req->hook);
396     req->c             = c;
397     req->notify_events = notify_events;
398
399     if (rate == 0)
400         rate = 1;
401     if (pitch == 0)
402         pitch = 1;
403
404     req->id = srs_render_voice(srs, msg, (char **)tags, voice, rate, pitch,
405                                timeout, notify_events | forced,
406                                client_voice_event, req);
407
408     if (req->id != SRS_VOICE_INVALID) {
409         mrp_list_append(&c->voices, &req->hook);
410
411         return req->id;
412     }
413     else {
414         mrp_free(req);
415
416         return SRS_VOICE_INVALID;
417     }
418 }
419
420
421 void client_cancel_voice(srs_client_t *c, uint32_t id)
422 {
423     srs_context_t   *srs = c->srs;
424     mrp_list_hook_t *p, *n;
425     voice_req_t     *req;
426
427     mrp_list_foreach(&c->voices, p, n) {
428         req = mrp_list_entry(p, typeof(*req), hook);
429
430         if (req->id == id) {
431             srs_cancel_voice(srs, id, FALSE);
432
433             mrp_list_delete(&req->hook);
434             mrp_free(req);
435
436             return;
437         }
438     }
439 }
440
441
442 int client_query_voices(srs_client_t *c, const char *language,
443                         srs_voice_actor_t **actorsp)
444 {
445     srs_context_t *srs  = c->srs;
446     const char    *lang = language && *language ? language : NULL;
447
448     return srs_query_voices(c->srs, lang, actorsp);
449 }
450
451
452 void client_free_queried_voices(srs_voice_actor_t *actors)
453 {
454     srs_free_queried_voices(actors);
455 }