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