Search code examples
pythonpython-3.xgettext-to-speechpyttsx

Get an audio file with HTTP GET and then play it in python 3. TTS in python 3?


Basically what i want is TTS in python 3... I already tried pyttsx but aparently it only works with python 2 I already tried other things too... Now i'm trying with the api from voicerss.org but i can't figure out how can HTTP GET it and then play it with python with pyglet...

here is my code so far:

import pyglet
import urllib3

http = urllib3.PoolManager()
speechrequest = http.request('GET', 'http://api.voicerss.org/?key=04f49802d32d442ca997d4d2ea76d3d5&hl=pt-pt&c=wav&src=texto')
speech = pyglet.media.load(speechrequest)
speech.play()
pyglet.app.run()

The errors are:

Traceback (most recent call last):
  File "C:/Users/Diogo/Documents/test.py", line 6, in <module>
    speech = pyglet.media.load(speechrequest)
  File "C:\Users\Diogo\AppData\Local\Programs\Python\Python35\lib\site-packages\pyglet\media\__init__.py", line 1429, in load
    source = get_source_loader().load(filename, file)
  File "C:\Users\Diogo\AppData\Local\Programs\Python\Python35\lib\site-packages\pyglet\media\__init__.py", line 1410, in load
    return riff.WaveSource(filename, file)
  File "C:\Users\Diogo\AppData\Local\Programs\Python\Python35\lib\site-packages\pyglet\media\riff.py", line 185, in __init__
    file = open(filename, 'rb')
TypeError: invalid file: <urllib3.response.HTTPResponse object at 0x0000002EB0730D30>

i guess its not returning an wav file to python don't know why because. If i enter 'http://api.voicerss.org/?key=04f49802d32d442ca997d4d2ea76d3d5&hl=pt-pt&c=wav&src=texto' in internet explorer it returns a wav file...

So, my questions are:
what i am doing wrong with the code or if is impossible this way how can python speak?

Please be patient with me, i'm only 18 years old and i can't speak english properly, because i'm portuguese...


Solution

  • You could save the remote wav file locally and play it using a default application:

    #!/usr/bin/env python3
    from urllib.request import urlretrieve
    import webbrowser
    
    filename = 'speech.wav'
    urlretrieve("http://api.voicerss.org/?key=04f49802d32d442ca997d4d2ea76d3d5"
                "&hl=pt-pt&c=wav&src=texto", filename)
    webbrowser.open(filename) # play the file using default application
    

    On Windows, you could use os.startfile() instead of webbrowser.open() or to play the wav file directly in Python without an external application:

    import winsound
    
    winsound.PlaySound('speech.wav', winsound.SND_FILENAME)
    

    Your code have at least two issues:

    1. You should pass a filename (a string) to pyglet.media.load() function, not an HTTPResponse object.
    2. pyglet may fail to play it anyway: I get pyglet.media.riff.RIFFFormatException: Size of format chunk is incorrect. error on my system.

    You could also play a remote wav file without saving it to disk i.e., you can play streaming content:

    #!/usr/bin/env python3
    from urllib.parse import quote
    from urllib.request import urlopen
    
    api_key = open('api.key', encoding='ascii').read().strip()
    language = 'pt-pt'
    text = quote("1 2 3")
    playwav(urlopen('https://api.voicerss.org/?c=wav&f=8khz_8bit_mono&ssml=false&'
                    'key={api_key}&src={text}&hl={language}'.format(**vars())))
    

    where playwav():

    import wave
    from contextlib import closing
    
    import pyaudio  # Windows: py -m pip install pyaudio
                    # $ sudo apt-get install python{,3}-pyaudio
    player = None
    def playwav(path_or_file):
        global player
        if player is None:
            player = pyaudio.PyAudio()
        with closing(wave.open(path_or_file, 'rb')) as wavfile, \
             closing(player.open(
                 format=player.get_format_from_width(wavfile.getsampwidth()),
                 channels=wavfile.getnchannels(),
                 rate=wavfile.getframerate(),
                 output=True)) as stream:
            while True:
                data = wavfile.readframes(1024) # read 1024 frames at once
                if not data: # EOF
                    break
                stream.write(data)