Initial import to Tizen
[profile/ivi/pocketsphinx.git] / src / gst-plugin / gstpocketsphinx.c
1 /* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* ====================================================================
3  * Copyright (c) 2007 Carnegie Mellon University.  All rights
4  * reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer. 
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * This work was supported in part by funding from the Defense Advanced 
19  * Research Projects Agency and the National Science Foundation of the 
20  * United States of America, and the CMU Sphinx Speech Consortium.
21  *
22  * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND 
23  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
24  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
26  * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
28  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
32  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * ====================================================================
35  *
36  * Author: David Huggins-Daines <dhuggins@cs.cmu.edu>
37  */
38
39 #ifdef HAVE_CONFIG_H
40 #  include <config.h>
41 #endif
42
43 #include <string.h>
44 #include <gst/gst.h>
45
46 #include <sphinxbase/strfuncs.h>
47
48 #include "gstpocketsphinx.h"
49 #include "gstvader.h"
50 #include "psmarshal.h"
51
52 GST_DEBUG_CATEGORY_STATIC(pocketsphinx_debug);
53 #define GST_CAT_DEFAULT pocketsphinx_debug
54
55 /*
56  * Forward declarations.
57  */
58
59 static void gst_pocketsphinx_set_property(GObject * object, guint prop_id,
60                                           const GValue * value, GParamSpec * pspec);
61 static void gst_pocketsphinx_get_property(GObject * object, guint prop_id,
62                                           GValue * value, GParamSpec * pspec);
63 static GstFlowReturn gst_pocketsphinx_chain(GstPad * pad, GstBuffer * buffer);
64 static gboolean gst_pocketsphinx_event(GstPad *pad, GstEvent *event);
65
66 enum
67 {
68     SIGNAL_PARTIAL_RESULT,
69     SIGNAL_RESULT,
70     LAST_SIGNAL
71 };
72
73 enum
74 {
75     PROP_0,
76     PROP_HMM_DIR,
77     PROP_LM_FILE,
78     PROP_LMCTL_FILE,
79     PROP_LM_NAME,
80     PROP_DICT_FILE,
81     PROP_FSG_FILE,
82     PROP_FSG_MODEL,
83     PROP_FWDFLAT,
84     PROP_BESTPATH,
85     PROP_MAXHMMPF,
86     PROP_MAXWPF,
87     PROP_DSRATIO,
88     PROP_LATDIR,
89     PROP_LATTICE,
90     PROP_DECODER,
91     PROP_CONFIGURED
92 };
93
94 /*
95  * Static data.
96  */
97
98 /* Default command line. (will go away soon and be constructed using properties) */
99 static char *default_argv[] = {
100     "gst-pocketsphinx",
101     "-samprate", "8000",
102     "-cmn", "prior",
103     "-nfft", "256",
104     "-fwdflat", "no",
105     "-bestpath", "no",
106     "-maxhmmpf", "1000",
107     "-maxwpf", "10"
108 };
109 static const int default_argc = sizeof(default_argv)/sizeof(default_argv[0]);
110
111 static GstStaticPadTemplate sink_factory =
112     GST_STATIC_PAD_TEMPLATE("sink",
113                             GST_PAD_SINK,
114                             GST_PAD_ALWAYS,
115                             GST_STATIC_CAPS("audio/x-raw-int, "
116                                             "width = (int) 16, "
117                                             "depth = (int) 16, "
118                                             "signed = (boolean) true, "
119                                             "endianness = (int) BYTE_ORDER, "
120                                             "channels = (int) 1, "
121                                             "rate = (int) 8000")
122         );
123
124 static GstStaticPadTemplate src_factory =
125     GST_STATIC_PAD_TEMPLATE("src",
126                             GST_PAD_SRC,
127                             GST_PAD_ALWAYS,
128                             GST_STATIC_CAPS("text/plain")
129         );
130 static guint gst_pocketsphinx_signals[LAST_SIGNAL];
131
132 /*
133  * Boxing of ps_lattice_t.
134  */
135
136 GType
137 ps_lattice_get_type(void)
138 {
139     static GType ps_lattice_type = 0;
140
141     if (G_UNLIKELY(ps_lattice_type == 0)) {
142         ps_lattice_type = g_boxed_type_register_static
143             ("PSLattice",
144              /* Conveniently, these should just work. */
145              (GBoxedCopyFunc) ps_lattice_retain,
146              (GBoxedFreeFunc) ps_lattice_free);
147     }
148
149     return ps_lattice_type;
150 }
151
152 /*
153  * Boxing of ps_decoder_t.
154  */
155
156 GType
157 ps_decoder_get_type(void)
158 {
159     static GType ps_decoder_type = 0;
160
161     if (G_UNLIKELY(ps_decoder_type == 0)) {
162         ps_decoder_type = g_boxed_type_register_static
163             ("PSDecoder",
164              /* Conveniently, these should just work. */
165              (GBoxedCopyFunc) ps_retain,
166              (GBoxedFreeFunc) ps_free);
167     }
168
169     return ps_decoder_type;
170 }
171
172
173 /*
174  * gst_pocketsphinx element.
175  */
176 GST_BOILERPLATE (GstPocketSphinx, gst_pocketsphinx, GstElement, GST_TYPE_ELEMENT);
177
178 static void
179 gst_pocketsphinx_base_init(gpointer gclass)
180 {
181     static const GstElementDetails element_details = {
182         "PocketSphinx",
183         "Filter/Audio",
184         "Convert speech to text",
185         "David Huggins-Daines <dhuggins@cs.cmu.edu>"
186     };
187     GstElementClass *element_class = GST_ELEMENT_CLASS(gclass);
188
189     gst_element_class_add_pad_template(element_class,
190                                        gst_static_pad_template_get(&sink_factory));
191     gst_element_class_add_pad_template(element_class,
192                                        gst_static_pad_template_get(&src_factory));
193     gst_element_class_set_details(element_class, &element_details);
194 }
195
196 static void
197 string_disposal(gpointer key, gpointer value, gpointer user_data)
198 {
199     g_free(value);
200 }
201
202 static void
203 gst_pocketsphinx_finalize(GObject * gobject)
204 {
205     GstPocketSphinx *ps = GST_POCKETSPHINX(gobject);
206
207     g_hash_table_foreach(ps->arghash, string_disposal, NULL);
208     g_hash_table_destroy(ps->arghash);
209     g_free(ps->last_result);
210     ps_free(ps->ps);
211     GST_CALL_PARENT(G_OBJECT_CLASS, finalize,(gobject));
212 }
213
214 static void
215 gst_pocketsphinx_class_init(GstPocketSphinxClass * klass)
216 {
217     GObjectClass *gobject_class;
218
219     gobject_class =(GObjectClass *) klass;
220
221     gobject_class->set_property = gst_pocketsphinx_set_property;
222     gobject_class->get_property = gst_pocketsphinx_get_property;
223     gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_pocketsphinx_finalize);
224
225     /* TODO: We will bridge cmd_ln.h properties to GObject
226      * properties here somehow eventually. */
227     g_object_class_install_property
228         (gobject_class, PROP_HMM_DIR,
229          g_param_spec_string("hmm", "HMM Directory",
230                              "Directory containing acoustic model parameters",
231                              NULL,
232                              G_PARAM_READWRITE));
233     g_object_class_install_property
234         (gobject_class, PROP_LM_FILE,
235          g_param_spec_string("lm", "LM File",
236                              "Language model file",
237                              NULL,
238                              G_PARAM_READWRITE));
239     g_object_class_install_property
240         (gobject_class, PROP_LMCTL_FILE,
241          g_param_spec_string("lmctl", "LM Control File",
242                              "Language model control file (for class LMs)",
243                              NULL,
244                              G_PARAM_READWRITE));
245     g_object_class_install_property
246         (gobject_class, PROP_LM_NAME,
247          g_param_spec_string("lmname", "LM Name",
248                              "Language model name (to select LMs from lmctl)",
249                              NULL,
250                              G_PARAM_READWRITE));
251     g_object_class_install_property
252         (gobject_class, PROP_FSG_FILE,
253          g_param_spec_string("fsg", "FSG File",
254                              "Finite state grammar file",
255                              NULL,
256                              G_PARAM_READWRITE));
257     g_object_class_install_property
258         (gobject_class, PROP_FSG_MODEL,
259          g_param_spec_pointer("fsg_model", "FSG Model",
260                               "Finite state grammar object (fsg_model_t *)",
261                               G_PARAM_WRITABLE));
262     g_object_class_install_property
263         (gobject_class, PROP_DICT_FILE,
264          g_param_spec_string("dict", "Dictionary File",
265                              "Dictionary File",
266                              NULL,
267                              G_PARAM_READWRITE));
268
269     g_object_class_install_property
270         (gobject_class, PROP_FWDFLAT,
271          g_param_spec_boolean("fwdflat", "Flat Lexicon Search",
272                               "Enable Flat Lexicon Search",
273                               FALSE,
274                               G_PARAM_READWRITE));
275     g_object_class_install_property
276         (gobject_class, PROP_BESTPATH,
277          g_param_spec_boolean("bestpath", "Graph Search",
278                               "Enable Graph Search",
279                               FALSE,
280                               G_PARAM_READWRITE));
281
282     g_object_class_install_property
283         (gobject_class, PROP_LATDIR,
284          g_param_spec_string("latdir", "Lattice Directory",
285                              "Output Directory for Lattices",
286                              NULL,
287                              G_PARAM_READWRITE));
288     g_object_class_install_property
289         (gobject_class, PROP_LATTICE,
290          g_param_spec_boxed("lattice", "Word Lattice",
291                             "Word lattice object for most recent result",
292                             PS_LATTICE_TYPE,
293                             G_PARAM_READABLE));
294
295     g_object_class_install_property
296         (gobject_class, PROP_MAXHMMPF,
297          g_param_spec_int("maxhmmpf", "Maximum HMMs per frame",
298                           "Maximum number of HMMs searched per frame",
299                           1, 100000, 1000,
300                           G_PARAM_READWRITE));
301     g_object_class_install_property
302         (gobject_class, PROP_MAXWPF,
303          g_param_spec_int("maxwpf", "Maximum words per frame",
304                           "Maximum number of words searched per frame",
305                           1, 100000, 10,
306                           G_PARAM_READWRITE));
307     g_object_class_install_property
308         (gobject_class, PROP_DSRATIO,
309          g_param_spec_int("dsratio", "Frame downsampling ratio",
310                           "Evaluate acoustic model every N frames",
311                           1, 10, 1,
312                           G_PARAM_READWRITE));
313
314     g_object_class_install_property
315         (gobject_class, PROP_DECODER,
316          g_param_spec_boxed("decoder", "Decoder object",
317                             "The underlying decoder",
318                             PS_DECODER_TYPE,
319                             G_PARAM_READABLE));
320     g_object_class_install_property
321         (gobject_class, PROP_CONFIGURED,
322          g_param_spec_boolean("configured", "Finalize configuration",
323                               "Set this to finalize configuration",
324                               FALSE,
325                               G_PARAM_READWRITE));
326
327     gst_pocketsphinx_signals[SIGNAL_PARTIAL_RESULT] = 
328         g_signal_new("partial_result",
329                      G_TYPE_FROM_CLASS(klass),
330                      G_SIGNAL_RUN_LAST,
331                      G_STRUCT_OFFSET(GstPocketSphinxClass, partial_result),
332                      NULL, NULL,
333                      ps_marshal_VOID__STRING_STRING,
334                      G_TYPE_NONE,
335                      2, G_TYPE_STRING, G_TYPE_STRING
336             );
337
338     gst_pocketsphinx_signals[SIGNAL_RESULT] = 
339         g_signal_new("result",
340                      G_TYPE_FROM_CLASS(klass),
341                      G_SIGNAL_RUN_LAST,
342                      G_STRUCT_OFFSET(GstPocketSphinxClass, result),
343                      NULL, NULL,
344                      ps_marshal_VOID__STRING_STRING,
345                      G_TYPE_NONE,
346                      2, G_TYPE_STRING, G_TYPE_STRING
347             );
348
349     GST_DEBUG_CATEGORY_INIT(pocketsphinx_debug, "pocketsphinx", 0,
350                             "Automatic Speech Recognition");
351 }
352
353 static void
354 gst_pocketsphinx_set_string(GstPocketSphinx *ps,
355                             const gchar *key, const GValue *value)
356 {
357     gchar *oldstr, *newstr;
358
359     if (value != NULL)
360         newstr = g_strdup(g_value_get_string(value));
361     else
362         newstr = NULL;
363     if ((oldstr = g_hash_table_lookup(ps->arghash, key)))
364         g_free(oldstr);
365     cmd_ln_set_str_r(ps->config, key, newstr);
366     g_hash_table_foreach(ps->arghash, (gpointer)key, newstr);
367 }
368
369 static void
370 gst_pocketsphinx_set_int(GstPocketSphinx *ps,
371                          const gchar *key, const GValue *value)
372 {
373     cmd_ln_set_int32_r(ps->config, key, g_value_get_int(value));
374 }
375
376 static void
377 gst_pocketsphinx_set_boolean(GstPocketSphinx *ps,
378                              const gchar *key, const GValue *value)
379 {
380     cmd_ln_set_boolean_r(ps->config, key, g_value_get_boolean(value));
381 }
382
383 static void
384 gst_pocketsphinx_set_property(GObject * object, guint prop_id,
385                               const GValue * value, GParamSpec * pspec)
386 {
387     GstPocketSphinx *ps = GST_POCKETSPHINX(object);
388
389     switch (prop_id) {
390     case PROP_CONFIGURED:
391         if (ps->ps)
392             ps_reinit(ps->ps, NULL);
393         else
394             ps->ps = ps_init(ps->config);
395         break;
396     case PROP_HMM_DIR:
397         gst_pocketsphinx_set_string(ps, "-hmm", value);
398         if (ps->ps) {
399             /* Reinitialize the decoder with the new acoustic model. */
400             ps_reinit(ps->ps, NULL);
401         }
402         break;
403     case PROP_LM_FILE:
404         /* FSG and LM are mutually exclusive. */
405         gst_pocketsphinx_set_string(ps, "-fsg", NULL);
406         gst_pocketsphinx_set_string(ps, "-lmctl", NULL);
407         gst_pocketsphinx_set_string(ps, "-lm", value);
408         if (ps->ps) {
409             ngram_model_t *lm, *lmset;
410
411             /* Switch to this new LM. */
412             lm = ngram_model_read(ps->config,
413                                   g_value_get_string(value),
414                                   NGRAM_AUTO,
415                                   ps_get_logmath(ps->ps));
416             lmset = ps_get_lmset(ps->ps);
417             ngram_model_set_add(lmset, lm, g_value_get_string(value),
418                                 1.0, TRUE);
419             ps_update_lmset(ps->ps, lmset);
420         }
421         break;
422     case PROP_LMCTL_FILE:
423         /* FSG and LM are mutually exclusive. */
424         gst_pocketsphinx_set_string(ps, "-fsg", NULL);
425         gst_pocketsphinx_set_string(ps, "-lmctl", value);
426         gst_pocketsphinx_set_string(ps, "-lm", NULL);
427         if (ps->ps) {
428             ngram_model_t *lmset;
429             lmset = ngram_model_set_read(ps->config,
430                                          g_value_get_string(value),
431                                          ps_get_logmath(ps->ps));
432             ps_update_lmset(ps->ps, lmset);
433         }
434         break;
435     case PROP_LM_NAME:
436         gst_pocketsphinx_set_string(ps, "-fsg", NULL);
437         gst_pocketsphinx_set_string(ps, "-lmname", value);
438         if (ps->ps) {
439             ngram_model_t *lm, *lmset;
440
441             lmset = ps_get_lmset(ps->ps);
442             lm = ngram_model_set_select(lmset, g_value_get_string(value));
443             ps_update_lmset(ps->ps, lmset);
444         }
445
446     case PROP_DICT_FILE:
447         gst_pocketsphinx_set_string(ps, "-dict", value);
448         if (ps->ps) {
449             /* Reinitialize the decoder with the new dictionary. */
450             ps_reinit(ps->ps, NULL);
451         }
452         break;
453     case PROP_FSG_MODEL:
454     {
455         fsg_set_t *fsgs = ps_get_fsgset(ps->ps);
456         fsg_model_t *fsg = g_value_get_pointer(value);
457
458         fsg_set_remove_byname(fsgs, fsg_model_name(fsg));
459         fsg_set_add(fsgs, fsg_model_name(fsg), fsg);
460         fsg_set_select(fsgs, fsg_model_name(fsg));
461         break;
462     }
463     case PROP_FSG_FILE:
464         /* FSG and LM are mutually exclusive */
465         gst_pocketsphinx_set_string(ps, "-lm", NULL);
466         gst_pocketsphinx_set_string(ps, "-fsg", value);
467
468         if (ps->ps) {
469             /* Switch to this new FSG. */
470             fsg_set_t *fsgs = ps_get_fsgset(ps->ps);
471             fsg_model_t *fsg;
472
473             fsg = fsg_model_readfile(g_value_get_string(value),
474                                      ps_get_logmath(ps->ps),
475                                      cmd_ln_float32_r(ps->config, "-lw"));
476             if (fsg) {
477                 fsg_set_add(fsgs, fsg_model_name(fsg), fsg);
478                 fsg_set_select(fsgs, fsg_model_name(fsg));
479             }
480         }
481         break;
482     case PROP_FWDFLAT:
483         gst_pocketsphinx_set_boolean(ps, "-fwdflat", value);
484         break;
485     case PROP_BESTPATH:
486         gst_pocketsphinx_set_boolean(ps, "-bestpath", value);
487         break;
488     case PROP_LATDIR:
489         if (ps->latdir)
490             g_free(ps->latdir);
491         ps->latdir = g_strdup(g_value_get_string(value));
492         break;
493     case PROP_MAXHMMPF:
494         gst_pocketsphinx_set_int(ps, "-maxhmmpf", value);
495         break;
496     case PROP_MAXWPF:
497         gst_pocketsphinx_set_int(ps, "-maxwpf", value);
498         break;
499     case PROP_DSRATIO:
500         gst_pocketsphinx_set_int(ps, "-ds", value);
501         break;
502     default:
503         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
504         return;
505     }
506 }
507
508 static void
509 gst_pocketsphinx_get_property(GObject * object, guint prop_id,
510                               GValue * value, GParamSpec * pspec)
511 {
512     GstPocketSphinx *ps = GST_POCKETSPHINX(object);
513
514     switch (prop_id) {
515     case PROP_DECODER:
516         g_value_set_boxed(value, ps->ps);
517         break;
518     case PROP_CONFIGURED:
519         g_value_set_boolean(value, ps->ps != NULL);
520         break;
521     case PROP_HMM_DIR:
522         g_value_set_string(value, cmd_ln_str_r(ps->config, "-hmm"));
523         break;
524     case PROP_LM_FILE:
525         g_value_set_string(value, cmd_ln_str_r(ps->config, "-lm"));
526         break;
527     case PROP_LMCTL_FILE:
528         g_value_set_string(value, cmd_ln_str_r(ps->config, "-lmctl"));
529         break;
530     case PROP_LM_NAME:
531         g_value_set_string(value, cmd_ln_str_r(ps->config, "-lmname"));
532         break;
533     case PROP_DICT_FILE:
534         g_value_set_string(value, cmd_ln_str_r(ps->config, "-dict"));
535         break;
536     case PROP_FSG_FILE:
537         g_value_set_string(value, cmd_ln_str_r(ps->config, "-fsg"));
538         break;
539     case PROP_FWDFLAT:
540         g_value_set_boolean(value, cmd_ln_boolean_r(ps->config, "-fwdflat"));
541         break;
542     case PROP_BESTPATH:
543         g_value_set_boolean(value, cmd_ln_boolean_r(ps->config, "-bestpath"));
544         break;
545     case PROP_LATDIR:
546         g_value_set_string(value, ps->latdir);
547         break;
548     case PROP_LATTICE: {
549         ps_lattice_t *dag;
550
551         if (ps->ps && (dag = ps_get_lattice(ps->ps)))
552             g_value_set_boxed(value, dag);
553         else
554             g_value_set_boxed(value, NULL);
555         break;
556     }
557     case PROP_MAXHMMPF:
558         g_value_set_int(value, cmd_ln_int32_r(ps->config, "-maxhmmpf"));
559         break;
560     case PROP_MAXWPF:
561         g_value_set_int(value, cmd_ln_int32_r(ps->config, "-maxwpf"));
562         break;
563     case PROP_DSRATIO:
564         g_value_set_int(value, cmd_ln_int32_r(ps->config, "-ds"));
565         break;
566     default:
567         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
568         break;
569     }
570 }
571
572 static void
573 gst_pocketsphinx_init(GstPocketSphinx * ps,
574                       GstPocketSphinxClass * gclass)
575 {
576     ps->sinkpad =
577         gst_pad_new_from_static_template(&sink_factory, "sink");
578     ps->srcpad =
579         gst_pad_new_from_static_template(&src_factory, "src");
580
581     /* Create the hash table to store argument strings. */
582     ps->arghash = g_hash_table_new(g_str_hash, g_str_equal);
583
584     /* Parse default command-line options. */
585     ps->config = cmd_ln_parse_r(NULL, ps_args(), default_argc, default_argv, FALSE);
586
587     /* Set up pads. */
588     gst_element_add_pad(GST_ELEMENT(ps), ps->sinkpad);
589     gst_pad_set_chain_function(ps->sinkpad, gst_pocketsphinx_chain);
590     gst_pad_set_event_function(ps->sinkpad, gst_pocketsphinx_event);
591     gst_pad_use_fixed_caps(ps->sinkpad);
592
593     gst_element_add_pad(GST_ELEMENT(ps), ps->srcpad);
594     gst_pad_use_fixed_caps(ps->srcpad);
595
596     /* Initialize time. */
597     ps->last_result_time = 0;
598     ps->last_result = NULL;
599 }
600
601 static GstFlowReturn
602 gst_pocketsphinx_chain(GstPad * pad, GstBuffer * buffer)
603 {
604     GstPocketSphinx *ps;
605
606     ps = GST_POCKETSPHINX(GST_OBJECT_PARENT(pad));
607
608     /* Start an utterance for the first buffer we get (i.e. we assume
609      * that the VADER is "leaky") */
610     if (!ps->listening) {
611         ps->listening = TRUE;
612         ps_start_utt(ps->ps, NULL);
613     }
614     ps_process_raw(ps->ps,
615                    (short *)GST_BUFFER_DATA(buffer),
616                    GST_BUFFER_SIZE(buffer) / sizeof(short),
617                    FALSE, FALSE);
618
619     /* Get a partial result every now and then, see if it is different. */
620     if (ps->last_result_time == 0
621         /* Check every 100 milliseconds. */
622         || (GST_BUFFER_TIMESTAMP(buffer) - ps->last_result_time) > 100*10*1000) {
623         int32 score;
624         char const *hyp;
625         char const *uttid;
626
627         hyp = ps_get_hyp(ps->ps, &score, &uttid);
628         ps->last_result_time = GST_BUFFER_TIMESTAMP(buffer);
629         if (hyp && strlen(hyp) > 0) {
630             if (ps->last_result == NULL || 0 != strcmp(ps->last_result, hyp)) {
631                 g_free(ps->last_result);
632                 ps->last_result = g_strdup(hyp);
633                 /* Emit a signal for applications. */
634                 g_signal_emit(ps, gst_pocketsphinx_signals[SIGNAL_PARTIAL_RESULT],
635                               0, hyp, uttid);
636             }
637         }
638     }
639     return GST_FLOW_OK;
640 }
641
642 static gboolean
643 gst_pocketsphinx_event(GstPad *pad, GstEvent *event)
644 {
645     GstPocketSphinx *ps;
646
647     ps = GST_POCKETSPHINX(GST_OBJECT_PARENT(pad));
648
649     /* Pick out VAD events. */
650     switch (event->type) {
651     case GST_EVENT_NEWSEGMENT:
652         /* Initialize the decoder once the audio starts, if it's not
653          * there yet. */
654         if (ps->ps == NULL) {
655             ps->ps = ps_init(ps->config);
656             if (ps->ps == NULL) {
657                 GST_ELEMENT_ERROR(GST_ELEMENT(ps), LIBRARY, INIT,
658                                   ("Failed to initialize PocketSphinx"),
659                                   ("Failed to initialize PocketSphinx"));
660                 return FALSE;
661             }
662         }
663         return gst_pad_event_default(pad, event);
664     case GST_EVENT_VADER_START:
665         ps->listening = TRUE;
666         ps_start_utt(ps->ps, NULL);
667         /* Forward this event. */
668         return gst_pad_event_default(pad, event);
669     case GST_EVENT_EOS:
670     case GST_EVENT_VADER_STOP: {
671         GstBuffer *buffer;
672         int32 score;
673         char const *hyp;
674         char const *uttid;
675
676         hyp = NULL;
677         if (ps->listening) {
678             ps->listening = FALSE;
679             ps_end_utt(ps->ps);
680             hyp = ps_get_hyp(ps->ps, &score, &uttid);
681             /* Dump the lattice if requested. */
682             if (ps->latdir) {
683                 char *latfile = string_join(ps->latdir, "/", uttid, ".lat", NULL);
684                 ps_lattice_t *dag;
685
686                 if ((dag = ps_get_lattice(ps->ps)))
687                     ps_lattice_write(dag, latfile);
688                 ckd_free(latfile);
689             }
690         }
691         if (hyp) {
692             /* Emit a signal for applications. */
693             g_signal_emit(ps, gst_pocketsphinx_signals[SIGNAL_RESULT],
694                           0, hyp, uttid);
695             /* Forward this result in a buffer. */
696             buffer = gst_buffer_new_and_alloc(strlen(hyp) + 2);
697             strcpy((char *)GST_BUFFER_DATA(buffer), hyp);
698             GST_BUFFER_DATA(buffer)[strlen(hyp)] = '\n';
699             GST_BUFFER_DATA(buffer)[strlen(hyp)+1] = '\0';
700             GST_BUFFER_TIMESTAMP(buffer) = GST_EVENT_TIMESTAMP(event);
701             gst_buffer_set_caps(buffer, GST_PAD_CAPS(ps->srcpad));
702             gst_pad_push(ps->srcpad, buffer);
703         }
704
705         /* Forward this event. */
706         return gst_pad_event_default(pad, event);
707     }
708     default:
709         /* Don't bother with other events. */
710         return gst_pad_event_default(pad, event);
711     }
712 }
713
714 static gboolean
715 plugin_init(GstPlugin * plugin)
716 {
717     if (!gst_element_register(plugin, "pocketsphinx",
718                               GST_RANK_NONE, GST_TYPE_POCKETSPHINX))
719         return FALSE;
720     if (!gst_element_register(plugin, "vader",
721                               GST_RANK_NONE, GST_TYPE_VADER))
722         return FALSE;
723     return TRUE;
724 }
725
726 #define VERSION PACKAGE_VERSION
727 #define PACKAGE PACKAGE_NAME
728 GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
729                   GST_VERSION_MINOR,
730                   "pocketsphinx",
731                   "PocketSphinx plugin",
732                   plugin_init, VERSION,
733 #if (GST_VERSION_MINOR == 10 && GST_VERSION_MICRO < 15) /* Nokia's bogus old GStreamer */
734                   "LGPL",
735 #else
736                   "BSD",
737 #endif
738                   "PocketSphinx", "http://cmusphinx.sourceforge.net/")