Pythonの一時ファイル
(これはPython勉強の備忘録です。)
語学(台湾華語)の勉強用に「自分なりの読み上げツール」を作りたい。・・と思っています。
中文テキストデータを与えて、mp3のファイルで音声データを保存する、というのは、
の中で
from gtts import gTTS
# 文字列をmp3ファイルに保存
mytext = "你的就是我的,我的還是我的"
language = "zh-TW"
myobj = gTTS(text=mytext, lang=language, slow=False)
myobj.save("test.mp3")
のように書いて実行すればファイルが作成されることを確認しています。
また、前回
の中で、
import time
from pygame import mixer
mixer.init()
mixer.music.load("test.mp3")
time.sleep(1)
mixer.music.play(loops=1)
while mixer.music.get_busy():
time.sleep(0.1)
こんなふうにやると、音声の再生ができることも確認しました。(playの前のsleep は自分の環境では1でなくても0.3くらいでも大丈夫のようです。でも0.1だと再生音の頭が切れました)
試行錯誤
上では仮に、"test.mp3" というファイル名をつけているのですが、既存ファイルにたまたまそんな名前のファイルがあって、衝突すると困るかも。また、使い終わったら邪魔だから消しておきたい。こういう場合、tempfile と言うモジュールで、一時ファイルを使うと良いらしい。
ここの説明を参考にして
import tempfile
with tempfile.NamedTemporaryFile(delete=True, suffix='.mp3') as f
・・・
のような感じで使えそう・・と思ったのですが
import tempfile
import time
from gtts import gTTS
from pygame import mixer
with tempfile.NamedTemporaryFile(delete=True, dir='.', suffix='.mp3') as f:
# 文字列をmp3ファイルに保存
mytext = "你的就是我的,我的還是我的"
language = "zh-TW"
myobj = gTTS(text=mytext, lang=language, slow=False)
myobj.save(f.name)
#mp3再生
mixer.init()
mixer.music.load(f.name)
time.sleep(1)
mixer.music.play(loops=1)
while mixer.music.get_busy():
time.sleep(0.1)
こんな感じで行けるかな、と思ったら、PermissionError出てしまいました。
開いているファイルの名前と同じ名前で保存しようとしているので、myobj.save(f.name) のところでエラーになったようです。
gTTSで使える関数を探すと
saveの他にstream とか、write_to_fpとかあるようですけど、write_to_fpがそれっぽい気がします。これを試してみます。
import tempfile
import time
from gtts import gTTS
from pygame import mixer
with tempfile.NamedTemporaryFile(delete=True, dir='.', suffix='.mp3') as f:
# 文字列をmp3ファイルに保存
mytext = "你的就是我的,我的還是我的"
language = "zh-TW"
myobj = gTTS(text=mytext, lang=language, slow=False)
myobj.write_to_fp(f)
#mp3再生
mixer.init()
mixer.music.load(f.name)
time.sleep(1)
mixer.music.play(loops=1)
while mixer.music.get_busy():
time.sleep(0.1)
今度はmixer.music.load(f.name) のところでエラー。
pygame.error: No such file or directory:・・・
closeしていないから、ファイル名でloadはできない?
mixer.music.loadの引数は、ファイル名じゃなくてファイルオブジェクトでもいいようなので、
mixer.music.load(f)
としてみたら、
pygame.error: Couldn't read first 12 bytes of audio data
と出てうまく行かず。ああ、そうだ、ファイルの先頭に戻るために
f.seek(0)
が必要でしたね。
import tempfile
import time
from gtts import gTTS
from pygame import mixer
with tempfile.NamedTemporaryFile(delete=True, dir='.', suffix='.mp3') as f:
# 文字列をmp3ファイルに保存
mytext = "你的就是我的,我的還是我的"
language = "zh-TW"
myobj = gTTS(text=mytext, lang=language, slow=False)
myobj.write_to_fp(f)
#mp3再生
mixer.init()
f.seek(0)
mixer.music.load(f)
time.sleep(0.3)
mixer.music.play(loops=1)
while mixer.music.get_busy():
time.sleep(0.1)
これで無事、音声の再生ができました。コードをよく見たら、「ファイル名」は全く使っていないのでNamedTemporaryFileじゃなくて、ただのTemporaryFileでいいよね。
完成
import tempfile
import time
from gtts import gTTS
from pygame import mixer
with tempfile.TemporaryFile(delete=True, dir='.', suffix='.mp3') as f:
# 文字列をmp3ファイルに保存
mytext = "你的就是我的,我的還是我的"
language = "zh-TW"
myobj = gTTS(text=mytext, lang=language, slow=False)
myobj.write_to_fp(f)
#mp3再生
mixer.init()
f.seek(0)
mixer.music.load(f)
time.sleep(0.3)
mixer.music.play(loops=1)
while mixer.music.get_busy():
time.sleep(0.1)
これで問題なくいけています。一時的なファイルが作成されて再生が終わると自動的に削除されており、当初の期待通りの動作です。
補足
今回、ネット情報を探していても、なかなかしっくりくる説明を見つけられなかったのでした。また、参考書として持っているこちらの本
では同じようなケースで
NamedTemporaryFileで生成したファイル名をf.nameで取得して(tmp???)
save('{}.mp3'.format(f.name))
load('{}.mp3'.format(f.name))
のようにやるサンプルが出ていたのですが、自分の環境で試してみると再生後に自動的に消えるのは NamedTemporaryFileが作るtmp??? というファイルだけでして、myobj.saveで作成されたtmp???.mp3というファイルは、そのままゴミとして残ってしまった。一時的にとりあえずなら使えるけど、ゴミtmpファイルがたまっていくのは嫌です。
なので、試行錯誤の後に期待した動作になったときは、ひさびさの達成感が味わえたのでした(笑)。
追記、関連しそうな情報
後からいろいろネット情報が見つかりましたので追記