2006-04-10 23:29:50 +00:00
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2006, Digium, Inc.
*
* Joshua Colp <jcolp@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief Speech Recognition Utility Applications
*
* \author Joshua Colp <jcolp@digium.com>
*
* \ingroup applications
*/
2006-06-07 18:54:56 +00:00
#include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , "$Revision$" );
2006-04-10 23:29:50 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
#include "asterisk/speech.h"
/* Descriptions for each application */
static char * speechcreate_descrip =
"SpeechCreate(engine name) \n "
"This application creates information to be used by all the other applications. It must be called before doing any speech recognition activities such as activating a grammar. \n "
"It takes the engine name to use as the argument, if not specified the default engine will be used. \n " ;
static char * speechactivategrammar_descrip =
"SpeechActivateGrammar(Grammar Name) \n "
"This activates the specified grammar to be recognized by the engine. A grammar tells the speech recognition engine what to recognize, \n "
"and how to portray it back to you in the dialplan. The grammar name is the only argument to this application. \n " ;
static char * speechstart_descrip =
"SpeechStart() \n "
"Tell the speech recognition engine that it should start trying to get results from audio being fed to it. This has no arguments. \n " ;
static char * speechbackground_descrip =
"SpeechBackground(Sound File|Timeout) \n "
"This application plays a sound file and waits for the person to speak. Once they start speaking playback of the file stops, and silence is heard. \n "
"Once they stop talking the processing sound is played to indicate the speech recognition engine is working. \n "
2006-05-24 22:00:37 +00:00
"Once results are available the application returns and results (score and text) are available using dialplan functions. \n "
2006-05-24 23:08:39 +00:00
"The first text and score are ${SPEECH_TEXT(0)} AND ${SPEECH_SCORE(0)} while the second are ${SPEECH_TEXT(1)} and ${SPEECH_SCORE(1)}. \n "
2006-08-24 15:44:24 +00:00
"The first argument is the sound file and the second is the timeout. Note the timeout will only start once the sound file has stopped playing. \n " ;
2006-04-10 23:29:50 +00:00
static char * speechdeactivategrammar_descrip =
"SpeechDeactivateGrammar(Grammar Name) \n "
"This deactivates the specified grammar so that it is no longer recognized. The only argument is the grammar name to deactivate. \n " ;
static char * speechprocessingsound_descrip =
"SpeechProcessingSound(Sound File) \n "
"This changes the processing sound that SpeechBackground plays back when the speech recognition engine is processing and working to get results. \n "
"It takes the sound file as the only argument. \n " ;
static char * speechdestroy_descrip =
"SpeechDestroy() \n "
"This destroys the information used by all the other speech recognition applications. \n "
"If you call this application but end up wanting to recognize more speech, you must call SpeechCreate \n "
"again before calling any other application. It takes no arguments. \n " ;
2006-04-13 00:18:52 +00:00
static char * speechload_descrip =
"SpeechLoadGrammar(Grammar Name|Path) \n "
"Load a grammar only on the channel, not globally. \n "
"It takes the grammar name as first argument and path as second. \n " ;
static char * speechunload_descrip =
"SpeechUnloadGrammar(Grammar Name) \n "
"Unload a grammar. It takes the grammar name as the only argument. \n " ;
2006-04-10 23:29:50 +00:00
/*! \brief Helper function used by datastores to destroy the speech structure upon hangup */
static void destroy_callback ( void * data )
{
struct ast_speech * speech = ( struct ast_speech * ) data ;
if ( speech == NULL ) {
return ;
}
/* Deallocate now */
ast_speech_destroy ( speech );
return ;
}
/*! \brief Static structure for datastore information */
static const struct ast_datastore_info speech_datastore = {
. type = "speech" ,
. destroy = destroy_callback
};
/*! \brief Helper function used to find the speech structure attached to a channel */
static struct ast_speech * find_speech ( struct ast_channel * chan )
{
struct ast_speech * speech = NULL ;
struct ast_datastore * datastore = NULL ;
datastore = ast_channel_datastore_find ( chan , & speech_datastore , NULL );
if ( datastore == NULL ) {
return NULL ;
}
speech = datastore -> data ;
return speech ;
}
2007-04-06 01:14:00 +00:00
/* Helper function to find a specific speech recognition result by number and nbest alternative */
static struct ast_speech_result * find_result ( struct ast_speech_result * results , char * result_num )
2006-04-13 00:18:52 +00:00
{
2007-04-06 01:14:00 +00:00
struct ast_speech_result * result = results ;
char * tmp = NULL ;
int nbest_num = 0 , wanted_num = 0 , i = 0 ;
2007-04-13 18:08:02 +00:00
if ( ! result )
return NULL ;
2007-04-06 01:14:00 +00:00
if (( tmp = strchr ( result_num , '/' ))) {
* tmp ++ = '\0' ;
nbest_num = atoi ( result_num );
wanted_num = atoi ( tmp );
} else {
wanted_num = atoi ( result_num );
}
2006-04-13 00:18:52 +00:00
2007-04-06 01:14:00 +00:00
do {
if ( result -> nbest_num != nbest_num )
continue ;
if ( i == wanted_num )
2006-04-13 00:18:52 +00:00
break ;
i ++ ;
2007-04-06 01:14:00 +00:00
} while (( result = result -> next ));
2006-04-13 00:18:52 +00:00
return result ;
}
/*! \brief SPEECH_SCORE() Dialplan Function */
static int speech_score ( struct ast_channel * chan , char * cmd , char * data ,
char * buf , size_t len )
{
struct ast_speech_result * result = NULL ;
struct ast_speech * speech = find_speech ( chan );
char tmp [ 128 ] = "" ;
2007-04-06 01:14:00 +00:00
if ( data == NULL || speech == NULL || ! ( result = find_result ( speech -> results , data )))
2006-04-13 00:18:52 +00:00
return - 1 ;
snprintf ( tmp , sizeof ( tmp ), "%d" , result -> score );
ast_copy_string ( buf , tmp , len );
return 0 ;
}
static struct ast_custom_function speech_score_function = {
. name = "SPEECH_SCORE" ,
2006-05-01 14:57:48 +00:00
. synopsis = "Gets the confidence score of a result." ,
2007-04-06 01:14:00 +00:00
. syntax = "SPEECH_SCORE([nbest number/]result number)" ,
2006-04-13 00:18:52 +00:00
. desc =
"Gets the confidence score of a result. \n " ,
. read = speech_score ,
. write = NULL ,
};
/*! \brief SPEECH_TEXT() Dialplan Function */
static int speech_text ( struct ast_channel * chan , char * cmd , char * data ,
char * buf , size_t len )
{
struct ast_speech_result * result = NULL ;
struct ast_speech * speech = find_speech ( chan );
2007-04-06 01:14:00 +00:00
if ( data == NULL || speech == NULL || ! ( result = find_result ( speech -> results , data )))
2006-04-13 00:18:52 +00:00
return - 1 ;
if ( result -> text != NULL )
ast_copy_string ( buf , result -> text , len );
return 0 ;
}
static struct ast_custom_function speech_text_function = {
. name = "SPEECH_TEXT" ,
2006-05-01 14:57:48 +00:00
. synopsis = "Gets the recognized text of a result." ,
2007-04-06 01:14:00 +00:00
. syntax = "SPEECH_TEXT([nbest number/]result number)" ,
2006-04-13 00:18:52 +00:00
. desc =
"Gets the recognized text of a result. \n " ,
. read = speech_text ,
. write = NULL ,
};
/*! \brief SPEECH_GRAMMAR() Dialplan Function */
static int speech_grammar ( struct ast_channel * chan , char * cmd , char * data ,
char * buf , size_t len )
{
struct ast_speech_result * result = NULL ;
struct ast_speech * speech = find_speech ( chan );
2007-04-06 01:14:00 +00:00
if ( data == NULL || speech == NULL || ! ( result = find_result ( speech -> results , data )))
2006-04-13 00:18:52 +00:00
return - 1 ;
if ( result -> grammar != NULL )
ast_copy_string ( buf , result -> grammar , len );
return 0 ;
}
static struct ast_custom_function speech_grammar_function = {
. name = "SPEECH_GRAMMAR" ,
. synopsis = "Gets the matched grammar of a result if available." ,
2007-04-06 01:14:00 +00:00
. syntax = "SPEECH_GRAMMAR([nbest number/]result number)" ,
2006-04-13 00:18:52 +00:00
. desc =
"Gets the matched grammar of a result if available. \n " ,
. read = speech_grammar ,
. write = NULL ,
};
2006-07-18 16:22:26 +00:00
/*! \brief SPEECH_ENGINE() Dialplan Function */
static int speech_engine_write ( struct ast_channel * chan , char * cmd , char * data , const char * value )
{
struct ast_speech * speech = find_speech ( chan );
if ( data == NULL || speech == NULL )
return - 1 ;
ast_speech_change ( speech , data , value );
return 0 ;
}
static struct ast_custom_function speech_engine_function = {
. name = "SPEECH_ENGINE" ,
. synopsis = "Change a speech engine specific attribute." ,
. syntax = "SPEECH_ENGINE(name)=value" ,
. desc =
"Changes a speech engine specific attribute. \n " ,
. read = NULL ,
. write = speech_engine_write ,
};
2007-04-06 01:14:00 +00:00
/*! \brief SPEECH_RESULTS_TYPE() Dialplan Function */
static int speech_results_type_write ( struct ast_channel * chan , char * cmd , char * data , const char * value )
{
struct ast_speech * speech = find_speech ( chan );
if ( data == NULL || speech == NULL )
return - 1 ;
if ( ! strcasecmp ( value , "normal" ))
ast_speech_change_results_type ( speech , AST_SPEECH_RESULTS_TYPE_NORMAL );
else if ( ! strcasecmp ( value , "nbest" ))
ast_speech_change_results_type ( speech , AST_SPEECH_RESULTS_TYPE_NBEST );
return 0 ;
}
static struct ast_custom_function speech_results_type_function = {
. name = "SPEECH_RESULTS_TYPE" ,
. synopsis = "Sets the type of results that will be returned." ,
. syntax = "SPEECH_RESULTS_TYPE()=results type" ,
. desc =
"Sets the type of results that will be returned. Valid options are normal or nbest." ,
. read = NULL ,
. write = speech_results_type_write ,
};
2006-04-13 00:18:52 +00:00
/*! \brief SPEECH() Dialplan Function */
static int speech_read ( struct ast_channel * chan , char * cmd , char * data ,
char * buf , size_t len )
{
int results = 0 ;
struct ast_speech_result * result = NULL ;
struct ast_speech * speech = find_speech ( chan );
char tmp [ 128 ] = "" ;
/* Now go for the various options */
if ( ! strcasecmp ( data , "status" )) {
if ( speech != NULL )
ast_copy_string ( buf , "1" , len );
else
ast_copy_string ( buf , "0" , len );
return 0 ;
}
/* Make sure we have a speech structure for everything else */
if ( speech == NULL ) {
return - 1 ;
}
/* Check to see if they are checking for silence */
if ( ! strcasecmp ( data , "spoke" )) {
if ( ast_test_flag ( speech , AST_SPEECH_SPOKE ))
ast_copy_string ( buf , "1" , len );
else
ast_copy_string ( buf , "0" , len );
} else if ( ! strcasecmp ( data , "results" )) {
/* Count number of results */
result = speech -> results ;
while ( result ) {
results ++ ;
result = result -> next ;
}
snprintf ( tmp , sizeof ( tmp ), "%d" , results );
ast_copy_string ( buf , tmp , len );
}
return 0 ;
}
static struct ast_custom_function speech_function = {
. name = "SPEECH" ,
. synopsis = "Gets information about speech recognition results." ,
. syntax = "SPEECH(argument)" ,
. desc =
"Gets information about speech recognition results. \n "
"status: Returns 1 upon speech object existing, or 0 if not \n "
"spoke: Returns 1 if spoker spoke, or 0 if not \n "
"results: Returns number of results that were recognized \n " ,
. read = speech_read ,
. write = NULL ,
};
2006-04-10 23:29:50 +00:00
/*! \brief SpeechCreate() Dialplan Application */
static int speech_create ( struct ast_channel * chan , void * data )
{
2006-08-21 02:11:39 +00:00
struct ast_module_user * u = NULL ;
2006-04-10 23:29:50 +00:00
struct ast_speech * speech = NULL ;
struct ast_datastore * datastore = NULL ;
2006-08-21 02:11:39 +00:00
u = ast_module_user_add ( chan );
2006-04-10 23:29:50 +00:00
/* Request a speech object */
speech = ast_speech_new ( data , AST_FORMAT_SLINEAR );
if ( speech == NULL ) {
/* Not available */
pbx_builtin_setvar_helper ( chan , "ERROR" , "1" );
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-10 23:29:50 +00:00
return 0 ;
}
datastore = ast_channel_datastore_alloc ( & speech_datastore , NULL );
if ( datastore == NULL ) {
ast_speech_destroy ( speech );
pbx_builtin_setvar_helper ( chan , "ERROR" , "1" );
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-10 23:29:50 +00:00
return 0 ;
}
datastore -> data = speech ;
ast_channel_datastore_add ( chan , datastore );
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-10 23:29:50 +00:00
return 0 ;
}
2006-04-13 00:18:52 +00:00
/*! \brief SpeechLoadGrammar(Grammar Name|Path) Dialplan Application */
static int speech_load ( struct ast_channel * chan , void * data )
{
int res = 0 , argc = 0 ;
2006-08-21 02:11:39 +00:00
struct ast_module_user * u = NULL ;
2006-04-13 00:18:52 +00:00
struct ast_speech * speech = find_speech ( chan );
char * argv [ 2 ], * args = NULL , * name = NULL , * path = NULL ;
2006-05-10 13:22:15 +00:00
args = ast_strdupa ( data );
2006-04-13 00:18:52 +00:00
2006-08-21 02:11:39 +00:00
u = ast_module_user_add ( chan );
2006-04-13 00:18:52 +00:00
if ( speech == NULL ) {
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-13 00:18:52 +00:00
return - 1 ;
}
/* Parse out arguments */
argc = ast_app_separate_args ( args , '|' , argv , sizeof ( argv ) / sizeof ( argv [ 0 ]));
if ( argc != 2 ) {
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-13 00:18:52 +00:00
return - 1 ;
}
name = argv [ 0 ];
path = argv [ 1 ];
/* Load the grammar locally on the object */
res = ast_speech_grammar_load ( speech , name , path );
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-13 00:18:52 +00:00
return res ;
}
/*! \brief SpeechUnloadGrammar(Grammar Name) Dialplan Application */
static int speech_unload ( struct ast_channel * chan , void * data )
{
int res = 0 ;
2006-08-21 02:11:39 +00:00
struct ast_module_user * u = NULL ;
2006-04-13 00:18:52 +00:00
struct ast_speech * speech = find_speech ( chan );
2006-08-21 02:11:39 +00:00
u = ast_module_user_add ( chan );
2006-04-13 00:18:52 +00:00
if ( speech == NULL ) {
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-13 00:18:52 +00:00
return - 1 ;
}
/* Unload the grammar */
res = ast_speech_grammar_unload ( speech , data );
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-13 00:18:52 +00:00
return res ;
}
2006-04-10 23:29:50 +00:00
/*! \brief SpeechDeactivateGrammar(Grammar Name) Dialplan Application */
static int speech_deactivate ( struct ast_channel * chan , void * data )
{
int res = 0 ;
2006-08-21 02:11:39 +00:00
struct ast_module_user * u = NULL ;
2006-04-10 23:29:50 +00:00
struct ast_speech * speech = find_speech ( chan );
2006-08-21 02:11:39 +00:00
u = ast_module_user_add ( chan );
2006-04-10 23:29:50 +00:00
if ( speech == NULL ) {
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-10 23:29:50 +00:00
return - 1 ;
}
/* Deactivate the grammar on the speech object */
res = ast_speech_grammar_deactivate ( speech , data );
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-10 23:29:50 +00:00
return res ;
}
/*! \brief SpeechActivateGrammar(Grammar Name) Dialplan Application */
static int speech_activate ( struct ast_channel * chan , void * data )
{
int res = 0 ;
2006-08-21 02:11:39 +00:00
struct ast_module_user * u = NULL ;
2006-04-10 23:29:50 +00:00
struct ast_speech * speech = find_speech ( chan );
2006-08-21 02:11:39 +00:00
u = ast_module_user_add ( chan );
2006-04-10 23:29:50 +00:00
if ( speech == NULL ) {
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-10 23:29:50 +00:00
return - 1 ;
}
/* Activate the grammar on the speech object */
res = ast_speech_grammar_activate ( speech , data );
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-10 23:29:50 +00:00
return res ;
}
/*! \brief SpeechStart() Dialplan Application */
static int speech_start ( struct ast_channel * chan , void * data )
{
int res = 0 ;
2006-08-21 02:11:39 +00:00
struct ast_module_user * u = NULL ;
2006-04-10 23:29:50 +00:00
struct ast_speech * speech = find_speech ( chan );
2006-08-21 02:11:39 +00:00
u = ast_module_user_add ( chan );
2006-04-10 23:29:50 +00:00
if ( speech == NULL ) {
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-10 23:29:50 +00:00
return - 1 ;
}
ast_speech_start ( speech );
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-10 23:29:50 +00:00
return res ;
}
/*! \brief SpeechProcessingSound(Sound File) Dialplan Application */
static int speech_processing_sound ( struct ast_channel * chan , void * data )
{
int res = 0 ;
2006-08-21 02:11:39 +00:00
struct ast_module_user * u = NULL ;
2006-04-10 23:29:50 +00:00
struct ast_speech * speech = find_speech ( chan );
2006-08-21 02:11:39 +00:00
u = ast_module_user_add ( chan );
2006-04-10 23:29:50 +00:00
if ( speech == NULL ) {
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-10 23:29:50 +00:00
return - 1 ;
}
if ( speech -> processing_sound != NULL ) {
free ( speech -> processing_sound );
speech -> processing_sound = NULL ;
}
speech -> processing_sound = strdup ( data );
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-10 23:29:50 +00:00
return res ;
}
/*! \brief Helper function used by speech_background to playback a soundfile */
static int speech_streamfile ( struct ast_channel * chan , const char * filename , const char * preflang )
{
2007-01-10 20:25:44 +00:00
struct ast_filestream * fs = NULL ;
if ( ! ( fs = ast_openstream ( chan , filename , preflang )))
return - 1 ;
if ( ast_applystream ( chan , fs ))
return - 1 ;
if ( ast_playstream ( fs ))
return - 1 ;
return 0 ;
2006-04-10 23:29:50 +00:00
}
/*! \brief SpeechBackground(Sound File|Timeout) Dialplan Application */
static int speech_background ( struct ast_channel * chan , void * data )
{
2006-04-13 00:18:52 +00:00
unsigned int timeout = 0 ;
2007-02-08 17:54:32 +00:00
int res = 0 , done = 0 , argc = 0 , started = 0 , quieted = 0 ;
2006-08-21 02:11:39 +00:00
struct ast_module_user * u = NULL ;
2006-04-13 00:18:52 +00:00
struct ast_speech * speech = find_speech ( chan );
struct ast_frame * f = NULL ;
int oldreadformat = AST_FORMAT_SLINEAR ;
char dtmf [ AST_MAX_EXTENSION ] = "" ;
time_t start , current ;
struct ast_datastore * datastore = NULL ;
2007-01-10 20:25:44 +00:00
char * argv [ 2 ], * args = NULL , * filename_tmp = NULL , * filename = NULL , tmp [ 2 ] = "" ;
2006-04-13 00:18:52 +00:00
2006-05-10 13:22:15 +00:00
args = ast_strdupa ( data );
2006-04-10 23:29:50 +00:00
2006-08-21 02:11:39 +00:00
u = ast_module_user_add ( chan );
2006-04-10 23:29:50 +00:00
2006-04-13 00:18:52 +00:00
if ( speech == NULL ) {
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-13 00:18:52 +00:00
return - 1 ;
}
2006-04-10 23:29:50 +00:00
2006-05-15 15:30:21 +00:00
/* If channel is not already answered, then answer it */
if ( chan -> _state != AST_STATE_UP && ast_answer ( chan )) {
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-05-15 15:30:21 +00:00
return - 1 ;
}
2006-04-13 00:18:52 +00:00
/* Record old read format */
oldreadformat = chan -> readformat ;
2006-04-10 23:29:50 +00:00
2006-04-13 00:18:52 +00:00
/* Change read format to be signed linear */
if ( ast_set_read_format ( chan , AST_FORMAT_SLINEAR )) {
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-13 00:18:52 +00:00
return - 1 ;
}
2006-04-10 23:29:50 +00:00
2006-04-13 00:18:52 +00:00
/* Parse out options */
argc = ast_app_separate_args ( args , '|' , argv , sizeof ( argv ) / sizeof ( argv [ 0 ]));
if ( argc > 0 ) {
/* Yay sound file */
2007-01-10 20:25:44 +00:00
filename_tmp = ast_strdupa ( argv [ 0 ]);
2007-03-26 19:34:14 +00:00
if ( ! ast_strlen_zero ( argv [ 1 ])) {
if (( timeout = atoi ( argv [ 1 ])) == 0 )
timeout = - 1 ;
} else
timeout = 0 ;
2006-04-13 00:18:52 +00:00
}
2006-04-10 23:29:50 +00:00
2006-04-13 00:18:52 +00:00
/* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */
if ( speech -> state == AST_SPEECH_STATE_NOT_READY || speech -> state == AST_SPEECH_STATE_DONE ) {
ast_speech_change_state ( speech , AST_SPEECH_STATE_NOT_READY );
ast_speech_start ( speech );
}
2006-04-10 23:29:50 +00:00
2007-01-10 20:25:44 +00:00
/* Ensure no streams are currently running */
ast_stopstream ( chan );
2006-04-13 00:18:52 +00:00
/* Okay it's streaming so go into a loop grabbing frames! */
while ( done == 0 ) {
2007-01-10 20:25:44 +00:00
/* If the filename is null and stream is not running, start up a new sound file */
2007-02-21 20:03:38 +00:00
if ( ! quieted && ( chan -> streamid == - 1 && chan -> timingfunc == NULL ) && ( filename = strsep ( & filename_tmp , "&" ))) {
2007-01-10 20:25:44 +00:00
/* Discard old stream information */
ast_stopstream ( chan );
/* Start new stream */
speech_streamfile ( chan , filename , chan -> language );
}
2006-04-13 00:18:52 +00:00
/* Run scheduled stuff */
2006-04-10 23:29:50 +00:00
ast_sched_runq ( chan -> sched );
2006-04-13 00:18:52 +00:00
/* Yay scheduling */
res = ast_sched_wait ( chan -> sched );
if ( res < 0 ) {
res = 1000 ;
}
/* If there is a frame waiting, get it - if not - oh well */
if ( ast_waitfor ( chan , res ) > 0 ) {
f = ast_read ( chan );
if ( f == NULL ) {
/* The channel has hung up most likely */
done = 3 ;
break ;
}
}
/* Do timeout check (shared between audio/dtmf) */
2007-04-03 19:40:59 +00:00
if (( ! quieted || strlen ( dtmf )) && started == 1 ) {
2006-04-13 00:18:52 +00:00
time ( & current );
if (( current - start ) >= timeout ) {
done = 1 ;
2006-06-11 14:52:04 +00:00
if ( f )
ast_frfree ( f );
2006-04-10 23:29:50 +00:00
break ;
}
}
2006-04-13 00:18:52 +00:00
/* Do checks on speech structure to see if it's changed */
ast_mutex_lock ( & speech -> lock );
2007-02-28 17:45:50 +00:00
if ( ast_test_flag ( speech , AST_SPEECH_QUIET )) {
if ( chan -> stream )
ast_stopstream ( chan );
2006-04-10 23:29:50 +00:00
ast_clear_flag ( speech , AST_SPEECH_QUIET );
2007-02-08 17:54:32 +00:00
quieted = 1 ;
2006-04-13 00:18:52 +00:00
}
/* Check state so we can see what to do */
switch ( speech -> state ) {
case AST_SPEECH_STATE_READY :
/* If audio playback has stopped do a check for timeout purposes */
if ( chan -> streamid == - 1 && chan -> timingfunc == NULL )
ast_stopstream ( chan );
2007-03-26 19:34:14 +00:00
if ( ! quieted && chan -> stream == NULL && timeout && started == 0 && ! filename_tmp ) {
if ( timeout == - 1 ) {
done = 1 ;
if ( f )
ast_frfree ( f );
break ;
}
2006-04-13 00:18:52 +00:00
time ( & start );
started = 1 ;
}
2007-02-16 00:48:48 +00:00
/* Write audio frame out to speech engine if no DTMF has been received */
if ( ! strlen ( dtmf ) && f != NULL && f -> frametype == AST_FRAME_VOICE ) {
2006-04-13 00:18:52 +00:00
ast_speech_write ( speech , f -> data , f -> datalen );
}
break ;
case AST_SPEECH_STATE_WAIT :
/* Cue up waiting sound if not already playing */
2007-02-16 00:48:48 +00:00
if ( ! strlen ( dtmf )) {
if ( chan -> stream == NULL ) {
if ( speech -> processing_sound != NULL ) {
if ( strlen ( speech -> processing_sound ) > 0 && strcasecmp ( speech -> processing_sound , "none" )) {
speech_streamfile ( chan , speech -> processing_sound , chan -> language );
}
}
} else if ( chan -> streamid == - 1 && chan -> timingfunc == NULL ) {
ast_stopstream ( chan );
if ( speech -> processing_sound != NULL ) {
if ( strlen ( speech -> processing_sound ) > 0 && strcasecmp ( speech -> processing_sound , "none" )) {
speech_streamfile ( chan , speech -> processing_sound , chan -> language );
}
}
}
}
2006-04-13 00:18:52 +00:00
break ;
case AST_SPEECH_STATE_DONE :
2007-02-16 00:48:48 +00:00
/* Now that we are done... let's switch back to not ready state */
2006-04-13 00:18:52 +00:00
ast_speech_change_state ( speech , AST_SPEECH_STATE_NOT_READY );
2007-02-16 00:48:48 +00:00
if ( ! strlen ( dtmf )) {
/* Copy to speech structure the results, if available */
speech -> results = ast_speech_results_get ( speech );
/* Break out of our background too */
done = 1 ;
/* Stop audio playback */
if ( chan -> stream != NULL ) {
ast_stopstream ( chan );
}
}
2006-04-13 00:18:52 +00:00
break ;
default :
break ;
}
ast_mutex_unlock ( & speech -> lock );
/* Deal with other frame types */
if ( f != NULL ) {
/* Free the frame we received */
switch ( f -> frametype ) {
case AST_FRAME_DTMF :
2006-04-10 23:29:50 +00:00
if ( f -> subclass == '#' ) {
done = 1 ;
} else {
if ( chan -> stream != NULL ) {
ast_stopstream ( chan );
2007-04-03 19:40:59 +00:00
}
if ( ! started ) {
2006-04-13 00:18:52 +00:00
/* Change timeout to be 5 seconds for DTMF input */
2007-03-26 18:13:06 +00:00
timeout = ( chan -> pbx && chan -> pbx -> dtimeout ) ? chan -> pbx -> dtimeout : 5 ;
2006-04-13 00:18:52 +00:00
started = 1 ;
2006-04-10 23:29:50 +00:00
}
2007-04-03 19:40:59 +00:00
time ( & start );
2006-04-10 23:29:50 +00:00
snprintf ( tmp , sizeof ( tmp ), "%c" , f -> subclass );
strncat ( dtmf , tmp , sizeof ( dtmf ));
}
2006-04-13 00:18:52 +00:00
break ;
case AST_FRAME_CONTROL :
switch ( f -> subclass ) {
case AST_CONTROL_HANGUP :
/* Since they hung up we should destroy the speech structure */
done = 3 ;
default :
break ;
}
default :
break ;
}
ast_frfree ( f );
f = NULL ;
}
}
2006-04-10 23:29:50 +00:00
2007-02-16 00:48:48 +00:00
if ( strlen ( dtmf )) {
2006-04-13 00:18:52 +00:00
/* We sort of make a results entry */
speech -> results = ast_calloc ( 1 , sizeof ( * speech -> results ));
if ( speech -> results != NULL ) {
speech -> results -> score = 1000 ;
speech -> results -> text = strdup ( dtmf );
speech -> results -> grammar = strdup ( "dtmf" );
2006-04-10 23:29:50 +00:00
}
}
2006-04-13 00:18:52 +00:00
/* See if it was because they hung up */
if ( done == 3 ) {
/* Destroy speech structure */
ast_speech_destroy ( speech );
datastore = ast_channel_datastore_find ( chan , & speech_datastore , NULL );
if ( datastore != NULL ) {
ast_channel_datastore_remove ( chan , datastore );
}
} else {
/* Channel is okay so restore read format */
ast_set_read_format ( chan , oldreadformat );
}
2006-04-10 23:29:50 +00:00
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-13 00:18:52 +00:00
return 0 ;
2006-04-10 23:29:50 +00:00
}
2006-04-13 00:18:52 +00:00
2006-04-10 23:29:50 +00:00
/*! \brief SpeechDestroy() Dialplan Application */
static int speech_destroy ( struct ast_channel * chan , void * data )
{
int res = 0 ;
2006-08-21 02:11:39 +00:00
struct ast_module_user * u = NULL ;
2006-04-10 23:29:50 +00:00
struct ast_speech * speech = find_speech ( chan );
struct ast_datastore * datastore = NULL ;
2006-08-21 02:11:39 +00:00
u = ast_module_user_add ( chan );
2006-04-10 23:29:50 +00:00
if ( speech == NULL ) {
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-10 23:29:50 +00:00
return - 1 ;
}
/* Destroy speech structure */
ast_speech_destroy ( speech );
datastore = ast_channel_datastore_find ( chan , & speech_datastore , NULL );
if ( datastore != NULL ) {
ast_channel_datastore_remove ( chan , datastore );
}
2006-08-21 02:11:39 +00:00
ast_module_user_remove ( u );
2006-04-10 23:29:50 +00:00
return res ;
}
2006-08-21 02:11:39 +00:00
static int unload_module ( void )
2006-04-10 23:29:50 +00:00
{
int res = 0 ;
res = ast_unregister_application ( "SpeechCreate" );
2006-04-13 00:18:52 +00:00
res |= ast_unregister_application ( "SpeechLoadGrammar" );
res |= ast_unregister_application ( "SpeechUnloadGrammar" );
2006-04-10 23:29:50 +00:00
res |= ast_unregister_application ( "SpeechActivateGrammar" );
res |= ast_unregister_application ( "SpeechDeactivateGrammar" );
res |= ast_unregister_application ( "SpeechStart" );
res |= ast_unregister_application ( "SpeechBackground" );
res |= ast_unregister_application ( "SpeechDestroy" );
2006-04-13 00:18:52 +00:00
res |= ast_unregister_application ( "SpeechProcessingSound" );
res |= ast_custom_function_unregister ( & speech_function );
res |= ast_custom_function_unregister ( & speech_score_function );
res |= ast_custom_function_unregister ( & speech_text_function );
res |= ast_custom_function_unregister ( & speech_grammar_function );
2006-07-18 16:22:26 +00:00
res |= ast_custom_function_unregister ( & speech_engine_function );
2007-04-06 01:14:00 +00:00
res |= ast_custom_function_unregister ( & speech_results_type_function );
2006-04-10 23:29:50 +00:00
2006-08-21 02:11:39 +00:00
ast_module_user_hangup_all ();
2006-04-10 23:29:50 +00:00
return res ;
}
2006-08-21 02:11:39 +00:00
static int load_module ( void )
2006-04-10 23:29:50 +00:00
{
int res = 0 ;
res = ast_register_application ( "SpeechCreate" , speech_create , "Create a Speech Structure" , speechcreate_descrip );
2006-04-13 00:18:52 +00:00
res |= ast_register_application ( "SpeechLoadGrammar" , speech_load , "Load a Grammar" , speechload_descrip );
res |= ast_register_application ( "SpeechUnloadGrammar" , speech_unload , "Unload a Grammar" , speechunload_descrip );
2006-04-10 23:29:50 +00:00
res |= ast_register_application ( "SpeechActivateGrammar" , speech_activate , "Activate a Grammar" , speechactivategrammar_descrip );
res |= ast_register_application ( "SpeechDeactivateGrammar" , speech_deactivate , "Deactivate a Grammar" , speechdeactivategrammar_descrip );
2006-05-30 16:01:50 +00:00
res |= ast_register_application ( "SpeechStart" , speech_start , "Start recognizing voice in the audio stream" , speechstart_descrip );
2006-04-10 23:29:50 +00:00
res |= ast_register_application ( "SpeechBackground" , speech_background , "Play a sound file and wait for speech to be recognized" , speechbackground_descrip );
res |= ast_register_application ( "SpeechDestroy" , speech_destroy , "End speech recognition" , speechdestroy_descrip );
res |= ast_register_application ( "SpeechProcessingSound" , speech_processing_sound , "Change background processing sound" , speechprocessingsound_descrip );
2006-04-13 00:18:52 +00:00
res |= ast_custom_function_register ( & speech_function );
res |= ast_custom_function_register ( & speech_score_function );
res |= ast_custom_function_register ( & speech_text_function );
res |= ast_custom_function_register ( & speech_grammar_function );
2006-07-18 16:22:26 +00:00
res |= ast_custom_function_register ( & speech_engine_function );
2007-04-06 01:14:00 +00:00
res |= ast_custom_function_register ( & speech_results_type_function );
2006-04-13 00:18:52 +00:00
2006-04-10 23:29:50 +00:00
return res ;
}
2006-08-21 02:11:39 +00:00
AST_MODULE_INFO_STANDARD ( ASTERISK_GPL_KEY , "Dialplan Speech Applications" );