packaging: bumped version, updated changelog.
[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     if (commands != NULL) {
202         c->commands = parse_commands(commands, ncommand);
203         c->ncommand = ncommand;
204     }
205
206     if ((c->commands == NULL && ncommand) || srs_srec_add_client(srs, c) != 0) {
207         client_destroy(c);
208         return NULL;
209     }
210
211     c->rset = srs_resctl_create(srs, c->appclass, resource_event, c);
212
213     mrp_list_append(&srs->clients, &c->hook);
214
215     mrp_log_info("created client %s (%s:%s)", c->id, c->appclass, c->name);
216
217     return c;
218 }
219
220
221 static void purge_voice_requests(srs_client_t *c)
222 {
223     mrp_list_hook_t *p, *n;
224     voice_req_t     *req;
225
226     mrp_list_foreach(&c->voices, p, n) {
227         req = mrp_list_entry(p, typeof(*req), hook);
228         client_cancel_voice(c, req->id);
229     }
230 }
231
232
233 void client_destroy(srs_client_t *c)
234 {
235     if (c != NULL) {
236         mrp_log_info("destroying client %s (%s:%s)", c->id,
237                      c->appclass, c->name);
238
239         srs_srec_del_client(c->srs, c);
240         srs_resctl_destroy(c->rset);
241         c->rset = NULL;
242
243         mrp_list_delete(&c->hook);
244
245         mrp_free(c->name);
246         mrp_free(c->appclass);
247         mrp_free(c->id);
248
249         free_commands(c->commands, c->ncommand);
250         purge_voice_requests(c);
251     }
252 }
253
254
255 srs_client_t *client_lookup_by_id(srs_context_t *srs, const char *id)
256 {
257     srs_client_t    *c;
258     mrp_list_hook_t *p, *n;
259
260     /* for now we just do a linear search... */
261
262     mrp_list_foreach(&srs->clients, p, n) {
263         c = mrp_list_entry(p, typeof(*c), hook);
264
265         if (!strcmp(c->id, id))
266             return c;
267     }
268
269     return NULL;
270 }
271
272
273 static const char *focus_string(srs_voice_focus_t focus)
274 {
275     switch (focus) {
276     case SRS_VOICE_FOCUS_NONE:      return "none";
277     case SRS_VOICE_FOCUS_SHARED:    return "shared";
278     case SRS_VOICE_FOCUS_EXCLUSIVE: return "exclusive";
279     default:                        return "<invalid>";
280     }
281 }
282
283
284 int client_request_focus(srs_client_t *c, srs_voice_focus_t focus)
285 {
286     mrp_debug("client %s requested %s focus", c->id, focus_string(focus));
287
288     if (c->requested != focus) {
289         c->requested = focus;
290
291         if (c->requested != SRS_VOICE_FOCUS_NONE) {
292             c->enabled = TRUE;
293             c->shared  = focus == SRS_VOICE_FOCUS_SHARED;
294             return srs_resctl_acquire(c->rset, c->shared);
295         }
296         else
297             return srs_resctl_release(c->rset);
298     }
299     else
300         mrp_debug("client %s has already the requested %s focus", c->id,
301                   focus_string(focus));
302
303     return TRUE;
304 }
305
306
307 static void notify_focus(srs_client_t *c, int granted)
308 {
309     srs_voice_focus_t focus;
310
311     if (!c->enabled)
312         return;
313
314     if (granted & SRS_RESCTL_MASK_SREC) {
315         if (c->shared)
316             focus = SRS_VOICE_FOCUS_SHARED;
317         else
318             focus = SRS_VOICE_FOCUS_EXCLUSIVE;
319     }
320     else
321         focus = SRS_VOICE_FOCUS_NONE;
322
323     mrp_log_info("Client %s has %s %svoice focus.", c->id,
324                  focus ? "gained" : "lost",
325                  focus ? (c->shared ? "shared " : "exclusive ") : "");
326
327     c->ops.notify_focus(c, focus);
328
329     c->granted = granted;
330 }
331
332
333 static void resource_event(srs_resctl_event_t *e, void *user_data)
334 {
335     srs_client_t *c = (srs_client_t *)user_data;
336
337     if (e->type != SRS_RESCTL_EVENT_RESOURCE)
338         return;
339
340     notify_focus(c, e->resource.granted);
341
342     c->granted = e->resource.granted;
343 }
344
345
346 void client_notify_command(srs_client_t *c, int index,
347                            int ntoken, const char **tokens,
348                            uint32_t *start, uint32_t *end,
349                            srs_audiobuf_t *audio)
350 {
351     if (!c->enabled)
352         return;
353
354     if (c->rset != NULL && !(c->granted & SRS_RESCTL_MASK_SREC))
355         return;
356
357     if (0 <= index && index < c->ncommand) {
358         c->ops.notify_command(c, index, ntoken, (char **)tokens,
359                               start, end, audio);
360     }
361 }
362
363
364 static void client_voice_event(srs_voice_event_t *event, void *notify_data)
365 {
366     voice_req_t *req  = (void *)notify_data;
367     int          mask = (1 << event->type);
368     int          done;
369
370     if (mask & SRS_VOICE_MASK_DONE) {
371         mrp_list_delete(&req->hook);
372         done = TRUE;
373     }
374     else
375         done = FALSE;
376
377     if (req->notify_events & mask)
378         req->c->ops.notify_render(req->c, event);
379
380     if (done)
381         mrp_free(req);
382 }
383
384
385 uint32_t client_render_voice(srs_client_t *c, const char *msg,
386                              const char *voice, double rate, double pitch,
387                              int timeout, int notify_events)
388 {
389     srs_context_t *srs    = c->srs;
390     const char    *tags[] = { "media.role=speech", NULL };
391     int            forced = SRS_VOICE_MASK_DONE;
392     voice_req_t   *req;
393
394     if ((req = mrp_allocz(sizeof(*req))) == NULL)
395         return SRS_VOICE_INVALID;
396
397     mrp_list_init(&req->hook);
398     req->c             = c;
399     req->notify_events = notify_events;
400
401     if (rate == 0)
402         rate = 1;
403     if (pitch == 0)
404         pitch = 1;
405
406     req->id = srs_render_voice(srs, msg, (char **)tags, voice, rate, pitch,
407                                timeout, notify_events | forced,
408                                client_voice_event, req);
409
410     if (req->id != SRS_VOICE_INVALID) {
411         mrp_list_append(&c->voices, &req->hook);
412
413         return req->id;
414     }
415     else {
416         mrp_free(req);
417
418         return SRS_VOICE_INVALID;
419     }
420 }
421
422
423 void client_cancel_voice(srs_client_t *c, uint32_t id)
424 {
425     srs_context_t   *srs = c->srs;
426     mrp_list_hook_t *p, *n;
427     voice_req_t     *req;
428
429     mrp_list_foreach(&c->voices, p, n) {
430         req = mrp_list_entry(p, typeof(*req), hook);
431
432         if (req->id == id) {
433             srs_cancel_voice(srs, id, FALSE);
434
435             mrp_list_delete(&req->hook);
436             mrp_free(req);
437
438             return;
439         }
440     }
441 }
442
443
444 int client_query_voices(srs_client_t *c, const char *language,
445                         srs_voice_actor_t **actorsp)
446 {
447     srs_context_t *srs  = c->srs;
448     const char    *lang = language && *language ? language : NULL;
449
450     return srs_query_voices(srs, lang, actorsp);
451 }
452
453
454 void client_free_queried_voices(srs_voice_actor_t *actors)
455 {
456     srs_free_queried_voices(actors);
457 }