2 * Copyright (c) 2012, Intel Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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.
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.
30 #include <murphy/common/mm.h>
31 #include <murphy/common/log.h>
33 #include "srs/daemon/resctl.h"
34 #include "srs/daemon/recognizer.h"
35 #include "srs/daemon/client.h"
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 */
45 static void resource_event(srs_resctl_event_t *e, void *user_data);
47 void client_reset_resources(srs_context_t *srs)
49 mrp_list_hook_t *p, *n;
52 mrp_list_foreach(&srs->clients, p, n) {
53 c = mrp_list_entry(p, typeof(*c), hook);
54 srs_resctl_offline(c->rset);
59 void client_create_resources(srs_context_t *srs)
61 mrp_list_hook_t *p, *n;
65 mrp_list_foreach(&srs->clients, p, n) {
66 c = mrp_list_entry(p, typeof(*c), hook);
69 c->rset = srs_resctl_create(srs, c->appclass, resource_event, c);
71 srs_resctl_online(srs, c->rset);
73 if (c->rset != NULL) {
75 c->requested = SRS_VOICE_FOCUS_NONE;
76 client_request_focus(c, f);
82 static void free_commands(srs_command_t *cmds, int ncmd)
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]);
102 static int parse_command(srs_command_t *cmd, char *command)
104 char **tokens, *p, *b, *e;
105 int ntoken, len, osize, nsize, i;
114 while (*b == ' ' || *b == '\t')
118 while (*e != ' ' && *e != '\t' && *e != '\0')
122 osize = sizeof(*tokens) * ntoken;
123 nsize = sizeof(*tokens) * (ntoken + 1);
125 if (mrp_reallocz(tokens, osize, nsize) == NULL)
128 tokens[ntoken] = mrp_datadup(b, len + 1);
130 if (tokens[ntoken] == NULL)
133 tokens[ntoken][len] = '\0';
139 cmd->tokens = tokens;
140 cmd->ntoken = ntoken;
145 for (i = 0; i < ntoken; i++)
153 static srs_command_t *parse_commands(char **commands, int ncommand)
158 cmds = mrp_allocz_array(typeof(*cmds), ncommand);
161 for (i = 0; i < ncommand; i++)
162 if (!parse_command(cmds + i, commands[i]))
169 free_commands(cmds, ncommand);
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)
181 c = mrp_allocz(sizeof(*c));
186 mrp_list_init(&c->hook);
187 mrp_list_init(&c->voices);
191 c->user_data = user_data;
192 c->name = mrp_strdup(name);
193 c->appclass = mrp_strdup(appclass);
194 c->id = mrp_strdup(id);
196 if (c->name == NULL || c->appclass == NULL || c->id == NULL) {
201 if (commands != NULL) {
202 c->commands = parse_commands(commands, ncommand);
203 c->ncommand = ncommand;
206 if ((c->commands == NULL && ncommand) || srs_srec_add_client(srs, c) != 0) {
211 c->rset = srs_resctl_create(srs, c->appclass, resource_event, c);
213 mrp_list_append(&srs->clients, &c->hook);
215 mrp_log_info("created client %s (%s:%s)", c->id, c->appclass, c->name);
221 static void purge_voice_requests(srs_client_t *c)
223 mrp_list_hook_t *p, *n;
226 mrp_list_foreach(&c->voices, p, n) {
227 req = mrp_list_entry(p, typeof(*req), hook);
228 client_cancel_voice(c, req->id);
233 void client_destroy(srs_client_t *c)
236 mrp_log_info("destroying client %s (%s:%s)", c->id,
237 c->appclass, c->name);
239 srs_srec_del_client(c->srs, c);
240 srs_resctl_destroy(c->rset);
243 mrp_list_delete(&c->hook);
246 mrp_free(c->appclass);
249 free_commands(c->commands, c->ncommand);
250 purge_voice_requests(c);
255 srs_client_t *client_lookup_by_id(srs_context_t *srs, const char *id)
258 mrp_list_hook_t *p, *n;
260 /* for now we just do a linear search... */
262 mrp_list_foreach(&srs->clients, p, n) {
263 c = mrp_list_entry(p, typeof(*c), hook);
265 if (!strcmp(c->id, id))
273 static const char *focus_string(srs_voice_focus_t 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>";
284 int client_request_focus(srs_client_t *c, srs_voice_focus_t focus)
286 mrp_debug("client %s requested %s focus", c->id, focus_string(focus));
288 if (c->requested != focus) {
289 c->requested = focus;
291 if (c->requested != SRS_VOICE_FOCUS_NONE) {
293 c->shared = focus == SRS_VOICE_FOCUS_SHARED;
294 return srs_resctl_acquire(c->rset, c->shared);
297 return srs_resctl_release(c->rset);
300 mrp_debug("client %s has already the requested %s focus", c->id,
301 focus_string(focus));
307 static void notify_focus(srs_client_t *c, int granted)
309 srs_voice_focus_t focus;
314 if (granted & SRS_RESCTL_MASK_SREC) {
316 focus = SRS_VOICE_FOCUS_SHARED;
318 focus = SRS_VOICE_FOCUS_EXCLUSIVE;
321 focus = SRS_VOICE_FOCUS_NONE;
323 mrp_log_info("Client %s has %s %svoice focus.", c->id,
324 focus ? "gained" : "lost",
325 focus ? (c->shared ? "shared " : "exclusive ") : "");
327 c->ops.notify_focus(c, focus);
329 c->granted = granted;
333 static void resource_event(srs_resctl_event_t *e, void *user_data)
335 srs_client_t *c = (srs_client_t *)user_data;
337 if (e->type != SRS_RESCTL_EVENT_RESOURCE)
340 notify_focus(c, e->resource.granted);
342 c->granted = e->resource.granted;
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)
354 if (c->rset != NULL && !(c->granted & SRS_RESCTL_MASK_SREC))
357 if (0 <= index && index < c->ncommand) {
358 c->ops.notify_command(c, index, ntoken, (char **)tokens,
364 static void client_voice_event(srs_voice_event_t *event, void *notify_data)
366 voice_req_t *req = (void *)notify_data;
367 int mask = (1 << event->type);
370 if (mask & SRS_VOICE_MASK_DONE) {
371 mrp_list_delete(&req->hook);
377 if (req->notify_events & mask)
378 req->c->ops.notify_render(req->c, event);
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)
389 srs_context_t *srs = c->srs;
390 const char *tags[] = { "media.role=speech", NULL };
391 int forced = SRS_VOICE_MASK_DONE;
394 if ((req = mrp_allocz(sizeof(*req))) == NULL)
395 return SRS_VOICE_INVALID;
397 mrp_list_init(&req->hook);
399 req->notify_events = notify_events;
406 req->id = srs_render_voice(srs, msg, (char **)tags, voice, rate, pitch,
407 timeout, notify_events | forced,
408 client_voice_event, req);
410 if (req->id != SRS_VOICE_INVALID) {
411 mrp_list_append(&c->voices, &req->hook);
418 return SRS_VOICE_INVALID;
423 void client_cancel_voice(srs_client_t *c, uint32_t id)
425 srs_context_t *srs = c->srs;
426 mrp_list_hook_t *p, *n;
429 mrp_list_foreach(&c->voices, p, n) {
430 req = mrp_list_entry(p, typeof(*req), hook);
433 srs_cancel_voice(srs, id, FALSE);
435 mrp_list_delete(&req->hook);
444 int client_query_voices(srs_client_t *c, const char *language,
445 srs_voice_actor_t **actorsp)
447 srs_context_t *srs = c->srs;
448 const char *lang = language && *language ? language : NULL;
450 return srs_query_voices(srs, lang, actorsp);
454 void client_free_queried_voices(srs_voice_actor_t *actors)
456 srs_free_queried_voices(actors);