gTTS 无法保存文件两次

gTTS unable to save file twice

我正在尝试将 gTTS 用作个人助理项目的真实文本转语音模块。查询 google 服务时,我可以保存 mp3 文件并 运行 使用 pygame.

from pygame import mixer
from gtts import gTTS    

def speak(data):
    tts = gTTS(text=data, lang='en')
    tts.save('speech.mp3')
    mixer.init()
    mixer.music.load('speech.mp3')
    mixer.music.play()

在 运行 函数 "speak" 成功输出后,但是当 运行 再次调用它时它失败并出现错误。

Traceback (most recent call last):
  File "C:\Users\user1\Desktop\project_ai\assistant.py", line 7, in <module>
    text_to_speech.main('hello')
  File "C:\Users\user1\Desktop\project_ai\modules\speech_text_synthesis\text_to_speech.py", line 8, in main
    tts.save('speech.mp3')                                                                                                                                       File "C:\Users\user1\AppData\Local\Programs\Python\Python36\lib\site-packages\gtts\tts.py", line 246, in save
    with open(savefile, 'wb') as f:                                                                                                                                   PermissionError: [Errno 13] Permission denied: 'speech.mp3'

再次尝试将文本保存到另一个 mp3 时发生错误。因此 pygame 无法播放。我知道我可以简单地更改文件名来保存它,但我不想这样做。我怎样才能做到这一点?

这似乎是 pygame 的一个常见问题,至少我在 SO 上发现了一些关于这个主题的已删除问题。

尝试在保存后通过内存映射文件加载文件,而不是仅使用文件名,如下所示:

import mmap
...
def speak(data):
    tts = gTTS(text=data, lang='en')
    tts.save('speech.mp3')
    with open('speech.mp3') as f: 
        m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) 

    pygame.mixer.music.load(m) 
    pygame.mixer.music.play() 

    m.close() 

使用上下文管理器确保文件在复制到内存后实际关闭,并且pygame.mixer.music.load根本不会触及文件。

终于……成功了……

我已经研究这个问题好几个小时了。除了 PyGame(pyaudio、playsound、pyglet 等)之外,我还尝试了多种不同的声音播放器。我走的路和其他人走的路一样。

然后......它击中了我......一条不同的道路......使用两个文件名。(一种称为双缓冲的技术)。

虽然我没有使用这个特定的代码,但我认为我应该使用您的代码给出答案....

from pygame import mixer
from gtts import gTTS    

count = 0

def speak(data):
    global count

    tts = gTTS(text=data, lang='en')
    tts.save(f'speech{count%2}.mp3')
    mixer.init()
    mixer.music.load(f'speech{count%2}.mp3')
    mixer.music.play()
    count += 1

之所以有效,是因为TTS库在切换到新文件时会释放之前的文件。我们在尝试使用 1 个文件名时面临的问题是我们很难找到释放文件资源的方法。当您切换到另一个文件名时,此方法会释放它。

享受互联网!去制作一些很酷的程序吧!