Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Voice is not played when running under Windows Task Scheduler #263

Open
ran-peled opened this issue Mar 17, 2023 · 6 comments
Open

Voice is not played when running under Windows Task Scheduler #263

ran-peled opened this issue Mar 17, 2023 · 6 comments
Labels
bug engine:sapi Related to sapi code

Comments

@ran-peled
Copy link

ran-peled commented Mar 17, 2023

I have a script using pyttx3 working fine when run regularly. When run under Windows Task Scheduler, no voice is heard, although no errors are thrown. I also notice that engine.runAndWait() returns immediately, indicating it is not playing the voice (as opposed to playing but the voice is not heard).

I debugged and verified SAPI5Driver._tts is initialized with a proper system voice (in my case "Headphones (Realtek(R) Audio)").

Suspecting that Windows Scheduled Task might be blocking sound, I tested playing voice-to-text using Windows API from a task, which works fine. The command used was: powershell.exe -Command "Add-Type -AssemblyName System.Speech; $synthesizer = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer; $synthesizer.Speak('Hello, world!')"

I would assume this is not directly a pyttsx3 issue, but some missing setting on the driver maybe. I hope you have some clue or advice.

Attaching the small script for reference:

import sys
import time
import pyttsx3

logging.basicConfig(stream=sys.stdout)
file_handler = logging.FileHandler('C:/temp/speak.log')
file_handler.setLevel(logging.DEBUG)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(file_handler)

class Speaker(object):

    def __init__(self) -> None:
        self.engine = pyttsx3.init(debug=True)
        self.voices = self.engine.getProperty('voices')
        self.rate = self.engine.getProperty('rate')
        self.voice_num = 0
        logger.info("current rate: {}".format(self.rate))

    def speak(self, text, rate_factor = 1.0):
        self.engine.setProperty('rate', self.rate * rate_factor)
        self.engine.setProperty('voice', self.voices[self.voice_num].id)
        self.engine.say(text)
        self.engine.runAndWait()

    def set_voice(self, num):
        self.voice_num = num
        logger.info("Voice {}: {}".format(num, str(self.voices[num])))


if __name__ == '__main__':
    
    speaker = Speaker()

    logger.info("{} voices, {}".format(len(speaker.voices), str(speaker.voices)))
        
    for rate in [0.8, 1.0, 1.2]:
        for i in range(len(speaker.voices)):
            speaker.set_voice(i)
            speaker.speak("This is a test", rate)
            time.sleep(0.2)
@Jiangshan00001
Copy link

sorry, i have no idea

@WispySparks
Copy link

I think it's possible we're running into error -2147200925 SPERR_NOT_ACTIVE_SESSION (Neither audio output nor input is supported for non-active console sessions). At least, that's the error I get trying to run sapi directly using pywin32.

@willwade willwade added bug engine:sapi Related to sapi code labels Nov 6, 2024
@willwade
Copy link
Collaborator

willwade commented Nov 6, 2024

Yeah it sounds like this is to do with the restriction on accessing sapi in non active console sessions. I’m thinking there might a nice workaround to this we can have in the code. And it links weirdly with an issue we have with Linux of choosing the output device. But this is way trickier due to the way this library is built around event sink etc.

Anyway. Couple of other ideas

  1. Run the Task Only When Logged In and with an Active Session

    • Configure the Task Scheduler to run the task only when the user is logged in and ensure that an active console session (e.g., desktop login) is present. (General - security - run only when logged in). This avoids the isolated Session 0 and may allow audio playback to work.

  2. Output Speech to File as a Workaround

    • If running with an active session isn’t possible, consider using save_to_file to save the synthesized speech to a WAV file. You can then play the file back using an audio utility like PowerShell or VLC in Task Scheduler. Here’s an example using PowerShell:

Start-Process -FilePath "powershell" -ArgumentList "-c (New-Object Media.SoundPlayer 'C:\path\to\file.wav').PlaySync()"

But for sure - I do wonder if there is something we can do to the driver..

https://stackoverflow.com/questions/24293808/simple-text-to-speech-with-sapi-in-a-windows-service

@WispySparks
Copy link

Another option would be to have a driver do speech synthesis under WinRT with pywinrt as this stack overflow answer claims these restrictions don't apply to WinRT https://stackoverflow.com/a/69921152.

@willwade
Copy link
Collaborator

willwade commented Nov 6, 2024

Yeah - i Drafted that one too. The only problem is - UWP (WinRT) is really limited. You cant do word events for a start and SSML is limited. But bigger than that - its very lmited number of voices as MSFT seem to have pretty much dropped it

This is hideous code - its a car crash right now but esence of something is within it. #375

@WispySparks
Copy link

Here's just a really simple sample that worked for me, running off of the Windows Task Scheduler in the background(pythonw).

import asyncio
from time import sleep

from winrt.windows.media.core import MediaSource
from winrt.windows.media.playback import MediaPlayer
from winrt.windows.media.speechsynthesis import SpeechSynthesizer


async def main():
    player = MediaPlayer()
    synth = SpeechSynthesizer()
    stream = await synth.synthesize_text_to_stream_async("Hello World")
    player.source = MediaSource.create_from_stream(stream, stream.content_type)
    player.play()
    sleep(5)
    stream.close()
    synth.close()
    player.close()


asyncio.run(main())

This will work for my purposes but I don't know if it has all the functionality you're looking for.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug engine:sapi Related to sapi code
Projects
None yet
Development

No branches or pull requests

4 participants