Wed Oct 28 11:45:42 2009

Asterisk developer's documentation


res_speech.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2006, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Generic Speech Recognition API
00022  *
00023  * \author Joshua Colp <jcolp@digium.com>
00024  */
00025 
00026 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 89511 $");
00029 
00030 #include "asterisk/channel.h"
00031 #include "asterisk/module.h"
00032 #include "asterisk/lock.h"
00033 #include "asterisk/linkedlists.h"
00034 #include "asterisk/cli.h"
00035 #include "asterisk/term.h"
00036 #include "asterisk/speech.h"
00037 
00038 
00039 static AST_RWLIST_HEAD_STATIC(engines, ast_speech_engine);
00040 static struct ast_speech_engine *default_engine = NULL;
00041 
00042 /*! \brief Find a speech recognition engine of specified name, if NULL then use the default one */
00043 static struct ast_speech_engine *find_engine(char *engine_name)
00044 {
00045    struct ast_speech_engine *engine = NULL;
00046 
00047    /* If no name is specified -- use the default engine */
00048    if (ast_strlen_zero(engine_name))
00049       return default_engine;
00050 
00051    AST_RWLIST_RDLOCK(&engines);
00052    AST_RWLIST_TRAVERSE(&engines, engine, list) {
00053       if (!strcasecmp(engine->name, engine_name)) {
00054          break;
00055       }
00056    }
00057    AST_RWLIST_UNLOCK(&engines);
00058 
00059    return engine;
00060 }
00061 
00062 /*! \brief Activate a loaded (either local or global) grammar */
00063 int ast_speech_grammar_activate(struct ast_speech *speech, char *grammar_name)
00064 {
00065    return (speech->engine->activate ? speech->engine->activate(speech, grammar_name) : -1);
00066 }
00067 
00068 /*! \brief Deactivate a loaded grammar on a speech structure */
00069 int ast_speech_grammar_deactivate(struct ast_speech *speech, char *grammar_name)
00070 {
00071    return (speech->engine->deactivate ? speech->engine->deactivate(speech, grammar_name) : -1);
00072 }
00073 
00074 /*! \brief Load a local grammar on a speech structure */
00075 int ast_speech_grammar_load(struct ast_speech *speech, char *grammar_name, char *grammar)
00076 {
00077    return (speech->engine->load ? speech->engine->load(speech, grammar_name, grammar) : -1);
00078 }
00079 
00080 /*! \brief Unload a local grammar from a speech structure */
00081 int ast_speech_grammar_unload(struct ast_speech *speech, char *grammar_name)
00082 {
00083    return (speech->engine->unload ? speech->engine->unload(speech, grammar_name) : -1);
00084 }
00085 
00086 /*! \brief Return the results of a recognition from the speech structure */
00087 struct ast_speech_result *ast_speech_results_get(struct ast_speech *speech)
00088 {
00089    return (speech->engine->get ? speech->engine->get(speech) : NULL);
00090 }
00091 
00092 /*! \brief Free a list of results */
00093 int ast_speech_results_free(struct ast_speech_result *result)
00094 {
00095    struct ast_speech_result *current_result = result, *prev_result = NULL;
00096    int res = 0;
00097 
00098    while (current_result != NULL) {
00099       prev_result = current_result;
00100       /* Deallocate what we can */
00101       if (current_result->text != NULL) {
00102          ast_free(current_result->text);
00103          current_result->text = NULL;
00104       }
00105       if (current_result->grammar != NULL) {
00106          ast_free(current_result->grammar);
00107          current_result->grammar = NULL;
00108       }
00109       /* Move on and then free ourselves */
00110       current_result = AST_LIST_NEXT(current_result, list);
00111       ast_free(prev_result);
00112       prev_result = NULL;
00113    }
00114 
00115    return res;
00116 }
00117 
00118 /*! \brief Start speech recognition on a speech structure */
00119 void ast_speech_start(struct ast_speech *speech)
00120 {
00121 
00122    /* Clear any flags that may affect things */
00123    ast_clear_flag(speech, AST_SPEECH_SPOKE);
00124    ast_clear_flag(speech, AST_SPEECH_QUIET);
00125    ast_clear_flag(speech, AST_SPEECH_HAVE_RESULTS);
00126 
00127    /* If results are on the structure, free them since we are starting again */
00128    if (speech->results) {
00129       ast_speech_results_free(speech->results);
00130       speech->results = NULL;
00131    }
00132 
00133    /* If the engine needs to start stuff up, do it */
00134    if (speech->engine->start)
00135       speech->engine->start(speech);
00136 
00137    return;
00138 }
00139 
00140 /*! \brief Write in signed linear audio to be recognized */
00141 int ast_speech_write(struct ast_speech *speech, void *data, int len)
00142 {
00143    /* Make sure the speech engine is ready to accept audio */
00144    if (speech->state != AST_SPEECH_STATE_READY)
00145       return -1;
00146 
00147    return speech->engine->write(speech, data, len);
00148 }
00149 
00150 /*! \brief Signal to the engine that DTMF was received */
00151 int ast_speech_dtmf(struct ast_speech *speech, const char *dtmf)
00152 {
00153    int res = 0;
00154 
00155    if (speech->state != AST_SPEECH_STATE_READY)
00156       return -1;
00157 
00158    if (speech->engine->dtmf != NULL) {
00159       res = speech->engine->dtmf(speech, dtmf);
00160    }
00161 
00162    return res;
00163 }
00164 
00165 /*! \brief Change an engine specific attribute */
00166 int ast_speech_change(struct ast_speech *speech, char *name, const char *value)
00167 {
00168    return (speech->engine->change ? speech->engine->change(speech, name, value) : -1);
00169 }
00170 
00171 /*! \brief Create a new speech structure using the engine specified */
00172 struct ast_speech *ast_speech_new(char *engine_name, int formats)
00173 {
00174    struct ast_speech_engine *engine = NULL;
00175    struct ast_speech *new_speech = NULL;
00176    int format = AST_FORMAT_SLINEAR;
00177 
00178    /* Try to find the speech recognition engine that was requested */
00179    if (!(engine = find_engine(engine_name)))
00180       return NULL;
00181 
00182    /* Before even allocating the memory below do some codec negotiation, we choose the best codec possible and fall back to signed linear if possible */
00183    if ((format = (engine->formats & formats)))
00184       format = ast_best_codec(format);
00185    else if ((engine->formats & AST_FORMAT_SLINEAR))
00186       format = AST_FORMAT_SLINEAR;
00187    else
00188       return NULL;
00189 
00190    /* Allocate our own speech structure, and try to allocate a structure from the engine too */
00191    if (!(new_speech = ast_calloc(1, sizeof(*new_speech))))
00192       return NULL;
00193 
00194    /* Initialize the lock */
00195    ast_mutex_init(&new_speech->lock);
00196 
00197    /* Make sure no results are present */
00198    new_speech->results = NULL;
00199 
00200    /* Copy over our engine pointer */
00201    new_speech->engine = engine;
00202 
00203    /* Can't forget the format audio is going to be in */
00204    new_speech->format = format;
00205 
00206    /* We are not ready to accept audio yet */
00207    ast_speech_change_state(new_speech, AST_SPEECH_STATE_NOT_READY);
00208 
00209    /* Pass ourselves to the engine so they can set us up some more and if they error out then do not create a structure */
00210    if (engine->create(new_speech, format)) {
00211       ast_mutex_destroy(&new_speech->lock);
00212       ast_free(new_speech);
00213       new_speech = NULL;
00214    }
00215 
00216    return new_speech;
00217 }
00218 
00219 /*! \brief Destroy a speech structure */
00220 int ast_speech_destroy(struct ast_speech *speech)
00221 {
00222    int res = 0;
00223 
00224    /* Call our engine so we are destroyed properly */
00225    speech->engine->destroy(speech);
00226 
00227    /* Deinitialize the lock */
00228    ast_mutex_destroy(&speech->lock);
00229 
00230    /* If results exist on the speech structure, destroy them */
00231    if (speech->results)
00232       ast_speech_results_free(speech->results);
00233 
00234    /* If a processing sound is set - free the memory used by it */
00235    if (speech->processing_sound)
00236       ast_free(speech->processing_sound);
00237 
00238    /* Aloha we are done */
00239    ast_free(speech);
00240 
00241    return res;
00242 }
00243 
00244 /*! \brief Change state of a speech structure */
00245 int ast_speech_change_state(struct ast_speech *speech, int state)
00246 {
00247    int res = 0;
00248 
00249    switch (state) {
00250    case AST_SPEECH_STATE_WAIT:
00251       /* The engine heard audio, so they spoke */
00252       ast_set_flag(speech, AST_SPEECH_SPOKE);
00253    default:
00254       speech->state = state;
00255       break;
00256    }
00257 
00258    return res;
00259 }
00260 
00261 /*! \brief Change the type of results we want */
00262 int ast_speech_change_results_type(struct ast_speech *speech, enum ast_speech_results_type results_type)
00263 {
00264    speech->results_type = results_type;
00265 
00266    return (speech->engine->change_results_type ? speech->engine->change_results_type(speech, results_type) : 0);
00267 }
00268 
00269 /*! \brief Register a speech recognition engine */
00270 int ast_speech_register(struct ast_speech_engine *engine)
00271 {
00272    struct ast_speech_engine *existing_engine = NULL;
00273    int res = 0;
00274 
00275    /* Confirm the engine meets the minimum API requirements */
00276    if (!engine->create || !engine->write || !engine->destroy) {
00277       ast_log(LOG_WARNING, "Speech recognition engine '%s' did not meet minimum API requirements.\n", engine->name);
00278       return -1;
00279    }
00280 
00281    /* If an engine is already loaded with this name, error out */
00282    if ((existing_engine = find_engine(engine->name))) {
00283       ast_log(LOG_WARNING, "Speech recognition engine '%s' already exists.\n", engine->name);
00284       return -1;
00285    }
00286 
00287    ast_verb(2, "Registered speech recognition engine '%s'\n", engine->name);
00288 
00289    /* Add to the engine linked list and make default if needed */
00290    AST_RWLIST_WRLOCK(&engines);
00291    AST_RWLIST_INSERT_HEAD(&engines, engine, list);
00292    if (!default_engine) {
00293       default_engine = engine;
00294       ast_verb(2, "Made '%s' the default speech recognition engine\n", engine->name);
00295    }
00296    AST_RWLIST_UNLOCK(&engines);
00297 
00298    return res;
00299 }
00300 
00301 /*! \brief Unregister a speech recognition engine */
00302 int ast_speech_unregister(char *engine_name)
00303 {
00304    struct ast_speech_engine *engine = NULL;
00305    int res = -1;
00306 
00307    if (ast_strlen_zero(engine_name))
00308       return -1;
00309 
00310    AST_RWLIST_WRLOCK(&engines);
00311    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&engines, engine, list) {
00312       if (!strcasecmp(engine->name, engine_name)) {
00313          /* We have our engine... removed it */
00314          AST_RWLIST_REMOVE_CURRENT(list);
00315          /* If this was the default engine, we need to pick a new one */
00316          if (!default_engine)
00317             default_engine = AST_RWLIST_FIRST(&engines);
00318          ast_verb(2, "Unregistered speech recognition engine '%s'\n", engine_name);
00319          /* All went well */
00320          res = 0;
00321          break;
00322       }
00323    }
00324    AST_RWLIST_TRAVERSE_SAFE_END;
00325    AST_RWLIST_UNLOCK(&engines);
00326 
00327    return res;
00328 }
00329 
00330 static int unload_module(void)
00331 {
00332    /* We can not be unloaded */
00333    return -1;
00334 }
00335 
00336 static int load_module(void)
00337 {
00338    return AST_MODULE_LOAD_SUCCESS;
00339 }
00340 
00341 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Generic Speech Recognition API",
00342       .load = load_module,
00343       .unload = unload_module,
00344       );

Generated on Wed Oct 28 11:45:42 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6