Search code examples
pythonpython-3.xaudiopygametext-to-speech

Python - audio file not being closed -> os.remove()


I'm trying to make a program with text-to-speech capabilities using tts module from Github (from DeepHorizons). As I want to be able to stop the program while it's talking, I'm recording the audio to a file and then playing it. To be able to create many times the same name of file (say.wav), I need to delete the old one. The problem is that I can't delete it because it's being used (PermissionError: [WinError 32] The process cannot access the file because it is being used by another process), but it only happens at the second time it speaks - the first time, it quits pygame and deletes the file; at the second time, it probably doesn't quit pygame, because it gives me the error. I've tried with some libraries (like pyaudio or pygame) but none of them can completely close the audio file after playing it. By the way, I'm using Windows. My main funcion's code is this (using pygame, as I'm more familiar with it):

import os
import datetime
import time
import tts.sapi
import pygame

voice = tts.sapi.Sapi()

directory_uncom=os.getcwd()
if not "speech" in directory_uncom:
    directory=directory_uncom+"/speech"
else:
    directory=directory_uncom

def speak(say):
    global directory
    global voice
    commands_read=open(directory+"/commands.txt","r")
    lines3=commands_read.read().splitlines()
    lines3[0]="stop_speaking=false"
    commands_write=open(directory+"/commands.txt","w")
    commands_write.write(lines3[0])
    for i in lines3[1:]:
        commands_write.write("\n"+i)
    commands_write.close()
    stop=lines3[0][14:]
    voice.create_recording("say.wav", say)
    pygame.mixer.init(22000)
    pygame.mixer.music.load("say.wav")
    pygame.mixer.music.play()
    while pygame.mixer.music.get_busy() == True and stop=="false":
        commands_read=open(directory+"/commands.txt","r")
        lines3=commands_read.read().splitlines()
        stop=lines3[0][14:]
    pygame.quit()
    os.remove("say.wav")
speak("hello") #In this one, it does everything correctly.
speak("hello") #In this one, it gives the error.

I am doing something wrong? Or can anyone tell me a better way to do this?

Thanks in advance.

EDIT -> I found an "alternative" which is the following:

voice.create_recording("say.wav", say)
with open("say.wav") as say_wav_read:
    say_wav= mmap.mmap(say_wav_read.fileno(), 0, access=mmap.ACCESS_READ) 
pygame.mixer.init(22000)
pygame.mixer.music.load(say_wav)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy() == True and stop=="false":
    commands_read=open(directory+"/commands.txt","r")
    lines3=commands_read.read().splitlines()
    stop=lines3[0][14:]
pygame.quit()
say_wav_read.close()

Now it can overwrite the file (os.remove() is no longer needed, because pygame just needs to close the file and I don't need to delete it - it's just overwritten), but there is a problem: it gets stuck and loops the same part of the sentence (ex: hello how are you -> ow are y / ow are y / ow are y / ...). Any way to solve this problem?

With the first "alternative" and without os.remove(), it gives this error: _ctypes.COMError: (-2147287038, None, (None, None, None, 0, None)). Probably because the file is still opened (pygame). So os.remove() is not necessary at all. Better alternative?


Solution

  • Finally I found a way similar to the first I posted. I read somewhere a question from a comment of a question if the sound the OP wanted to play couldn't be played as a sound and not as a music. I searched in Pygame documentation and there's a module called Sound (which, by the way, only works with OGG and uncompressed WAV files). I used it and now it closes the file successfully (don't know why) and it doesn't loop what is being said. In the documentation, they say the music is streamed and never loaded all at once. I noticed that when I ordered the program to stop speaking the errors began (probably something corruption, I don't know), so I presume that was the problem causing the loops. The reason why it was not closing, I don't know but it closes now and there are no loops, so now it's working perfectly! The final code is this:

    import os
    import datetime
    import time
    import tts.sapi
    import pygame
    
    voice = tts.sapi.Sapi()
    
    directory_uncom=os.getcwd()
    if not "speech" in directory_uncom:
        directory=directory_uncom+"/speech"
    else:
        directory=directory_uncom
    
    def speak(say):
        global directory
        global voice
        commands_read=open(directory+"/commands.txt","r")
        lines3=commands_read.read().splitlines()
        lines3[0]="stop_speaking=false"
        commands_write=open(directory+"/commands.txt","w")
        commands_write.write(lines3[0])
        for i in lines3[1:]:
            commands_write.write("\n"+i)
        commands_write.close()
        stop=lines3[0][14:]
        voice.create_recording("say.wav", say)
        pygame.mixer.init(22000)
        sound=pygame.mixer.Sound("say.wav")
        sound.play()
        while pygame.mixer.get_busy() == True and stop=="false":
            commands_read=open(directory+"/commands.txt","r")
            lines3=commands_read.read().splitlines()
            stop=lines3[0][14:]
        else:
            sound.stop()
            pygame.quit()