mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-02-08 08:51:50 +00:00
285 lines
8.0 KiB
C++
285 lines
8.0 KiB
C++
/*
|
|
* Copyright 2008-2010 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.
|
|
*
|
|
* $Id: synthsession.cpp 1586 2010-03-12 19:39:02Z achaloyan $
|
|
*/
|
|
|
|
#include "synthsession.h"
|
|
#include "synthscenario.h"
|
|
#include "mrcp_message.h"
|
|
#include "mrcp_generic_header.h"
|
|
#include "mrcp_synth_header.h"
|
|
#include "mrcp_synth_resource.h"
|
|
|
|
struct SynthChannel
|
|
{
|
|
/** MRCP channel */
|
|
mrcp_channel_t* m_pMrcpChannel;
|
|
/** IN-PROGRESS SPEAK request */
|
|
mrcp_message_t* m_pSpeakRequest;
|
|
/** File to write audio stream to */
|
|
FILE* m_pAudioOut;
|
|
|
|
SynthChannel() : m_pMrcpChannel(NULL), m_pSpeakRequest(NULL), m_pAudioOut(NULL) {}
|
|
};
|
|
|
|
SynthSession::SynthSession(const SynthScenario* pScenario) :
|
|
UmcSession(pScenario),
|
|
m_pSynthChannel(NULL)
|
|
{
|
|
}
|
|
|
|
SynthSession::~SynthSession()
|
|
{
|
|
}
|
|
|
|
bool SynthSession::Start()
|
|
{
|
|
if(!GetScenario()->IsSpeakEnabled())
|
|
return false;
|
|
|
|
/* create channel and associate all the required data */
|
|
m_pSynthChannel = CreateSynthChannel();
|
|
if(!m_pSynthChannel)
|
|
return false;
|
|
|
|
/* add channel to session (send asynchronous request) */
|
|
if(!AddMrcpChannel(m_pSynthChannel->m_pMrcpChannel))
|
|
{
|
|
delete m_pSynthChannel;
|
|
m_pSynthChannel = NULL;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SynthSession::Stop()
|
|
{
|
|
if(!UmcSession::Stop())
|
|
return false;
|
|
|
|
if(!m_pSynthChannel)
|
|
return false;
|
|
|
|
mrcp_message_t* pStopMessage = CreateMrcpMessage(m_pSynthChannel->m_pMrcpChannel,SYNTHESIZER_STOP);
|
|
if(!pStopMessage)
|
|
return false;
|
|
|
|
if(m_pSynthChannel->m_pSpeakRequest)
|
|
{
|
|
mrcp_generic_header_t* pGenericHeader;
|
|
/* get/allocate generic header */
|
|
pGenericHeader = (mrcp_generic_header_t*) mrcp_generic_header_prepare(pStopMessage);
|
|
if(pGenericHeader)
|
|
{
|
|
pGenericHeader->active_request_id_list.count = 1;
|
|
pGenericHeader->active_request_id_list.ids[0] =
|
|
m_pSynthChannel->m_pSpeakRequest->start_line.request_id;
|
|
mrcp_generic_header_property_add(pStopMessage,GENERIC_HEADER_ACTIVE_REQUEST_ID_LIST);
|
|
}
|
|
|
|
m_pSynthChannel->m_pSpeakRequest = NULL;
|
|
}
|
|
|
|
return SendMrcpRequest(m_pSynthChannel->m_pMrcpChannel,pStopMessage);
|
|
}
|
|
|
|
bool SynthSession::OnSessionTerminate(mrcp_sig_status_code_e status)
|
|
{
|
|
if(m_pSynthChannel)
|
|
{
|
|
FILE* pAudioOut = m_pSynthChannel->m_pAudioOut;
|
|
if(pAudioOut)
|
|
{
|
|
m_pSynthChannel->m_pAudioOut = NULL;
|
|
fclose(pAudioOut);
|
|
}
|
|
|
|
delete m_pSynthChannel;
|
|
m_pSynthChannel = NULL;
|
|
}
|
|
return UmcSession::OnSessionTerminate(status);
|
|
}
|
|
|
|
static apt_bool_t WriteStream(mpf_audio_stream_t* pStream, const mpf_frame_t* pFrame)
|
|
{
|
|
SynthChannel* pSynthChannel = (SynthChannel*) pStream->obj;
|
|
if(pSynthChannel && pSynthChannel->m_pAudioOut)
|
|
{
|
|
fwrite(pFrame->codec_frame.buffer,1,pFrame->codec_frame.size,pSynthChannel->m_pAudioOut);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
SynthChannel* SynthSession::CreateSynthChannel()
|
|
{
|
|
mrcp_channel_t* pChannel;
|
|
mpf_termination_t* pTermination;
|
|
mpf_stream_capabilities_t* pCapabilities;
|
|
apr_pool_t* pool = GetSessionPool();
|
|
|
|
/* create channel */
|
|
SynthChannel* pSynthChannel = new SynthChannel;
|
|
|
|
/* create sink stream capabilities */
|
|
pCapabilities = mpf_sink_stream_capabilities_create(pool);
|
|
GetScenario()->InitCapabilities(pCapabilities);
|
|
|
|
static const mpf_audio_stream_vtable_t audio_stream_vtable =
|
|
{
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
WriteStream
|
|
};
|
|
|
|
pTermination = CreateAudioTermination(
|
|
&audio_stream_vtable, /* virtual methods table of audio stream */
|
|
pCapabilities, /* capabilities of audio stream */
|
|
pSynthChannel); /* object to associate */
|
|
|
|
pChannel = CreateMrcpChannel(
|
|
MRCP_SYNTHESIZER_RESOURCE, /* MRCP resource identifier */
|
|
pTermination, /* media termination, used to terminate audio stream */
|
|
NULL, /* RTP descriptor, used to create RTP termination (NULL by default) */
|
|
pSynthChannel); /* object to associate */
|
|
if(!pChannel)
|
|
{
|
|
delete pSynthChannel;
|
|
return NULL;
|
|
}
|
|
|
|
pSynthChannel->m_pMrcpChannel = pChannel;
|
|
return pSynthChannel;
|
|
}
|
|
|
|
bool SynthSession::OnChannelAdd(mrcp_channel_t* pMrcpChannel, mrcp_sig_status_code_e status)
|
|
{
|
|
if(!UmcSession::OnChannelAdd(pMrcpChannel,status))
|
|
return false;
|
|
|
|
SynthChannel* pSynthChannel = (SynthChannel*) mrcp_application_channel_object_get(pMrcpChannel);
|
|
if(status != MRCP_SIG_STATUS_CODE_SUCCESS)
|
|
{
|
|
/* error case, just terminate the demo */
|
|
return Terminate();
|
|
}
|
|
|
|
/* create MRCP message */
|
|
mrcp_message_t* pMrcpMessage = CreateSpeakRequest(pMrcpChannel);
|
|
if(pMrcpMessage)
|
|
{
|
|
SendMrcpRequest(pSynthChannel->m_pMrcpChannel,pMrcpMessage);
|
|
}
|
|
|
|
const mpf_codec_descriptor_t* pDescriptor = mrcp_application_sink_descriptor_get(pMrcpChannel);
|
|
pSynthChannel->m_pAudioOut = GetAudioOut(pDescriptor,GetSessionPool());
|
|
return true;
|
|
}
|
|
|
|
bool SynthSession::OnMessageReceive(mrcp_channel_t* pMrcpChannel, mrcp_message_t* pMrcpMessage)
|
|
{
|
|
if(!UmcSession::OnMessageReceive(pMrcpChannel,pMrcpMessage))
|
|
return false;
|
|
|
|
if(pMrcpMessage->start_line.message_type == MRCP_MESSAGE_TYPE_RESPONSE)
|
|
{
|
|
/* received MRCP response */
|
|
if(pMrcpMessage->start_line.method_id == SYNTHESIZER_SPEAK)
|
|
{
|
|
/* received the response to SPEAK request */
|
|
if(pMrcpMessage->start_line.request_state == MRCP_REQUEST_STATE_INPROGRESS)
|
|
{
|
|
SynthChannel* pSynthChannel = (SynthChannel*) mrcp_application_channel_object_get(pMrcpChannel);
|
|
if(pSynthChannel)
|
|
pSynthChannel->m_pSpeakRequest = GetMrcpMessage();
|
|
|
|
/* waiting for SPEAK-COMPLETE event */
|
|
}
|
|
else
|
|
{
|
|
/* received unexpected response, terminate the session */
|
|
Terminate();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* received unexpected response */
|
|
}
|
|
}
|
|
else if(pMrcpMessage->start_line.message_type == MRCP_MESSAGE_TYPE_EVENT)
|
|
{
|
|
/* received MRCP event */
|
|
if(pMrcpMessage->start_line.method_id == SYNTHESIZER_SPEAK_COMPLETE)
|
|
{
|
|
SynthChannel* pSynthChannel = (SynthChannel*) mrcp_application_channel_object_get(pMrcpChannel);
|
|
if(pSynthChannel)
|
|
pSynthChannel->m_pSpeakRequest = NULL;
|
|
/* received SPEAK-COMPLETE event, terminate the session */
|
|
Terminate();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
mrcp_message_t* SynthSession::CreateSpeakRequest(mrcp_channel_t* pMrcpChannel)
|
|
{
|
|
mrcp_message_t* pMrcpMessage = CreateMrcpMessage(pMrcpChannel,SYNTHESIZER_SPEAK);
|
|
if(!pMrcpMessage)
|
|
return NULL;
|
|
|
|
const SynthScenario* pScenario = GetScenario();
|
|
|
|
mrcp_generic_header_t* pGenericHeader;
|
|
mrcp_synth_header_t* pSynthHeader;
|
|
/* get/allocate generic header */
|
|
pGenericHeader = (mrcp_generic_header_t*) mrcp_generic_header_prepare(pMrcpMessage);
|
|
if(pGenericHeader)
|
|
{
|
|
/* set generic header fields */
|
|
apt_string_assign(&pGenericHeader->content_type,pScenario->GetContentType(),pMrcpMessage->pool);
|
|
mrcp_generic_header_property_add(pMrcpMessage,GENERIC_HEADER_CONTENT_TYPE);
|
|
|
|
/* set message body */
|
|
if(pScenario->GetContent())
|
|
apt_string_assign(&pMrcpMessage->body,pScenario->GetContent(),pMrcpMessage->pool);
|
|
}
|
|
/* get/allocate synthesizer header */
|
|
pSynthHeader = (mrcp_synth_header_t*) mrcp_resource_header_prepare(pMrcpMessage);
|
|
if(pSynthHeader)
|
|
{
|
|
/* set synthesizer header fields */
|
|
pSynthHeader->voice_param.age = 28;
|
|
mrcp_resource_header_property_add(pMrcpMessage,SYNTHESIZER_HEADER_VOICE_AGE);
|
|
}
|
|
|
|
return pMrcpMessage;
|
|
}
|
|
|
|
FILE* SynthSession::GetAudioOut(const mpf_codec_descriptor_t* pDescriptor, apr_pool_t* pool) const
|
|
{
|
|
char* pFileName = apr_psprintf(pool,"synth-%dkHz-%s.pcm",
|
|
pDescriptor ? pDescriptor->sampling_rate/1000 : 8, GetMrcpSessionId());
|
|
apt_dir_layout_t* pDirLayout = GetScenario()->GetDirLayout();
|
|
char* pFilePath = apt_datadir_filepath_get(pDirLayout,pFileName,pool);
|
|
if(!pFilePath)
|
|
return NULL;
|
|
|
|
return fopen(pFilePath,"wb");
|
|
}
|