56225001804c867f2d62c73da0741205a657bc94
[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 void free_renderer(renderer_t *r)
287 {
288     if (r != NULL) {
289         mrp_list_delete(&r->hook);
290         unregister_actors(r);
291         mrp_free(r->name);
292     }
293 }
294
295
296 static void notify_request(request_t *req, srs_voice_event_t *event)
297 {
298     renderer_t        *r     = req->r;
299     int                mask  = (1 << event->type);
300     srs_voice_event_t  e;
301
302     if (req->notify != NULL && (mask & req->notify_mask)) {
303         e    = *event;
304         e.id = req->id;
305         req->notify(&e, req->notify_data);
306     }
307 }
308
309
310 static void voice_notify_cb(srs_voice_event_t *event, void *notify_data)
311 {
312     renderer_t        *r     = (renderer_t *)notify_data;
313     state_t           *state = (state_t *)r->state;
314     uint32_t           vid   = event->id;
315     request_t         *req   = find_request(state, -1, vid);
316     int                mask  = (1 << event->type);
317
318     if (req == NULL) {
319         mrp_log_error("Failed to find request for event 0x%x of <%d>.",
320                       event->type, event->id);
321         return;
322     }
323
324     if (event->type == SRS_VOICE_EVENT_STARTED) {
325         mrp_del_timer(req->timer);
326         req->timer = NULL;
327     }
328
329     notify_request(req, event);
330
331     if (mask & SRS_VOICE_MASK_DONE) {
332         mrp_del_timer(req->timer);
333         req->timer = NULL;
334         if (state->cancelling != req) {
335             mrp_list_delete(&req->hook);
336             mrp_free(req);
337         }
338
339         if (state->active == req) {
340             state->active = NULL;
341             activate_next(state);
342         }
343     }
344 }
345
346
347 int srs_register_voice(srs_context_t *srs, const char *name,
348                        srs_voice_api_t *api, void *api_data,
349                        srs_voice_actor_t *actors, int nactor,
350                        srs_voice_notify_t *notify, void **notify_data)
351 {
352     state_t           *state = (state_t *)srs->synthesizer;
353     renderer_t        *r;
354     srs_voice_actor_t *a;
355     int                i;
356
357     if (state == NULL) {
358         srs->synthesizer = state = mrp_allocz(sizeof(*state));
359
360         if (state == NULL)
361             return -1;
362
363         mrp_list_init(&state->synthesizers);
364         mrp_list_init(&state->languages);
365         mrp_list_init(&state->requests);
366         state->nextid = 1;
367     }
368
369     if (api == NULL || name == NULL || actors == NULL || nactor < 1) {
370         errno = EINVAL;
371         return -1;
372     }
373
374     r = mrp_allocz(sizeof(*r));
375
376     if (r == NULL)
377         return -1;
378
379     mrp_list_init(&r->hook);
380     r->id    = state->nsynthesizer++;
381     r->srs   = srs;
382     r->state = state;
383     r->name  = mrp_strdup(name);
384
385     if (r->name == NULL) {
386         free_renderer(r);
387         return -1;
388     }
389
390     r->api      = *api;
391     r->api_data = api_data;
392
393     for (i = 0; i < nactor; i++) {
394         if (register_actor(r, actors + i) != 0)
395             free_renderer(r);
396     }
397
398     mrp_log_info("Registered voice/TTS backend '%s'.", r->name);
399
400     mrp_list_append(&state->synthesizers, &r->hook);
401
402     *notify      = voice_notify_cb;
403     *notify_data = r;
404
405     return 0;
406 }
407
408
409 void srs_unregister_voice(srs_context_t *srs, const char *name)
410 {
411     state_t         *state = (state_t *)srs->synthesizer;
412     renderer_t      *r;
413     mrp_list_hook_t *p, *n;
414
415     if (state != NULL) {
416         mrp_list_foreach(&state->synthesizers, p, n) {
417             r = mrp_list_entry(p, typeof(*r), hook);
418
419             if (!strcmp(r->name, name)) {
420                 mrp_log_info("Unregistering voice/TTS backend '%s'.", name);
421                 free_renderer(r);
422                 return;
423             }
424         }
425     }
426 }
427
428
429 static renderer_t *find_renderer(state_t *state, const char *voice,
430                                  uint32_t *actor)
431 {
432     language_t      *l;
433     actor_t         *a, *fallback;
434     mrp_list_hook_t *ap, *an;
435     char             lang[128], *e;
436     int              n;
437
438     if (state == NULL) {
439         errno = ENOSYS;
440
441         return NULL;
442     }
443
444     if ((e = strchr(voice, '-')) == NULL)
445         l = find_language(state, voice, FALSE);
446     else {
447         n = e - voice;
448         if (snprintf(lang, sizeof(lang), "%*.*s", n, n, voice) >= sizeof(lang))
449             l = NULL;
450         else
451             l = find_language(state, lang, FALSE);
452     }
453
454     if (l == NULL)
455         return NULL;
456
457     fallback = NULL;
458     mrp_list_foreach(&l->actors, ap, an) {
459         a = mrp_list_entry(ap, typeof(*a), hook);
460
461         if (!strcmp(a->voice, voice)) {
462             *actor = a->id;
463
464             return a->r;
465         }
466
467         if (fallback == NULL)
468             fallback = a;
469     }
470
471     if (fallback != NULL) {
472         *actor = fallback->id;
473
474         return fallback->r;
475     }
476
477     return NULL;
478 }
479
480
481 #if 0
482 static renderer_t *select_renderer(state_t *state, const char *voice,
483                                    uint32_t *actid)
484 {
485     renderer_t         *r;
486     language_t         *l;
487     srs_voice_actor_t  *a;
488     srs_voice_gender_t  gender;
489     mrp_list_hook_t    *p, *n;
490     renderer_t         *dfltv;
491     uint32_t            dfltid;
492     int                 i;
493
494     if (state == NULL) {
495         errno = ENOSYS;
496         goto notfound;
497     }
498
499     if (!strcmp(actor, SRS_VOICE_FEMALE)) {
500         gender = SRS_VOICE_GENDER_FEMALE;
501         actor  = NULL;
502     }
503     else if (!strcmp(actor, SRS_VOICE_MALE)) {
504         gender = SRS_VOICE_GENDER_MALE;
505         actor  = NULL;
506     }
507     else
508         gender = SRS_VOICE_GENDER_ANY;
509
510     dfltv  = NULL;
511     dfltid = SRS_VOICE_INVALID;
512
513     mrp_list_foreach(&state->synthesizers, p, n) {
514         r = mrp_list_entry(p, typeof(*r), hook);
515
516         for (i = 0, a = r->actors; i < r->nactor; i++, a++) {
517             if (strcmp(a->lang, lang))
518                 continue;
519
520             if (actor == NULL) {
521                 if (gender == a->gender) {
522                     *actid = a->id;
523                     return r;
524                 }
525             }
526             else {
527                 if (!strcmp(a->name, actor)) {
528                     *actid = a->id;
529                     return r;
530                 }
531
532                 if (dfltid == SRS_VOICE_INVALID) {
533                     dfltv  = r;
534                     dfltid = a->id;
535                 }
536             }
537         }
538     }
539
540  notfound:
541     *actid = dfltid;
542     return dfltv;
543 }
544 #endif
545
546 static void free_tags(char **tags)
547 {
548     int i;
549
550     if (tags == NULL)
551         return;
552
553     for (i = 0; tags[i] != NULL; i++)
554         mrp_free(tags[i]);
555
556     mrp_free(tags);
557 }
558
559
560 static char **copy_tags(char **tags)
561 {
562     char **cp = NULL;
563     int    i;
564
565     if (tags == NULL)
566         return NULL;
567
568     for (i = 0; tags[i] != NULL; i++) {
569         if (!mrp_reallocz(cp, i, i + 1))
570             goto fail;
571         if ((cp[i] = mrp_strdup(tags[i])) == NULL)
572             goto fail;
573     }
574
575     if (mrp_reallocz(cp, i, i + 1))
576         return cp;
577     /* fall through */
578  fail:
579     free_tags(cp);
580     return NULL;
581 }
582
583
584 static void request_timer_cb(mrp_timer_t *t, void *user_data)
585 {
586     queued_t          *qr  = (queued_t *)user_data;
587     request_t         *req = &qr->req;
588     srs_voice_event_t  event;
589
590     mrp_log_info("Voice/TTS request #%u timed out.", qr->req.id);
591
592     mrp_del_timer(req->timer);
593     req->timer = NULL;
594
595     mrp_clear(&event);
596     event.type = SRS_VOICE_EVENT_TIMEOUT;
597     event.id   = req->id;
598
599     notify_request(req, &event);
600
601     mrp_list_delete(&req->hook);
602
603     mrp_free(qr->msg);
604     free_tags(qr->tags);
605
606     mrp_free(qr);
607 }
608
609
610 static request_t *enqueue_request(state_t *state, const char *msg, char **tags,
611                                   renderer_t *r, uint32_t actor, double rate,
612                                   double pitch, int timeout, int notify_mask,
613                                   srs_voice_notify_t notify, void *notify_data)
614 {
615     queued_t *qr;
616
617     qr = mrp_allocz(sizeof(*qr));
618
619     if (qr == NULL)
620         return NULL;
621
622     mrp_list_init(&qr->req.hook);
623
624     qr->req.id          = state->nextid++;
625     qr->req.r           = r;
626     qr->req.vid         = SRS_VOICE_INVALID;
627     qr->req.notify_mask = notify_mask;
628     qr->req.notify      = notify;
629     qr->req.notify_data = notify_data;
630
631     qr->msg     = mrp_strdup(msg);
632     qr->tags    = copy_tags(tags);
633     qr->actor   = actor;
634     qr->timeout = timeout;
635
636     if (qr->msg != NULL && (qr->tags != NULL || tags == NULL)) {
637         mrp_list_append(&state->requests, &qr->req.hook);
638
639         if (timeout > 0)
640             qr->req.timer = mrp_add_timer(r->srs->ml, timeout,
641                                           request_timer_cb, qr);
642
643         return &qr->req;
644     }
645     else {
646         mrp_free(qr->msg);
647         free_tags(qr->tags);
648         mrp_free(qr);
649
650         return NULL;
651     }
652 }
653
654
655 static request_t *activate_next(state_t *state)
656 {
657     queued_t        *qr;
658     renderer_t      *r;
659     mrp_list_hook_t *p, *n;
660
661     if (state->active != NULL)
662         return NULL;
663
664     qr = NULL;
665     mrp_list_foreach(&state->requests, p, n) {
666         mrp_list_delete(p);
667         qr = mrp_list_entry(p, typeof(*qr), req.hook);
668         break;
669     }
670
671     if (qr == NULL)
672         return NULL;
673
674     mrp_del_timer(qr->req.timer);
675     qr->req.timer = NULL;
676
677     r = qr->req.r;
678     qr->req.vid = r->api.render(qr->msg, qr->tags, qr->actor, qr->rate,
679                                 qr->pitch, qr->req.notify_mask, r->api_data);
680
681     mrp_free(qr->msg);
682     qr->msg = NULL;
683     free_tags(qr->tags);
684     qr->tags = NULL;
685
686     if (qr->req.vid == SRS_VOICE_INVALID) {
687         if (qr->req.notify != NULL &&
688             (qr->req.notify_mask & 1 << SRS_VOICE_EVENT_ABORTED)) {
689             srs_voice_event_t e;
690
691             mrp_clear(&e);
692             e.type = SRS_VOICE_EVENT_ABORTED;
693             e.id   = qr->req.id;
694
695             voice_notify_cb(&e, qr->req.notify_data);
696
697             free(qr);
698         }
699
700         return NULL;
701     }
702     else {
703         state->active = &qr->req;
704
705         return &qr->req;
706     }
707 }
708
709
710 request_t *render_request(state_t *state, const char *msg, char **tags,
711                           renderer_t *r, uint32_t actor, double rate,
712                           double pitch, int timeout, int notify_mask,
713                           srs_voice_notify_t notify, void *notify_data)
714 {
715     request_t *req;
716
717     req = mrp_allocz(sizeof(*req));
718
719     if (req == NULL)
720         return NULL;
721
722     mrp_list_init(&req->hook);
723     req->id  = state->nextid++;
724     req->r   = r;
725     req->vid = r->api.render(msg, tags, actor, rate, pitch,
726                              notify_mask, r->api_data);
727
728     if (req->vid == SRS_VOICE_INVALID) {
729         mrp_free(req);
730         return NULL;
731     }
732
733     req->notify      = notify;
734     req->notify_mask = notify_mask;
735     req->notify_data = notify_data;
736
737     state->active = req;
738
739     return req;
740 }
741
742
743 uint32_t srs_render_voice(srs_context_t *srs, const char *msg,
744                           char **tags, const char *voice, double rate,
745                           double pitch, int timeout, int notify_mask,
746                           srs_voice_notify_t notify, void *user_data)
747 {
748     state_t    *state = (state_t *)srs->synthesizer;
749     renderer_t *r;
750     request_t  *req;
751     uint32_t    actid;
752
753     if (state == NULL) {
754         errno = ENOSYS;
755
756         return SRS_VOICE_INVALID;
757     }
758
759     r = find_renderer(state, voice, &actid);
760
761     if (r == NULL) {
762         errno = EINVAL;
763
764         return SRS_VOICE_INVALID;
765     }
766
767     if (state->active == NULL)
768         req = render_request(state, msg, tags, r, actid, rate, pitch, timeout,
769                              notify_mask, notify, user_data);
770     else {
771         if (timeout == SRS_VOICE_IMMEDIATE) {
772             errno = EBUSY;
773             req   = NULL;
774         }
775         else
776             req = enqueue_request(state, msg, tags, r, actid, rate, pitch,
777                                   timeout, notify_mask, notify, user_data);
778     }
779
780     if (req != NULL)
781         return req->id;
782     else
783         return SRS_VOICE_INVALID;
784 }
785
786
787 static request_t *find_request(state_t *state, uint32_t rid, uint32_t vid)
788 {
789     mrp_list_hook_t *p, *n;
790     request_t       *req;
791
792     if (state == NULL) {
793         errno = ENOSYS;
794         goto error;
795     }
796
797     if ((req = state->active) != NULL) {
798         if ((rid == -1 || req->id == rid) && (vid == -1 || req->vid == vid))
799             return req;
800     }
801
802     mrp_list_foreach(&state->requests, p, n) {
803         req = mrp_list_entry(p, typeof(*req), hook);
804
805         if ((rid == -1 || req->id == rid) && (vid == -1 || req->vid == vid))
806             return req;
807     }
808
809     errno = EINVAL;
810
811  error:
812     return NULL;
813 }
814
815
816 void srs_cancel_voice(srs_context_t *srs, uint32_t rid, int notify)
817 {
818     state_t    *state = (state_t *)srs->synthesizer;
819     request_t  *req   = find_request(state, rid, -1);
820     renderer_t *voice = req ? req->r : NULL;
821
822     if (req == NULL)
823         return;
824
825     mrp_del_timer(req->timer);
826     req->timer = NULL;
827     state->cancelling = req;
828
829     voice->api.cancel(req->vid, voice->api_data);
830
831     mrp_list_delete(&req->hook);
832     mrp_free(req);
833
834     if (state->active == req) {
835         state->active = NULL;
836         activate_next(state);
837     }
838 }
839
840
841 int srs_query_voices(srs_context_t *srs, const char *language,
842                      srs_voice_actor_t **actorsp)
843 {
844     state_t            *state = (state_t *)srs->synthesizer;
845     srs_voice_actor_t  *actors, *actor;
846     int                 nactor, i;
847     language_t         *l;
848     mrp_list_hook_t    *lp, *ln;
849     actor_t            *a;
850     mrp_list_hook_t    *ap, *an;
851
852     if (state == NULL) {
853         *actorsp = NULL;
854
855         return 0;
856     }
857
858     actors = NULL;
859     nactor = 0;
860
861     mrp_list_foreach(&state->languages, lp, ln) {
862         l = mrp_list_entry(lp, typeof(*l), hook);
863
864         if (language != NULL && strcasecmp(l->lang, language))
865             continue;
866
867         mrp_list_foreach(&l->actors, ap, an) {
868             a = mrp_list_entry(ap, typeof(*a), hook);
869
870             if (mrp_reallocz(actors, nactor, nactor + 1) == NULL)
871                 goto fail;
872
873             actor = actors + nactor++;
874
875             actor->name        = mrp_strdup(a->voice);
876             actor->lang        = mrp_strdup(l->lang);
877             actor->dialect     = mrp_strdup(a->dialect);
878             actor->gender      = a->gender;
879             actor->age         = a->age;
880             actor->description = mrp_strdup(a->description);
881
882             if (!actor->name || !actor->lang ||
883                 (!actor->dialect && a->dialect) ||
884                 (!actor->description && a->description))
885                 goto fail;
886         }
887     }
888
889     if (actors != NULL) {
890         if (!mrp_reallocz(actors, nactor, nactor + 1))
891             goto fail;
892     }
893
894     *actorsp = actors;
895
896     return nactor;
897
898  fail:
899     for (i = 0; i < nactor; i++) {
900         mrp_free(actors[i].name);
901         mrp_free(actors[i].lang);
902         mrp_free(actors[i].dialect);
903         mrp_free(actors[i].description);
904     }
905
906     return -1;
907 }
908
909
910 void srs_free_queried_voices(srs_voice_actor_t *actors)
911 {
912     srs_voice_actor_t *a;
913
914     if (actors != NULL) {
915         for (a = actors; a->lang; a++) {
916             mrp_free(a->name);
917             mrp_free(a->lang);
918             mrp_free(a->dialect);
919             mrp_free(a->description);
920         }
921         mrp_free(actors);
922     }
923 }