voice: allow rendering actor to be specified as by 'renderer/name'.
[profile/ivi/speech-recognition.git] / src / daemon / voice.c
1 /*
2  * Copyright (c) 2012 - 2013, 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 <unistd.h>
31 #include <errno.h>
32
33 #include <murphy/common/macros.h>
34 #include <murphy/common/mm.h>
35 #include <murphy/common/list.h>
36
37 #include "srs/daemon/context.h"
38 #include "srs/daemon/voice.h"
39
40 typedef struct state_s state_t;
41
42 /*
43  * a speech synthesizer backend
44  */
45
46 typedef struct {
47     int                id;               /* internal backend ID */
48     srs_context_t     *srs;              /* main context */
49     char              *name;             /* engine name */
50     srs_voice_api_t    api;              /* backend API */
51     void              *api_data;         /* opaque engine data */
52     srs_voice_actor_t *actors;           /* backend voice actors */
53     int                nactor;           /* number of actors */
54     mrp_list_hook_t    hook;             /* to list of backends */
55     state_t           *state;            /* our state */
56 } renderer_t;
57
58
59 /*
60  * languages
61  */
62
63 typedef struct {
64     char            *lang;               /* language name */
65     mrp_list_hook_t  hook;               /* to list of languages */
66     mrp_list_hook_t  actors;             /* list of actors */
67     int              nmale;              /* number of male actors */
68     int              nfemale;            /* number of female actors */
69 } language_t;
70
71
72 /*
73  * actors
74  */
75
76 typedef struct {
77     char               *voice;           /* globally unique voice id */
78     renderer_t         *r;               /* rendering backend */
79     int                 id;              /* backend voice id */
80     char               *dialect;         /* language dialect, if any */
81     srs_voice_gender_t  gender;          /* actor gender */
82     int                 age;             /* actor age */
83     char               *description;     /* description */
84     mrp_list_hook_t     hook;            /* to list of languages */
85 } actor_t;
86
87
88 /*
89  * ative and queued rendering requests
90  */
91
92 typedef struct {
93     uint32_t            id;              /* request id */
94     renderer_t         *r;               /* rendering backend */
95     uint32_t            vid;             /* backend id */
96     int                 notify_mask;     /* notification event mask */
97     srs_voice_notify_t  notify;          /* notification callback */
98     void               *notify_data;     /* opaque notification data */
99     mrp_timer_t        *timer;           /* request timeout timer */
100     mrp_list_hook_t     hook;            /* hook to list of requests */
101 } request_t;
102
103
104 typedef struct {
105     request_t           req;             /* request */
106     char               *msg;             /* message to render */
107     char              **tags;            /* stream tags */
108     uint32_t            actor;           /* actor id */
109     double              rate;            /* synthesis rate (1 = normal) */
110     double              pitch;           /* synthesis pitch (1 = default) */
111     int                 timeout;         /* timeout */
112 } queued_t;
113
114
115 /*
116  * speech synthesizer state
117  */
118
119 struct state_s {
120     mrp_list_hook_t  synthesizers;       /* registered synthesizers */
121     int              nsynthesizer;       /* number of synthesizers */
122     mrp_list_hook_t  languages;          /* list of supported languages */
123     uint32_t         nextid;             /* next voice id */
124     mrp_list_hook_t  requests;           /* request queue */
125     request_t       *active;             /* active request */
126     request_t       *cancelling;         /* request being cancelled */
127 };
128
129
130 static request_t *find_request(state_t *state, uint32_t rid, uint32_t vid);
131 static request_t *activate_next(state_t *state);
132
133
134 static language_t *find_language(state_t *state, const char *lang, int create)
135 {
136     mrp_list_hook_t *p, *n;
137     language_t      *l;
138
139     mrp_list_foreach(&state->languages, p, n) {
140         l = mrp_list_entry(p, typeof(*l), hook);
141
142         if (!strcasecmp(l->lang, lang))
143             return l;
144     }
145
146     if (!create)
147         return NULL;
148
149     l = mrp_allocz(sizeof(*l));
150
151     if (l == NULL)
152         return NULL;
153
154     mrp_list_init(&l->hook);
155     mrp_list_init(&l->actors);
156
157     l->lang = mrp_strdup(lang);
158
159     if (l->lang == NULL) {
160         mrp_free(l);
161         return NULL;
162     }
163
164     mrp_list_append(&state->languages, &l->hook);
165
166     return l;
167 }
168
169
170 static int register_actor(renderer_t *r, srs_voice_actor_t *act)
171 {
172     state_t    *state = r->state;
173     language_t *l;
174     actor_t    *a;
175     char        voice[256];
176     const char *g;
177     int        *n;
178
179     l = find_language(state, act->lang, TRUE);
180
181     if (l == NULL)
182         return -1;
183
184     a = mrp_allocz(sizeof(*a));
185
186     if (a == NULL)
187         return -1;
188
189
190     mrp_list_init(&a->hook);
191     a->r           = r;
192     a->id          = act->id;
193     a->dialect     = mrp_strdup(act->dialect);
194     a->gender      = act->gender;
195     a->age         = act->age;
196     a->description = mrp_strdup(act->description);
197
198     if ((act->dialect && !a->dialect) ||
199         (act->description && !a->description)) {
200         mrp_free(a->dialect);
201         mrp_free(a->description);
202         mrp_free(a);
203
204         return -1;
205     }
206
207     if (a->gender == SRS_VOICE_GENDER_MALE) {
208     male:
209         g = "-male";
210         n = &l->nmale;
211     }
212     else if (a->gender == SRS_VOICE_GENDER_FEMALE) {
213         g = "-female";
214         n = &l->nfemale;
215     }
216     else {
217         a->gender = SRS_VOICE_GENDER_MALE;
218         goto male;
219     }
220
221     if (*n > 0)
222         snprintf(voice, sizeof(voice), "%s%s%s%s-%d", l->lang,
223                  a->dialect ? "-" : "", a->dialect ? a->dialect : "", g, *n);
224     else
225         snprintf(voice, sizeof(voice), "%s%s%s%s", l->lang,
226                  a->dialect ? "-" : "", a->dialect ? a->dialect : "", g);
227
228     a->voice = mrp_strdup(voice);
229
230     if (a->voice == NULL) {
231         mrp_free(a->dialect);
232         mrp_free(a->description);
233         mrp_free(a);
234
235         return -1;
236     }
237
238     mrp_list_append(&l->actors, &a->hook);
239     *n += 1;
240
241     mrp_log_info("Registered voice %s/%s.", r->name, voice);
242
243     return 0;
244 }
245
246
247 static void unregister_actors(renderer_t *r)
248 {
249     state_t         *state = r->state;
250     mrp_list_hook_t *lp, *ln;
251     language_t      *l;
252     mrp_list_hook_t *ap, *an;
253     actor_t         *a;
254
255     mrp_list_foreach(&state->languages, lp, ln) {
256         l = mrp_list_entry(lp, typeof(*l), hook);
257
258         mrp_list_foreach(&l->actors, ap, an) {
259             a = mrp_list_entry(ap, typeof(*a), hook);
260
261             if (a->r == r) {
262                 if (a->gender == SRS_VOICE_GENDER_MALE)
263                     l->nmale--;
264                 else
265                     l->nfemale--;
266
267                 mrp_log_info("Unregistering voice %s/%s.", r->name, a->voice);
268
269                 mrp_list_delete(&l->hook);
270
271                 mrp_free(a->voice);
272                 mrp_free(a->dialect);
273                 mrp_free(a->description);
274                 mrp_free(a);
275             }
276         }
277
278         if (mrp_list_empty(&l->actors)) {
279             mrp_list_delete(&l->hook);
280             mrp_free(l);
281         }
282     }
283 }
284
285
286 static actor_t *find_actor(state_t *state, const char *r, const char *v)
287 {
288     mrp_list_hook_t *lp, *ln, *ap, *an;
289     language_t      *l;
290     actor_t         *a;
291
292     mrp_list_foreach(&state->languages, lp, ln) {
293         l = mrp_list_entry(lp, typeof(*l), hook);
294
295         mrp_list_foreach(&l->actors, ap, an) {
296             a = mrp_list_entry(ap, typeof(*a), hook);
297
298             if (!strcmp(a->r->name, r) && !strcmp(a->voice, v))
299                 return a;
300         }
301     }
302
303     return NULL;
304 }
305
306
307 static void free_renderer(renderer_t *r)
308 {
309     if (r != NULL) {
310         mrp_list_delete(&r->hook);
311         unregister_actors(r);
312         mrp_free(r->name);
313     }
314 }
315
316
317 static void notify_request(request_t *req, srs_voice_event_t *event)
318 {
319     renderer_t        *r     = req->r;
320     int                mask  = (1 << event->type);
321     srs_voice_event_t  e;
322
323     if (req->notify != NULL && (mask & req->notify_mask)) {
324         e    = *event;
325         e.id = req->id;
326         req->notify(&e, req->notify_data);
327     }
328 }
329
330
331 static void voice_notify_cb(srs_voice_event_t *event, void *notify_data)
332 {
333     renderer_t        *r     = (renderer_t *)notify_data;
334     state_t           *state = (state_t *)r->state;
335     uint32_t           vid   = event->id;
336     request_t         *req   = find_request(state, -1, vid);
337     int                mask  = (1 << event->type);
338
339     if (req == NULL) {
340         mrp_log_error("Failed to find request for event 0x%x of <%d>.",
341                       event->type, event->id);
342         return;
343     }
344
345     if (event->type == SRS_VOICE_EVENT_STARTED) {
346         mrp_del_timer(req->timer);
347         req->timer = NULL;
348     }
349
350     notify_request(req, event);
351
352     if (mask & SRS_VOICE_MASK_DONE) {
353         mrp_del_timer(req->timer);
354         req->timer = NULL;
355         if (state->cancelling != req) {
356             mrp_list_delete(&req->hook);
357             mrp_free(req);
358         }
359
360         if (state->active == req) {
361             state->active = NULL;
362             activate_next(state);
363         }
364     }
365 }
366
367
368 int srs_register_voice(srs_context_t *srs, const char *name,
369                        srs_voice_api_t *api, void *api_data,
370                        srs_voice_actor_t *actors, int nactor,
371                        srs_voice_notify_t *notify, void **notify_data)
372 {
373     state_t           *state = (state_t *)srs->synthesizer;
374     renderer_t        *r;
375     srs_voice_actor_t *a;
376     int                i;
377
378     if (state == NULL) {
379         srs->synthesizer = state = mrp_allocz(sizeof(*state));
380
381         if (state == NULL)
382             return -1;
383
384         mrp_list_init(&state->synthesizers);
385         mrp_list_init(&state->languages);
386         mrp_list_init(&state->requests);
387         state->nextid = 1;
388     }
389
390     if (api == NULL || name == NULL || actors == NULL || nactor < 1) {
391         errno = EINVAL;
392         return -1;
393     }
394
395     r = mrp_allocz(sizeof(*r));
396
397     if (r == NULL)
398         return -1;
399
400     mrp_list_init(&r->hook);
401     r->id    = state->nsynthesizer++;
402     r->srs   = srs;
403     r->state = state;
404     r->name  = mrp_strdup(name);
405
406     if (r->name == NULL) {
407         free_renderer(r);
408         return -1;
409     }
410
411     r->api      = *api;
412     r->api_data = api_data;
413
414     for (i = 0; i < nactor; i++) {
415         if (register_actor(r, actors + i) != 0)
416             free_renderer(r);
417     }
418
419     mrp_log_info("Registered voice/TTS backend '%s'.", r->name);
420
421     mrp_list_append(&state->synthesizers, &r->hook);
422
423     *notify      = voice_notify_cb;
424     *notify_data = r;
425
426     return 0;
427 }
428
429
430 void srs_unregister_voice(srs_context_t *srs, const char *name)
431 {
432     state_t         *state = (state_t *)srs->synthesizer;
433     renderer_t      *r;
434     mrp_list_hook_t *p, *n;
435
436     if (state != NULL) {
437         mrp_list_foreach(&state->synthesizers, p, n) {
438             r = mrp_list_entry(p, typeof(*r), hook);
439
440             if (!strcmp(r->name, name)) {
441                 mrp_log_info("Unregistering voice/TTS backend '%s'.", name);
442                 free_renderer(r);
443                 return;
444             }
445         }
446     }
447 }
448
449
450 static renderer_t *find_renderer(state_t *state, const char *voice,
451                                  uint32_t *actor)
452 {
453     language_t      *l;
454     actor_t         *a, *fallback;
455     mrp_list_hook_t *ap, *an;
456     char             lang[128], renderer[128], *e;
457     int              n;
458
459     if (state == NULL) {
460         errno = ENOSYS;
461
462         return NULL;
463     }
464
465     if ((e = strchr(voice, '/')) != NULL) {
466         n = e - voice;
467
468         if (n >= sizeof(renderer) - 1)
469             return NULL;
470
471         strncpy(renderer, voice, n);
472         renderer[n] = '\0';
473
474         if ((a = find_actor(state, renderer, e + 1)) != NULL) {
475             *actor = a->id;
476             return a->r;
477         }
478         else
479             return NULL;
480     }
481
482     if ((e = strchr(voice, '-')) == NULL)
483         l = find_language(state, voice, FALSE);
484     else {
485         n = e - voice;
486         if (snprintf(lang, sizeof(lang), "%*.*s", n, n, voice) >= sizeof(lang))
487             l = NULL;
488         else
489             l = find_language(state, lang, FALSE);
490     }
491
492     if (l == NULL)
493         return NULL;
494
495     fallback = NULL;
496     mrp_list_foreach(&l->actors, ap, an) {
497         a = mrp_list_entry(ap, typeof(*a), hook);
498
499         if (!strcmp(a->voice, voice)) {
500             *actor = a->id;
501
502             return a->r;
503         }
504
505         if (fallback == NULL)
506             fallback = a;
507     }
508
509     if (fallback != NULL) {
510         *actor = fallback->id;
511
512         return fallback->r;
513     }
514
515     return NULL;
516 }
517
518
519 #if 0
520 static renderer_t *select_renderer(state_t *state, const char *voice,
521                                    uint32_t *actid)
522 {
523     renderer_t         *r;
524     language_t         *l;
525     srs_voice_actor_t  *a;
526     srs_voice_gender_t  gender;
527     mrp_list_hook_t    *p, *n;
528     renderer_t         *dfltv;
529     uint32_t            dfltid;
530     int                 i;
531
532     if (state == NULL) {
533         errno = ENOSYS;
534         goto notfound;
535     }
536
537     if (!strcmp(actor, SRS_VOICE_FEMALE)) {
538         gender = SRS_VOICE_GENDER_FEMALE;
539         actor  = NULL;
540     }
541     else if (!strcmp(actor, SRS_VOICE_MALE)) {
542         gender = SRS_VOICE_GENDER_MALE;
543         actor  = NULL;
544     }
545     else
546         gender = SRS_VOICE_GENDER_ANY;
547
548     dfltv  = NULL;
549     dfltid = SRS_VOICE_INVALID;
550
551     mrp_list_foreach(&state->synthesizers, p, n) {
552         r = mrp_list_entry(p, typeof(*r), hook);
553
554         for (i = 0, a = r->actors; i < r->nactor; i++, a++) {
555             if (strcmp(a->lang, lang))
556                 continue;
557
558             if (actor == NULL) {
559                 if (gender == a->gender) {
560                     *actid = a->id;
561                     return r;
562                 }
563             }
564             else {
565                 if (!strcmp(a->name, actor)) {
566                     *actid = a->id;
567                     return r;
568                 }
569
570                 if (dfltid == SRS_VOICE_INVALID) {
571                     dfltv  = r;
572                     dfltid = a->id;
573                 }
574             }
575         }
576     }
577
578  notfound:
579     *actid = dfltid;
580     return dfltv;
581 }
582 #endif
583
584 static void free_tags(char **tags)
585 {
586     int i;
587
588     if (tags == NULL)
589         return;
590
591     for (i = 0; tags[i] != NULL; i++)
592         mrp_free(tags[i]);
593
594     mrp_free(tags);
595 }
596
597
598 static char **copy_tags(char **tags)
599 {
600     char **cp = NULL;
601     int    i;
602
603     if (tags == NULL)
604         return NULL;
605
606     for (i = 0; tags[i] != NULL; i++) {
607         if (!mrp_reallocz(cp, i, i + 1))
608             goto fail;
609         if ((cp[i] = mrp_strdup(tags[i])) == NULL)
610             goto fail;
611     }
612
613     if (mrp_reallocz(cp, i, i + 1))
614         return cp;
615     /* fall through */
616  fail:
617     free_tags(cp);
618     return NULL;
619 }
620
621
622 static void request_timer_cb(mrp_timer_t *t, void *user_data)
623 {
624     queued_t          *qr  = (queued_t *)user_data;
625     request_t         *req = &qr->req;
626     srs_voice_event_t  event;
627
628     mrp_log_info("Voice/TTS request #%u timed out.", qr->req.id);
629
630     mrp_del_timer(req->timer);
631     req->timer = NULL;
632
633     mrp_clear(&event);
634     event.type = SRS_VOICE_EVENT_TIMEOUT;
635     event.id   = req->id;
636
637     notify_request(req, &event);
638
639     mrp_list_delete(&req->hook);
640
641     mrp_free(qr->msg);
642     free_tags(qr->tags);
643
644     mrp_free(qr);
645 }
646
647
648 static request_t *enqueue_request(state_t *state, const char *msg, char **tags,
649                                   renderer_t *r, uint32_t actor, double rate,
650                                   double pitch, int timeout, int notify_mask,
651                                   srs_voice_notify_t notify, void *notify_data)
652 {
653     queued_t *qr;
654
655     qr = mrp_allocz(sizeof(*qr));
656
657     if (qr == NULL)
658         return NULL;
659
660     mrp_list_init(&qr->req.hook);
661
662     qr->req.id          = state->nextid++;
663     qr->req.r           = r;
664     qr->req.vid         = SRS_VOICE_INVALID;
665     qr->req.notify_mask = notify_mask;
666     qr->req.notify      = notify;
667     qr->req.notify_data = notify_data;
668
669     qr->msg     = mrp_strdup(msg);
670     qr->tags    = copy_tags(tags);
671     qr->actor   = actor;
672     qr->timeout = timeout;
673
674     if (qr->msg != NULL && (qr->tags != NULL || tags == NULL)) {
675         mrp_list_append(&state->requests, &qr->req.hook);
676
677         if (timeout > 0)
678             qr->req.timer = mrp_add_timer(r->srs->ml, timeout,
679                                           request_timer_cb, qr);
680
681         return &qr->req;
682     }
683     else {
684         mrp_free(qr->msg);
685         free_tags(qr->tags);
686         mrp_free(qr);
687
688         return NULL;
689     }
690 }
691
692
693 static request_t *activate_next(state_t *state)
694 {
695     queued_t        *qr;
696     renderer_t      *r;
697     mrp_list_hook_t *p, *n;
698
699     if (state->active != NULL)
700         return NULL;
701
702     qr = NULL;
703     mrp_list_foreach(&state->requests, p, n) {
704         mrp_list_delete(p);
705         qr = mrp_list_entry(p, typeof(*qr), req.hook);
706         break;
707     }
708
709     if (qr == NULL)
710         return NULL;
711
712     mrp_del_timer(qr->req.timer);
713     qr->req.timer = NULL;
714
715     r = qr->req.r;
716     qr->req.vid = r->api.render(qr->msg, qr->tags, qr->actor, qr->rate,
717                                 qr->pitch, qr->req.notify_mask, r->api_data);
718
719     mrp_free(qr->msg);
720     qr->msg = NULL;
721     free_tags(qr->tags);
722     qr->tags = NULL;
723
724     if (qr->req.vid == SRS_VOICE_INVALID) {
725         if (qr->req.notify != NULL &&
726             (qr->req.notify_mask & 1 << SRS_VOICE_EVENT_ABORTED)) {
727             srs_voice_event_t e;
728
729             mrp_clear(&e);
730             e.type = SRS_VOICE_EVENT_ABORTED;
731             e.id   = qr->req.id;
732
733             voice_notify_cb(&e, qr->req.notify_data);
734
735             free(qr);
736         }
737
738         return NULL;
739     }
740     else {
741         state->active = &qr->req;
742
743         return &qr->req;
744     }
745 }
746
747
748 request_t *render_request(state_t *state, const char *msg, char **tags,
749                           renderer_t *r, uint32_t actor, double rate,
750                           double pitch, int timeout, int notify_mask,
751                           srs_voice_notify_t notify, void *notify_data)
752 {
753     request_t *req;
754
755     req = mrp_allocz(sizeof(*req));
756
757     if (req == NULL)
758         return NULL;
759
760     mrp_list_init(&req->hook);
761     req->id  = state->nextid++;
762     req->r   = r;
763     req->vid = r->api.render(msg, tags, actor, rate, pitch,
764                              notify_mask, r->api_data);
765
766     if (req->vid == SRS_VOICE_INVALID) {
767         mrp_free(req);
768         return NULL;
769     }
770
771     req->notify      = notify;
772     req->notify_mask = notify_mask;
773     req->notify_data = notify_data;
774
775     state->active = req;
776
777     return req;
778 }
779
780
781 uint32_t srs_render_voice(srs_context_t *srs, const char *msg,
782                           char **tags, const char *voice, double rate,
783                           double pitch, int timeout, int notify_mask,
784                           srs_voice_notify_t notify, void *user_data)
785 {
786     state_t    *state = (state_t *)srs->synthesizer;
787     renderer_t *r;
788     request_t  *req;
789     uint32_t    actid;
790
791     if (state == NULL) {
792         errno = ENOSYS;
793
794         return SRS_VOICE_INVALID;
795     }
796
797     r = find_renderer(state, voice, &actid);
798
799     if (r == NULL) {
800         errno = EINVAL;
801
802         return SRS_VOICE_INVALID;
803     }
804
805     if (state->active == NULL)
806         req = render_request(state, msg, tags, r, actid, rate, pitch, timeout,
807                              notify_mask, notify, user_data);
808     else {
809         if (timeout == SRS_VOICE_IMMEDIATE) {
810             errno = EBUSY;
811             req   = NULL;
812         }
813         else
814             req = enqueue_request(state, msg, tags, r, actid, rate, pitch,
815                                   timeout, notify_mask, notify, user_data);
816     }
817
818     if (req != NULL)
819         return req->id;
820     else
821         return SRS_VOICE_INVALID;
822 }
823
824
825 static request_t *find_request(state_t *state, uint32_t rid, uint32_t vid)
826 {
827     mrp_list_hook_t *p, *n;
828     request_t       *req;
829
830     if (state == NULL) {
831         errno = ENOSYS;
832         goto error;
833     }
834
835     if ((req = state->active) != NULL) {
836         if ((rid == -1 || req->id == rid) && (vid == -1 || req->vid == vid))
837             return req;
838     }
839
840     mrp_list_foreach(&state->requests, p, n) {
841         req = mrp_list_entry(p, typeof(*req), hook);
842
843         if ((rid == -1 || req->id == rid) && (vid == -1 || req->vid == vid))
844             return req;
845     }
846
847     errno = EINVAL;
848
849  error:
850     return NULL;
851 }
852
853
854 void srs_cancel_voice(srs_context_t *srs, uint32_t rid, int notify)
855 {
856     state_t    *state = (state_t *)srs->synthesizer;
857     request_t  *req   = find_request(state, rid, -1);
858     renderer_t *voice = req ? req->r : NULL;
859
860     if (req == NULL)
861         return;
862
863     mrp_del_timer(req->timer);
864     req->timer = NULL;
865     state->cancelling = req;
866
867     voice->api.cancel(req->vid, voice->api_data);
868
869     mrp_list_delete(&req->hook);
870     mrp_free(req);
871
872     if (state->active == req) {
873         state->active = NULL;
874         activate_next(state);
875     }
876 }
877
878
879 int srs_query_voices(srs_context_t *srs, const char *language,
880                      srs_voice_actor_t **actorsp)
881 {
882     state_t            *state = (state_t *)srs->synthesizer;
883     srs_voice_actor_t  *actors, *actor;
884     int                 nactor, i;
885     language_t         *l;
886     mrp_list_hook_t    *lp, *ln;
887     actor_t            *a;
888     mrp_list_hook_t    *ap, *an;
889
890     if (state == NULL) {
891         *actorsp = NULL;
892
893         return 0;
894     }
895
896     actors = NULL;
897     nactor = 0;
898
899     mrp_list_foreach(&state->languages, lp, ln) {
900         l = mrp_list_entry(lp, typeof(*l), hook);
901
902         if (language != NULL && strcasecmp(l->lang, language))
903             continue;
904
905         mrp_list_foreach(&l->actors, ap, an) {
906             a = mrp_list_entry(ap, typeof(*a), hook);
907
908             if (mrp_reallocz(actors, nactor, nactor + 1) == NULL)
909                 goto fail;
910
911             actor = actors + nactor++;
912
913             actor->name        = mrp_strdup(a->voice);
914             actor->lang        = mrp_strdup(l->lang);
915             actor->dialect     = mrp_strdup(a->dialect);
916             actor->gender      = a->gender;
917             actor->age         = a->age;
918             actor->description = mrp_strdup(a->description);
919
920             if (!actor->name || !actor->lang ||
921                 (!actor->dialect && a->dialect) ||
922                 (!actor->description && a->description))
923                 goto fail;
924         }
925     }
926
927     if (actors != NULL) {
928         if (!mrp_reallocz(actors, nactor, nactor + 1))
929             goto fail;
930     }
931
932     *actorsp = actors;
933
934     return nactor;
935
936  fail:
937     for (i = 0; i < nactor; i++) {
938         mrp_free(actors[i].name);
939         mrp_free(actors[i].lang);
940         mrp_free(actors[i].dialect);
941         mrp_free(actors[i].description);
942     }
943
944     return -1;
945 }
946
947
948 void srs_free_queried_voices(srs_voice_actor_t *actors)
949 {
950     srs_voice_actor_t *a;
951
952     if (actors != NULL) {
953         for (a = actors; a->lang; a++) {
954             mrp_free(a->name);
955             mrp_free(a->lang);
956             mrp_free(a->dialect);
957             mrp_free(a->description);
958         }
959         mrp_free(actors);
960     }
961 }