pythonpython-3.xpygamegtts

"pygame.error: ModPlug_Load failed" When trying to play sound from bytes


I'm trying to make a simple Python script which should take a text as input and using gtts, play it as audio. I'm using the following code, but it's not working and I don't know why.

from gtts import gTTS
import pygame
from io import BytesIO

pygame.init()


def say(text):
    tts = gTTS(text=text, lang='en')
    fp = BytesIO()
    tts.write_to_fp(fp)
    fp.seek(0)
    pygame.mixer.init()
    pygame.mixer.music.load(fp)
    pygame.mixer.music.play()
    while pygame.mixer.music.get_busy():
        pygame.time.Clock().tick(10)

say("hello")

Python version 3.9.1
gTTS version 2.2.1
pygame version 2.0.1 Windows 10

EDIT: This is the complete error I get:

pygame 2.0.1 (SDL 2.0.14, Python 3.9.1)
Hello from the pygame community. https://www.pygame.org/contribute.html
Traceback (most recent call last):
  File "c:\Users\Marco\Documents\Programmazione\gtts\main2.py", line 19, in <module>
    say("hello")
  File "c:\Users\Marco\Documents\Programmazione\gtts\main2.py", line 14, in say     
    pygame.mixer.music.load(fp)
pygame.error: ModPlug_Load failed

Solution

  • I tried this code on Linux and I get error

    Module format not recognized
    

    so I think gtts (Google Text-To-Speech) could change MP3 format (maybe it has better quality) and now pygame has problem to play it.

    In PyGame doc you can even see

    "Be aware that MP3 support is limited. 
     On some systems an unsupported format can crash the program, e.g. Debian Linux."
    

    I tested few other modules and at this moment work for me only pydub, mpg123.

    I try to use modules which don't have to save in file (I don't check if they do save it on disk somewhere deeper in code) and don't use subprocess to run external player.


    pydub - which uses ffmpeg or libav and simpleaudio, pyaudio (pip install pydub)

    from gtts import gTTS
    from io import BytesIO
    
    tts = gTTS(text="Hello", lang='en')
    fp = BytesIO()
    tts.write_to_fp(fp)
    fp.seek(0)
    
    # --- 
    
    from pydub import AudioSegment
    from pydub.playback import play
    
    song = AudioSegment.from_file(fp, format="mp3")
    play(song)
    

    mpg123 - which uses libmpg123 (probably pygame also use it) (pip install mpg123)

    from gtts import gTTS
    from io import BytesIO
    
    tts = gTTS(text="Hello", lang='en')
    fp = BytesIO()
    tts.write_to_fp(fp)
    fp.seek(0)
    
    # --- 
    
    from mpg123 import Mpg123, Out123
    
    mp3 = Mpg123()
    mp3.feed(fp.read())
    
    out = Out123()
    
    for frame in mp3.iter_frames(out.start):
        out.play(frame)
    

    Maybe if to change code in gtts it could read it even without BytesIO. because gtts uses requests which can give direct acces to file data.