commit dbea6143d6b63765f2d0eba26728c3903d3d2606

Author: achaloyan <achaloyan@f001bc3a-424a-0410-80a0-a715b8f413a8>
Date:   Wed Jul 8 17:37:24 2009 +0000

    Added yet another buffer of media frames mpf_frame_buffer.
    There are a number of similar buffers (mpf_jitter_buffer, mpf_buffer, mpf_frame_buffer). All of them produce mpf_frames as output, but input is somewhat dif
ferent. Input is RTP payload in case of mpf_jitter_buffer, synthesized chunks of voice in case of mpf_buffer, and mpf_frame in case of mpf_frame_buffer
    
    git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1043 f001bc3a-424a-0410-80a0-a715b8f413a8

commit 1bf7a66012180649bd1f01fd72e483a9e5de3495
Author: achaloyan <achaloyan@f001bc3a-424a-0410-80a0-a715b8f413a8>
Date:   Wed Jul 8 16:33:36 2009 +0000

    Fixed termination of client and server stacks (Issue-28)
    Network client/server stacks generated double TERMINATE_COMPLETE event, while mpf engine generated no TERMINATE_COMPLETE event.
    
    git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1042 f001bc3a-424a-0410-80a0-a715b8f413a8

commit 0327cf17779430b51290d22cc7d51130f997f2f2
Author: achaloyan <achaloyan@f001bc3a-424a-0410-80a0-a715b8f413a8>
Date:   Sun Jul 5 19:22:31 2009 +0000

    Enhanced flite plugin to
    - support more than one SPEAK requests in a session (SPEAK -> STOP -> SPEAK or SPEAK-> SPEAK-COMPLETE->SPEAK), task is created upon channel creation and is 
waiting for consecutive SPEAK requests to process
    - properly handle race between STOP request and SPEAK-COMPLETE event
    - respond with IN-PROGRESS, when all the required parameters are checked to be valid and synthesizing is due to start
    
    git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1037 f001bc3a-424a-0410-80a0-a715b8f413a8

commit e2f73f9279e54ef3a40cb637a38ab1ff485ea5d3
Author: achaloyan <achaloyan@f001bc3a-424a-0410-80a0-a715b8f413a8>
Date:   Sun Jul 5 03:46:13 2009 +0000

    Separated flite voices (unified voice register/unregister routine, select voice by name)
    
    git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1036 f001bc3a-424a-0410-80a0-a715b8f413a8

commit 694783a527cb795ca45982c2388895da06dd6009
Author: achaloyan <achaloyan@f001bc3a-424a-0410-80a0-a715b8f413a8>
Date:   Sat Jul 4 05:06:17 2009 +0000

    Checked content-type to be "text/plain" as flite doesn't support SSML yet.
    
    git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1035 f001bc3a-424a-0410-80a0-a715b8f413a8

commit aafdc6e30e77c40e5470cce844f48ec28a254786
Author: achaloyan <achaloyan@f001bc3a-424a-0410-80a0-a715b8f413a8>
Date:   Fri Jul 3 14:55:10 2009 +0000

    Enhanced float value generation to use the precision (digits after decimal sign) user provides by removing optional trailing 0s (if any)
    Issue-35
    
    git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1034 f001bc3a-424a-0410-80a0-a715b8f413a8

commit bcc2f137956f5c532c0256b064c8a3d1015a46d0
Author: achaloyan <achaloyan@f001bc3a-424a-0410-80a0-a715b8f413a8>
Date:   Fri Jul 3 07:04:07 2009 +0000

    Enhanced RTP port management (Issue-34, Thanks cpsoares)

    git-svn-id: https://unimrcp.googlecode.com/svn/trunk@1033 f001bc3a-424a-0410-80a0-a715b8f413a8



git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@14166 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Brian West 2009-07-09 15:19:27 +00:00
parent 72b4dfd6c1
commit 915fa372d8
16 changed files with 664 additions and 294 deletions

View File

@ -1 +1 @@
Wed Jul 1 19:53:07 CDT 2009 Thu Jul 9 10:17:26 CDT 2009

View File

@ -22,8 +22,6 @@
* @brief Text Stream Parse/Generate Routine * @brief Text Stream Parse/Generate Routine
*/ */
#include <stdlib.h>
#include <stdio.h>
#include "apt_string.h" #include "apt_string.h"
#include "apt_pair.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 */ /** Generate boolean-value */
APT_DECLARE(apt_bool_t) apt_boolean_value_generate(apt_bool_t value, apt_text_stream_t *str); APT_DECLARE(apt_bool_t) apt_boolean_value_generate(apt_bool_t value, apt_text_stream_t *str);
/** Parse size_t value */ /** Parse size_t value */
static APR_INLINE apr_size_t apt_size_value_parse(const apt_str_t *str) 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 */ /** Generate apr_size_t value */
static APR_INLINE apt_bool_t apt_size_value_generate(apr_size_t value, apt_text_stream_t *stream) 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 */ /** Parse float value */
static APR_INLINE float apt_float_value_parse(const apt_str_t *str) APT_DECLARE(float) apt_float_value_parse(const apt_str_t *str);
{
return str->buf ? (float)atof(str->buf) : 0;
}
/** Generate float value */ /** Generate float value */
static APR_INLINE apt_bool_t apt_float_value_generate(float value, apt_text_stream_t *stream) APT_DECLARE(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;
}
/** Generate string value */ /** Generate string value */
static APR_INLINE apt_bool_t apt_string_value_generate(const apt_str_t *str, apt_text_stream_t *stream) static APR_INLINE apt_bool_t apt_string_value_generate(const apt_str_t *str, apt_text_stream_t *stream)

View File

@ -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_net_client_task_pollset_destroy(task);
apt_task_child_terminate(task->base);
return TRUE; return TRUE;
} }

View File

@ -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_net_server_task_pollset_destroy(task);
apt_task_child_terminate(task->base);
return TRUE; return TRUE;
} }

View File

@ -14,6 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
#include <stdlib.h>
#include <stdio.h>
#include <apr_uuid.h> #include <apr_uuid.h>
#include "apt_text_stream.h" #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; 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. */ /** 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) APT_DECLARE(apt_bool_t) apt_var_length_value_generate(apr_size_t *value, apr_size_t max_count, apt_str_t *str)

View File

@ -20,6 +20,7 @@ include_HEADERS = codecs/g711/g711.h \
include/mpf_context.h \ include/mpf_context.h \
include/mpf_engine.h \ include/mpf_engine.h \
include/mpf_frame.h \ include/mpf_frame.h \
include/mpf_frame_buffer.h \
include/mpf_message.h \ include/mpf_message.h \
include/mpf_object.h \ include/mpf_object.h \
include/mpf_stream.h \ include/mpf_stream.h \
@ -55,6 +56,7 @@ libmpf_la_SOURCES = codecs/g711/g711.c \
src/mpf_termination.c \ src/mpf_termination.c \
src/mpf_rtp_termination_factory.c \ src/mpf_rtp_termination_factory.c \
src/mpf_file_termination_factory.c \ src/mpf_file_termination_factory.c \
src/mpf_frame_buffer.c \
src/mpf_timer.c \ src/mpf_timer.c \
src/mpf_encoder.c \ src/mpf_encoder.c \
src/mpf_decoder.c \ src/mpf_decoder.c \

View File

@ -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__*/

View File

@ -207,6 +207,10 @@
RelativePath=".\include\mpf_frame.h" RelativePath=".\include\mpf_frame.h"
> >
</File> </File>
<File
RelativePath=".\include\mpf_frame_buffer.h"
>
</File>
<File <File
RelativePath=".\include\mpf_jitter_buffer.h" RelativePath=".\include\mpf_jitter_buffer.h"
> >
@ -332,6 +336,10 @@
RelativePath=".\src\mpf_file_termination_factory.c" RelativePath=".\src\mpf_file_termination_factory.c"
> >
</File> </File>
<File
RelativePath=".\src\mpf_frame_buffer.c"
>
</File>
<File <File
RelativePath=".\src\mpf_jitter_buffer.c" RelativePath=".\src\mpf_jitter_buffer.c"
> >

View File

@ -128,6 +128,7 @@ static apt_bool_t mpf_engine_terminate(apt_task_t *task)
mpf_timer_stop(engine->timer); mpf_timer_stop(engine->timer);
mpf_engine_contexts_destroy(engine); mpf_engine_contexts_destroy(engine);
apt_task_child_terminate(task);
return TRUE; return TRUE;
} }

View File

@ -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; i<buffer->frame_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;
}

View File

@ -83,21 +83,6 @@ MPF_DECLARE(mpf_audio_stream_t*) mpf_rtp_stream_create(mpf_termination_t *termin
return rtp_stream->base; 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) 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; 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) { if(remote_media) {
local_media->base.id = remote_media->base.id; 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); do {
if(mpf_rtp_socket_create(rtp_stream,local_media) == FALSE) { 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; local_media->base.state = MPF_MEDIA_DISABLED;
status = FALSE; status = FALSE;
} }

View File

@ -14,6 +14,6 @@ INCLUDES = -Iinclude \
plugin_LTLIBRARIES = mrcpflite.la 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_LDFLAGS = -module $(PLUGIN_LT_VERSION)
mrcpflite_la_LIBADD = $(UNIMRCP_FLITE_LIBS) -lm mrcpflite_la_LIBADD = $(UNIMRCP_FLITE_LIBS) -lm

View File

@ -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 <flite.h>
#include <apr_hash.h>
#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__*/

View File

@ -149,11 +149,19 @@
Filter="h;hpp;hxx;hm;inl;inc;xsd" Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
> >
<File
RelativePath=".\include\flite_voices.h"
>
</File>
</Filter> </Filter>
<Filter <Filter
Name="src" Name="src"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
> >
<File
RelativePath=".\src\flite_voices.c"
>
</File>
<File <File
RelativePath=".\src\mrcp_flite.c" RelativePath=".\src\mrcp_flite.c"
> >

View File

@ -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;
}

View File

@ -24,6 +24,7 @@
* 4. Methods (callbacks) of the MPF engine stream MUST not block. * 4. Methods (callbacks) of the MPF engine stream MUST not block.
*/ */
#include "flite_voices.h"
#include "mrcp_resource_engine.h" #include "mrcp_resource_engine.h"
#include "mrcp_synth_resource.h" #include "mrcp_synth_resource.h"
#include "mrcp_synth_header.h" #include "mrcp_synth_header.h"
@ -33,7 +34,6 @@
#include "apr_time.h" #include "apr_time.h"
#include "apt_consumer_task.h" #include "apt_consumer_task.h"
#include "apt_log.h" #include "apt_log.h"
#include "flite.h"
typedef struct flite_synth_engine_t flite_synth_engine_t; typedef struct flite_synth_engine_t flite_synth_engine_t;
typedef struct flite_synth_channel_t flite_synth_channel_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_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_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 **/ /** 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_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); 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 */ /** 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 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 = { static const mpf_audio_stream_vtable_t audio_stream_vtable = {
flite_synth_stream_destroy, NULL,
flite_synth_stream_open, NULL,
flite_synth_stream_close, NULL,
flite_synth_stream_read, flite_synth_stream_read,
NULL, NULL,
NULL, NULL,
@ -92,39 +87,25 @@ static const mpf_audio_stream_vtable_t audio_stream_vtable = {
/** Declaration of flite synthesizer engine */ /** Declaration of flite synthesizer engine */
struct flite_synth_engine_t { struct flite_synth_engine_t {
int iChannels; /** Table of flite voices */
struct { flite_voices_t *voices;
cst_voice *awb; int iChannels;
cst_voice *kal;
cst_voice *rms;
cst_voice *slt;
} voices;
}; };
/** 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 */ /** Declaration of flite synthesizer channel */
struct flite_synth_channel_t { struct flite_synth_channel_t {
flite_synth_engine_t *flite_engine; // Back pointer to engine flite_synth_engine_t *flite_engine; /* Back pointer to engine */
mrcp_engine_channel_t *channel; // Engine channel base mrcp_engine_channel_t *channel; /* Engine channel base */
mrcp_message_t *speak_request; // Active (in-progress) speak request mrcp_message_t *speak_request; /* Active (in-progress) speak request */
mrcp_message_t *stop_response; // Pending stop response mrcp_message_t *speak_response;/* Pending speak response */
apt_bool_t paused; // Is paused mrcp_message_t *stop_response; /* Pending stop response */
mpf_buffer_t *audio_buffer; // Audio buffer apt_bool_t synthesizing; /* Is synthesizer task processing speak request */
int iId; // Synth channel simultaneous reference count apt_bool_t paused; /* Is paused */
cst_voice *voice; mpf_buffer_t *audio_buffer; /* Audio buffer */
apr_pool_t *pool; int iId; /* Synth channel simultaneous reference count */
apt_consumer_task_t *task; apr_pool_t *pool;
apr_thread_mutex_t *channel_guard; apt_task_t *task;
apt_task_msg_pool_t *msg_pool;
}; };
/** Declaration of flite synthesizer task message */ /** 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; typedef struct flite_speak_msg_t flite_speak_msg_t;
// we have a special task for the actual synthesis - /* we have a special task for the actual synthesis -
// the task is created when a mrcp speak message is received 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); 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 */ /** 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 */ /* create flite engine */
flite_synth_engine_t *flite_engine = (flite_synth_engine_t *) apr_palloc(pool,sizeof(flite_synth_engine_t)); flite_synth_engine_t *flite_engine = (flite_synth_engine_t *) apr_palloc(pool,sizeof(flite_synth_engine_t));
flite_engine->iChannels = 0; flite_engine->iChannels = 0;
/* create resource engine base */ /* 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"); apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_engine_open");
flite_init(); flite_init();
flite_engine->voices.awb = register_cmu_us_awb();
flite_engine->voices.kal = register_cmu_us_kal(); flite_engine->voices = flite_voices_load(engine->pool);
flite_engine->voices.rms = register_cmu_us_rms();
flite_engine->voices.slt = register_cmu_us_slt();
apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite init success"); apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite init success");
return TRUE; 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; flite_synth_engine_t *flite_engine = (flite_synth_engine_t *) engine->obj;
apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_engine_close"); apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_engine_close");
unregister_cmu_us_awb(flite_engine->voices.awb); flite_voices_unload(flite_engine->voices);
unregister_cmu_us_kal(flite_engine->voices.kal);
unregister_cmu_us_rms(flite_engine->voices.rms);
unregister_cmu_us_slt(flite_engine->voices.slt);
return TRUE; 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 */ /** 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) 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; mpf_codec_descriptor_t *codec_descriptor = NULL;
apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_engine_channel_create"); apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_engine_channel_create");
#if 0
// codec_descriptor = (mpf_codec_descriptor_t *) apr_palloc(pool,sizeof(mpf_codec_descriptor_t)); codec_descriptor = (mpf_codec_descriptor_t *) apr_palloc(pool,sizeof(mpf_codec_descriptor_t));
// mpf_codec_descriptor_init(codec_descriptor); mpf_codec_descriptor_init(codec_descriptor);
// codec_descriptor->channel_count = 1; codec_descriptor->channel_count = 1;
// codec_descriptor->payload_type = 96; codec_descriptor->payload_type = 96;
// apt_string_set(&codec_descriptor->name,"LPCM"); apt_string_set(&codec_descriptor->name,"LPCM");
// codec_descriptor->sampling_rate = 16000; codec_descriptor->sampling_rate = 16000;
#endif
synth_channel->flite_engine = (flite_synth_engine_t *) engine->obj; synth_channel->flite_engine = (flite_synth_engine_t *) engine->obj;
synth_channel->speak_request = NULL; // no active speak request in progress synth_channel->speak_request = NULL; // no active speak request in progress
synth_channel->speak_response = NULL;
synth_channel->stop_response = NULL; synth_channel->stop_response = NULL;
synth_channel->synthesizing = FALSE;
synth_channel->paused = FALSE; synth_channel->paused = FALSE;
synth_channel->pool = pool; synth_channel->pool = pool;
synth_channel->audio_buffer = NULL; synth_channel->audio_buffer = NULL;
synth_channel->voice = NULL;
synth_channel->iId = 0; synth_channel->iId = 0;
synth_channel->task = NULL;
if (apr_thread_mutex_create(&synth_channel->channel_guard,APR_THREAD_MUTEX_DEFAULT,pool) != APR_SUCCESS) synth_channel->msg_pool = NULL;
{ if(flite_synth_task_create(synth_channel) != TRUE) {
apt_log(APT_LOG_MARK, APT_PRIO_ERROR, "Failed to create channel guard"); apt_log(APT_LOG_MARK, APT_PRIO_WARNING, "flite_synth_task_create failed");
return NULL; return NULL;
} }
/* create engine channel base */ /* create engine channel base */
synth_channel->channel = mrcp_engine_source_channel_create( synth_channel->channel = mrcp_engine_source_channel_create(
engine, /* resource engine */ 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 */ codec_descriptor, /* codec descriptor might be NULL by default */
pool); /* pool to allocate memory from */ 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_log(APT_LOG_MARK, APT_PRIO_WARNING, "flite_synth_engine_channel_create failed");
apt_task_destroy(synth_channel->task);
return NULL; 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; 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); apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_engine_channel_create created channel %d", synth_channel->iId);
return synth_channel->channel; 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; 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); apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_channel_destroy - channel %d", synth_channel->iId);
if(synth_channel->task) if(synth_channel->task) {
{ apt_task_destroy(synth_channel->task);
apt_task_t *task = apt_consumer_task_base_get(synth_channel->task); synth_channel->task = NULL;
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);
}
} }
synth_channel->task = NULL;
synth_channel->flite_engine->iChannels--; synth_channel->flite_engine->iChannels--;
apr_thread_mutex_destroy(synth_channel->channel_guard);
return TRUE; 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; 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); 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); 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; 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); 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; 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) { switch(request->start_line.method_id) {
case SYNTHESIZER_SET_PARAMS: 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; break;
case SYNTHESIZER_GET_PARAMS: case SYNTHESIZER_GET_PARAMS:
break; 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); mrcp_engine_channel_message_send(channel,response);
} }
return TRUE; 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; mrcp_synth_header_t *synth_header = mrcp_resource_header_prepare(response);
apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "flite_synth_channel_close_t - channel %d", synth_channel->iId); if(!synth_header) {
return FALSE;
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_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; return TRUE;
} }
/** Process SPEAK request */ /** Process SPEAK 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_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; 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); apt_log(APT_LOG_MARK, APT_PRIO_INFO, "flite_synth_channel_speak - channel %d", synth_channel->iId);
if (!synth_channel->speak_request) generic_header = mrcp_generic_header_get(request);
{ if(generic_header) {
apt_task_msg_pool_t *msg_pool = apt_task_msg_pool_create_dynamic( sizeof(flite_speak_msg_t),synth_channel->pool); /* content-type must be specified */
apt_task_vtable_t *task_vtable = 0; if(mrcp_generic_header_property_check(request,GENERIC_HEADER_CONTENT_TYPE) == TRUE) {
apt_task_t * task = 0; content_type = generic_header->content_type.buf;
apt_task_msg_t *msg = 0; }
flite_speak_msg_t *flite_msg = 0; }
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 */ /* Flite currently supports only text/plain (no SSML) */
apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "Create flite speak task - channel: %d", synth_channel->iId); if(strstr(content_type,"text") == NULL) {
synth_channel->task = apt_consumer_task_create(synth_channel, msg_pool, synth_channel->pool); apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Not Supported Content-Type [%s]",content_type);
if (!synth_channel->task) synth_response_construct(response,MRCP_STATUS_CODE_UNSUPPORTED_PARAM_VALUE,SYNTHESIZER_COMPLETION_CAUSE_ERROR);
{ return FALSE;
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(synth_channel->task); synth_channel->speak_request = request;
if (!task_vtable) synth_channel->speak_response = response;
{
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; msg = apt_task_msg_acquire(synth_channel->msg_pool);
synth_channel->speak_request = request; msg->type = TASK_MSG_USER;
flite_msg = (flite_speak_msg_t*) msg->data;
apt_log(APT_LOG_MARK,APT_PRIO_DEBUG, "Start task - channel %d", synth_channel->iId); flite_msg->channel = synth_channel;
task = apt_consumer_task_base_get(synth_channel->task); flite_msg->request = request;
if (apt_task_start(task) == FALSE) 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_ERROR, "flite_synth_channel_speak failed to start task - channel: %d", synth_channel->iId); 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); synth_channel->speak_request = NULL;
return FALSE; synth_channel->speak_response = NULL;
} synth_response_construct(response,MRCP_STATUS_CODE_METHOD_FAILED,SYNTHESIZER_COMPLETION_CAUSE_ERROR);
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;
}
} }
return TRUE; 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_speak_msg_t *flite_msg = (flite_speak_msg_t*)msg->data;
flite_synth_channel_t *synth_channel = (flite_synth_channel_t *) flite_msg->channel; 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 start = 0;
apr_time_t elapsed = 0; apr_time_t elapsed = 0;
apr_time_t stamp = 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); 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); 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 start = apr_time_now(); // in microsec
if (synth_channel->speak_request->body.length) if(!body->length) {
{ synth_channel->speak_request = NULL;
// TODO synth_response_construct(response,MRCP_STATUS_CODE_MISSING_PARAM,SYNTHESIZER_COMPLETION_CAUSE_ERROR);
// create small units of text from synth_channel->speak_request->body.buf ( , . ? ! but ... mrcp_engine_channel_message_send(synth_channel->channel,response);
// synthesize small unit and store in audio_buffer return FALSE;
// 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 (rate != 16000) voice = flite_voices_best_match_get(
{ synth_channel->flite_engine->voices,
cst_wave_resample(wave, rate); synth_channel->speak_request);
elapsed = (apr_time_now() - stamp)/1000; if(!voice) {
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); /* error case: no voice found, appropriate respond must be sent */
} synth_channel->speak_request = NULL;
mpf_buffer_audio_write(synth_channel->audio_buffer, cst_wave_samples(wave), cst_wave_num_samples(wave) * 2); synth_response_construct(response,MRCP_STATUS_CODE_METHOD_FAILED,SYNTHESIZER_COMPLETION_CAUSE_ERROR);
delete_wave(wave); 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); // this will notify the callback that feeds the client that synthesis is complete
stop_response = synth_channel->stop_response; mpf_buffer_event_write(synth_channel->audio_buffer, MEDIA_FRAME_TYPE_EVENT);
apr_thread_mutex_unlock(synth_channel->channel_guard); synth_channel->synthesizing = FALSE;
if (!stop_response) apt_log(APT_LOG_MARK, APT_PRIO_DEBUG, "> flite_speak_msg_process speak - end of TTS - %d", synth_channel->iId);
{
// 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);
}
return TRUE; 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); 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 */ /* 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; synth_channel->stop_response = response;
apr_thread_mutex_unlock(synth_channel->channel_guard);
return TRUE; return TRUE;
} }
@ -541,29 +522,6 @@ static apt_bool_t flite_synth_channel_resume(mrcp_engine_channel_t *channel, mrc
return TRUE; 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 */ /** Raise SPEAK-COMPLETE event */
static apt_bool_t flite_synth_speak_complete_raise(flite_synth_channel_t *synth_channel) 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) 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; 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 */ /* 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->speak_request = NULL;
synth_channel->paused = FALSE; synth_channel->paused = FALSE;
mrcp_engine_channel_message_send(synth_channel->channel,stop_response);
return TRUE; return TRUE;
} }
/* check if there is active SPEAK request and it isn't in paused state */ /* 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 */ /* normal processing */
mpf_buffer_frame_read(synth_channel->audio_buffer,frame); 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)); // 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); flite_synth_speak_complete_raise(synth_channel);
} }
} }
return TRUE; return TRUE;
} }