My new blog here at www.mostovenko.com

Saturday, April 21, 2012

Voicing messages in python, or fun with Google Translate.

This will be a quite short article, that will describe how to voice some text via using google translate. I don't know where we can use this, but i am sure that you may face situation, where this feature will be useful. Besides, as for me - it's also simple and interesting.
I decided to create python package "speak" that will give us functionality for voicing string messages. As for example i will simplify the implementation of the package, so don't judge the source code too strict.


All source code is available on bitbucket. But first i suggest you to follow this article, write some code by your self and if something is going wrong check my sources.





Ok, let's write some code:
First - create new folder "speak" with __init__.py inside to define that it's not simple folder - but package.

Google Translate
There are a couple of services where you can sound your string messages, but i've chosen google translate - as most widely used.

Getting audio file from Google translate.
So, lets create first module in our package, and call it voice.py. The main purpose of this module is providing speak function that will accept string phrase as a parameter and will sound it immediately. Let's create function that will accept our string phrase and than will return .mp3 file with our voiced phrase.

import urllib
import urllib2

#----------------------------------------------------------------------
def get_google_voice(phrase):
    """
    Function that will send http request to google translate
    and save audio file from responce with voiced input phrase.
    Parameters:
    @phrase: phrase to voice.
    Returns:
    If ok - name of created file, else - returns None.
    """
    
    language='en' #Setting language.
    url = "http://translate.google.com/translate_tts" #Google translate url for getting sound.
    user_agent="Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5." 
    file_name="temp.mp3" #Temp file for saving our voiced phrase.

    params = urllib.urlencode({'q':phrase, 'tl':language}) #query parameters.
    request = urllib2.Request(url, params) #http request.
    request.add_header('User-Agent', user_agent) #adding agent as header.
    response = urllib2.urlopen(request) 
    
    if response.headers['content-type'] == 'audio/mpeg':
        with open(file_name, 'wb') as file:
            file.write(response.read())
        return file_name
    else:
        return None

Some comments to function:
As we see this function creates request object, initializes and get's audio file from response. The file is saved as "temp.mp3" parameter. For now you may test this function form the python shell, it works fine - accepts phrase and creates audio file ("temp.mp3").

Playing audio file.
Actually i've done this part by using mpg123 player - you may use another one. I think this player is included in your's Linux distributive repository. If no - you may download and install it from here or another place, it's open-source so i think there will be no troubles with this. After you downloaded and installed it, we may use it from our python script. To launch playing of our audio file we need to type in bash "mpg123 temp.mp3" and we will here our voiced text. Now we need to call shell scripts from our python script. This operation can be done with the help of  subprocess module. So let's add another method to our voice.py that will accept filename as a parameter and play it. Here is what i've done
import subprocess
#----------------------------------------------------------------------
def play_file_in_mpg123(filename):
    """
    Function plays file with player mpg123.
    Parameters:
    @filename: name of file to play.
    """
    
    subprocess.Popen(('mpg123', filename), stdout = subprocess.PIPE, stderr = subprocess.PIPE)

And that's it, now we have method that will play our audio file from the python script. Popen function provides underlying process creation and command management. stdout and stderr parameters are set to execute our command "silently" without no output to our shell. Ofcourse "siletnly" doesn't mean that we will here nothing))) We will here our file playing but without no output to console.

Let's wrap it!
Ok, now we have two useful functions that will do all the job. But i think it's a good idea to create some kind of wrapper for calling, and initialization of this functions. Let's create class Speaker that will encapsulate all logic. I decided to create it in separate module speaker.py, i use to separate my code by logic into different modules to have nice reusability in future, but you may place it in one module if you want. Here is my speaker.py module:
# -*- coding: utf-8 -*-
"""
Module for keeping b logic for speaker object.
"""

########################################################################
class Speaker(object):
    """Class for voicing string messages"""

    #----------------------------------------------------------------------
    def __init__(self, get_file_func, play_func):
        """
        Parameters:
        @get_file_func : delegate that will accept string to voice, and return audio file name.
        @play_func : delegate that plays music file(mp3, wav, e.t.c.).
        """
        
        self.get_file_func = get_file_func
        self.play_func = play_func    
            
    def voice(self, message):
        """ 
        Function that will voice our string message.
        Parameters:
        @message: message for voicing.
        """
            
        file = self.get_file_func(message)
        if file is not None:
            self.play_func(file)
It's a wrapper class for our functions. Why it is useful to make such wrappers? I may say that in future we will have an ability to initialize our functions with different settings in constructor of this class and i found that it is good idea to think about future scalability. As you see this class accepts our two functions in constructors as delegates, and than calls them in right order.
Last part
As for the last part, let's import our Speaker class to the voice.py  module, create an instance, inject our two functions to the constructor, and create a function that will be imported outside of speakers package. Here is what i've add to voice.py:

from .speaker import Speaker

#Speaker object bootstrapping.
s = Speaker(get_google_voice, play_file_in_mpg123)

#----------------------------------------------------------------------
def speak(phrase):
    """Voicing function"""
    s.voice(phrase)
Now we can import our voice.py  module, and our functions will be initialized. As speak function may be imported out of our package - let's add it to our __init__.py file:

# -*- coding: utf-8 -*-
"""
Packaget for voicing string messages.
"""

from .voice import speak
Congrats! Now you may test this package by importing speak function from package and use it in other projects.
Thank's for attention) Feel free to correct me if i have made mistakes.