11from PyQt6 .QtCore import QObject , pyqtSignal
22import os
33import tempfile
4- import time
5- import wave
6- import array
74import pyttsx3 # For offline TTS
8- import requests # For online TTS
9- import json
10- import io
5+ # The following imports are kept for the OnlineTTSWorker implementation
6+ # Will be used when the API implementation is completed
7+ import requests # For online TTS - unused for now but kept for future implementation # noqa
8+ import json # For online TTS - unused for now but kept for future implementation # noqa
119
1210class TTSWorkerBase (QObject ):
1311 # Base class remains unchanged
@@ -30,12 +28,12 @@ def cancel(self):
3028class OfflineTTSWorker (TTSWorkerBase ):
3129 """Worker for offline text-to-speech processing"""
3230
33- def __init__ (self , text , model_path , speech_rate = 1.0 ):
34- super ().__init__ (text , None , speech_rate )
35- self .model_path = model_path
31+ def __init__ (self , text , voice_id = None , speech_rate = 1.0 , volume = 1.0 ):
32+ super ().__init__ (text , voice_id , speech_rate )
33+ self .volume = volume
3634
3735 def generate_speech (self ):
38- """Generate speech using offline TTS model """
36+ """Generate speech using offline TTS engine """
3937 try :
4038 # Create temporary file for audio output
4139 temp_file = tempfile .NamedTemporaryFile (delete = False , suffix = ".wav" )
@@ -44,16 +42,30 @@ def generate_speech(self):
4442
4543 self .progress .emit (10 )
4644
47- # Use pyttsx3 for offline TTS
45+ # Initialize pyttsx3 engine
4846 engine = pyttsx3 .init ()
49- engine .setProperty ('rate' , int (engine .getProperty ('rate' ) * self .speech_rate ))
50-
51- # Set voice if available
52- voices = engine .getProperty ('voices' )
53- if voices and len (voices ) > 0 :
54- engine .setProperty ('voice' , voices [0 ].id )
5547
56- self .progress .emit (30 )
48+ # Configure voice properties
49+ engine .setProperty ('rate' , int (engine .getProperty ('rate' ) * self .speech_rate ))
50+ engine .setProperty ('volume' , self .volume )
51+
52+ # Set specific voice if requested
53+ if self .voice_id :
54+ voices = engine .getProperty ('voices' )
55+ for voice in voices :
56+ if self .voice_id .lower () in voice .id .lower ():
57+ engine .setProperty ('voice' , voice .id )
58+ break
59+ # Otherwise use default voice
60+ else :
61+ voices = engine .getProperty ('voices' )
62+ if voices :
63+ engine .setProperty ('voice' , voices [0 ].id )
64+
65+ self .progress .emit (40 )
66+
67+ if self .is_cancelled :
68+ raise Exception ("TTS generation cancelled" )
5769
5870 # Save to file
5971 engine .save_to_file (self .text , output_path )
@@ -63,6 +75,11 @@ def generate_speech(self):
6375 # Wait for file generation to complete
6476 engine .runAndWait ()
6577
78+ if self .is_cancelled :
79+ if os .path .exists (output_path ):
80+ os .remove (output_path )
81+ raise Exception ("TTS generation cancelled" )
82+
6683 self .progress .emit (100 )
6784 self .speech_ready .emit (output_path )
6885
@@ -87,21 +104,23 @@ def generate_speech(self):
87104
88105 self .progress .emit (10 )
89106
90- # Example using a generic TTS service API (replace with your preferred service)
91107 # This is a placeholder - you'll need to implement the specific API calls
92108 # for your chosen service (Google, AWS, Azure, etc.)
93109
94- headers = {
110+ # These variables are prepared for future API implementation
111+ # and will be used when the API call is uncommented
112+ headers = { # Unused for now - will be used with actual API implementation # noqa
95113 "Content-Type" : "application/json" ,
96114 "Authorization" : f"Bearer { self .api_key } "
97115 }
98116
99- data = {
117+ data = { # Unused for now - will be used with actual API implementation # noqa
100118 "text" : self .text ,
101119 "voice" : self .voice_id ,
102120 "rate" : self .speech_rate
103121 }
104122
123+
105124 self .progress .emit (30 )
106125
107126 # This is a placeholder for the API call
0 commit comments