[Freeswitch] TTS on python

Freeswitch 에서 TTS 서비스를 제공하는 방법은 크게 두가지가 있다.

1. Dialplan 에서 해당 모듈을 직접 호출하는 방법.
2. Python, Lua 와 같은 스크립트 모듈에서 호출하는 방법

여기에서는 Python 스크립트 모듈에서 사용하는 방법을 기술한다.

 

mod_tts_commandline 을 사용하는 방법

Freeswitch 에서는 TTS 서비스를 위해 여러가지 모듈들을 제공하는데, 그 중 mod_tts_commandline 모듈은 서버에 설치되어 있는 TTS 프로그램과 연동하여 TTS 서비스를 제공하는 모듈이다 .

먼저 mod_tts_commandline 모듈을 설치하자.

cd /freeswitch/souce/directory
vi 편집기로 modules.conf 파일에서 asr_tts/mod_tts_commandline 부분을 주석 해제하자.
./configure && make && make install

설치가 끝나면, 해당 모듈이 자동 로드될 수 있도록 해주자.

vi /freeswitch/config_directory/conf/autoload_configs/modules.conf.xml
<load module=”mod_tts_commandline”/> 부분을 주석해제해주자.

tts_commandline.conf.xml 파일을 수정해야 한다. 여러가지 명령어 구문들이 가능한데.. 다음은 epseak 를 사용한 예제 명령어 구문이다.

<param name=”command” value=”echo ${text} | espeak –stdin –stdout -v default| sox -t wav – -r ${rate} ${file}”/>

기타 다른 예시 명령어 문구는 https://wiki.freeswitch.org/wiki/Mod_tts_commandline 에서 확인할 수 있다.

이후 Python 스크립트에서 다음과 같이 호출하여 사용하면 된다.

session.set_tts_params("espeak", "default");
session.speak("Hello, world!");

 

mod_shout 를 사용하는 방법

본래, mod_shout 는 외부 자원을 재생시키는 모듈이다. 하지만 이를 이용하면 외부(웹)에서 제공하는 TTS 서비스를 사용할 수 있다.
외부(웹)에서 TTS 를 제공하는 곳은 크게, Bing/Google 이 있는데, 여기서는 Google 을 기준으로 설명한다.

간단하다. mod_shout 설치 후, 다음과 같이 사용하면 된다.

session.streamFile("shout://translate.google.com/translate_tts?tl=en&q=Hello+world", "")
# 혹은...
recvDtmf=session.playAndGetDigits(1, 1, 1, 1, "", "shout://translate.google.com/translate_tts?tl=en&q=Hello+world", "", "", "", 0, "")

 

참조:

https://wiki.freeswitch.org/wiki/Mod_shout

https://wiki.freeswitch.org/wiki/Session_streamFile

https://wiki.freeswitch.org/wiki/Playing_recording_external_media

https://wiki.freeswitch.org/wiki/Mod_tts_commandline

http://wiki.freeswitch.org/wiki/Category:TTS

https://wiki.freeswitch.org/wiki/Mod_lua

[Freeswitch] python-bridge

Freeswitch 에서 Python 으로 dialplan 작성시…

Call-Bridge 를 해야 하는 경우..

call_addr = ‘sofia/profile_name/call_to@host_ip’
session.execute(“bridge”, call_addr)

과 같이 입력하면 된다.

Freeswitch Python 모듈에서 제공하는 Transfer 의 경우, 이미 작성되어 있는 Dailplan 으로 넘겨주는 역할밖에 하지 못한다.
즉, 다른 곳으로 전화를 걸어서 그쪽으로 넘겨주는 기능이 아닌 것이다.

가장 대표적인 기능이 음성 사서함에서의 콜백 기능인데, 콜백 기능을 Freeswitch Python 으로 작성할 경우, 위와 같이 작성하면 된다.

만약, Bridge 와 동시(즉, INVITE 시)에 특정 값들을 같이 넘겨 주어야 하는 경우, Originate 와 마찬가지로 call_addr 앞부분에 입력하고자 하는 파라미터 값을 넣어주면 된다.

[Freeswitch] python-originate

Freeswitch 에서 python 으로 dialplan 작성시,.. originate 를 해야 하는 경우.

from freeswitch import *

new_session = Session(sofia/profile_name/call_to@host_ip)

만약 콜 생성을 하면서 (INVITE) 시, 특정 옵션 값들을 입력하고 싶다면..

{sip_h_TestOpt=test}sofia/internal/200@pchero21.com

과 같이 sofia 앞부분에 “{}” 를 넣어서 입력하고자 하는 값들을 입력하면 된다.

 

출처 : http://lists.freeswitch.org/pipermail/freeswitch-users/2011-March/070126.html

[Freeswitch] Originate

새로운 콜을 생성할 때 사용하는 모듈이다.

기본 메뉴얼. https://wiki.freeswitch.org/wiki/Mod_commands#originate

freeswitch@internal> originate
-USAGE: <call url> <exten>|&<application_name>(<app_args>) [<dialplan>] [<context>] [<cid_name>] [<cid_num>] [<timeout_sec>]

Parameters:

<call_url> : 전화를 걸고자 하는 URL 을 입력한다. PBX 서버의 IP 주소 혹은 Freeswitch 내부에 설정된 설정값의 항목을 입력한다.

목적지 주소(즉 전화를 걸고자하는 번호)는 다음 중 하나가 되어야 한다 :

<exten> Dailplan 에 입력된 Extension 번호.

&<application_name>(<app_args>) :

“&” 은 다음에 뒤따라 오는 값이 Application 속성인지/Exten 속성인지를 결정한다. 즉, “&” 가 없으면 Extension, 있으면 Application 으로 인식한다.

(<app_args>) 은 옵션이며 Application 에서 요구하는 항목에 따라 달라진다. 예를 들어 Park() Application 의 경우, 별도의 인자값을 요구하지 않는다.

사용가능한 Application 리스트: park, bridge, javascript/lua/perl, playback (remove mod_native_file), and many others.

Note: Use single quotes to pass arguments with spaces, e.g. ‘&lua(test.lua arg1 arg2)’

Note: There is no space between & and the application name

<dialplan> : 사용하고하는 Dailplan. 비어있을 경우, 기본값으로 ‘XML’를 사용한다.

<context> : 사용하고자 하는 Context. 비어있을 경우, 기본값으로 ‘default’를 사용한다.

<cid_name> : CallerID name

<cid_num> : CallerID number

<timeout_sec> : Timeout in seconds

 

Options:

이 Option 설정들은 ‘{ }’로 싸여져서 사용된다.
ex. “originate {ignore_early_media=true}sofia/example/user 8334”

또한, Option 설정들은 반드시 ‘,’ 로 구분되어야 한다.
ex. “originate {ignore_early_meida=true,originate_timeout=2}sofia/example/user 8334”

group_confirm_key

group_confirm_file

forked_dial

fail_on_single_reject

ignore_early_media

return_ring_ready

originate_retries

originate_retry_sleep_ms

originate_caller_id_name

originate_caller_id_number

originate_timeout

sip_auto_answer

[Freeswitch] Making freeswitch python script

실행되는 기본 함수

Freeswitch 에서 Python 스크립트를 호출할 때, 호출하는 주체가 어디냐에 따라 자동으로 실행되는 Default 함수가 달라진다.

 

– Dialplan 내에서 <action application=”python” data=”mod_fstest.python_example”/> 와 같은 형식으로 호출되는 경우.

호출되는 스크립트 내, handler() 함수가 호출된다.

handler() 함수의 모습은

# APPLICATION
#
# default name for apps is "handler" it can be overridden with <modname>::<function>
# session is a session object
# args is all the args passed after the module name
def handler(session, args):

	consoleLog('info', 'Answering call from Python.n')

처럼 되어야 한다.

 

– fs_cli 등에서 python 명령으로 호출되는 스크립트의 경우.
호출되는 스크립트 내의 fsapi() 함수가 호출된다.

fsapi() 함수의 모습은

# FSAPI CALL FROM CLI, DP HTTP etc
#
# default name for python FSAPI is "fsapi" it can be overridden with <modname>::<function>
# stream is a switch_stream, anything written with stream.write() is returned to the caller
# env is a switch_event
# args is all the args passed after the module name
# session is a session object when called from the dial plan or the string "na" when not.
def fsapi(session, stream, env, args):

	stream.write("w00t!n" + env.serialize())

처럼 되어야 한다.

 

– fs_cli 등에서 pyrun 명령으로 호출되는 스크립트의 경우,

호출되는 스크립트 내의 runtime() 함수가 호출된다.

runtime() 함수의 모습은

# RUN A FUNCTION IN A THREAD
#
# default name for pyrun is "runtime" it can be overridden with <modname>::<function>
# args is all the args passed after the module name
def runtime(args):

	print args + "n"

처럼 되어야 한다.

 

지원 API

mod_python 모듈을 이용하여 스크립트 작성시, freeswitch 에서 제공하는 여러가지 API 들을 이용하여 편리하게 프로그래밍을 할 수 있다.
지원하는 API 내용은 mod_lua 와 똑같은 내용의 API 를 지원하며 자세한 기능 및 API 일람은 아래의 링크에서 확인할 수 있다.

mod_lua API 목록: https://wiki.freeswitch.org/wiki/Mod_lua

 

예제 python Script

예제로 사용한 python script 이다. 원본은 https://wiki.freeswitch.org/wiki/Mod_python#Sample_Python_Scripts 에서 구할 수 있다.

import os
from freeswitch import *

# HANGUP HOOK
#
# session is a session object
# what is "hangup" or "transfer"
# if you pass an extra arg to setInputCallback then append 'arg' to get that value
# def hangup_hook(session, what, arg):
def hangup_hook(session, what):

	consoleLog("info","hangup hook for %s!!nn" % what)
	return


# INPUT CALLBACK
#
# session is a session object
# what is "dtmf" or "event"
# obj is a dtmf object or an event object depending on the 'what' var.
# if you pass an extra arg to setInputCallback then append 'arg' to get that value
# def input_callback(session, what, obj, arg):
def input_callback(session, what, obj):

	if (what == "dtmf"):
		consoleLog("info", what + " " + obj.digit + "n")
	else:
		consoleLog("info", what + " " + obj.serialize() + "n")		
	return "pause"

# APPLICATION
#
# default name for apps is "handler" it can be overridden with <modname>::<function>
# session is a session object
# args is all the args passed after the module name
def handler(session, args):

	consoleLog('info', 'Answering call from Python.n')
	consoleLog('info', 'Arguments: %sn' % args)

	session.answer()
	session.setHangupHook(hangup_hook)
	session.setInputCallback(input_callback)
	session.execute("playback", session.getVariable("hold_music"))

# FSAPI CALL FROM CLI, DP HTTP etc
#
# default name for python FSAPI is "fsapi" it can be overridden with <modname>::<function>
# stream is a switch_stream, anything written with stream.write() is returned to the caller
# env is a switch_event
# args is all the args passed after the module name
# session is a session object when called from the dial plan or the string "na" when not.
def fsapi(session, stream, env, args):

	stream.write("w00t!n" + env.serialize())
	

# RUN A FUNCTION IN A THREAD
#
# default name for pyrun is "runtime" it can be overridden with <modname>::<function>
# args is all the args passed after the module name
def runtime(args):

	print args + "n"

# BIND TO AN XML LOOKUP
#
# default name for binding to an XML lookup is "xml_fetch" it can be overridden with <modname>::<function>
# params a switch_event with all the relevant data about what is being searched for in the XML registry.
#
def xml_fetch(params):

	xml = '''
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="freeswitch/xml">
  <section name="dialplan" description="RE Dial Plan For FreeSWITCH">
    <context name="default">
      <extension name="generated">
        <condition>
         <action application="answer"/>
         <action application="playback" data="${hold_music}"/>
        </condition>
      </extension>
    </context>
  </section>
</document>
'''

	return xml

 

예제로 사용한 dialplan 이다. “5555555”로 전화를 걸면 스크립트가 사용된다.

<include>
  <extension name="python_test">
    <condition field="destination_number" expression="^(5555555)$">
      <!--
      If you're hosting multiple domains you will want to set the
      target_domain on these calls so they hit the proper domain after you
      transfer the caller into the default context. 
      
      $${domain} is the default domain set from vars.xml but you can set it
      to any domain you have setup in your user directory.

      -->
      <action application="info"/>
      <action application="python" data="mod_fstest.python_example"/>
      <!--<action application="set" data="domain_name=$${domain}"/>-->
      <!-- This example maps the DID 5551212 to ring 1000 in the default context -->
      <!--<action application="transfer" data="1000 XML default"/>-->
    </condition>
  </extension>
</include>

 

작성한 Python 스크립트를 dialplan 에서 호출하기 위해서는, 당연하겠지만 Dialplan 내에서 어떤 스크립트를 호출할지를 지정해 주어야 한다.

<action application=”python” data=”foo.bar”/>

위에 입력한 foo.bar 는 foo package 에 있는 bar 모듈을 호출한다는 설정이다. 뒤에 확장자 .py 를 붙이지 않도록 주의하자.
이 경우, 사용하게 되는 함수는 ‘handler’ 함수이다.

2014-05-06 17:55:40.838121 [NOTICE] mod_python.c:212 Invoking py module: mod_fstest.python_example
2014-05-06 17:55:40.838121 [DEBUG] mod_python.c:281 Call python script 
2014-05-06 17:55:40.838121 [INFO] switch_cpp.cpp:1293 Answering call from Python.
2014-05-06 17:55:40.838121 [INFO] switch_cpp.cpp:1293 Arguments:

handler 함수.

# APPLICATION
#
# default name for apps is "handler" it can be overridden with <modname>::<function>
# session is a session object
# args is all the args passed after the module name
def handler(session, args):

	consoleLog('info', 'Answering call from Python.n')
	consoleLog('info', 'Arguments: %sn' % args)

	session.answer()
	session.setHangupHook(hangup_hook)
	session.setInputCallback(input_callback)
	session.execute("playback", session.getVariable("hold_music"))

인자 값으로 session 과 args 를 입력받는다.
session 은 python 스크립트 내에서 사용되는 freeswitch interface 객체이며, args 는 모듈 호출시 넘겨지는 인자값들이다.

 

또한, fs_cli 에서 바로 작성한 python 스크립트를 호출할 수 있다. 이 경우, 사용한 명령어에 따라 호출되는 함수가 달라지는데, python 명령으로 호출할 경우, 스크립트 내, ‘fsapi’ 함수가 바로 호출이 된다.

freeswitch@192.168.200.10@internal> python mod_fstest.python_example
w00t!
'Event-Name: API
Core-UUID: 86d48a48-6f22-475c-89ac-146e6d0217d9
FreeSWITCH-Hostname: MyDebian
FreeSWITCH-Switchname: MyDebian
FreeSWITCH-IPv4: 10.0.2.10
FreeSWITCH-IPv6: %3A%3A1
Event-Date-Local: 2014-05-06%2017%3A43%3A01
Event-Date-GMT: Tue,%2006%20May%202014%2015%3A43%3A01%20GMT
Event-Date-Timestamp: 1399390981818146
Event-Calling-File: switch_loadable_module.c
Event-Calling-Function: switch_api_execute
Event-Calling-Line-Number: 2406
Event-Sequence: 4158
API-Command: python
API-Command-Argument: mod_fstest.python_example

'
2014-05-06 17:43:01.818146 [NOTICE] mod_python.c:212 Invoking py module: mod_fstest.python_example
2014-05-06 17:43:01.818146 [DEBUG] mod_python.c:281 Call python script 
2014-05-06 17:43:01.818146 [DEBUG] mod_python.c:284 Finished calling python script

 

참조 : https://wiki.freeswitch.org/wiki/Mod_python#Sample_Python_Scripts