aghilTQ commited on
Commit
367b153
·
verified ·
1 Parent(s): 0496a05

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -141
app.py CHANGED
@@ -1,161 +1,82 @@
1
- import spaces
2
- import gradio as gr
3
  import edge_tts
4
- import asyncio
5
  import tempfile
6
- import os
7
- import re
8
- from pydub import AudioSegment
9
- import numpy as np
10
 
11
- # Get all available voices
12
- async def get_voices():
13
- voices = await edge_tts.list_voices()
14
- return {f"{v['ShortName']} - {v['Locale']} ({v['Gender']})": v['ShortName'] for v in voices}
 
15
 
16
- # Text-to-speech function with word timing estimation
17
- async def text_to_speech(text, voice, rate, pitch):
18
- if not text.strip():
19
- return None, None, gr.Warning("Please enter text to convert.")
20
- if not voice:
21
- return None, None, gr.Warning("Please select a voice.")
22
-
23
- voice_short_name = voice.split(" - ")[0]
24
- rate_str = f"{rate:+d}%"
25
- pitch_str = f"{pitch:+d}Hz"
26
-
27
- # Generate full audio
28
- communicate = edge_tts.Communicate(text, voice_short_name, rate=rate_str, pitch=pitch_str)
29
- with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
30
  tmp_path = tmp_file.name
31
  await communicate.save(tmp_path)
32
-
33
- # Estimate word timings
34
- audio = AudioSegment.from_file(tmp_path)
35
- duration = len(audio) / 1000.0 # in seconds
36
- words = re.findall(r'\b\w+\b', text)
37
- n_words = len(words)
38
- word_timings = []
39
-
40
- if n_words > 0:
41
- time_per_word = duration / n_words
42
- for i, word in enumerate(words):
43
- start_time = i * time_per_word
44
- end_time = (i + 1) * time_per_word
45
- word_timings.append({
46
- "word": word,
47
- "start": start_time,
48
- "end": end_time
49
- })
50
-
51
- # Create clickable transcript HTML
52
- transcript_html = ""
53
- for i, wt in enumerate(word_timings):
54
- transcript_html += f'<span class="word" data-start="{wt["start"]}" data-end="{wt["end"]}" data-index="{i}">{wt["word"]}</span> '
55
-
56
- return tmp_path, transcript_html, None
57
-
58
- # Gradio interface function
59
- @spaces.GPU
60
- def tts_interface(text, voice, rate, pitch):
61
- audio_file, transcript, warning = asyncio.run(text_to_speech(text, voice, rate, pitch))
62
- return audio_file, transcript, warning
63
 
64
- # Create Gradio application
65
- async def create_demo():
66
- voices = await get_voices()
67
-
68
- description = """
69
- Experience the power of Voicecloning.be for text-to-speech conversion.
70
- <br><b>NEW:</b> Click on any word in the transcript to start playback from that position!
71
- """
72
-
73
- # JavaScript for interactive transcript
74
- js = """
75
  <script>
76
- function handleWordClick(event) {
77
- const audio = document.querySelector('#audio-player audio');
78
- const start = parseFloat(event.target.dataset.start);
79
- if (!isNaN(start) && audio) {
80
- audio.currentTime = start;
81
- audio.play();
82
-
83
- // Highlight clicked word
84
- document.querySelectorAll('.word').forEach(w =>
85
- w.style.backgroundColor = 'transparent');
86
- event.target.style.backgroundColor = '#e6f7ff';
87
- }
88
- }
89
-
90
- document.addEventListener('DOMContentLoaded', () => {
91
- document.querySelectorAll('.word').forEach(word => {
92
- word.addEventListener('click', handleWordClick);
93
  });
94
  });
 
95
  </script>
96
  """
97
-
98
- css = """
99
  .word {
100
  cursor: pointer;
101
- padding: 2px 4px;
102
- border-radius: 4px;
103
- transition: background-color 0.3s;
104
  }
105
  .word:hover {
106
- background-color: #f0f0f0;
107
- }
108
- #transcript-container {
109
- max-height: 200px;
110
- overflow-y: auto;
111
- border: 1px solid #e0e0e0;
112
- padding: 10px;
113
- border-radius: 4px;
114
- margin-top: 10px;
115
  }
 
116
  """
117
-
118
- with gr.Blocks(css=css) as demo:
119
- gr.Markdown("# Voicecloning.be Text-to-Speech")
120
- gr.Markdown(description)
121
-
122
- with gr.Row():
123
- with gr.Column():
124
- text_input = gr.Textbox(label="Input Text", lines=5)
125
- voice_dropdown = gr.Dropdown(
126
- choices=[""] + list(voices.keys()),
127
- label="Select Voice",
128
- value=""
129
- )
130
- rate_slider = gr.Slider(
131
- minimum=-50, maximum=50, value=0,
132
- label="Speech Rate Adjustment (%)", step=1
133
- )
134
- pitch_slider = gr.Slider(
135
- minimum=-20, maximum=20, value=0,
136
- label="Pitch Adjustment (Hz)", step=1
137
- )
138
- submit_btn = gr.Button("Generate Speech", variant="primary")
139
-
140
- with gr.Column():
141
- audio_output = gr.Audio(label="Generated Audio", elem_id="audio-player")
142
- transcript_output = gr.HTML(
143
- label="Interactive Transcript",
144
- elem_id="transcript-container"
145
- )
146
- warning_output = gr.Markdown(visible=False)
147
-
148
- submit_btn.click(
149
- fn=tts_interface,
150
- inputs=[text_input, voice_dropdown, rate_slider, pitch_slider],
151
- outputs=[audio_output, transcript_output, warning_output]
152
- )
153
-
154
- gr.HTML(js)
155
-
156
- return demo
157
 
158
- # Run the application
159
  if __name__ == "__main__":
160
- demo = asyncio.run(create_demo())
161
- demo.launch()
 
 
 
1
  import edge_tts
2
+ import gradio as gr
3
  import tempfile
4
+ import anyio
5
+ import wave
 
 
6
 
7
+ language_dict = {
8
+ 'English-Jenny (Female)': 'en-US-JennyNeural',
9
+ 'English-Guy (Male)': 'en-US-GuyNeural',
10
+ # Add more if needed
11
+ }
12
 
13
+ async def text_to_speech_edge(text, language_code):
14
+ voice = language_dict.get(language_code, "en-US-JennyNeural")
15
+ communicate = edge_tts.Communicate(text, voice)
16
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file:
 
 
 
 
 
 
 
 
 
 
17
  tmp_path = tmp_file.name
18
  await communicate.save(tmp_path)
19
+ return text, tmp_path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
+ def make_interactive_transcript(text, duration):
22
+ words = text.split()
23
+ word_count = len(words)
24
+ est_duration_per_word = duration / word_count if word_count else 0.5
25
+ spans = []
26
+ for i, word in enumerate(words):
27
+ start_time = round(i * est_duration_per_word, 2)
28
+ spans.append(f'<span class="word" data-start="{start_time}">{word}</span>')
29
+ joined = ' '.join(spans)
30
+ script = """
 
31
  <script>
32
+ document.addEventListener("DOMContentLoaded", () => {
33
+ document.querySelectorAll('.word').forEach(span => {
34
+ span.addEventListener('click', () => {
35
+ const audio = document.querySelector("audio");
36
+ const start = parseFloat(span.dataset.start);
37
+ if (audio) {
38
+ audio.currentTime = start;
39
+ audio.play();
40
+ }
 
 
 
 
 
 
 
 
41
  });
42
  });
43
+ });
44
  </script>
45
  """
46
+ style = """
47
+ <style>
48
  .word {
49
  cursor: pointer;
50
+ padding: 0 2px;
 
 
51
  }
52
  .word:hover {
53
+ background-color: #ffe58a;
 
 
 
 
 
 
 
 
54
  }
55
+ </style>
56
  """
57
+ return style + joined + script
58
+
59
+ async def tts_with_interactive_transcript(text, language_code):
60
+ text_out, audio_path = await text_to_speech_edge(text, language_code)
61
+
62
+ with wave.open(audio_path, 'rb') as wf:
63
+ duration = wf.getnframes() / wf.getframerate()
64
+
65
+ transcript_html = make_interactive_transcript(text_out, duration)
66
+ return transcript_html, audio_path
67
+
68
+ input_text = gr.Textbox(lines=5, label="Input Text")
69
+ output_html = gr.HTML(label="Interactive Transcript")
70
+ output_audio = gr.Audio(type="filepath", label="Exported Audio")
71
+ language = gr.Dropdown(choices=list(language_dict.keys()), label="Choose the Voice Model")
72
+
73
+ interface = gr.Interface(
74
+ fn=tts_with_interactive_transcript,
75
+ inputs=[input_text, language],
76
+ outputs=[output_html, output_audio],
77
+ title="Edge TTS with Interactive Transcript",
78
+ description="Click on any word in the transcript to jump to that part of the audio.",
79
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
 
81
  if __name__ == "__main__":
82
+ anyio.run(interface.launch, backend="asyncio")