00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "asterisk.h"
00029
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 284610 $");
00031
00032 #include "asterisk/file.h"
00033 #include "asterisk/channel.h"
00034 #include "asterisk/pbx.h"
00035 #include "asterisk/module.h"
00036 #include "asterisk/lock.h"
00037 #include "asterisk/app.h"
00038 #include "asterisk/speech.h"
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249 static void destroy_callback(void *data)
00250 {
00251 struct ast_speech *speech = (struct ast_speech*)data;
00252
00253 if (speech == NULL) {
00254 return;
00255 }
00256
00257
00258 ast_speech_destroy(speech);
00259
00260 return;
00261 }
00262
00263
00264 static const struct ast_datastore_info speech_datastore = {
00265 .type = "speech",
00266 .destroy = destroy_callback
00267 };
00268
00269
00270 static struct ast_speech *find_speech(struct ast_channel *chan)
00271 {
00272 struct ast_speech *speech = NULL;
00273 struct ast_datastore *datastore = NULL;
00274
00275 datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
00276 if (datastore == NULL) {
00277 return NULL;
00278 }
00279 speech = datastore->data;
00280
00281 return speech;
00282 }
00283
00284
00285 static struct ast_speech_result *find_result(struct ast_speech_result *results, char *result_num)
00286 {
00287 struct ast_speech_result *result = results;
00288 char *tmp = NULL;
00289 int nbest_num = 0, wanted_num = 0, i = 0;
00290
00291 if (!result) {
00292 return NULL;
00293 }
00294
00295 if ((tmp = strchr(result_num, '/'))) {
00296 *tmp++ = '\0';
00297 nbest_num = atoi(result_num);
00298 wanted_num = atoi(tmp);
00299 } else {
00300 wanted_num = atoi(result_num);
00301 }
00302
00303 do {
00304 if (result->nbest_num != nbest_num)
00305 continue;
00306 if (i == wanted_num)
00307 break;
00308 i++;
00309 } while ((result = AST_LIST_NEXT(result, list)));
00310
00311 return result;
00312 }
00313
00314
00315 static int speech_score(struct ast_channel *chan, const char *cmd, char *data,
00316 char *buf, size_t len)
00317 {
00318 struct ast_speech_result *result = NULL;
00319 struct ast_speech *speech = find_speech(chan);
00320 char tmp[128] = "";
00321
00322 if (data == NULL || speech == NULL || !(result = find_result(speech->results, data))) {
00323 return -1;
00324 }
00325
00326 snprintf(tmp, sizeof(tmp), "%d", result->score);
00327
00328 ast_copy_string(buf, tmp, len);
00329
00330 return 0;
00331 }
00332
00333 static struct ast_custom_function speech_score_function = {
00334 .name = "SPEECH_SCORE",
00335 .read = speech_score,
00336 .write = NULL,
00337 };
00338
00339
00340 static int speech_text(struct ast_channel *chan, const char *cmd, char *data,
00341 char *buf, size_t len)
00342 {
00343 struct ast_speech_result *result = NULL;
00344 struct ast_speech *speech = find_speech(chan);
00345
00346 if (data == NULL || speech == NULL || !(result = find_result(speech->results, data))) {
00347 return -1;
00348 }
00349
00350 if (result->text != NULL) {
00351 ast_copy_string(buf, result->text, len);
00352 } else {
00353 buf[0] = '\0';
00354 }
00355
00356 return 0;
00357 }
00358
00359 static struct ast_custom_function speech_text_function = {
00360 .name = "SPEECH_TEXT",
00361 .read = speech_text,
00362 .write = NULL,
00363 };
00364
00365
00366 static int speech_grammar(struct ast_channel *chan, const char *cmd, char *data,
00367 char *buf, size_t len)
00368 {
00369 struct ast_speech_result *result = NULL;
00370 struct ast_speech *speech = find_speech(chan);
00371
00372 if (data == NULL || speech == NULL || !(result = find_result(speech->results, data))) {
00373 return -1;
00374 }
00375
00376 if (result->grammar != NULL) {
00377 ast_copy_string(buf, result->grammar, len);
00378 } else {
00379 buf[0] = '\0';
00380 }
00381
00382 return 0;
00383 }
00384
00385 static struct ast_custom_function speech_grammar_function = {
00386 .name = "SPEECH_GRAMMAR",
00387 .read = speech_grammar,
00388 .write = NULL,
00389 };
00390
00391
00392 static int speech_engine_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00393 {
00394 struct ast_speech *speech = find_speech(chan);
00395
00396 if (data == NULL || speech == NULL) {
00397 return -1;
00398 }
00399
00400 ast_speech_change(speech, data, value);
00401
00402 return 0;
00403 }
00404
00405 static struct ast_custom_function speech_engine_function = {
00406 .name = "SPEECH_ENGINE",
00407 .read = NULL,
00408 .write = speech_engine_write,
00409 };
00410
00411
00412 static int speech_results_type_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00413 {
00414 struct ast_speech *speech = find_speech(chan);
00415
00416 if (data == NULL || speech == NULL)
00417 return -1;
00418
00419 if (!strcasecmp(value, "normal"))
00420 ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NORMAL);
00421 else if (!strcasecmp(value, "nbest"))
00422 ast_speech_change_results_type(speech, AST_SPEECH_RESULTS_TYPE_NBEST);
00423
00424 return 0;
00425 }
00426
00427 static struct ast_custom_function speech_results_type_function = {
00428 .name = "SPEECH_RESULTS_TYPE",
00429 .read = NULL,
00430 .write = speech_results_type_write,
00431 };
00432
00433
00434 static int speech_read(struct ast_channel *chan, const char *cmd, char *data,
00435 char *buf, size_t len)
00436 {
00437 int results = 0;
00438 struct ast_speech_result *result = NULL;
00439 struct ast_speech *speech = find_speech(chan);
00440 char tmp[128] = "";
00441
00442
00443 if (!strcasecmp(data, "status")) {
00444 if (speech != NULL)
00445 ast_copy_string(buf, "1", len);
00446 else
00447 ast_copy_string(buf, "0", len);
00448 return 0;
00449 }
00450
00451
00452 if (speech == NULL) {
00453 return -1;
00454 }
00455
00456
00457 if (!strcasecmp(data, "spoke")) {
00458 if (ast_test_flag(speech, AST_SPEECH_SPOKE))
00459 ast_copy_string(buf, "1", len);
00460 else
00461 ast_copy_string(buf, "0", len);
00462 } else if (!strcasecmp(data, "results")) {
00463
00464 for (result = speech->results; result; result = AST_LIST_NEXT(result, list))
00465 results++;
00466 snprintf(tmp, sizeof(tmp), "%d", results);
00467 ast_copy_string(buf, tmp, len);
00468 } else {
00469 buf[0] = '\0';
00470 }
00471
00472 return 0;
00473 }
00474
00475 static struct ast_custom_function speech_function = {
00476 .name = "SPEECH",
00477 .read = speech_read,
00478 .write = NULL,
00479 };
00480
00481
00482
00483
00484 static int speech_create(struct ast_channel *chan, const char *data)
00485 {
00486 struct ast_speech *speech = NULL;
00487 struct ast_datastore *datastore = NULL;
00488
00489
00490 speech = ast_speech_new(data, chan->nativeformats);
00491 if (speech == NULL) {
00492
00493 pbx_builtin_setvar_helper(chan, "ERROR", "1");
00494 return 0;
00495 }
00496
00497 datastore = ast_datastore_alloc(&speech_datastore, NULL);
00498 if (datastore == NULL) {
00499 ast_speech_destroy(speech);
00500 pbx_builtin_setvar_helper(chan, "ERROR", "1");
00501 return 0;
00502 }
00503 pbx_builtin_setvar_helper(chan, "ERROR", NULL);
00504 datastore->data = speech;
00505 ast_channel_datastore_add(chan, datastore);
00506
00507 return 0;
00508 }
00509
00510
00511 static int speech_load(struct ast_channel *chan, const char *vdata)
00512 {
00513 int res = 0;
00514 struct ast_speech *speech = find_speech(chan);
00515 char *data;
00516 AST_DECLARE_APP_ARGS(args,
00517 AST_APP_ARG(grammar);
00518 AST_APP_ARG(path);
00519 );
00520
00521 data = ast_strdupa(vdata);
00522 AST_STANDARD_APP_ARGS(args, data);
00523
00524 if (speech == NULL)
00525 return -1;
00526
00527 if (args.argc != 2)
00528 return -1;
00529
00530
00531 res = ast_speech_grammar_load(speech, args.grammar, args.path);
00532
00533 return res;
00534 }
00535
00536
00537 static int speech_unload(struct ast_channel *chan, const char *data)
00538 {
00539 int res = 0;
00540 struct ast_speech *speech = find_speech(chan);
00541
00542 if (speech == NULL)
00543 return -1;
00544
00545
00546 res = ast_speech_grammar_unload(speech, data);
00547
00548 return res;
00549 }
00550
00551
00552 static int speech_deactivate(struct ast_channel *chan, const char *data)
00553 {
00554 int res = 0;
00555 struct ast_speech *speech = find_speech(chan);
00556
00557 if (speech == NULL)
00558 return -1;
00559
00560
00561 res = ast_speech_grammar_deactivate(speech, data);
00562
00563 return res;
00564 }
00565
00566
00567 static int speech_activate(struct ast_channel *chan, const char *data)
00568 {
00569 int res = 0;
00570 struct ast_speech *speech = find_speech(chan);
00571
00572 if (speech == NULL)
00573 return -1;
00574
00575
00576 res = ast_speech_grammar_activate(speech, data);
00577
00578 return res;
00579 }
00580
00581
00582 static int speech_start(struct ast_channel *chan, const char *data)
00583 {
00584 int res = 0;
00585 struct ast_speech *speech = find_speech(chan);
00586
00587 if (speech == NULL)
00588 return -1;
00589
00590 ast_speech_start(speech);
00591
00592 return res;
00593 }
00594
00595
00596 static int speech_processing_sound(struct ast_channel *chan, const char *data)
00597 {
00598 int res = 0;
00599 struct ast_speech *speech = find_speech(chan);
00600
00601 if (speech == NULL)
00602 return -1;
00603
00604 if (speech->processing_sound != NULL) {
00605 ast_free(speech->processing_sound);
00606 speech->processing_sound = NULL;
00607 }
00608
00609 speech->processing_sound = ast_strdup(data);
00610
00611 return res;
00612 }
00613
00614
00615 static int speech_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
00616 {
00617 struct ast_filestream *fs = NULL;
00618
00619 if (!(fs = ast_openstream(chan, filename, preflang)))
00620 return -1;
00621
00622 if (ast_applystream(chan, fs))
00623 return -1;
00624
00625 ast_playstream(fs);
00626
00627 return 0;
00628 }
00629
00630 enum {
00631 SB_OPT_NOANSWER = (1 << 0),
00632 };
00633
00634 AST_APP_OPTIONS(speech_background_options, BEGIN_OPTIONS
00635 AST_APP_OPTION('n', SB_OPT_NOANSWER),
00636 END_OPTIONS );
00637
00638
00639 static int speech_background(struct ast_channel *chan, const char *data)
00640 {
00641 unsigned int timeout = 0;
00642 int res = 0, done = 0, started = 0, quieted = 0, max_dtmf_len = 0;
00643 struct ast_speech *speech = find_speech(chan);
00644 struct ast_frame *f = NULL;
00645 int oldreadformat = AST_FORMAT_SLINEAR;
00646 char dtmf[AST_MAX_EXTENSION] = "";
00647 struct timeval start = { 0, 0 }, current;
00648 struct ast_datastore *datastore = NULL;
00649 char *parse, *filename_tmp = NULL, *filename = NULL, tmp[2] = "", dtmf_terminator = '#';
00650 const char *tmp2 = NULL;
00651 struct ast_flags options = { 0 };
00652 AST_DECLARE_APP_ARGS(args,
00653 AST_APP_ARG(soundfile);
00654 AST_APP_ARG(timeout);
00655 AST_APP_ARG(options);
00656 );
00657
00658 parse = ast_strdupa(data);
00659 AST_STANDARD_APP_ARGS(args, parse);
00660
00661 if (speech == NULL)
00662 return -1;
00663
00664 if (!ast_strlen_zero(args.options)) {
00665 char *options_buf = ast_strdupa(args.options);
00666 ast_app_parse_options(speech_background_options, &options, NULL, options_buf);
00667 }
00668
00669
00670 if (chan->_state != AST_STATE_UP && !ast_test_flag(&options, SB_OPT_NOANSWER)
00671 && ast_answer(chan)) {
00672 return -1;
00673 }
00674
00675
00676 oldreadformat = chan->readformat;
00677
00678
00679 if (ast_set_read_format(chan, speech->format))
00680 return -1;
00681
00682 if (!ast_strlen_zero(args.soundfile)) {
00683
00684 filename_tmp = ast_strdupa(args.soundfile);
00685 if (!ast_strlen_zero(args.timeout)) {
00686 if ((timeout = atof(args.timeout) * 1000.0) == 0)
00687 timeout = -1;
00688 } else
00689 timeout = 0;
00690 }
00691
00692
00693 ast_channel_lock(chan);
00694 if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_MAXLEN")) && !ast_strlen_zero(tmp2)) {
00695 max_dtmf_len = atoi(tmp2);
00696 }
00697
00698
00699 if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_TERMINATOR"))) {
00700 if (ast_strlen_zero(tmp2))
00701 dtmf_terminator = '\0';
00702 else
00703 dtmf_terminator = tmp2[0];
00704 }
00705 ast_channel_unlock(chan);
00706
00707
00708 if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) {
00709 ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
00710 ast_speech_start(speech);
00711 }
00712
00713
00714 ast_stopstream(chan);
00715
00716
00717 while (done == 0) {
00718
00719 if (!quieted && (chan->streamid == -1 && chan->timingfunc == NULL) && (filename = strsep(&filename_tmp, "&"))) {
00720
00721 ast_stopstream(chan);
00722
00723 speech_streamfile(chan, filename, chan->language);
00724 }
00725
00726
00727 ast_sched_runq(chan->sched);
00728
00729
00730 res = ast_sched_wait(chan->sched);
00731 if (res < 0)
00732 res = 1000;
00733
00734
00735 if (ast_waitfor(chan, res) > 0) {
00736 f = ast_read(chan);
00737 if (f == NULL) {
00738
00739 done = 3;
00740 break;
00741 }
00742 }
00743
00744
00745 if ((!quieted || strlen(dtmf)) && started == 1) {
00746 current = ast_tvnow();
00747 if ((ast_tvdiff_ms(current, start)) >= timeout) {
00748 done = 1;
00749 if (f)
00750 ast_frfree(f);
00751 break;
00752 }
00753 }
00754
00755
00756 ast_mutex_lock(&speech->lock);
00757 if (ast_test_flag(speech, AST_SPEECH_QUIET)) {
00758 if (chan->stream)
00759 ast_stopstream(chan);
00760 ast_clear_flag(speech, AST_SPEECH_QUIET);
00761 quieted = 1;
00762 }
00763
00764 switch (speech->state) {
00765 case AST_SPEECH_STATE_READY:
00766
00767 if (chan->streamid == -1 && chan->timingfunc == NULL)
00768 ast_stopstream(chan);
00769 if (!quieted && chan->stream == NULL && timeout && started == 0 && !filename_tmp) {
00770 if (timeout == -1) {
00771 done = 1;
00772 if (f)
00773 ast_frfree(f);
00774 break;
00775 }
00776 start = ast_tvnow();
00777 started = 1;
00778 }
00779
00780 if (!strlen(dtmf) && f != NULL && f->frametype == AST_FRAME_VOICE) {
00781 ast_speech_write(speech, f->data.ptr, f->datalen);
00782 }
00783 break;
00784 case AST_SPEECH_STATE_WAIT:
00785
00786 if (!strlen(dtmf)) {
00787 if (chan->stream == NULL) {
00788 if (speech->processing_sound != NULL) {
00789 if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound, "none")) {
00790 speech_streamfile(chan, speech->processing_sound, chan->language);
00791 }
00792 }
00793 } else if (chan->streamid == -1 && chan->timingfunc == NULL) {
00794 ast_stopstream(chan);
00795 if (speech->processing_sound != NULL) {
00796 if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound, "none")) {
00797 speech_streamfile(chan, speech->processing_sound, chan->language);
00798 }
00799 }
00800 }
00801 }
00802 break;
00803 case AST_SPEECH_STATE_DONE:
00804
00805 ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
00806 if (!strlen(dtmf)) {
00807
00808 speech->results = ast_speech_results_get(speech);
00809
00810 done = 1;
00811
00812 if (chan->stream != NULL) {
00813 ast_stopstream(chan);
00814 }
00815 }
00816 break;
00817 default:
00818 break;
00819 }
00820 ast_mutex_unlock(&speech->lock);
00821
00822
00823 if (f != NULL) {
00824
00825 switch (f->frametype) {
00826 case AST_FRAME_DTMF:
00827 if (dtmf_terminator != '\0' && f->subclass.integer == dtmf_terminator) {
00828 done = 1;
00829 } else {
00830 quieted = 1;
00831 if (chan->stream != NULL) {
00832 ast_stopstream(chan);
00833 }
00834 if (!started) {
00835
00836 timeout = (chan->pbx && chan->pbx->dtimeoutms) ? chan->pbx->dtimeoutms : 5000;
00837 started = 1;
00838 }
00839 start = ast_tvnow();
00840 snprintf(tmp, sizeof(tmp), "%c", f->subclass.integer);
00841 strncat(dtmf, tmp, sizeof(dtmf) - strlen(dtmf) - 1);
00842
00843 if (max_dtmf_len && strlen(dtmf) == max_dtmf_len)
00844 done = 1;
00845 }
00846 break;
00847 case AST_FRAME_CONTROL:
00848 switch (f->subclass.integer) {
00849 case AST_CONTROL_HANGUP:
00850
00851 done = 3;
00852 default:
00853 break;
00854 }
00855 default:
00856 break;
00857 }
00858 ast_frfree(f);
00859 f = NULL;
00860 }
00861 }
00862
00863 if (!ast_strlen_zero(dtmf)) {
00864
00865 speech->results = ast_calloc(1, sizeof(*speech->results));
00866 if (speech->results != NULL) {
00867 ast_speech_dtmf(speech, dtmf);
00868 speech->results->score = 1000;
00869 speech->results->text = ast_strdup(dtmf);
00870 speech->results->grammar = ast_strdup("dtmf");
00871 }
00872 ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY);
00873 }
00874
00875
00876 if (done == 3) {
00877
00878 ast_speech_destroy(speech);
00879 datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
00880 if (datastore != NULL)
00881 ast_channel_datastore_remove(chan, datastore);
00882 } else {
00883
00884 ast_set_read_format(chan, oldreadformat);
00885 }
00886
00887 return 0;
00888 }
00889
00890
00891
00892 static int speech_destroy(struct ast_channel *chan, const char *data)
00893 {
00894 int res = 0;
00895 struct ast_speech *speech = find_speech(chan);
00896 struct ast_datastore *datastore = NULL;
00897
00898 if (speech == NULL)
00899 return -1;
00900
00901
00902 ast_speech_destroy(speech);
00903
00904 datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL);
00905 if (datastore != NULL) {
00906 ast_channel_datastore_remove(chan, datastore);
00907 }
00908
00909 return res;
00910 }
00911
00912 static int unload_module(void)
00913 {
00914 int res = 0;
00915
00916 res = ast_unregister_application("SpeechCreate");
00917 res |= ast_unregister_application("SpeechLoadGrammar");
00918 res |= ast_unregister_application("SpeechUnloadGrammar");
00919 res |= ast_unregister_application("SpeechActivateGrammar");
00920 res |= ast_unregister_application("SpeechDeactivateGrammar");
00921 res |= ast_unregister_application("SpeechStart");
00922 res |= ast_unregister_application("SpeechBackground");
00923 res |= ast_unregister_application("SpeechDestroy");
00924 res |= ast_unregister_application("SpeechProcessingSound");
00925 res |= ast_custom_function_unregister(&speech_function);
00926 res |= ast_custom_function_unregister(&speech_score_function);
00927 res |= ast_custom_function_unregister(&speech_text_function);
00928 res |= ast_custom_function_unregister(&speech_grammar_function);
00929 res |= ast_custom_function_unregister(&speech_engine_function);
00930 res |= ast_custom_function_unregister(&speech_results_type_function);
00931
00932 return res;
00933 }
00934
00935 static int load_module(void)
00936 {
00937 int res = 0;
00938
00939 res = ast_register_application_xml("SpeechCreate", speech_create);
00940 res |= ast_register_application_xml("SpeechLoadGrammar", speech_load);
00941 res |= ast_register_application_xml("SpeechUnloadGrammar", speech_unload);
00942 res |= ast_register_application_xml("SpeechActivateGrammar", speech_activate);
00943 res |= ast_register_application_xml("SpeechDeactivateGrammar", speech_deactivate);
00944 res |= ast_register_application_xml("SpeechStart", speech_start);
00945 res |= ast_register_application_xml("SpeechBackground", speech_background);
00946 res |= ast_register_application_xml("SpeechDestroy", speech_destroy);
00947 res |= ast_register_application_xml("SpeechProcessingSound", speech_processing_sound);
00948 res |= ast_custom_function_register(&speech_function);
00949 res |= ast_custom_function_register(&speech_score_function);
00950 res |= ast_custom_function_register(&speech_text_function);
00951 res |= ast_custom_function_register(&speech_grammar_function);
00952 res |= ast_custom_function_register(&speech_engine_function);
00953 res |= ast_custom_function_register(&speech_results_type_function);
00954
00955 return res;
00956 }
00957
00958 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Dialplan Speech Applications",
00959 .load = load_module,
00960 .unload = unload_module,
00961 .nonoptreq = "res_speech",
00962 );