File size: 7,186 Bytes
92c9b3d
8468afb
 
92c9b3d
8468afb
7dea2dd
8468afb
 
 
 
 
 
 
49a48a4
 
 
 
 
 
 
 
7c38a46
49a48a4
 
7c38a46
8468afb
 
92c9b3d
7dea2dd
 
8468afb
7dea2dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8468afb
 
 
 
49a48a4
8468afb
 
 
 
 
 
49a48a4
 
b95b786
49a48a4
92c9b3d
 
8468afb
 
 
 
 
 
 
92c9b3d
8468afb
 
 
 
92c9b3d
8468afb
 
 
 
92c9b3d
8468afb
 
92c9b3d
 
8468afb
 
49a48a4
8468afb
92c9b3d
 
8468afb
 
92c9b3d
 
 
 
 
 
 
 
 
 
 
 
 
8468afb
e73ce82
 
 
 
49a48a4
92c9b3d
e73ce82
8468afb
 
 
49a48a4
92c9b3d
 
8468afb
92c9b3d
49a48a4
92c9b3d
 
 
0b876d2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# FILE: ui_layout.py
import gradio as gr

# --- UI Constants and Configuration ---
TTS_MODELS_AVAILABLE = ["tts-1", "tts-1-hd", "gpt-4o-mini-tts"]
MODEL_DEFAULT_ENV = "gpt-4o-mini-tts"

SPEAKER_CONFIG_METHODS = [
    "Single Voice (Global)",
    "Random per Speaker",
    "A/B Round Robin",
    "Detailed Configuration (Per Speaker UI)"
]
DEFAULT_SPEAKER_CONFIG_METHOD = "Random per Speaker"

# Assuming utils.openai_tts.OPENAI_VOICES exists and provides a list of voice names
# If not, define APP_AVAILABLE_VOICES directly here for now.
try:
    from utils.openai_tts import OPENAI_VOICES as ALL_TTS_VOICES
    APP_AVAILABLE_VOICES = ALL_TTS_VOICES.copy()
    if not APP_AVAILABLE_VOICES: # Fallback if OPENAI_VOICES is empty
        APP_AVAILABLE_VOICES = ["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "sage", "nova", "shimmer", "verse"]
except ImportError:
    print("Warning: Could not import OPENAI_VOICES from utils.openai_tts. Using default list.")
    APP_AVAILABLE_VOICES = ["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "sage", "nova", "shimmer", "verse"]

PREDEFINED_VIBES = {
    "None": "", "Calm": "Speak in a calm, composed, and relaxed manner.",
    "Serene": "Speak softly and gently, radiating tranquility and warmth.",
    "Excited": "Speak with energetic enthusiasm and lively expression.",
    "Happy": "Speak with a cheerful, bright, and joyful voice.",
    "Sad": "Speak slowly with a soft, melancholic tone.",
    "Whisper": "Speak very softly, as if whispering to the listener.",
    "Angry": "Speak with a firm, tense, and forceful voice.",
    "Fearful": "Speak in a trembling, hesitant voice that conveys anxiety.",
    "Dramatic": "Speak in a low, suspenseful tone with deliberate pauses.",
    "Formal": "Speak clearly and precisely with professional neutrality.",
    "Authoritative": "Speak with commanding confidence and steady emphasis.",
    "Friendly": "Speak warmly and conversationally, sounding approachable.",
    "Playful": "Speak in a light‑hearted, lively, and teasing manner.",
    "Sarcastic": "Speak with dry, wry intonation to convey ironic humor.",
    "Narrative": "Speak like a storyteller with engaging, expressive cadence.",
    "Motivational": "Speak with uplifting energy and inspiring conviction.",
    "Mysterious": "Speak slowly and quietly, hinting at hidden intrigue.",
    "Romantic": "Speak softly and tenderly with gentle affection.",
    "ASMR": "Speak very softly and slowly for a soothing, tingle effect.",
    "Corporate": "Speak in a polished, business‑like and neutral tone.",
    "News": "Speak in a steady, neutral broadcast style for clear facts.",
    "Custom...": "CUSTOM"
}
VIBE_CHOICES = list(PREDEFINED_VIBES.keys())
DEFAULT_VIBE = "None"
DEFAULT_GLOBAL_VOICE = APP_AVAILABLE_VOICES[0] if APP_AVAILABLE_VOICES else "alloy"


# --- UI Element Creation Functions ---

def create_main_input_components(model_default_value):
    """Creates the main input components for script, model, pause, and global settings."""
    with gr.Row(elem_id="main_input_row"):
        with gr.Column(scale=2, elem_id="script_input_column"):
            script_input = gr.TextArea(label="Dialogue Script", placeholder="[Speaker1] Hello world!\n[Speaker2] How are you today?", lines=10, elem_id="script_input_area")
        with gr.Column(scale=1, elem_id="model_config_column"):
            tts_model_dropdown = gr.Dropdown(TTS_MODELS_AVAILABLE, label="TTS Model", value=model_default_value, elem_id="tts_model_dropdown")
            pause_input = gr.Number(label="Pause Between Lines (ms)", value=500, minimum=0, maximum=5000, step=50, elem_id="pause_input")
            
            is_tts1_family_default = model_default_value in ["tts-1", "tts-1-hd"]
            is_gpt_mini_tts_default = model_default_value == "gpt-4o-mini-tts"

            global_speed_input = gr.Slider(
                minimum=0.25, maximum=4.0, value=1.0, step=0.05,
                label="Global Speed (for tts-1/hd)",
                visible=is_tts1_family_default, interactive=True, elem_id="global_speed_input"
            )
            global_instructions_input = gr.Textbox(
                label="Global Instructions (for gpt-4o-mini-tts)",
                placeholder="e.g., Speak with a calm tone.",
                visible=is_gpt_mini_tts_default, interactive=True, lines=2, elem_id="global_instructions_input"
            )
    return script_input, tts_model_dropdown, pause_input, global_speed_input, global_instructions_input

def create_speaker_config_components():
    """Creates components for speaker configuration method selection and the container for detailed UI."""
    gr.Markdown("### Speaker Voice & Style Configuration")
    speaker_config_method_dropdown = gr.Dropdown(
        SPEAKER_CONFIG_METHODS, label="Configuration Method", value=DEFAULT_SPEAKER_CONFIG_METHOD,
        elem_id="speaker_config_method_dropdown"
    )

    with gr.Group(visible=(DEFAULT_SPEAKER_CONFIG_METHOD == "Single Voice (Global)"), elem_id="single_voice_config_group") as single_voice_group:
        global_voice_dropdown = gr.Dropdown(
            APP_AVAILABLE_VOICES, label="Global Voice", value=DEFAULT_GLOBAL_VOICE, interactive=True,
            elem_id="global_voice_dropdown"
        )

    detailed_per_speaker_ui_group_container = gr.Column(
        visible=(DEFAULT_SPEAKER_CONFIG_METHOD == "Detailed Configuration (Per Speaker UI)"),
        elem_id="detailed_per_speaker_ui_group_container" 
    )
    with detailed_per_speaker_ui_group_container:
        load_per_speaker_ui_button = gr.Button(
            "Load/Refresh Per-Speaker Settings UI (from Script Above)",
            elem_id="load_per_speaker_ui_button"
        )
        gr.Markdown(
            "<small>Click button above to populate settings for each speaker found in the script. "
            "Settings are applied per-speaker. If script changes, click again to refresh.</small>"
        )
    
    return (
        speaker_config_method_dropdown, 
        single_voice_group, 
        global_voice_dropdown, 
        detailed_per_speaker_ui_group_container,
        load_per_speaker_ui_button
    )

def create_action_and_output_components():
    """Creates buttons for actions (cost, generate) and output display areas."""
    with gr.Row(elem_id="action_buttons_row"):
        calculate_cost_button = gr.Button("Calculate Estimated Cost", elem_id="calculate_cost_button")
        generate_button = gr.Button("Generate Audio", variant="primary", elem_id="generate_button")
    
    cost_output = gr.Textbox(label="Estimated Cost", interactive=False, elem_id="cost_output")
    with gr.Row(elem_id="output_files_row"):
        individual_lines_zip_output = gr.File(label="Download Individual Lines (ZIP)", elem_id="individual_lines_zip_output")
        merged_dialogue_mp3_output = gr.Audio(label="Play/Download Merged Dialogue (MP3)", type="filepath", elem_id="merged_dialogue_mp3_output")
    status_output = gr.Textbox(label="Status", interactive=False, lines=2, max_lines=5, elem_id="status_output")
    return calculate_cost_button, generate_button, cost_output, individual_lines_zip_output, merged_dialogue_mp3_output, status_output