日記/2020-11

<< 2020-11 >>
S M T W T F S
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30

2020-11-22

PythonのgTTSが遅い・続

 

 先日の記事、「PythonのgTTSが遅い」の続き。
 同様の不具合の報せが作者の元に届いたようで、gTTSは2.1.2(2020/11/10)、2.2.0(2020/11/14)、2.2.1(2020/11/15)と怒濤のリリースラッシュ。CHANGELOGを読むと、tokenが取れない問題も、なぜか激ノロになっちゃう問題も、両方解決した模様。
 gTTSをアップデートしたら、以下のサンプルコードも快調に動作し、Google Homeが待ち時間なくしゃべるようになった。

 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 
 import pychromecast
 from gtts import gTTS
 import time
 
 tts = gTTS(text="ハローワールド", lang='ja')
 tts.save('./test.mp3')
 mp3url = 'http://192.168.0.XXX:8000/test.mp3';
 
 #IPアドレスで特定する
 googleHome = pychromecast.Chromecast('192.168.0.YYY')
 
 if not googleHome.is_idle:
     print("Killing current running app")
     googleHome.quit_app()
     time.sleep(5)
 
 #しゃべらせる
 googleHome.wait()
 googleHome.media_controller.play_media(mp3url, 'audio/mp3')
 googleHome.media_controller.block_until_active()

 以上、続報でした。


2020-11-19

燃えよタイガース! '99

 

 ハードディスクの奥底から、1999年に作ったと思われる替え歌の歌詞が発掘された。
 ご笑覧のほど。
 元ネタは、燃えよドラゴンズ! '99

燃えよタイガース! '99

阪神高速にこだまする
トラの泣き声耳にして
甲子園球場につめかけた
僕らをしゅーんと萎えさせる
いいぞ、がんばれ! タイガース!
燃えよタイガース!

一番坪井が塁に出て
二番秀太でダブルプレー
三番新庄三振で
四番ブロワーズ帰国する
いいぞ、がんばれ! タイガース!
燃えよタイガース!

五番ジョンソン真が無く
六番矢野は夢破れ
七番今岡空を切り
八番桧山は天仰ぐ
いいぞ、がんばれ! タイガース!
燃えよタイガース!

藪は大きく負け越して
川尻税金ごまかして
メイは監督批判して
吉田は初回でK.O.だ
いいぞ、がんばれ! タイガース!
燃えよタイガース!

福原だけは信じてる
今でも中込太ってる
遠山左を封じても
竹内一発食らってる
いいぞ、がんばれ! タイガース!
燃えよタイガース!

葛西湯舟と火だるまに
油をそそいで杉山だ
田村伊藤はバテバテで
クイックできない怪人リベラ
いいぞ、がんばれ! タイガース!
燃えよタイガース!

僕もあなたもあきれてる
怒りを込めてボヤいてる
それはいつもの最下位だ
野村監督の更迭だ
いいぞ、がんばれ! タイガース!
燃えよタイガース!
がんばれ! がんばれ! タイガース!
燃えよタイガース!


2020-11-8

PythonのgTTSが遅い

 

 ラズパイ(OSは「Raspbian Stretch」)からPythonで書かれたスクリプトを使ってGoogle Homeをしゃべらせていたのだが、ここ数日、しゃべらなかったり、しゃべりはじめるのに何分もかかったりと、動作がおかしくなっていた。このスクリプトでは、gTTSというモジュールを使っていて、内部でGoogle Text To Speech APIを叩いているのだが、どうやらこのモジュールがうまく動かなくなっているらしい。
 試しに以下のようなスクリプト(speech.py)を動作させてみた。

 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 
 import pychromecast
 from gtts import gTTS
 import time
 
 tts = gTTS(text="ハローワールド", lang='ja')
 tts.save('./test.mp3')
 mp3url = 'http://192.168.0.XXX:8000/test.mp3';
 
 #IPアドレスで特定する
 googleHome = pychromecast.Chromecast('192.168.0.YYY')
 
 if not googleHome.is_idle:
     print("Killing current running app")
     googleHome.quit_app()
     time.sleep(5)
 
 #しゃべらせる
 googleHome.wait()
 googleHome.media_controller.play_media(mp3url, 'audio/mp3')
 googleHome.media_controller.block_until_active()

 ラズパイのIPアドレスは「192.168.0.XXX」、そこでウェブサーバーを起動しておき、そのドキュメントルートに、gTTSを使って生成された音声ファイル(test.mp3)を保存し、それをPyChromecastというモジュールを使ってGoogle Home(IPアドレスは「192.168.0.YYY」)にしゃべらせている。
 すると、しゃべらないときには、以下のようなエラーを吐いていることがわかった。

 Traceback (most recent call last):
   File "./speech.py", line 11, in <module>
     tts.save('./test.mp3')
   File "/usr/local/lib/python3.8/site-packages/gtts/tts.py", line 295, in save
     self.write_to_fp(f)
   File "/usr/local/lib/python3.8/site-packages/gtts/tts.py", line 251, in write_to_fp
     prepared_requests = self._prepare_requests()
   File "/usr/local/lib/python3.8/site-packages/gtts/tts.py", line 194, in _prepare_requests
     part_tk = self.token.calculate_token(part)
   File "/usr/local/lib/python3.8/site-packages/gtts_token/gtts_token.py", line 29, in calculate_token
     seed = self._get_token_key()
   File "/usr/local/lib/python3.8/site-packages/gtts_token/gtts_token.py", line 58, in _get_token_key
     raise ValueError(
 ValueError: Unable to find token seed! Did https://translate.google.com change?

 なぜだかわからないが、Google Text To Speech APIからGETできるデータに所望のモノ(なにかしらのtoken?)がないらしい。
 そこで、gtts_token.pyにちょこっと追記して、retryモジュールを使って、_get_token_key()をリトライさせてみることにした。

 from retry import retry #追記
 
 (中略)
 
     @retry((ValueError, TypeError), tries=10, delay=1) #追記
     def _get_token_key(self):
         if self.token_key is not None:

 これでエラーは出なくなったものの、それでもまだ、しゃべり出すのに何分も待たされることがある。
 プロファイルを取ってみると、どうやらchardetという文字コード判定のモジュールの処理で時間がかかっているようなのだが、なぜそうなるのか(なぜ短時間で処理が終わることもあれば、何分も要することもあるのか)さっぱりわからない。

 pi@raspberrypi:~ $ python3 -m cProfile ./speech.py | sort +1 -r | head
          16048373 function calls (16031699 primitive calls) in 123.627 seconds
    Ordered by: standard name
    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   3918870   29.563    0.000   29.563    0.000 codingstatemachine.py:66(next_state)
         5   27.045    5.409   48.198    9.640 mbcharsetprober.py:61(feed)
         1   12.045   12.045   26.122   26.122 sjisprober.py:56(feed)
   1088785    7.701    0.000   12.662    0.000 jpcntx.py:143(feed)
         1    7.619    7.619   17.055   17.055 eucjpprober.py:56(feed)
      3106    5.281    0.002    5.281    0.002 {method 'findall' of 're.Pattern' objects}
        14    4.777    0.341   10.035    0.717 sbcharsetprober.py:77(feed)

 

解決策その1

 考えるのがめんどうになって、別のモジュールを使ってみることにした。
 ググって、やはりGoogle Text To Speech APIを使っているgoogle_speechというモジュールが公開されているのをみつけ、早速これに置き換えてみると、待たされることはなくなった。

 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 
 import pychromecast
 from google_speech import Speech
 import time
 
 speech = Speech("ハローワールド", 'ja')
 speech.save('./test.mp3')
 mp3url = 'http://192.168.0.XXX:8000/test.mp3';
 
 #IPアドレスで特定する
 googleHome = pychromecast.Chromecast('192.168.0.YYY')
 
 if not googleHome.is_idle:
     print("Killing current running app")
     googleHome.quit_app()
     time.sleep(5)
 
 #しゃべらせる
 googleHome.wait()
 googleHome.media_controller.play_media(mp3url, 'audio/mp3')
 googleHome.media_controller.block_until_active()

 

解決策その2

 さらに調べていると、この記事の記載から、どうやらモジュールを使うまでもなく、簡単にGoogle Text To Speech APIを叩けるらしい、ということがわかった。2011年に書かれた古い記事なので事情が変わってないか心配だったが、試してみると、あっさり動いてくれた。待たされることもないし、そもそもラズパイ上にmp3ファイルが保存されることもない。

 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 
 import pychromecast
 import time
 import urllib.parse
 
 s = "ハローワールド"
 s_quote = urllib.parse.quote(s)
 mp3url = "http://translate.google.com/translate_tts?ie=UTF-8&q="+s_quote+"&tl=ja&client=tw-ob"
 
 #IPアドレスで特定する
 googleHome = pychromecast.Chromecast('192.168.0.YYY')
 
 if not googleHome.is_idle:
     print("Killing current running app")
     googleHome.quit_app()
     time.sleep(5)
 
 #しゃべらせる
 googleHome.wait()
 googleHome.media_controller.play_media(mp3url, 'audio/mp3')
 googleHome.media_controller.block_until_active()

 

続報

 後日、gTTSの2.2.1へのアップデートによって問題が解決したことを確認。