static void cepstral_text_param_tts(switch_speech_handle_t *sh, char *param, const char *val) { cepstral_t *cepstral; cepstral = sh->private_info; assert(cepstral != NULL); if (!strcasecmp(param, "voice")) { const char *voice_name = val; if (!strcasecmp(voice_name, "next")) { if ((cepstral->voice = swift_port_find_next_voice(cepstral->port))) { if (SWIFT_FAILED(swift_port_set_voice(cepstral->port, cepstral->voice))) { cepstral->done = cepstral->done_gen = 1; return; } voice_name = swift_voice_get_attribute(cepstral->voice, "name"); } else { voice_name = NULL; } } else { if (voice_name && SWIFT_FAILED(swift_port_set_voice_by_name(cepstral->port, voice_name))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid voice %s!\n", voice_name); voice_name = NULL; } } if (!voice_name) { /* Find the first voice on the system */ if ((cepstral->voice = swift_port_find_first_voice(cepstral->port, NULL, NULL)) == NULL) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to find any voices!\n"); cepstral->done = cepstral->done_gen = 1; return; } /* Set the voice found by find_first_voice() as the port's current voice */ if (SWIFT_FAILED(swift_port_set_voice(cepstral->port, cepstral->voice))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to set voice.\n"); cepstral->done = cepstral->done_gen = 1; return; } voice_name = swift_voice_get_attribute(cepstral->voice, "name"); } if (voice_name) { switch_copy_string(sh->voice, voice_name, sizeof(sh->voice)); } return; } swift_port_set_param_string(cepstral->port, param, val, NULL); }
/** Swift engine callback */ static swift_result_t mrcp_swift_write_audio(swift_event *event, swift_event_t type, void *udata) { void *buf; int len; mrcp_swift_channel_t *synth_channel = udata; swift_event_t rv = SWIFT_SUCCESS; if(type & SWIFT_EVENT_END) { apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Swift Engine: Write End-of-Speech Event"); mpf_buffer_event_write(synth_channel->audio_buffer,MEDIA_FRAME_TYPE_EVENT); return rv; } rv = swift_event_get_audio(event, &buf, &len); if(!SWIFT_FAILED(rv)) { #if 0 /* Get the event times */ float time_start, time_len; swift_event_get_times(event, &time_start, &time_len); apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Swift Engine: Write Audio [%d | %0.4f | %0.4f]",len, time_start, time_len); #endif mpf_buffer_audio_write(synth_channel->audio_buffer,buf,len); } return rv; }
/** Set Swift port param */ static apt_bool_t mrcp_swift_channel_param_set(mrcp_swift_channel_t *synth_channel, const char *name, swift_val *val) { swift_result_t res; if(SWIFT_FAILED(res = swift_port_set_param(synth_channel->port,name,val,synth_channel->tts_stream)) ) { const char *error_string = swift_strerror(res); apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Swift Param %s: %s",name,error_string); return FALSE; } return TRUE; }
/* This callback caches the audio in the buffer */ static swift_result_t write_audio(swift_event * event, swift_event_t type, void *udata) { cepstral_t *cepstral; swift_event_t rv = SWIFT_SUCCESS; void *buf = NULL; int len = 0, i = 0; cepstral = udata; assert(cepstral != NULL); if (!cepstral->port || cepstral->done || cepstral->done_gen) { return SWIFT_UNKNOWN_ERROR; } /* Only proceed when we have success */ if (!SWIFT_FAILED((rv = swift_event_get_audio(event, &buf, &len)))) { while (!cepstral->done) { switch_mutex_lock(cepstral->audio_lock); if (switch_buffer_write(cepstral->audio_buffer, buf, len) > 0) { switch_mutex_unlock(cepstral->audio_lock); break; } switch_mutex_unlock(cepstral->audio_lock); if (!cepstral->done) { for (i = 0; i < 10; i++) { switch_yield(10000); if (cepstral->done) { break; } } } } } else { cepstral->done = 1; } if (cepstral->done) { rv = SWIFT_UNKNOWN_ERROR; } return rv; }
/** Set voice matching specified criteria */ static apt_bool_t mrcp_swift_channel_voice_set(mrcp_swift_channel_t *synth_channel, mrcp_message_t *message) { mrcp_synth_header_t *synth_header = mrcp_resource_header_get(message); char search_criteria[1024]; int offset = 0; swift_voice *voice; swift_result_t res; if(!synth_header) { /* no params to set */ return TRUE; } if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_VOICE_NAME) == TRUE) { offset += search_criteria_delimiter_add(search_criteria+offset,sizeof(search_criteria)-offset,(offset == 0)); offset += apr_snprintf(search_criteria+offset,sizeof(search_criteria)-offset,"speaker/name=%s",synth_header->voice_param.name.buf); } if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_VOICE_GENDER) == TRUE) { switch(synth_header->voice_param.gender) { case VOICE_GENDER_MALE: offset += search_criteria_delimiter_add(search_criteria+offset,sizeof(search_criteria)-offset,offset == 0); offset += apr_snprintf(search_criteria+offset,sizeof(search_criteria)-offset,"speaker/gender=male"); break; case VOICE_GENDER_FEMALE: offset += search_criteria_delimiter_add(search_criteria+offset,sizeof(search_criteria)-offset,offset == 0); offset += apr_snprintf(search_criteria+offset,sizeof(search_criteria)-offset,"speaker/gender=female"); break; default: break; } } if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_VOICE_AGE) == TRUE) { offset += search_criteria_delimiter_add(search_criteria+offset,sizeof(search_criteria)-offset,offset == 0); offset += apr_snprintf(search_criteria+offset,sizeof(search_criteria)-offset,"speaker/age=%d",synth_header->voice_param.age); } if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_SPEECH_LANGUAGE) == TRUE) { const char *swift_lang_name = NULL; if(swift_speech_language_table) { swift_lang_name = apr_table_get(swift_speech_language_table,synth_header->speech_language.buf); } if(!swift_lang_name) { swift_lang_name = synth_header->speech_language.buf; } offset += search_criteria_delimiter_add(search_criteria+offset,sizeof(search_criteria)-offset,offset == 0); offset += apr_snprintf(search_criteria+offset,sizeof(search_criteria)-offset,"language/name=%s",swift_lang_name); } if(offset > 0) { apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Find Voices Matching the Criteria [%s]",search_criteria); if((voice = swift_port_find_first_voice(synth_channel->port,search_criteria,NULL)) == NULL) { apt_log(APT_LOG_MARK,APT_PRIO_INFO,"No Swift Voice Available Matching the Criteria [%s]",search_criteria); voice = swift_port_find_first_voice(synth_channel->port,NULL,NULL); } if(SWIFT_FAILED(res = swift_port_set_voice(synth_channel->port,voice)) ) { const char *error_string = swift_strerror(res); apt_log(APT_LOG_MARK,APT_PRIO_INFO,error_string); return FALSE; } } return TRUE; }
static int app_exec(struct ast_channel *chan, void *data) { int res = 0, argc = 0, max_digits = 0, timeout = 0, alreadyran = 0, old_writeformat = 0; int ms, len, availatend; char *argv[3], *parse = NULL, *text = NULL, *rc = NULL; char tmp_exten[2], results[20]; struct ast_module_user *u; struct ast_frame *f; struct timeval next; struct stuff *ps; struct myframe { struct ast_frame f; unsigned char offset[AST_FRIENDLY_OFFSET]; unsigned char frdata[framesize]; } myf; swift_engine *engine; swift_port *port = NULL; swift_voice *voice; swift_params *params; swift_result_t sresult; swift_background_t tts_stream; unsigned int event_mask; memset(results, 0 ,20); memset(tmp_exten, 0, 2); memset(argv, 0, 3); parse = ast_strdupa(data); u = ast_module_user_add(chan); argc = ast_app_separate_args(parse, ',', argv, 3); text = argv[0]; if (!ast_strlen_zero(argv[1])) { timeout = strtol(argv[1], NULL, 0); } if (!ast_strlen_zero(argv[2])) { max_digits = strtol(argv[2], NULL, 0); } if (ast_strlen_zero(text)) { ast_log(LOG_WARNING, "%s requires text to speak!\n", app); return -1; } if (!ast_strlen_zero(text)) { ast_log(LOG_DEBUG, "Text to Speak : %s\n", text); } if (timeout > 0) { ast_log(LOG_DEBUG, "Timeout : %d\n", timeout); } if (max_digits > 0) { ast_log(LOG_DEBUG, "Max Digits : %d\n", max_digits); } ps = malloc(sizeof(struct stuff)); swift_init_stuff(ps); /* Setup synthesis */ if ((engine = swift_engine_open(NULL)) == NULL) { ast_log(LOG_ERROR, "Failed to open Swift Engine.\n"); goto exception; } params = swift_params_new(NULL); swift_params_set_string(params, "audio/encoding", "ulaw"); swift_params_set_string(params, "audio/sampling-rate", "8000"); swift_params_set_string(params, "audio/output-format", "raw"); swift_params_set_string(params, "tts/text-encoding", "utf-8"); /* Additional swift parameters * * swift_params_set_float(params, "speech/pitch/shift", 1.0); * swift_params_set_int(params, "speech/rate", 150); * swift_params_set_int(params, "audio/volume", 110); * swift_params_set_int(params, "audio/deadair", 0); */ if ((port = swift_port_open(engine, params)) == NULL) { ast_log(LOG_ERROR, "Failed to open Swift Port.\n"); goto exception; } if ((voice = swift_port_set_voice_by_name(port, cfg_voice)) == NULL) { ast_log(LOG_ERROR, "Failed to set voice.\n"); goto exception; } event_mask = SWIFT_EVENT_AUDIO | SWIFT_EVENT_END; swift_port_set_callback(port, &swift_cb, event_mask, ps); if (SWIFT_FAILED(swift_port_speak_text(port, text, 0, NULL, &tts_stream, NULL))) { ast_log(LOG_ERROR, "Failed to speak.\n"); goto exception; } if (chan->_state != AST_STATE_UP) { ast_answer(chan); } ast_stopstream(chan); old_writeformat = chan->writeformat; if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) { ast_log(LOG_WARNING, "Unable to set write format.\n"); goto exception; } res = 0; /* Wait 100ms first for synthesis to start crankin'; if that's not * enough the */ next = ast_tvadd(ast_tvnow(), ast_tv(0, 100000)); while (swift_generator_running(ps)) { ms = ast_tvdiff_ms(next, ast_tvnow()); if (ms <= 0) { if (swift_bytes_available(ps) > 0) { ASTOBJ_WRLOCK(ps); len = fmin(framesize, ps->qc); availatend = cfg_buffer_size - (ps->pq_r - ps->q); if (len > availatend) { /* read #1: to end of q buf */ memcpy(myf.frdata, ps->pq_r, availatend); ps->qc -= availatend; /* read #2: reset to start of q buf and get rest */ ps->pq_r = ps->q; memcpy(myf.frdata + availatend, ps->pq_r, len - availatend); ps->qc -= len - availatend; ps->pq_r += len - availatend; } else { ast_log(LOG_DEBUG, "Easy read; %d bytes and %d at end, %d free\n", len, availatend, cfg_buffer_size - ps->qc); memcpy(myf.frdata, ps->pq_r, len); ps->qc -= len; ps->pq_r += len; } myf.f.frametype = AST_FRAME_VOICE; myf.f.subclass = AST_FORMAT_ULAW; myf.f.datalen = len; myf.f.samples = len; myf.f.data.ptr = myf.frdata; myf.f.mallocd = 0; myf.f.offset = AST_FRIENDLY_OFFSET; myf.f.src = __PRETTY_FUNCTION__; myf.f.delivery.tv_sec = 0; myf.f.delivery.tv_usec = 0; if (ast_write(chan, &myf.f) < 0) { ast_log(LOG_DEBUG, "ast_write failed\n"); } ast_log(LOG_DEBUG, "wrote a frame of %d\n", len); if (ps->qc < 0) { ast_log(LOG_DEBUG, "queue claims to contain negative bytes. Huh? qc < 0\n"); } ASTOBJ_UNLOCK(ps); next = ast_tvadd(next, ast_samp2tv(myf.f.samples, samplerate)); } else { next = ast_tvadd(next, ast_samp2tv(framesize / 2, samplerate)); ast_log(LOG_DEBUG, "Whoops, writer starved for audio\n"); } } else { ms = ast_waitfor(chan, ms); if (ms < 0) { ast_log(LOG_DEBUG, "Hangup detected\n"); res = -1; ASTOBJ_WRLOCK(ps); ps->immediate_exit = 1; ASTOBJ_UNLOCK(ps); } else if (ms) { f = ast_read(chan); if (!f) { ast_log(LOG_DEBUG, "Null frame == hangup() detected\n"); res = -1; ASTOBJ_WRLOCK(ps); ps->immediate_exit = 1; ASTOBJ_UNLOCK(ps); } else if (f->frametype == AST_FRAME_DTMF && timeout > 0 && max_digits > 0) { char originDTMF = f->subclass; alreadyran = 1; res = 0; ASTOBJ_WRLOCK(ps); ps->immediate_exit = 1; ASTOBJ_UNLOCK(ps); if (max_digits > 1) { rc = listen_for_dtmf(chan, timeout, max_digits - 1); } if (rc) { sprintf(results, "%c%s", originDTMF, rc); } else { sprintf(results, "%c", originDTMF); } ast_log(LOG_NOTICE, "DTMF = %s\n", results); pbx_builtin_setvar_helper(chan, "SWIFT_DTMF", results); } ast_frfree(f); } } ASTOBJ_RDLOCK(ps); if (ps->immediate_exit && !ps->generating_done) { if (SWIFT_FAILED(sresult = swift_port_stop(port, tts_stream, SWIFT_EVENT_NOW))) { ast_log(LOG_NOTICE, "Early top of swift port failed\n"); } } ASTOBJ_UNLOCK(ps); } if (alreadyran == 0 && timeout > 0 && max_digits > 0) { rc = listen_for_dtmf(chan, timeout, max_digits); if (rc != NULL) { sprintf(results, "%s", rc); ast_log(LOG_NOTICE, "DTMF = %s\n", results); pbx_builtin_setvar_helper(chan, "SWIFT_DTMF", results); } } if (max_digits >= 1 && results != NULL) { if (cfg_goto_exten) { ast_log(LOG_NOTICE, "GoTo(%s|%s|%d) : ", chan->context, results, 1); if (ast_exists_extension (chan, chan->context, results, 1, chan->cid.cid_num)) { ast_log(LOG_NOTICE, "OK\n"); ast_copy_string(chan->exten, results, sizeof(chan->exten) - 1); chan->priority = 0; } else { ast_log(LOG_NOTICE, "FAILED\n"); } } } exception: if (port != NULL) { swift_port_close(port); } if (engine != NULL) { swift_engine_close(engine); } if (ps && ps->q) { ast_free(ps->q); ps->q = NULL; } if (ps) { ast_free(ps); ps = NULL; } if (!res && old_writeformat) { ast_set_write_format(chan, old_writeformat); } ast_module_user_remove(u); return res; }
static swift_result_t swift_cb(swift_event *event, swift_event_t type, void *udata) { void *buf; int len, spacefree; unsigned long sleepfor; swift_event_t rv = SWIFT_SUCCESS; struct stuff *ps = udata; if (type == SWIFT_EVENT_AUDIO) { rv = swift_event_get_audio(event, &buf, &len); if (!SWIFT_FAILED(rv) && len > 0) { ast_log(LOG_DEBUG, "audio callback\n"); ASTOBJ_WRLOCK(ps); /* Sleep while waiting for some queue space to become available */ while (len + ps->qc > cfg_buffer_size && !ps->immediate_exit) { /* Each byte is 125us of time, so assume queue space will become available at that rate and guess when we'll have enough space available. + another (125 usec/sample * framesize samples) (1 frame) for fudge */ sleepfor = ((unsigned long)(len - (cfg_buffer_size - ps->qc)) * 125UL) + (125UL * (unsigned long)framesize); /* ast_log(LOG_DEBUG, "generator: %d bytes to write but only %d space avail, sleeping %ldus\n", len, cfg_buffer_size - ps->qc, sleepfor); */ ASTOBJ_UNLOCK(ps); usleep(sleepfor); ASTOBJ_WRLOCK(ps); } if (ps->immediate_exit) { ASTOBJ_UNLOCK(ps); return SWIFT_SUCCESS; } spacefree = cfg_buffer_size - ((uintptr_t) ps->pq_w - (uintptr_t)ps->q); if (len > spacefree) { ast_log(LOG_DEBUG, "audio fancy write; %d bytes but only %d avail to end %d totalavail\n", len, spacefree, cfg_buffer_size - ps->qc); /* write #1 to end of mem */ memcpy(ps->pq_w, buf, spacefree); ps->pq_w = ps->q; ps->qc += spacefree; /* write #2 and beg of mem */ memcpy(ps->pq_w, buf + spacefree, len - spacefree); ps->pq_w += len - spacefree; ps->qc += len - spacefree; } else { ast_log(LOG_DEBUG, "audio easy write, %d avail to end %d totalavail\n", spacefree, cfg_buffer_size - ps->qc); memcpy(ps->pq_w, buf, len); ps->pq_w += len; ps->qc += len; } ASTOBJ_UNLOCK(ps); } else { ast_log(LOG_DEBUG, "got audio callback but get_audio call failed\n"); } } else if (type == SWIFT_EVENT_END) { ast_log(LOG_DEBUG, "got END callback; done generating audio\n"); ASTOBJ_WRLOCK(ps); ps->generating_done = 1; ASTOBJ_UNLOCK(ps); } else { ast_log(LOG_DEBUG, "UNKNOWN callback\n"); } return rv; }
static switch_status_t cepstral_speech_open(switch_speech_handle_t *sh, const char *voice_name, int rate, switch_speech_flag_t *flags) { cepstral_t *cepstral = switch_core_alloc(sh->memory_pool, sizeof(*cepstral)); char srate[25]; if (!cepstral) { return SWITCH_STATUS_MEMERR; } if (switch_buffer_create_dynamic(&cepstral->audio_buffer, MY_BLOCK_SIZE, MY_BUF_LEN, 0) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Write Buffer Failed!\n"); return SWITCH_STATUS_MEMERR; } switch_mutex_init(&cepstral->audio_lock, SWITCH_MUTEX_NESTED, sh->memory_pool); cepstral->params = swift_params_new(NULL); swift_params_set_string(cepstral->params, "audio/encoding", "pcm16"); switch_snprintf(srate, sizeof(srate), "%d", rate); swift_params_set_string(cepstral->params, "audio/sampling-rate", srate); /* Open a Swift Port through which to make TTS calls */ if (!(cepstral->port = swift_port_open(engine, cepstral->params))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open Swift Port.\n"); goto all_done; } if (voice_name && SWIFT_FAILED(swift_port_set_voice_by_name(cepstral->port, voice_name))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid voice %s!\n", voice_name); voice_name = NULL; } if (zstr(voice_name)) { /* Find the first voice on the system */ if ((cepstral->voice = swift_port_find_first_voice(cepstral->port, NULL, NULL)) == NULL) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to find any voices!\n"); goto all_done; } /* Set the voice found by find_first_voice() as the port's current voice */ if (SWIFT_FAILED(swift_port_set_voice(cepstral->port, cepstral->voice))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to set voice.\n"); goto all_done; } voice_name = (char *) swift_voice_get_attribute(cepstral->voice, "name"); } if (voice_name) { switch_copy_string(sh->voice, voice_name, sizeof(sh->voice)); } swift_port_set_callback(cepstral->port, &write_audio, SWIFT_EVENT_AUDIO, cepstral); sh->private_info = cepstral; return SWITCH_STATUS_SUCCESS; all_done: return SWITCH_STATUS_FALSE; }