Coverage for coffee_maker/utils/text_to_speech.py: 0%
54 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-21 05:58 +0000
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-21 05:58 +0000
1# co-author : Gemini 2.5 Pro Preview
2import logging
4# Set up a logger for this module.
5# In a larger application, the root logger would typically be configured elsewhere.
6# For a standalone script or a module intended to be used as a utility,
7# getting a logger instance like this is standard.
8logger = logging.getLogger(__name__)
11def text_to_speech_pyttsx3(text, voice_id=None, rate=150, volume=1.0):
12 """
13 Synthesizes text to speech using pyttsx3.
15 Args:
16 text (str): The text to synthesize.
17 voice_id (str, optional): The ID of the voice to use. If None, uses the default voice.
18 Defaults to None.
19 rate (int, optional): The speech rate (words per minute). Defaults to 150.
20 volume (float, optional): The speech volume (0.0 to 1.0). Defaults to 1.0.
21 """
22 try:
23 import pyttsx3
24 except ImportError:
25 logger.error("pyttsx3 is not installed. Please install.")
26 raise RuntimeError("pyttsx3 is not installed. Please install it.")
28 try:
29 logger.info("Initializing pyttsx3 engine.")
30 engine = pyttsx3.init()
32 # Configure speech rate
33 logger.debug(f"Setting speech rate to: {rate}")
34 engine.setProperty("rate", rate)
36 # Configure volume
37 logger.debug(f"Setting volume to: {volume}")
38 engine.setProperty("volume", volume)
40 # Get available voices
41 voices = engine.getProperty("voices")
42 logger.debug(f"Found {len(voices)} voices available on the system.")
44 # To list available voices and their properties in detail, uncomment the following:
45 # for voice in voices:
46 # logger.debug(f"Voice Details - ID: {voice.id} | Name: {voice.name} | Lang: {voice.languages} | Gender: {voice.gender}")
48 # Select a specific voice
49 if voice_id:
50 logger.info(f"Attempting to set voice ID to: {voice_id}")
51 engine.setProperty("voice", voice_id)
52 # Verify if voice was set (optional, pyttsx3 doesn't always provide easy verification)
53 # current_voice = engine.getProperty('voice')
54 # logger.debug(f"Current voice ID set to: {current_voice}")
55 # if current_voice != voice_id:
56 # logger.warning(f"Failed to set voice ID to {voice_id}. Current voice is {current_voice}")
57 else:
58 logger.info("No specific voice_id provided, using default voice.")
59 # Example to select a specific voice type if no voice_id is given
60 # This part is illustrative and would need adaptation based on available voices on your system.
61 # for voice in voices:
62 # if "english" in voice.name.lower() and voice.gender == "female": # Adapt condition
63 # engine.setProperty('voice', voice.id)
64 # logger.info(f"Selected voice programmatically: {voice.name} ({voice.id})")
65 # break
67 logger.info(f"Synthesizing text: '{text}'")
68 engine.say(text)
69 engine.runAndWait() # Blocks until all spoken text has been heard
71 # engine.stop() # Usually not needed after runAndWait() for simple use cases.
72 # Consider if managing event loop manually or for specific platform issues.
74 logger.info("Speech synthesis complete.")
76 except Exception as e:
77 logger.error(f"An error occurred during pyttsx3 operation: {e}", exc_info=True)
78 # exc_info=True will include traceback information in the log for errors.
81if __name__ == "__main__":
82 # Basic logging configuration for when the script is run directly.
83 # This will print log messages of level INFO and above to the console.
84 logging.basicConfig(
85 level=logging.INFO, # Change to logging.DEBUG for more detailed output
86 format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
87 datefmt="%Y-%m-%d %H:%M:%S",
88 )
90 logger.info("--- Text-to-Speech Example Script Starting ---")
92 # Example: Listing available voices (more detailed logging if DEBUG is enabled)
93 logger.info("Attempting to list available voices (details will show if log level is DEBUG)...")
94 try:
95 temp_engine = pyttsx3.init()
96 available_voices = temp_engine.getProperty("voices")
97 if not available_voices:
98 logger.warning("No voices found by pyttsx3 engine.")
99 for i, v in enumerate(available_voices):
100 log_message = f"Voice {i + 1}: ID='{v.id}', Name='{v.name}', Langs={v.languages}, Gender='{v.gender}'"
101 if logger.isEnabledFor(logging.DEBUG): # Log detailed voice info only if DEBUG is on
102 logger.debug(log_message)
103 elif i < 5: # Log first few voices at INFO level for a quick glance
104 logger.info(f"Voice {i + 1} (sample): Name='{v.name}', ID='{v.id}'")
105 if len(available_voices) > 5 and not logger.isEnabledFor(logging.DEBUG):
106 logger.info(f"... and {len(available_voices) - 5} more voices (set log level to DEBUG to see all).")
107 temp_engine.stop() # Clean up the temporary engine
108 except Exception as e:
109 logger.error(f"Could not list voices: {e}", exc_info=True)
111 logger.info("--- Default voice example ---")
112 text_to_speech_pyttsx3("Hello, this is a test using the default voice settings.")
114 logger.info("--- Custom settings example (faster and louder) ---")
115 text_to_speech_pyttsx3("This is a faster and louder message.", rate=220, volume=0.95)
117 # Example for finding and using a specific voice ID
118 # Replace with an actual voice ID from your system after listing them.
119 # macos_daniel_voice_id = 'com.apple.speech.synthesis.voice.daniel' # Example for macOS
120 # windows_zira_voice_id = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_EN-US_ZIRA_11.0' # Example for Windows
122 # selected_voice_id = None # Set this to a valid ID from your system
123 # if selected_voice_id:
124 # logger.info(f"--- Attempting to use specific voice ID: {selected_voice_id} ---")
125 # text_to_speech_pyttsx3("This should be a specifically selected voice.", voice_id=selected_voice_id)
126 # else:
127 # logger.info("--- Specific voice ID example skipped (no voice_id set) ---")
129 logger.info("--- French example (will use default if no French voice is specifically set/found) ---")
130 text_to_speech_pyttsx3("Bonjour le monde.")
132 logger.info("--- Text-to-Speech Example Script Finished ---")