diff --git a/libs/unimrcp/.update b/libs/unimrcp/.update index 61425074b3..6f16710afd 100644 --- a/libs/unimrcp/.update +++ b/libs/unimrcp/.update @@ -1 +1 @@ -Wed Jul 1 19:53:07 CDT 2009 +Thu Jul 9 10:17:26 CDT 2009 diff --git a/libs/unimrcp/libs/apr-toolkit/include/apt_text_stream.h b/libs/unimrcp/libs/apr-toolkit/include/apt_text_stream.h index e5aca8ab67..3a5a224aa3 100644 --- a/libs/unimrcp/libs/apr-toolkit/include/apt_text_stream.h +++ b/libs/unimrcp/libs/apr-toolkit/include/apt_text_stream.h @@ -22,8 +22,6 @@ * @brief Text Stream Parse/Generate Routine */ -#include -#include #include "apt_string.h" #include "apt_pair.h" @@ -95,40 +93,17 @@ APT_DECLARE(apt_bool_t) apt_boolean_value_parse(const apt_str_t *str, apt_bool_t /** Generate boolean-value */ APT_DECLARE(apt_bool_t) apt_boolean_value_generate(apt_bool_t value, apt_text_stream_t *str); - /** Parse size_t value */ -static APR_INLINE apr_size_t apt_size_value_parse(const apt_str_t *str) -{ - return str->buf ? atol(str->buf) : 0; -} +APT_DECLARE(apr_size_t) apt_size_value_parse(const apt_str_t *str); /** Generate apr_size_t value */ -static APR_INLINE apt_bool_t apt_size_value_generate(apr_size_t value, apt_text_stream_t *stream) -{ - int length = sprintf(stream->pos, "%"APR_SIZE_T_FMT, value); - if(length <= 0) { - return FALSE; - } - stream->pos += length; - return TRUE; -} +APT_DECLARE(apt_bool_t) apt_size_value_generate(apr_size_t value, apt_text_stream_t *stream); /** Parse float value */ -static APR_INLINE float apt_float_value_parse(const apt_str_t *str) -{ - return str->buf ? (float)atof(str->buf) : 0; -} +APT_DECLARE(float) apt_float_value_parse(const apt_str_t *str); /** Generate float value */ -static APR_INLINE apt_bool_t apt_float_value_generate(float value, apt_text_stream_t *stream) -{ - int length = sprintf(stream->pos,"%.2f",value); - if(length <= 0) { - return FALSE; - } - stream->pos += length; - return TRUE; -} +APT_DECLARE(apt_bool_t) apt_float_value_generate(float value, apt_text_stream_t *stream); /** Generate string value */ static APR_INLINE apt_bool_t apt_string_value_generate(const apt_str_t *str, apt_text_stream_t *stream) diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_net_client_task.c b/libs/unimrcp/libs/apr-toolkit/src/apt_net_client_task.c index eb7cf6393d..6e59440cbd 100644 --- a/libs/unimrcp/libs/apr-toolkit/src/apt_net_client_task.c +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_net_client_task.c @@ -300,8 +300,6 @@ static apt_bool_t apt_net_client_task_run(apt_task_t *base) } apt_net_client_task_pollset_destroy(task); - - apt_task_child_terminate(task->base); return TRUE; } diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_net_server_task.c b/libs/unimrcp/libs/apr-toolkit/src/apt_net_server_task.c index 77c48b980a..4d994f4bed 100644 --- a/libs/unimrcp/libs/apr-toolkit/src/apt_net_server_task.c +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_net_server_task.c @@ -350,8 +350,6 @@ static apt_bool_t apt_net_server_task_run(apt_task_t *base) } apt_net_server_task_pollset_destroy(task); - - apt_task_child_terminate(task->base); return TRUE; } diff --git a/libs/unimrcp/libs/apr-toolkit/src/apt_text_stream.c b/libs/unimrcp/libs/apr-toolkit/src/apt_text_stream.c index 2735337959..a276063301 100644 --- a/libs/unimrcp/libs/apr-toolkit/src/apt_text_stream.c +++ b/libs/unimrcp/libs/apr-toolkit/src/apt_text_stream.c @@ -14,6 +14,8 @@ * limitations under the License. */ +#include +#include #include #include "apt_text_stream.h" @@ -307,6 +309,45 @@ APT_DECLARE(apt_bool_t) apt_boolean_value_generate(apt_bool_t value, apt_text_st return TRUE; } +/** Parse size_t value */ +APT_DECLARE(apr_size_t) apt_size_value_parse(const apt_str_t *str) +{ + return str->buf ? atol(str->buf) : 0; +} + +/** Generate apr_size_t value */ +APT_DECLARE(apt_bool_t) apt_size_value_generate(apr_size_t value, apt_text_stream_t *stream) +{ + int length = sprintf(stream->pos, "%"APR_SIZE_T_FMT, value); + if(length <= 0) { + return FALSE; + } + stream->pos += length; + return TRUE; +} + +/** Parse float value */ +APT_DECLARE(float) apt_float_value_parse(const apt_str_t *str) +{ + return str->buf ? (float)atof(str->buf) : 0; +} + +/** Generate float value */ +APT_DECLARE(apt_bool_t) apt_float_value_generate(float value, apt_text_stream_t *stream) +{ + char *end; + int length = sprintf(stream->pos,"%f",value); + if(length <= 0) { + return FALSE; + } + + /* remove trailing 0s (if any) */ + end = stream->pos + length -1; + while(*end == 0x30 && end != stream->pos) end--; + + stream->pos = end + 1; + return TRUE; +} /** Generate value plus the length (number of digits) of the value itself. */ APT_DECLARE(apt_bool_t) apt_var_length_value_generate(apr_size_t *value, apr_size_t max_count, apt_str_t *str) diff --git a/libs/unimrcp/libs/mpf/Makefile.am b/libs/unimrcp/libs/mpf/Makefile.am index 65f1493499..4a73d91325 100644 --- a/libs/unimrcp/libs/mpf/Makefile.am +++ b/libs/unimrcp/libs/mpf/Makefile.am @@ -20,6 +20,7 @@ include_HEADERS = codecs/g711/g711.h \ include/mpf_context.h \ include/mpf_engine.h \ include/mpf_frame.h \ + include/mpf_frame_buffer.h \ include/mpf_message.h \ include/mpf_object.h \ include/mpf_stream.h \ @@ -55,6 +56,7 @@ libmpf_la_SOURCES = codecs/g711/g711.c \ src/mpf_termination.c \ src/mpf_rtp_termination_factory.c \ src/mpf_file_termination_factory.c \ + src/mpf_frame_buffer.c \ src/mpf_timer.c \ src/mpf_encoder.c \ src/mpf_decoder.c \ diff --git a/libs/unimrcp/libs/mpf/include/mpf_frame_buffer.h b/libs/unimrcp/libs/mpf/include/mpf_frame_buffer.h new file mode 100644 index 0000000000..8573963c6f --- /dev/null +++ b/libs/unimrcp/libs/mpf/include/mpf_frame_buffer.h @@ -0,0 +1,50 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __MPF_FRAME_BUFFER_H__ +#define __MPF_FRAME_BUFFER_H__ + +/** + * @file mpf_frame_buffer.h + * @brief Buffer of Media Frames + */ + +#include "mpf_frame.h" + +APT_BEGIN_EXTERN_C + +/** Opaque frame buffer declaration */ +typedef struct mpf_frame_buffer_t mpf_frame_buffer_t; + + +/** Create frame buffer */ +mpf_frame_buffer_t* mpf_frame_buffer_create(apr_size_t frame_size, apr_size_t frame_count, apr_pool_t *pool); + +/** Destroy frame buffer */ +void mpf_frame_buffer_destroy(mpf_frame_buffer_t *buffer); + +/** Restart frame buffer */ +apt_bool_t mpf_frame_buffer_restart(mpf_frame_buffer_t *buffer); + +/** Write frame to buffer */ +apt_bool_t mpf_frame_buffer_write(mpf_frame_buffer_t *buffer, const mpf_frame_t *frame); + +/** Read frame from buffer */ +apt_bool_t mpf_frame_buffer_read(mpf_frame_buffer_t *buffer, mpf_frame_t *frame); + +APT_END_EXTERN_C + +#endif /*__MPF_FRAME_BUFFER_H__*/ diff --git a/libs/unimrcp/libs/mpf/mpf.vcproj b/libs/unimrcp/libs/mpf/mpf.vcproj index d58fee87e7..6efaf489b8 100644 --- a/libs/unimrcp/libs/mpf/mpf.vcproj +++ b/libs/unimrcp/libs/mpf/mpf.vcproj @@ -207,6 +207,10 @@ RelativePath=".\include\mpf_frame.h" > + + @@ -332,6 +336,10 @@ RelativePath=".\src\mpf_file_termination_factory.c" > + + diff --git a/libs/unimrcp/libs/mpf/src/mpf_engine.c b/libs/unimrcp/libs/mpf/src/mpf_engine.c index 42aee780fc..2d34b67cfe 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_engine.c +++ b/libs/unimrcp/libs/mpf/src/mpf_engine.c @@ -128,6 +128,7 @@ static apt_bool_t mpf_engine_terminate(apt_task_t *task) mpf_timer_stop(engine->timer); mpf_engine_contexts_destroy(engine); + apt_task_child_terminate(task); return TRUE; } diff --git a/libs/unimrcp/libs/mpf/src/mpf_frame_buffer.c b/libs/unimrcp/libs/mpf/src/mpf_frame_buffer.c new file mode 100644 index 0000000000..bec7e5a0bc --- /dev/null +++ b/libs/unimrcp/libs/mpf/src/mpf_frame_buffer.c @@ -0,0 +1,124 @@ +/* + * Copyright 2009 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mpf_frame_buffer.h" + +struct mpf_frame_buffer_t { + apr_byte_t *raw_data; + mpf_frame_t *frames; + apr_size_t frame_count; + apr_size_t frame_size; + + apr_size_t write_pos; + apr_size_t read_pos; + + apr_thread_mutex_t *guard; + apr_pool_t *pool; +}; + + +mpf_frame_buffer_t* mpf_frame_buffer_create(apr_size_t frame_size, apr_size_t frame_count, apr_pool_t *pool) +{ + apr_size_t i; + mpf_frame_buffer_t *buffer = apr_palloc(pool,sizeof(mpf_frame_buffer_t)); + + buffer->frame_size = frame_size; + buffer->frame_count = frame_count; + buffer->raw_data = apr_palloc(pool,buffer->frame_size*buffer->frame_count); + buffer->frames = apr_palloc(pool,sizeof(mpf_frame_t)*buffer->frame_count); + for(i=0; iframe_count; i++) { + buffer->frames[i].type = MEDIA_FRAME_TYPE_NONE; + buffer->frames[i].codec_frame.buffer = buffer->raw_data + i*buffer->frame_size; + } + + buffer->write_pos = buffer->read_pos = 0; + apr_thread_mutex_create(&buffer->guard,APR_THREAD_MUTEX_UNNESTED,pool); + return buffer; +} + +void mpf_frame_buffer_destroy(mpf_frame_buffer_t *buffer) +{ + if(buffer->guard) { + apr_thread_mutex_destroy(buffer->guard); + buffer->guard = NULL; + } +} + +apt_bool_t mpf_frame_buffer_restart(mpf_frame_buffer_t *buffer) +{ + buffer->write_pos = buffer->read_pos; + return TRUE; +} + +static APR_INLINE mpf_frame_t* mpf_frame_buffer_frame_get(mpf_frame_buffer_t *buffer, apr_size_t pos) +{ + apr_size_t index = pos % buffer->frame_count; + return &buffer->frames[index]; +} + +apt_bool_t mpf_frame_buffer_write(mpf_frame_buffer_t *buffer, const mpf_frame_t *frame) +{ + mpf_frame_t *write_frame; + void *data = frame->codec_frame.buffer; + apr_size_t size = frame->codec_frame.size; + + apr_thread_mutex_lock(buffer->guard); + while(buffer->write_pos - buffer->read_pos < buffer->frame_count && size >= buffer->frame_size) { + write_frame = mpf_frame_buffer_frame_get(buffer,buffer->write_pos); + write_frame->type = frame->type; + write_frame->codec_frame.size = buffer->frame_size; + memcpy( + write_frame->codec_frame.buffer, + data, + write_frame->codec_frame.size); + + data = (char*)data + buffer->frame_size; + size -= buffer->frame_size; + buffer->write_pos ++; + } + + apr_thread_mutex_unlock(buffer->guard); + /* if size != 0 => non frame alligned or buffer is full */ + return size == 0 ? TRUE : FALSE; +} + +apt_bool_t mpf_frame_buffer_read(mpf_frame_buffer_t *buffer, mpf_frame_t *media_frame) +{ + apr_thread_mutex_lock(buffer->guard); + if(buffer->write_pos > buffer->read_pos) { + /* normal read */ + mpf_frame_t *src_media_frame = mpf_frame_buffer_frame_get(buffer,buffer->read_pos); + media_frame->type = src_media_frame->type; + if(media_frame->type & MEDIA_FRAME_TYPE_AUDIO) { + media_frame->codec_frame.size = src_media_frame->codec_frame.size; + memcpy( + media_frame->codec_frame.buffer, + src_media_frame->codec_frame.buffer, + media_frame->codec_frame.size); + } + if(media_frame->type & MEDIA_FRAME_TYPE_EVENT) { + media_frame->event_frame = src_media_frame->event_frame; + } + src_media_frame->type = MEDIA_FRAME_TYPE_NONE; + buffer->read_pos ++; + } + else { + /* underflow */ + media_frame->type = MEDIA_FRAME_TYPE_NONE; + } + apr_thread_mutex_unlock(buffer->guard); + return TRUE; +} diff --git a/libs/unimrcp/libs/mpf/src/mpf_rtp_stream.c b/libs/unimrcp/libs/mpf/src/mpf_rtp_stream.c index 3956d1e6e3..d97b1a81cb 100644 --- a/libs/unimrcp/libs/mpf/src/mpf_rtp_stream.c +++ b/libs/unimrcp/libs/mpf/src/mpf_rtp_stream.c @@ -83,21 +83,6 @@ MPF_DECLARE(mpf_audio_stream_t*) mpf_rtp_stream_create(mpf_termination_t *termin return rtp_stream->base; } -static void mpf_rtp_stream_ip_port_set(mpf_rtp_media_descriptor_t *media, mpf_rtp_config_t *config) -{ - if(media->base.ip.length == 0) { - media->base.ip = config->ip; - media->base.ext_ip = config->ext_ip; - } - if(media->base.port == 0) { - media->base.port = config->rtp_port_cur; - config->rtp_port_cur += 2; - if(config->rtp_port_cur == config->rtp_port_max) { - config->rtp_port_cur = config->rtp_port_min; - } - } -} - static apt_bool_t mpf_rtp_stream_local_media_create(mpf_rtp_stream_t *rtp_stream, mpf_rtp_media_descriptor_t *local_media, mpf_rtp_media_descriptor_t *remote_media) { apt_bool_t status = TRUE; @@ -111,9 +96,31 @@ static apt_bool_t mpf_rtp_stream_local_media_create(mpf_rtp_stream_t *rtp_stream if(remote_media) { local_media->base.id = remote_media->base.id; } + if(local_media->base.ip.length == 0) { + local_media->base.ip = rtp_stream->config->ip; + local_media->base.ext_ip = rtp_stream->config->ext_ip; + } + if(local_media->base.port == 0) { + /* RTP port management */ + apr_port_t first_port_in_search = rtp_stream->config->rtp_port_cur; + apt_bool_t is_port_ok = FALSE; - mpf_rtp_stream_ip_port_set(local_media,rtp_stream->config); - if(mpf_rtp_socket_create(rtp_stream,local_media) == FALSE) { + do { + local_media->base.port = rtp_stream->config->rtp_port_cur; + rtp_stream->config->rtp_port_cur += 2; + if(rtp_stream->config->rtp_port_cur == rtp_stream->config->rtp_port_max) { + rtp_stream->config->rtp_port_cur = rtp_stream->config->rtp_port_min; + } + if(mpf_rtp_socket_create(rtp_stream,local_media) == TRUE) { + is_port_ok = TRUE; + } + } while((is_port_ok == FALSE) && (first_port_in_search != rtp_stream->config->rtp_port_cur)); + if(is_port_ok == FALSE) { + local_media->base.state = MPF_MEDIA_DISABLED; + status = FALSE; + } + } + else if(mpf_rtp_socket_create(rtp_stream,local_media) == FALSE) { local_media->base.state = MPF_MEDIA_DISABLED; status = FALSE; } diff --git a/libs/unimrcp/plugins/mrcp-flite/Makefile.am b/libs/unimrcp/plugins/mrcp-flite/Makefile.am index b48f753614..0e1dcbd58f 100644 --- a/libs/unimrcp/plugins/mrcp-flite/Makefile.am +++ b/libs/unimrcp/plugins/mrcp-flite/Makefile.am @@ -14,6 +14,6 @@ INCLUDES = -Iinclude \ plugin_LTLIBRARIES = mrcpflite.la -mrcpflite_la_SOURCES = src/mrcp_flite.c +mrcpflite_la_SOURCES = src/mrcp_flite.c src/flite_voices.c mrcpflite_la_LDFLAGS = -module $(PLUGIN_LT_VERSION) mrcpflite_la_LIBADD = $(UNIMRCP_FLITE_LIBS) -lm diff --git a/libs/unimrcp/plugins/mrcp-flite/include/flite_voices.h b/libs/unimrcp/plugins/mrcp-flite/include/flite_voices.h new file mode 100644 index 0000000000..ab896f1040 --- /dev/null +++ b/libs/unimrcp/plugins/mrcp-flite/include/flite_voices.h @@ -0,0 +1,41 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FLITE_VOICES_H__ +#define __FLITE_VOICES_H__ + +/** + * @file flite_voices.h + * @brief Flite Voices + */ + +#include +#include +#include "mrcp_message.h" + +APT_BEGIN_EXTERN_C + + +typedef struct flite_voices_t flite_voices_t; + +flite_voices_t* flite_voices_load(apr_pool_t *pool); +void flite_voices_unload(flite_voices_t *voices); + +cst_voice* flite_voices_best_match_get(flite_voices_t *voices, mrcp_message_t *message); + +APT_END_EXTERN_C + +#endif /*__FLITE_VOICES_H__*/ diff --git a/libs/unimrcp/plugins/mrcp-flite/mrcpflite.vcproj b/libs/unimrcp/plugins/mrcp-flite/mrcpflite.vcproj index b271fd2e28..46867d7816 100644 --- a/libs/unimrcp/plugins/mrcp-flite/mrcpflite.vcproj +++ b/libs/unimrcp/plugins/mrcp-flite/mrcpflite.vcproj @@ -149,11 +149,19 @@ Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > + + + + diff --git a/libs/unimrcp/plugins/mrcp-flite/src/flite_voices.c b/libs/unimrcp/plugins/mrcp-flite/src/flite_voices.c new file mode 100644 index 0000000000..eb37963b32 --- /dev/null +++ b/libs/unimrcp/plugins/mrcp-flite/src/flite_voices.c @@ -0,0 +1,160 @@ +/* + * Copyright 2008 Arsen Chaloyan + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "flite_voices.h" +#include "mrcp_synth_header.h" + +typedef struct flite_voice_t flite_voice_t; + +/** Declaration of flite voice */ +struct flite_voice_t { + const char *name; + cst_voice *self; + cst_voice* (*register_voice)(void); + void (*unregister_voice)(cst_voice *); +}; + +struct flite_voices_t { + apr_hash_t *table; + apr_pool_t *pool; +}; + + +/* declarations for flite voices */ +cst_voice *register_cmu_us_awb(void); +cst_voice *register_cmu_us_kal(void); +cst_voice *register_cmu_us_rms(void); +cst_voice *register_cmu_us_slt(void); +void unregister_cmu_us_awb(cst_voice * v); +void unregister_cmu_us_kal(cst_voice * v); +void unregister_cmu_us_rms(cst_voice * v); +void unregister_cmu_us_slt(cst_voice * v); + + +static apt_bool_t flite_voices_init(flite_voices_t *voices, apr_pool_t *pool) +{ + flite_voice_t *voice; + + voice = apr_palloc(pool,sizeof(flite_voice_t)); + voice->name = "awb"; + voice->self = NULL; + voice->register_voice = register_cmu_us_awb; + voice->unregister_voice = unregister_cmu_us_awb; + apr_hash_set(voices->table,voice->name,APR_HASH_KEY_STRING,voice); + + voice = apr_palloc(pool,sizeof(flite_voice_t)); + voice->name = "kal"; + voice->self = NULL; + voice->register_voice = register_cmu_us_kal; + voice->unregister_voice = unregister_cmu_us_kal; + apr_hash_set(voices->table,voice->name,APR_HASH_KEY_STRING,voice); + + voice = apr_palloc(pool,sizeof(flite_voice_t)); + voice->name = "rms"; + voice->self = NULL; + voice->register_voice = register_cmu_us_rms; + voice->unregister_voice = unregister_cmu_us_rms; + apr_hash_set(voices->table,voice->name,APR_HASH_KEY_STRING,voice); + + voice = apr_palloc(pool,sizeof(flite_voice_t)); + voice->name = "slt"; + voice->self = NULL; + voice->register_voice = register_cmu_us_slt; + voice->unregister_voice = unregister_cmu_us_slt; + apr_hash_set(voices->table,voice->name,APR_HASH_KEY_STRING,voice); + + return TRUE; +} + + +flite_voices_t* flite_voices_load(apr_pool_t *pool) +{ + flite_voice_t *voice; + apr_hash_index_t *it; + void *val; + + flite_voices_t *voices = apr_palloc(pool,sizeof(flite_voices_t)); + voices->pool = pool; + voices->table = apr_hash_make(pool); + + /* init voices */ + flite_voices_init(voices,pool); + + /* register voices */ + it = apr_hash_first(pool,voices->table); + /* walk through the voices and register them */ + for(; it; it = apr_hash_next(it)) { + apr_hash_this(it,NULL,NULL,&val); + voice = val; + if(voice) { + voice->self = voice->register_voice(); + } + } + + return voices; +} + +void flite_voices_unload(flite_voices_t *voices) +{ + flite_voice_t *voice; + apr_hash_index_t *it; + void *val; + + /* unregister voices */ + it = apr_hash_first(voices->pool,voices->table); + /* walk through the voices and register them */ + for(; it; it = apr_hash_next(it)) { + apr_hash_this(it,NULL,NULL,&val); + voice = val; + if(voice && voice->self) { + voice->unregister_voice(voice->self); + } + } +} + +cst_voice* flite_voices_best_match_get(flite_voices_t *voices, mrcp_message_t *message) +{ + cst_voice *voice = NULL; + const char *voice_name = NULL; + mrcp_synth_header_t *synth_header = mrcp_resource_header_get(message); + if(synth_header) { + if(mrcp_resource_header_property_check(message,SYNTHESIZER_HEADER_VOICE_NAME) == TRUE) { + voice_name = synth_header->voice_param.name.buf; + } + } + + if(voice_name) { + /* get voice by name */ + flite_voice_t *flite_voice; + flite_voice = apr_hash_get(voices->table,voice_name,APR_HASH_KEY_STRING); + if(flite_voice) { + voice = flite_voice->self; + } + } + + if(!voice) { + /* still no voice found, get the default one */ + flite_voice_t *flite_voice = NULL; + void *val; + apr_hash_index_t *it = apr_hash_first(voices->pool,voices->table); + apr_hash_this(it,NULL,NULL,&val); + if(val) { + flite_voice = val; + voice = flite_voice->self; + } + } + return voice; +} diff --git a/libs/unimrcp/plugins/mrcp-flite/src/mrcp_flite.c b/libs/unimrcp/plugins/mrcp-flite/src/mrcp_flite.c index 1db710ed0c..a8b8a803aa 100644 --- a/libs/unimrcp/plugins/mrcp-flite/src/mrcp_flite.c +++ b/libs/unimrcp/plugins/mrcp-flite/src/mrcp_flite.c @@ -24,6 +24,7 @@ * 4. Methods (callbacks) of the MPF engine stream MUST not block. */ +#include "flite_voices.h" #include "mrcp_resource_engine.h" #include "mrcp_synth_resource.h" #include "mrcp_synth_header.h" @@ -33,7 +34,6 @@ #include "apr_time.h" #include "apt_consumer_task.h" #include "apt_log.h" -#include "flite.h" typedef struct flite_synth_engine_t flite_synth_engine_t; typedef struct flite_synth_channel_t flite_synth_channel_t; @@ -57,8 +57,6 @@ static apt_bool_t flite_synth_channel_open(mrcp_engine_channel_t *channel); static apt_bool_t flite_synth_channel_close(mrcp_engine_channel_t *channel); static apt_bool_t flite_synth_channel_request_process(mrcp_engine_channel_t *channel, mrcp_message_t *request); -static apt_bool_t flite_synth_channel_close_t(mrcp_engine_channel_t *channel); // wait for speak thread - /** flite channel methods for processing MRCP channel request **/ static apt_bool_t flite_synth_channel_speak(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response); static apt_bool_t flite_synth_channel_stop(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response); @@ -75,15 +73,12 @@ static const struct mrcp_engine_channel_method_vtable_t channel_vtable = { }; /** Declaration of synthesizer audio stream methods */ -static apt_bool_t flite_synth_stream_destroy(mpf_audio_stream_t *stream); -static apt_bool_t flite_synth_stream_open(mpf_audio_stream_t *stream); -static apt_bool_t flite_synth_stream_close(mpf_audio_stream_t *stream); static apt_bool_t flite_synth_stream_read(mpf_audio_stream_t *stream, mpf_frame_t *frame); static const mpf_audio_stream_vtable_t audio_stream_vtable = { - flite_synth_stream_destroy, - flite_synth_stream_open, - flite_synth_stream_close, + NULL, + NULL, + NULL, flite_synth_stream_read, NULL, NULL, @@ -92,39 +87,25 @@ static const mpf_audio_stream_vtable_t audio_stream_vtable = { /** Declaration of flite synthesizer engine */ struct flite_synth_engine_t { - int iChannels; - struct { - cst_voice *awb; - cst_voice *kal; - cst_voice *rms; - cst_voice *slt; - } voices; + /** Table of flite voices */ + flite_voices_t *voices; + int iChannels; }; -/** declarations for flite voices **/ -cst_voice *register_cmu_us_awb(void); -cst_voice *register_cmu_us_kal(void); -cst_voice *register_cmu_us_rms(void); -cst_voice *register_cmu_us_slt(void); -void unregister_cmu_us_awb(cst_voice * v); -void unregister_cmu_us_kal(cst_voice * v); -void unregister_cmu_us_rms(cst_voice * v); -void unregister_cmu_us_slt(cst_voice * v); - - /** Declaration of flite synthesizer channel */ struct flite_synth_channel_t { - flite_synth_engine_t *flite_engine; // Back pointer to engine - mrcp_engine_channel_t *channel; // Engine channel base - mrcp_message_t *speak_request; // Active (in-progress) speak request - mrcp_message_t *stop_response; // Pending stop response - apt_bool_t paused; // Is paused - mpf_buffer_t *audio_buffer; // Audio buffer - int iId; // Synth channel simultaneous reference count - cst_voice *voice; - apr_pool_t *pool; - apt_consumer_task_t *task; - apr_thread_mutex_t *channel_guard; + flite_synth_engine_t *flite_engine; /* Back pointer to engine */ + mrcp_engine_channel_t *channel; /* Engine channel base */ + mrcp_message_t *speak_request; /* Active (in-progress) speak request */ + mrcp_message_t *speak_response;/* Pending speak response */ + mrcp_message_t *stop_response; /* Pending stop response */ + apt_bool_t synthesizing; /* Is synthesizer task processing speak request */ + apt_bool_t paused; /* Is paused */ + mpf_buffer_t *audio_buffer; /* Audio buffer */ + int iId; /* Synth channel simultaneous reference count */ + apr_pool_t *pool; + apt_task_t *task; + apt_task_msg_pool_t *msg_pool; }; /** Declaration of flite synthesizer task message */ @@ -135,8 +116,8 @@ struct flite_speak_msg_t { typedef struct flite_speak_msg_t flite_speak_msg_t; -// we have a special task for the actual synthesis - -// the task is created when a mrcp speak message is received +/* we have a special task for the actual synthesis - + the task is created when a mrcp speak message is received */ static apt_bool_t flite_speak(apt_task_t *task, apt_task_msg_t *msg); /** Declare this macro to use log routine of the server where the plugin is loaded from */ @@ -147,7 +128,6 @@ MRCP_PLUGIN_DECLARE(mrcp_resource_engine_t*) mrcp_plugin_create(apr_pool_t *pool { /* create flite engine */ flite_synth_engine_t *flite_engine = (flite_synth_engine_t *) apr_palloc(pool,sizeof(flite_synth_engine_t)); - flite_engine->iChannels = 0; /* create resource engine base */ @@ -172,13 +152,10 @@ static apt_bool_t flite_synth_engine_open(mrcp_resource_engine_t *engine) apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_engine_open"); flite_init(); - flite_engine->voices.awb = register_cmu_us_awb(); - flite_engine->voices.kal = register_cmu_us_kal(); - flite_engine->voices.rms = register_cmu_us_rms(); - flite_engine->voices.slt = register_cmu_us_slt(); + + flite_engine->voices = flite_voices_load(engine->pool); apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite init success"); - return TRUE; } @@ -188,14 +165,36 @@ static apt_bool_t flite_synth_engine_close(mrcp_resource_engine_t *engine) flite_synth_engine_t *flite_engine = (flite_synth_engine_t *) engine->obj; apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_engine_close"); - unregister_cmu_us_awb(flite_engine->voices.awb); - unregister_cmu_us_kal(flite_engine->voices.kal); - unregister_cmu_us_rms(flite_engine->voices.rms); - unregister_cmu_us_slt(flite_engine->voices.slt); + flite_voices_unload(flite_engine->voices); return TRUE; } +static apt_bool_t flite_synth_task_create(flite_synth_channel_t *synth_channel) +{ + apt_task_msg_pool_t *msg_pool = apt_task_msg_pool_create_dynamic( sizeof(flite_speak_msg_t),synth_channel->pool); + apt_task_vtable_t *task_vtable = 0; + apt_consumer_task_t *consumer_task = 0; + + /* create task/thread to run flite synthesizer in */ + consumer_task = apt_consumer_task_create(synth_channel, msg_pool, synth_channel->pool); + if(!consumer_task) { + apt_log(APT_LOG_MARK,APT_PRIO_ERROR, "flite_synth_channel_speak failed to create flite speak task - channel:%d", synth_channel->iId); + return FALSE; + } + + task_vtable = apt_consumer_task_vtable_get(consumer_task); + if(!task_vtable) { + apt_log(APT_LOG_MARK,APT_PRIO_ERROR, "flite_synth_channel_speak cannot use flite speak task vtable - channel:%d", synth_channel->iId); + return FALSE; + } + + task_vtable->process_msg = flite_speak; + synth_channel->msg_pool = msg_pool; + synth_channel->task = apt_consumer_task_base_get(consumer_task); + return TRUE; +} + /** Create flite synthesizer channel derived from engine channel base */ static mrcp_engine_channel_t* flite_synth_engine_channel_create(mrcp_resource_engine_t *engine, apr_pool_t *pool) { @@ -204,30 +203,31 @@ static mrcp_engine_channel_t* flite_synth_engine_channel_create(mrcp_resource_en mpf_codec_descriptor_t *codec_descriptor = NULL; apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_engine_channel_create"); - -// codec_descriptor = (mpf_codec_descriptor_t *) apr_palloc(pool,sizeof(mpf_codec_descriptor_t)); -// mpf_codec_descriptor_init(codec_descriptor); -// codec_descriptor->channel_count = 1; -// codec_descriptor->payload_type = 96; -// apt_string_set(&codec_descriptor->name,"LPCM"); -// codec_descriptor->sampling_rate = 16000; +#if 0 + codec_descriptor = (mpf_codec_descriptor_t *) apr_palloc(pool,sizeof(mpf_codec_descriptor_t)); + mpf_codec_descriptor_init(codec_descriptor); + codec_descriptor->channel_count = 1; + codec_descriptor->payload_type = 96; + apt_string_set(&codec_descriptor->name,"LPCM"); + codec_descriptor->sampling_rate = 16000; +#endif synth_channel->flite_engine = (flite_synth_engine_t *) engine->obj; synth_channel->speak_request = NULL; // no active speak request in progress + synth_channel->speak_response = NULL; synth_channel->stop_response = NULL; + synth_channel->synthesizing = FALSE; synth_channel->paused = FALSE; synth_channel->pool = pool; synth_channel->audio_buffer = NULL; - synth_channel->voice = NULL; synth_channel->iId = 0; - - if (apr_thread_mutex_create(&synth_channel->channel_guard,APR_THREAD_MUTEX_DEFAULT,pool) != APR_SUCCESS) - { - apt_log(APT_LOG_MARK, APT_PRIO_ERROR, "Failed to create channel guard"); + synth_channel->task = NULL; + synth_channel->msg_pool = NULL; + if(flite_synth_task_create(synth_channel) != TRUE) { + apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "flite_synth_task_create failed"); return NULL; } - /* create engine channel base */ synth_channel->channel = mrcp_engine_source_channel_create( engine, /* resource engine */ @@ -237,9 +237,9 @@ static mrcp_engine_channel_t* flite_synth_engine_channel_create(mrcp_resource_en codec_descriptor, /* codec descriptor might be NULL by default */ pool); /* pool to allocate memory from */ - if (!synth_channel->channel) - { + if(!synth_channel->channel) { apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "flite_synth_engine_channel_create failed"); + apt_task_destroy(synth_channel->task); return NULL; } @@ -247,7 +247,6 @@ static mrcp_engine_channel_t* flite_synth_engine_channel_create(mrcp_resource_en synth_channel->iId = ++synth_channel->flite_engine->iChannels; apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_engine_channel_create created channel %d", synth_channel->iId); - return synth_channel->channel; } @@ -256,24 +255,12 @@ static apt_bool_t flite_synth_channel_destroy(mrcp_engine_channel_t *channel) { flite_synth_channel_t *synth_channel = (flite_synth_channel_t *) channel->method_obj; apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_channel_destroy - channel %d", synth_channel->iId); - if(synth_channel->task) - { - apt_task_t *task = apt_consumer_task_base_get(synth_channel->task); - if (!task || !apt_task_destroy(task)) - { - apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "Speak task destroy failed - channel %d", synth_channel->iId); - } - else - { - apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "Speak task destroyed - channel %d", synth_channel->iId); - } + if(synth_channel->task) { + apt_task_destroy(synth_channel->task); + synth_channel->task = NULL; } - synth_channel->task = NULL; synth_channel->flite_engine->iChannels--; - - apr_thread_mutex_destroy(synth_channel->channel_guard); - return TRUE; } @@ -283,7 +270,15 @@ static apt_bool_t flite_synth_channel_open(mrcp_engine_channel_t *channel) flite_synth_channel_t *synth_channel = (flite_synth_channel_t *) channel->method_obj; apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_channel_open - channel %d", synth_channel->iId); - synth_channel->voice = synth_channel->flite_engine->voices.awb; + if(synth_channel->task) { + if(apt_task_start(synth_channel->task) == TRUE) { + apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "Speak task started - channel %d", synth_channel->iId); + } + else { + apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "Speak task start failed - channel %d", synth_channel->iId); + } + } + return mrcp_engine_channel_open_respond(channel,TRUE); } @@ -292,8 +287,16 @@ static apt_bool_t flite_synth_channel_close(mrcp_engine_channel_t *channel) { flite_synth_channel_t *synth_channel = (flite_synth_channel_t *) channel->method_obj; apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_channel_close - channel %d", synth_channel->iId); - // create thread to wait for speak thread to terminate - flite_synth_channel_close_t(channel); + + if(synth_channel->task) { + if(apt_task_terminate(synth_channel->task,TRUE) == TRUE) { + apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "Speak task terminated - channel %d", synth_channel->iId); + } + else { + apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "Speak task terminate failed - channel %d", synth_channel->iId); + } + } + mrcp_engine_channel_close_respond(channel); return TRUE; } @@ -308,18 +311,6 @@ static apt_bool_t flite_synth_channel_request_process(mrcp_engine_channel_t *cha switch(request->start_line.method_id) { case SYNTHESIZER_SET_PARAMS: - // TODO set voices - // if (!strcasecmp(voice_name, "awb")) { - // synth_channel->voice = voices.awb; - // } else if (!strcasecmp(voice_name, "kal")) { - // synth_channel->voice = voices.kal; - // } else if (!strcasecmp(voice_name, "rms")) { - // synth_channel->voice = voices.rms; - // } else if (!strcasecmp(voice_name, "slt")) { - // synth_channel->voice = voices.slt; - // } else { - // apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "Valid voice names are awb, kal, rms or slt"); - // } break; case SYNTHESIZER_GET_PARAMS: break; @@ -350,92 +341,67 @@ static apt_bool_t flite_synth_channel_request_process(mrcp_engine_channel_t *cha mrcp_engine_channel_message_send(channel,response); } return TRUE; - } -static apt_bool_t flite_synth_channel_close_t(mrcp_engine_channel_t *channel) +/** Process SPEAK request */ +static apt_bool_t synth_response_construct(mrcp_message_t *response, mrcp_status_code_e status_code, mrcp_synth_completion_cause_e completion_cause) { - flite_synth_channel_t *synth_channel = (flite_synth_channel_t *) channel->method_obj; - apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_channel_close_t - channel %d", synth_channel->iId); - - if (synth_channel->task) - { - apt_task_t *task = apt_consumer_task_base_get(synth_channel->task); - if (!apt_task_terminate(task,TRUE)) - { - apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "Speak task terminate failed - channel %d", synth_channel->iId); - } - else - { - apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "Speak task terminated - channel %d", synth_channel->iId); - apt_task_destroy(task); - synth_channel->task = 0; - } + mrcp_synth_header_t *synth_header = mrcp_resource_header_prepare(response); + if(!synth_header) { + return FALSE; } - mrcp_engine_channel_close_respond(channel); + + response->start_line.status_code = status_code; + synth_header->completion_cause = completion_cause; + mrcp_resource_header_property_add(response,SYNTHESIZER_HEADER_COMPLETION_CAUSE); return TRUE; } - /** Process SPEAK request */ static apt_bool_t flite_synth_channel_speak(mrcp_engine_channel_t *channel, mrcp_message_t *request, mrcp_message_t *response) { + mrcp_generic_header_t *generic_header; + const char *content_type = NULL; flite_synth_channel_t *synth_channel = (flite_synth_channel_t *) channel->method_obj; + apt_task_msg_t *msg = 0; + flite_speak_msg_t *flite_msg = 0; apt_log(APT_LOG_MARK, APT_PRIO_INFO, "flite_synth_channel_speak - channel %d", synth_channel->iId); - if (!synth_channel->speak_request) - { - apt_task_msg_pool_t *msg_pool = apt_task_msg_pool_create_dynamic( sizeof(flite_speak_msg_t),synth_channel->pool); - apt_task_vtable_t *task_vtable = 0; - apt_task_t * task = 0; - apt_task_msg_t *msg = 0; - flite_speak_msg_t *flite_msg = 0; + generic_header = mrcp_generic_header_get(request); + if(generic_header) { + /* content-type must be specified */ + if(mrcp_generic_header_property_check(request,GENERIC_HEADER_CONTENT_TYPE) == TRUE) { + content_type = generic_header->content_type.buf; + } + } + if(!content_type) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Missing Content-Type"); + synth_response_construct(response,MRCP_STATUS_CODE_MISSING_PARAM,SYNTHESIZER_COMPLETION_CAUSE_ERROR); + return FALSE; + } - /* create task/thread to run flite so this function is not blocking */ - apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "Create flite speak task - channel: %d", synth_channel->iId); - synth_channel->task = apt_consumer_task_create(synth_channel, msg_pool, synth_channel->pool); - if (!synth_channel->task) - { - apt_log(APT_LOG_MARK,APT_PRIO_ERROR, "flite_synth_channel_speak failed to create flite speak task - channel:%d", synth_channel->iId); - return FALSE; - } + /* Flite currently supports only text/plain (no SSML) */ + if(strstr(content_type,"text") == NULL) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Not Supported Content-Type [%s]",content_type); + synth_response_construct(response,MRCP_STATUS_CODE_UNSUPPORTED_PARAM_VALUE,SYNTHESIZER_COMPLETION_CAUSE_ERROR); + return FALSE; + } - task_vtable = apt_consumer_task_vtable_get(synth_channel->task); - if (!task_vtable) - { - apt_log(APT_LOG_MARK,APT_PRIO_ERROR, "flite_synth_channel_speak cannot use flite speak task vtable - channel:%d", synth_channel->iId); - return FALSE; - } + synth_channel->speak_request = request; + synth_channel->speak_response = response; - task_vtable->process_msg = flite_speak; - synth_channel->speak_request = request; - - apt_log(APT_LOG_MARK,APT_PRIO_DEBUG, "Start task - channel %d", synth_channel->iId); - task = apt_consumer_task_base_get(synth_channel->task); - if (apt_task_start(task) == FALSE) - { - apt_log(APT_LOG_MARK,APT_PRIO_ERROR, "flite_synth_channel_speak failed to start task - channel: %d", synth_channel->iId); - apt_task_destroy(task); - return FALSE; - } - - msg = apt_task_msg_acquire(msg_pool); - msg->type = TASK_MSG_USER; - flite_msg = (flite_speak_msg_t*) msg->data; - flite_msg->channel = synth_channel; - flite_msg->request = request; - apt_log(APT_LOG_MARK,APT_PRIO_DEBUG, "Send signal to start speech synthesis - channel:%d", synth_channel->iId); - if (apt_task_msg_signal(task,msg)) - { - response->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS; - mrcp_engine_channel_message_send(channel,response); - } - else - { - apt_log(APT_LOG_MARK,APT_PRIO_WARNING, "Failed to send signal to start speech synthesis - channel:%d", synth_channel->iId); - apt_task_destroy(task); - return FALSE; - } + msg = apt_task_msg_acquire(synth_channel->msg_pool); + msg->type = TASK_MSG_USER; + flite_msg = (flite_speak_msg_t*) msg->data; + flite_msg->channel = synth_channel; + flite_msg->request = request; + apt_log(APT_LOG_MARK,APT_PRIO_DEBUG, "Send signal to start speech synthesis - channel:%d", synth_channel->iId); + if(apt_task_msg_signal(synth_channel->task,msg) != TRUE) { + apt_log(APT_LOG_MARK,APT_PRIO_WARNING, "Failed to send signal to start speech synthesis - channel:%d", synth_channel->iId); + synth_channel->speak_request = NULL; + synth_channel->speak_response = NULL; + synth_response_construct(response,MRCP_STATUS_CODE_METHOD_FAILED,SYNTHESIZER_COMPLETION_CAUSE_ERROR); + return FALSE; } return TRUE; } @@ -444,62 +410,80 @@ static apt_bool_t flite_speak(apt_task_t *task, apt_task_msg_t *msg) { flite_speak_msg_t *flite_msg = (flite_speak_msg_t*)msg->data; flite_synth_channel_t *synth_channel = (flite_synth_channel_t *) flite_msg->channel; + cst_wave *wave = NULL; + cst_voice *voice = NULL; apr_time_t start = 0; apr_time_t elapsed = 0; apr_time_t stamp = 0; + apt_str_t *body; + mrcp_message_t *response; - mrcp_message_t *stop_response = 0; mpf_codec_t * codec = mrcp_engine_source_stream_codec_get(synth_channel->channel); - apr_uint16_t rate = codec->descriptor->sampling_rate; + apr_uint16_t rate = codec->descriptor->sampling_rate; + body = &synth_channel->speak_request->body; + + response = synth_channel->speak_response; + synth_channel->speak_response = NULL; apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "< flite_speak_msg_process speak - channel %d", synth_channel->iId); - // just sequential stuff + /* just sequential stuff */ start = apr_time_now(); // in microsec - if (synth_channel->speak_request->body.length) - { - // TODO - // create small units of text from synth_channel->speak_request->body.buf ( , . ? ! but ... - // synthesize small unit and store in audio_buffer - // check for stop - // pause resume state could improve performance - // you can "pause" generating new speech from a unit of text - // by checking the (decreasing) size of the audio_buffer - // no need to generate more speech samples than can be listened to... - cst_wave *wave = 0; - wave = flite_text_to_wave(synth_channel->speak_request->body.buf, synth_channel->voice); - if (wave && cst_wave_num_samples(wave)) - { - int generated = (cst_wave_num_samples(wave)/cst_wave_sample_rate(wave)*1000); - stamp = apr_time_now(); - elapsed = (stamp - start)/1000; - apt_log(APT_LOG_MARK, APT_PRIO_INFO, "TTS (chan %d) took %"APR_TIME_T_FMT" to generate %d of speech (in millisec)", synth_channel->iId, elapsed, generated); + if(!body->length) { + synth_channel->speak_request = NULL; + synth_response_construct(response,MRCP_STATUS_CODE_MISSING_PARAM,SYNTHESIZER_COMPLETION_CAUSE_ERROR); + mrcp_engine_channel_message_send(synth_channel->channel,response); + return FALSE; + } - if (rate != 16000) - { - cst_wave_resample(wave, rate); - elapsed = (apr_time_now() - stamp)/1000; - apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "TTS resampling to %d on (chan %d) took %"APR_TIME_T_FMT" millisec", rate, synth_channel->iId, elapsed); - } - mpf_buffer_audio_write(synth_channel->audio_buffer, cst_wave_samples(wave), cst_wave_num_samples(wave) * 2); - delete_wave(wave); + voice = flite_voices_best_match_get( + synth_channel->flite_engine->voices, + synth_channel->speak_request); + if(!voice) { + /* error case: no voice found, appropriate respond must be sent */ + synth_channel->speak_request = NULL; + synth_response_construct(response,MRCP_STATUS_CODE_METHOD_FAILED,SYNTHESIZER_COMPLETION_CAUSE_ERROR); + mrcp_engine_channel_message_send(synth_channel->channel,response); + return FALSE; + } + + /* + TODO + create small units of text from synth_channel->speak_request->body.buf ( , . ? ! but ... + synthesize small unit and store in audio_buffer + check for stop + pause resume state could improve performance + you can "pause" generating new speech from a unit of text + by checking the (decreasing) size of the audio_buffer + no need to generate more speech samples than can be listened to... + */ + + /* send in-progress response and start synthesizing */ + response->start_line.request_state = MRCP_REQUEST_STATE_INPROGRESS; + mrcp_engine_channel_message_send(synth_channel->channel,response); + + synth_channel->synthesizing = TRUE; + wave = flite_text_to_wave(body->buf, voice); + if(wave && cst_wave_num_samples(wave)) { + int generated = (cst_wave_num_samples(wave)/cst_wave_sample_rate(wave)*1000); + stamp = apr_time_now(); + elapsed = (stamp - start)/1000; + apt_log(APT_LOG_MARK, APT_PRIO_INFO, "TTS (chan %d) took %"APR_TIME_T_FMT" to generate %d of speech (in millisec)", synth_channel->iId, elapsed, generated); + + if(rate != 16000) { + cst_wave_resample(wave, rate); + elapsed = (apr_time_now() - stamp)/1000; + apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "TTS resampling to %d on (chan %d) took %"APR_TIME_T_FMT" millisec", rate, synth_channel->iId, elapsed); } + mpf_buffer_audio_write(synth_channel->audio_buffer, cst_wave_samples(wave), cst_wave_num_samples(wave) * 2); + delete_wave(wave); } - apr_thread_mutex_lock(synth_channel->channel_guard); - stop_response = synth_channel->stop_response; - apr_thread_mutex_unlock(synth_channel->channel_guard); + // this will notify the callback that feeds the client that synthesis is complete + mpf_buffer_event_write(synth_channel->audio_buffer, MEDIA_FRAME_TYPE_EVENT); + synth_channel->synthesizing = FALSE; - if (!stop_response) - { - // this will notify the callback that feeds the client that synthesis is complete - mpf_buffer_event_write(synth_channel->audio_buffer, MEDIA_FRAME_TYPE_EVENT); - apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "> flite_speak_msg_process speak - end of TTS - %d", synth_channel->iId); - } - else - { - apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "> flite_speak_msg_process speak - channel %d", synth_channel->iId); - } + apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "> flite_speak_msg_process speak - end of TTS - %d", synth_channel->iId); return TRUE; } @@ -510,10 +494,7 @@ static apt_bool_t flite_synth_channel_stop(mrcp_engine_channel_t *channel, mrcp_ apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_channel_stop - channel %d", synth_channel->iId); /* store the request, make sure there is no more activity and only then send the response */ - apr_thread_mutex_lock(synth_channel->channel_guard); synth_channel->stop_response = response; - apr_thread_mutex_unlock(synth_channel->channel_guard); - return TRUE; } @@ -541,29 +522,6 @@ static apt_bool_t flite_synth_channel_resume(mrcp_engine_channel_t *channel, mrc return TRUE; } -/** Callback is called from MPF engine context to destroy any additional data associated with audio stream */ -static apt_bool_t flite_synth_stream_destroy(mpf_audio_stream_t *stream) -{ - apt_log(APT_LOG_MARK, APT_PRIO_INFO, "flite_synth_stream_destroy"); - return TRUE; -} - -/** Callback is called from MPF engine context to perform any action before open */ -static apt_bool_t flite_synth_stream_open(mpf_audio_stream_t *stream) -{ -// flite_synth_channel_t *synth_channel = (flite_synth_channel_t *) stream->obj; -// apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_stream_open - channel %d", synth_channel->iId); - return TRUE; -} - -/** Callback is called from MPF engine context to perform any action after close */ -static apt_bool_t flite_synth_stream_close(mpf_audio_stream_t *stream) -{ -// flite_synth_channel_t *synth_channel = (flite_synth_channel_t *) stream->obj; -// apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_stream_close - channel %d", synth_channel->iId); - return TRUE; -} - /** Raise SPEAK-COMPLETE event */ static apt_bool_t flite_synth_speak_complete_raise(flite_synth_channel_t *synth_channel) { @@ -602,26 +560,25 @@ static apt_bool_t flite_synth_speak_complete_raise(flite_synth_channel_t *synth_ static apt_bool_t flite_synth_stream_read(mpf_audio_stream_t *stream, mpf_frame_t *frame) { flite_synth_channel_t *synth_channel = (flite_synth_channel_t *) stream->obj; - if (synth_channel->stop_response && synth_channel->speak_request) - { + if(synth_channel->stop_response && synth_channel->synthesizing == FALSE) { /* send asynchronous response to STOP request */ - mrcp_engine_channel_message_send(synth_channel->channel, synth_channel->stop_response); + mrcp_message_t *stop_response = synth_channel->stop_response; + synth_channel->stop_response = NULL; synth_channel->speak_request = NULL; synth_channel->paused = FALSE; + mrcp_engine_channel_message_send(synth_channel->channel,stop_response); return TRUE; } /* check if there is active SPEAK request and it isn't in paused state */ - if (synth_channel->speak_request && synth_channel->paused == FALSE) - { + if(synth_channel->speak_request && synth_channel->paused == FALSE) { /* normal processing */ mpf_buffer_frame_read(synth_channel->audio_buffer,frame); // apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_stream_read - channel %d - size %d", synth_channel->iId, mpf_buffer_get_size(synth_channel->audio_buffer)); - if((frame->type & MEDIA_FRAME_TYPE_EVENT) == MEDIA_FRAME_TYPE_EVENT) - { + if((frame->type & MEDIA_FRAME_TYPE_EVENT) == MEDIA_FRAME_TYPE_EVENT) { flite_synth_speak_complete_raise(synth_channel); } } return TRUE; -} \ No newline at end of file +}