Home: Perl Programming Help: Beginner:
Google TTS script advice


Jan 3, 2013, 7:06 AM

Views: 5374
Google TTS script advice

Hi all,

I am putting together an Asterisk PBX system.

I am using dynamically generated voice prompts from this AGI script http://zaf.github.com/asterisk-googletts/ and Google TTS API. AGI scripts seem to be essentially Perl scripts.

However, there is a situation where it is not possible to call this AGI script from within Asterisk. The reason is that this script generates the prompt, plays it, deletes it and THEN returns to Asterisk.
In one particular situation, I have to generate the sound file, write it to the disk, exit the script, play it with Asterisk and then delete it with Asterisk.

Thus, I have to make my own Perl script.

And I suck at Perl. Unfortunately, I don't have the time to start from scratch and learn Perl right now as I need to get this Asterisk server done ASAP.

I am trying to put a script together that would take the language and the text to be rendered as arguments, make a wget using the generated URL, convert the result from mp3 to wav using mpg123, then converting it to asterisk sln 8khz format using sox.

I will then call this script from within Asterisk, it will generate the appropriate audio file in the /tmp/ directory, I will play it from within Asterisk after the script has done executing and then run another command from Asterisk to delete the /tmp/ file.

This is what I have written and it fails miserably:

#!/usr/bin/env perl 

# call with script.pl <text> <lang>
use strict;
use warnings;

my $lang = $ARGV[1];
my $text = $ARGV[0];

system("wget -O /tmp/galacb_name_announce.mp3 --user-agent=\"Mozilla/5.0 (X11; Linux; rv:8.0) Gecko/20100101\" http://translate.google.com/translate_tts?tl=$lang&q=$text");

system("mpg123\ -w /tmp/galacb_name_announce.wav /tmp/galacb_name_announce.mp3");

system("sox /tmp/galacb_name_announce.wav \-t raw \-r 8000 \-c 1 /tmp/galacb_name_announce.sln");

I'm calling the script with

./script.pl "<callername> calling. To take the call, please hold the line. To reject it, just hang up." en

Can someone point me in the right direction please? Thanks!

Veteran / Moderator

Jan 3, 2013, 8:18 AM

Views: 5372
Re: [HumbuckingCoil] Google TTS script advice

It would be helpful if you explain how it's failing.

What is it doing that you didn't expect?

What is is not doing?

Can you provide more details on exactly what you're needing to accomplish (call flow) and include the relevant portion of the dialplan?

Where is the text coming from? Is it hard coded in the dialplan or is it dynamically built based on channel parameters?

Based on what you've shown so far, it appears to me that you may be making this more difficult that it needs to be. Can't you record your own audio files and store them with the rest of the asterisk sound files (in their own sub dir of course)?

For agi scripts, you should look at using the Asterisk::AGI module.

Veteran / Moderator

Jan 3, 2013, 8:25 AM

Views: 5370
Re: [HumbuckingCoil] Google TTS script advice

Have you considered using espeak instead of google translate?


Jan 3, 2013, 8:41 AM

Views: 5368
Re: [FishMonger] Google TTS script advice

I am trying to setup a local DID so people calling us here don't get charged for an international call.

When the call comes in, the CallerID number is checked against entries in AstDB to make sure only authorised numbers can use this system.

Once this is established, it checks for local time and plays a prompt stating the local time asking the caller if she still wants to proceed.

Should the caller choose to go through, it then rings our phone while the caller is waiting to be connected.
To do this, I am using Dial(SIP/trunk/ournumber,,A(/path/to/generated/file.sln)) so that when we pick up the phone there is an annoucement of who is calling asking us if we want to take the call or not.

I'd rather not pre-record all the names because there are quite a few, and also because I'd like to keep this as dynamic as possible making it easy to add and remove authorised callers with just a line of code.

I am using the googletts.agi script for all my other prompts because it works perfectly.
But Dial() 'A' option will only take a sound file, it will not call a script to generate and play this sound file. The AGI script cannot be called in this case and this is where I'm stuck.

I have done a bash script that does a wget to google, saves the mp3 and converts it to sln with mpg123 then sox and saves it. It works fine when I run it myself in the shell but when I call it with Asterisk it doesn't work anymore.

If I run the bash script myself and comment the lines calling the bash script in the dialplan, the announcement plays fine.

Here is the bash script:

#!/usr/bin/env bash 
# Renders callername using Google TTS API
# call it with script.sh callername lang
wget -O /tmp/galacb_name_announce.mp3 --user-agent="Mozilla/5.0 (X11\; Linux\; rv:8.0) Gecko/20100101" http://translate.google.com/translate_tts?tl=$2\&q=$1+calling

mpg123 -w /tmp/galacb_name_announce.wav /tmp/galacb_name_announce.mp3

sox /tmp/galacb_name_announce.wav -t raw -r 8000 -c 1 /tmp/galacb_name_announce.sln

rm /tmp/galacb_name_announce.mp3
rm /tmp/galacb_name_announce.wav

chown root.root /tmp/galacb_name_announce.sln
chmod 644 /tmp/galacb_name_announce.sln

exit 0

Here is how I am calling it in the extensions.conf

exten => start,1,agi(googletts.agi,"Calling Gala now, please wait...",en)
same => n,Set(callername=${DB(auth_cid/${CALLERID(num)})})
same => n,Set(caller_name_announce="/home/user/caller_name_announce.sh ${callername} en")
same => n,System(${caller_name_announce}))
same => n,NoOp(${caller_name_announce})
same => n,Dial(SIP/voipms/15551231234,,A(/tmp/galacb_name_announce))

exten => h,1,System(rm /tmp/galacb_name_announce.*)

And here is the console being upset at it:

    -- Executing [1@checklocaltime:1] Goto("SIP/user1-00000082", "call-gala,start,1") in new stack 
-- Goto (call-gala,start,1)
-- Executing [start@call-gala:1] AGI("SIP/user1-00000082", "googletts.agi,"Calling Gala now, please wait...",en") in new stack
-- Launched AGI Script /usr/share/asterisk/agi-bin/googletts.agi
-- Playing '/tmp/5f93250388561e955898ba34bd4e7350' (escape_digits=) (sample_offset 0)
-- <SIP/user1-00000082>AGI Script googletts.agi completed, returning 0
-- Executing [start@call-gala:2] Set("SIP/user1-00000082", "callername=someuser") in new stack
-- Executing [start@call-gala:3] Set("SIP/user1-00000082", "caller_name_announce="/home/user/caller_name_announce.sh someuser en"") in new stack
-- Executing [start@call-gala:4] System("SIP/user1-00000082", ""/home/user/caller_name_announce.sh someuser en")") in new stack
-- Executing [start@call-gala:5] NoOp("SIP/user1-00000082", ""/home/user/caller_name_announce.sh someuser en"") in new stack
-- Executing [start@call-gala:6] Dial("SIP/user1-00000082", "SIP/voipms/15551231234,,A(/tmp/galacb_name_announce)") in new stack
== Using SIP RTP CoS mark 5
-- Called SIP/voipms/15551231234
-- SIP/voipms-00000083 is making progress passing it to SIP/user1-00000082
-- SIP/voipms-00000083 answered SIP/user1-00000082
[Jan 2 18:34:50] WARNING[28795][C-00000069]: file.c:698 ast_openstream_full: File /tmp/galacb_name_announce does not exist in any format
[Jan 2 18:34:50] WARNING[28795][C-00000069]: file.c:1014 ast_streamfile: Unable to open /tmp/galacb_name_announce (format (ulaw)): No such file or directory
[Jan 2 18:34:50] ERROR[28795][C-00000069]: app_dial.c:2758 dial_exec_full: error streaming file '/tmp/galacb_name_announce' to callee
-- Remotely bridging SIP/user1-00000082 and SIP/voipms-00000083
-- Executing [h@call-gala:1] System("SIP/user1-00000082", "rm /tmp/galacb_name_announce.sln") in new stack
== Spawn extension (call-gala, start, 6) exited non-zero on 'SIP/user1-00000082'

I was thinking it would work better in Perl for some reason since the AGI scripts are in Perl.

I'm quite stuck here, don't really know where to go from there to be honest.

(This post was edited by HumbuckingCoil on Jan 3, 2013, 8:48 AM)


Jan 3, 2013, 8:45 AM

Views: 5363
Re: [FishMonger] Google TTS script advice

I wasn't aware of eSpeak. Is the quality of the speech superior to Google TTS?

Veteran / Moderator

Jan 3, 2013, 9:06 AM

Views: 5357
Re: [HumbuckingCoil] Google TTS script advice

In Reply To
I wasn't aware of eSpeak. Is the quality of the speech superior to Google TTS?

I actually haven't used either of them. All of our IVR prompts are pre-recorded. That being said, between the 2 of them, I'd think espeak might work out better, at least in terms of call flow I'm not sure about the audio quality.

Instead of using a dial statement in the dialplan, you could handle that directly in the agi script, which gives you greater flexibility.

From what I see, the only portion of the recording that is dynamic is the caller's name, which is pulled from the db. If that's the case, then I'd prerecord the other portion(s) of the audio and use espeak or similar tool to playback the caller's name. Doing that would remove the unneeded system calls which will make it more efficient.


Jan 3, 2013, 9:16 AM

Views: 5354
Re: [FishMonger] Google TTS script advice

The AGI script is of a much higher quality than my dirty bash script.

I tried looking at it and modifying it but I don't know much about Perl and was unable to amend it.

Using eSpeak for the name would probably result in the same problem as the A option will only take a path to an audio file and nothing else. It can't call scripts, it can't execute applications (as far as I can tell from my tests, that is)

(This post was edited by HumbuckingCoil on Jan 3, 2013, 9:19 AM)

Veteran / Moderator

Jan 3, 2013, 9:19 AM

Views: 5350
Re: [HumbuckingCoil] Google TTS script advice

No need to prerecord the names. Simply do the db lookup and then pass that var to espeak to playback the TTS audio.

Veteran / Moderator

Jan 3, 2013, 9:22 AM

Views: 5348
Re: [HumbuckingCoil] Google TTS script advice

Using eSpeak for the name would probably result in the same problem as the A option will only take a path to an audio file and nothing else. It can't call scripts, it can't execute applications (as far as I can tell from my tests, that is)

That's why I suggested to use M instead of the A or do it in the agi script instead of the dial statement in the dialplan.

Veteran / Moderator

Jan 3, 2013, 9:31 AM

Views: 5347
Re: [HumbuckingCoil] Google TTS script advice

Actually, I may have steered you wrong. The approach I was thinking of would send the audio to the caller, not the callee.

Let me think about this and will get back to to.

Veteran / Moderator

Jan 3, 2013, 10:00 AM

Views: 5343
Re: [HumbuckingCoil] Google TTS script advice

While I'm still thinking about different options, lets look at the warning messages.

My first question is - why the multiple file conversions? Asterisk is able to playback each of those audio formats.

File /tmp/galacb_name_announce does not exist in any format

Are you sure that the sox command completed successfully?

Try commenting out both of the 'rm' statements to see if it finds the mp3 or wav file.

Personally, I'd probably use gsm format.


Jan 4, 2013, 8:28 AM

Views: 5336
Re: [FishMonger] Google TTS script advice

Alright, it seems I overlooked a typo, there was one too many ')' on the line where I call the script.

Thanks for your help everyone!