Forum Archive

Unable to use sound module

uncompleted

In order to read a Chinese story via TTS, the speech module is not perfect in reading Chinese because of multiple pronunciation.
So I use a online speech API to generate MP3.

def online_speech(text, vol, per, spd, pit):
   #api code
   ... #generate text speech link from API
   urllib.request.urlretrieve(url,'temp.mp3') #save mp3

   sound.play_effect('temp.mp3') #read mp3

if __name__ == '__main__':
   online_speech('Hello',5,'woman',2,5)

the result was, whatever the text I'd changed, it always said the same word 'Hello'
for example, if I changed the code below after running the code above

online_speech('Hello world',5,'woman',2,5)

it said 'Hello'. if I deleted the 'temp.mp3' and run again, it said 'Hello' again!!

I had no idea... what was going on, then I tried to use sound.player()

def online_speech(text, vol, per, spd, pit):
   #api code
   ... #generate text speech link from API
   urllib.request.urlretrieve(url,'temp.mp3') #save mp3

   player = sound.Player('temp.mp3') 
   player.play() #read mp3

if __name__ == '__main__':
   online_speech('Hello',5,'woman',2,5)

it didn't say anything!!
the player only works like this:

def online_speech(text, vol, per, spd, pit):
   #api code
   ... #generate text speech link from API
   urllib.request.urlretrieve(url,'temp.mp3') #save mp3

if __name__ == '__main__':
   online_speech('Hello',5,'woman',2,5)
   player = sound.Player('temp.mp3') 
   player.play() #read mp3

I have a class for reading story. so when I write like this:

def read_story(self,text):
   from online_speech import *
   online_speech(text,5,'woman',2,5)
   player = sound.Player('temp.mp3') 
   player.play() #read mp3

it kept silent .... I really don't know what to do here.... does anyone meet this before?
It really doesn't make any sense.

omz

The problem is basically that player is a local variable of your function, and it gets garbage-collected (i.e. removed from memory) as soon as the function returns (which also stops playback). You either need to ensure that the function doesn't return before the player has finished playback, or you need to keep a reference that is valid beyond the scope of your function, i.e. make it either a global variable (as in your third snippet), or perhaps a member of the class that contains the read_story method.

It might be as simple as replacing player = ... with self.player = ... in the read_story method.

uncompleted

Thanks for your reply. @omz
Yeah, in theread_story(), i forgot to type self. in the post. And I tried it before, it didn't work.
Yesterday I tried using a global variable and it succeeded!!!!

here's the code for anyone who also need it:

# -*- coding: utf-8 -*-

import requests,urllib
import sound,os,time
import ui

player = sound.Player('./sources/sound/temp.mp3')

class baidu_speech (ui.View):
    def __init__(self):
        self.width,self.height = ui.get_screen_size()
        self.img = ui.ImageView(
            image = ui.Image('iob:chatbubble_working_32'))
        self.img.x,self.img.y = (self.width-self.img.width)/2,(self.height-self.img.height)/2
        self.add_subview(self.img)

    @ui.in_background
    def speech(self,text,name,vol=5,per='lady',spd=2,pit=5):
        path = './sources/sound/'
        if os.path.exists(path+name+'.mp3') and name != 'temp':
            pass
        else:
            person = {'woman':0,'man':1,'man2':2,'actor':3,'girl':4,'lady':5}
            #start effect
            client_id = '********'
            secret = '********'
            token_url = 'https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s'%(client_id,secret)
            token = eval(requests.get(token_url).content)['access_token']
            convert_text = urllib.parse.quote(text)

            url = 'http://tsn.baidu.com/text2audio?lan=zh&ctp=1&cuid=abcdxxx&tok=%s&&tex=%s&vol=%d&per=%d&spd=%d&pit=%d'%(token,convert_text,vol,person[per],spd,pit)
            urllib.request.urlretrieve(url,path+name+'.mp3')
        #end effect
        if name == 'temp':
            global player
            player = sound.Player(path+'temp.mp3')
            player.play()
        else:
            sound.play_effect(path+name+'.mp3')
        self.superview.remove_subview(self)

if __name__ == '__main__':
    pass

the global variable is super super super important.... player = sound.Player('./sources/sound/temp.mp3') and use it in the method: global player

ccc

Does it work to change:

token = eval(requests.get(token_url).content)['access_token']
# to
token = requests.get(token_url).json()['access_token']

It is a security issue to run eval() on content from a remote source that you do not control.

uncompleted

@ccc very nice, many thanks!!