|
import tkinter as tk |
|
from tkinter import messagebox |
|
from tkinter import ttk |
|
import json,subprocess |
|
import random |
|
from TTS.api import TTS |
|
class VoiceProfile: |
|
def __init__(self, name, voice): |
|
self.name = name |
|
self.voice = voice |
|
|
|
def to_dict(self): |
|
return { |
|
'name': self.name, |
|
'voice': self.voice |
|
} |
|
|
|
@classmethod |
|
def from_dict(cls, profile_dict): |
|
return cls( |
|
profile_dict['name'], |
|
profile_dict['voice'] |
|
) |
|
|
|
def configure_tts(self): |
|
|
|
subprocess.run(["festival", "--tts", "(voice_" + self.voice + ")"]) |
|
|
|
class VoiceProfileManager: |
|
def __init__(self, filename="voice_profiles.json"): |
|
self.filename = filename |
|
self.profiles = [] |
|
self.load_profiles() |
|
|
|
def load_profiles(self): |
|
try: |
|
with open(self.filename, 'r') as file: |
|
profiles_data = json.load(file) |
|
self.profiles = [VoiceProfile.from_dict(profile) for profile in profiles_data] |
|
except FileNotFoundError: |
|
print(f"File '{self.filename}' not found. Starting with an empty profile list.") |
|
self.profiles = [] |
|
|
|
def save_profiles(self): |
|
profiles_data = [profile.to_dict() for profile in self.profiles] |
|
with open(self.filename, 'w') as file: |
|
json.dump(profiles_data, file, indent=4) |
|
print(f"Profiles saved to '{self.filename}'.") |
|
|
|
def add_profile(self, profile): |
|
self.profiles.append(profile) |
|
|
|
def generate_random_profile(self): |
|
name = f"Profile-{len(self.profiles) + 1}" |
|
voices = ["cmu_us_slt", "cmu_us_awb", "cmu_us_rms", "cmu_us_bdl"] |
|
voice = random.choice(voices) |
|
new_profile = VoiceProfile(name, voice) |
|
self.add_profile(new_profile) |
|
return new_profile |
|
|
|
def list_profiles(self): |
|
if not self.profiles: |
|
return "No profiles found." |
|
else: |
|
profiles_list = [] |
|
for idx, profile in enumerate(self.profiles, start=1): |
|
profiles_list.append(f"Profile {idx}: {profile.name} - Voice: {profile.voice}") |
|
return profiles_list |
|
|
|
def get_profile_by_name(self, profile_name): |
|
for profile in self.profiles: |
|
if profile.name == profile_name: |
|
return profile |
|
return None |
|
|
|
|
|
class VoiceProfileTool: |
|
def __init__(self, root): |
|
self.root = root |
|
self.root.title("Voice Profile Manager") |
|
|
|
self.profile_manager = VoiceProfileManager() |
|
|
|
self.create_widgets() |
|
|
|
def create_widgets(self): |
|
|
|
profile_frame = ttk.LabelFrame(self.root, text="Voice Profiles") |
|
profile_frame.grid(row=0, column=0, padx=10, pady=10, sticky=tk.W+tk.E+tk.N+tk.S) |
|
|
|
|
|
self.profiles_listbox = tk.Listbox(profile_frame, width=50, height=10) |
|
self.profiles_listbox.grid(row=0, column=0, padx=10, pady=10, sticky=tk.W+tk.E+tk.N+tk.S) |
|
|
|
|
|
scrollbar = ttk.Scrollbar(profile_frame, orient=tk.VERTICAL, command=self.profiles_listbox.yview) |
|
scrollbar.grid(row=0, column=1, pady=10, sticky=tk.N+tk.S) |
|
self.profiles_listbox.config(yscrollcommand=scrollbar.set) |
|
|
|
|
|
generate_btn = ttk.Button(profile_frame, text="Generate Random Profile", command=self.generate_random_profile) |
|
generate_btn.grid(row=1, column=0, padx=10, pady=5, sticky=tk.W+tk.E) |
|
|
|
|
|
refresh_btn = ttk.Button(profile_frame, text="Refresh List", command=self.refresh_profiles_list) |
|
refresh_btn.grid(row=1, column=1, padx=10, pady=5, sticky=tk.W+tk.E) |
|
|
|
|
|
tts_frame = ttk.LabelFrame(self.root, text="Text-to-Speech (TTS)") |
|
tts_frame.grid(row=1, column=0, padx=10, pady=10, sticky=tk.W+tk.E+tk.N+tk.S) |
|
|
|
|
|
ttk.Label(tts_frame, text="Enter text to speak:").grid(row=0, column=0, padx=10, pady=5, sticky=tk.W) |
|
self.tts_text_entry = ttk.Entry(tts_frame, width=50) |
|
self.tts_text_entry.grid(row=0, column=1, padx=10, pady=5, sticky=tk.W+tk.E) |
|
|
|
|
|
ttk.Label(tts_frame, text="Select profile:").grid(row=1, column=0, padx=10, pady=5, sticky=tk.W) |
|
self.profile_combobox = ttk.Combobox(tts_frame, width=48, state="readonly") |
|
self.profile_combobox.grid(row=1, column=1, padx=10, pady=5, sticky=tk.W+tk.E) |
|
|
|
|
|
speak_btn = ttk.Button(tts_frame, text="Speak", command=self.speak_text) |
|
speak_btn.grid(row=2, column=1, padx=10, pady=10, sticky=tk.W+tk.E) |
|
|
|
|
|
self.refresh_profiles_list() |
|
|
|
def refresh_profiles_list(self): |
|
|
|
self.profiles_listbox.delete(0, tk.END) |
|
self.profile_combobox['values'] = [] |
|
|
|
|
|
profiles = self.profile_manager.list_profiles() |
|
if profiles: |
|
for profile in profiles: |
|
self.profiles_listbox.insert(tk.END, profile) |
|
self.profile_combobox['values'] += (profile.split(':')[0],) |
|
|
|
def generate_random_profile(self): |
|
new_profile = self.profile_manager.generate_random_profile() |
|
messagebox.showinfo("Profile Generated", f"Generated new profile: {new_profile.name}") |
|
self.refresh_profiles_list() |
|
|
|
def speak_text(self): |
|
text_to_speak = self.tts_text_entry.get().strip() |
|
selected_profile_name = self.profile_combobox.get().strip() |
|
|
|
if not text_to_speak: |
|
messagebox.showwarning("Input Required", "Please enter text to speak.") |
|
return |
|
|
|
if not selected_profile_name: |
|
messagebox.showwarning("Profile Required", "Please select a profile.") |
|
return |
|
|
|
profile = self.profile_manager.get_profile_by_name(selected_profile_name) |
|
if profile: |
|
profile.configure_tts() |
|
subprocess.Popen(["festival", "--tts"], stdin=subprocess.PIPE).communicate(bytes(text_to_speak, 'utf-8')) |
|
messagebox.showinfo("Text-to-Speech", f"Text: {text_to_speak}\nProfile: {profile.name}\nVoice: {profile.voice}") |
|
else: |
|
messagebox.showerror("Profile Not Found", f"Profile '{selected_profile_name}' not found.") |
|
|
|
|
|
if __name__ == "__main__": |
|
root = tk.Tk() |
|
app = VoiceProfileTool(root) |
|
root.mainloop() |
|
""" Explanation: |
|
Integration with piper_tts: This example uses the TTS class from piper_tts for text-to-speech synthesis. The configure_tts method in VoiceProfile class is used to set parameters (language, pitch, speaking_rate) on the TTS engine before synthesizing speech. |
|
|
|
Tkinter GUI: The GUI interface (VoiceProfileTool class) is built using Tkinter widgets (Listbox, Entry, Combobox, Button, etc.) to manage voice profiles (list, generate random profile) and perform TTS (enter text, select profile, speak). |
|
|
|
Profile Management: VoiceProfileManager handles loading/saving profiles from/to JSON file, generating random profiles, listing profiles, and retrieving profiles by name. |
|
|
|
Handling TTS Output: After synthesizing speech with piper_tts, the example shows a message box with details about the synthesized text and the selected profile. |
|
""" |
|
|