Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
mrfakename
commited on
Commit
·
6e03e6f
1
Parent(s):
6356013
Autoplay, keyboard shortcuts
Browse files- app/messages.py +3 -0
- app/ui.py +103 -2
- app/ui_battle.py +37 -7
- app/ui_vote.py +39 -6
app/messages.py
CHANGED
@@ -7,7 +7,10 @@ from .config import *
|
|
7 |
MUST_BE_LOGGEDIN = "Please login with Hugging Face to participate in the TTS Arena."
|
8 |
DESCR = """
|
9 |
# TTS Arena: Benchmarking TTS Models in the Wild
|
|
|
10 |
Vote to help the community find the best available text-to-speech model!
|
|
|
|
|
11 |
""".strip()
|
12 |
BATTLE_INSTR = """
|
13 |
## Battle
|
|
|
7 |
MUST_BE_LOGGEDIN = "Please login with Hugging Face to participate in the TTS Arena."
|
8 |
DESCR = """
|
9 |
# TTS Arena: Benchmarking TTS Models in the Wild
|
10 |
+
|
11 |
Vote to help the community find the best available text-to-speech model!
|
12 |
+
|
13 |
+
Type ? to view keyboard shortcuts.
|
14 |
""".strip()
|
15 |
BATTLE_INSTR = """
|
16 |
## Battle
|
app/ui.py
CHANGED
@@ -35,11 +35,112 @@ textbox {resize: none}
|
|
35 |
background: var(--body-text-color);
|
36 |
}
|
37 |
"""
|
38 |
-
|
39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
gr.Markdown(DESCR)
|
41 |
gr.TabbedInterface([vote, battle, leaderboard, about], ['Vote', 'Battle', 'Leaderboard', 'About'])
|
42 |
if CITATION_TEXT:
|
43 |
with gr.Row():
|
44 |
with gr.Accordion("Citation", open=False):
|
45 |
gr.Markdown(f"If you use this data in your publication, please cite us!\n\nCopy the BibTeX citation to cite this source:\n\n```bibtext\n{CITATION_TEXT}\n```\n\nPlease note that all generated audio clips should be assumed unsuitable for redistribution or commercial use.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
background: var(--body-text-color);
|
36 |
}
|
37 |
"""
|
38 |
+
with gr.Blocks(css=CSS + """
|
39 |
+
/* Modal styles */
|
40 |
+
.shortcuts-modal {
|
41 |
+
display: none;
|
42 |
+
position: fixed;
|
43 |
+
top: 50%;
|
44 |
+
left: 50%;
|
45 |
+
transform: translate(-50%, -50%);
|
46 |
+
background: var(--background-fill-primary);
|
47 |
+
padding: 20px;
|
48 |
+
border-radius: 8px;
|
49 |
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
50 |
+
z-index: 1000;
|
51 |
+
max-width: 500px;
|
52 |
+
width: 90%;
|
53 |
+
}
|
54 |
+
.shortcuts-modal.show {
|
55 |
+
display: block;
|
56 |
+
}
|
57 |
+
.modal-backdrop {
|
58 |
+
display: none;
|
59 |
+
position: fixed;
|
60 |
+
top: 0;
|
61 |
+
left: 0;
|
62 |
+
width: 100%;
|
63 |
+
height: 100%;
|
64 |
+
background: rgba(0, 0, 0, 0.5);
|
65 |
+
backdrop-filter: blur(10px);
|
66 |
+
z-index: 999;
|
67 |
+
}
|
68 |
+
.modal-backdrop.show {
|
69 |
+
display: block;
|
70 |
+
}
|
71 |
+
.close-button {
|
72 |
+
position: absolute;
|
73 |
+
top: 10px;
|
74 |
+
right: 10px;
|
75 |
+
background: none;
|
76 |
+
border: none;
|
77 |
+
font-size: 20px;
|
78 |
+
cursor: pointer;
|
79 |
+
color: var(--body-text-color);
|
80 |
+
}
|
81 |
+
.close-button:hover {
|
82 |
+
color: var(--error-text-color);
|
83 |
+
}
|
84 |
+
""", theme=gr.themes.Default(font=[gr.themes.GoogleFont("Geist"), "sans-serif"]), title="TTS Arena") as app:
|
85 |
gr.Markdown(DESCR)
|
86 |
gr.TabbedInterface([vote, battle, leaderboard, about], ['Vote', 'Battle', 'Leaderboard', 'About'])
|
87 |
if CITATION_TEXT:
|
88 |
with gr.Row():
|
89 |
with gr.Accordion("Citation", open=False):
|
90 |
gr.Markdown(f"If you use this data in your publication, please cite us!\n\nCopy the BibTeX citation to cite this source:\n\n```bibtext\n{CITATION_TEXT}\n```\n\nPlease note that all generated audio clips should be assumed unsuitable for redistribution or commercial use.")
|
91 |
+
|
92 |
+
# Add modal HTML
|
93 |
+
gr.HTML("""
|
94 |
+
<div class="modal-backdrop"></div>
|
95 |
+
<div class="shortcuts-modal">
|
96 |
+
<button class="close-button" onclick="toggleModal(false)">×</button>
|
97 |
+
<h3>Keyboard Shortcuts</h3>
|
98 |
+
<p><strong>Global:</strong></p>
|
99 |
+
<ul>
|
100 |
+
<li><code>?</code> or <code>Shift + /</code> - Show this help dialog</li>
|
101 |
+
<li><code>Esc</code> - Close this dialog</li>
|
102 |
+
</ul>
|
103 |
+
<p><strong>Vote & Battle Mode:</strong></p>
|
104 |
+
<ul>
|
105 |
+
<li><code>r</code> - Generate random text</li>
|
106 |
+
<li><code>Ctrl/Cmd + Enter</code> - Synthesize text</li>
|
107 |
+
<li><code>a</code> - Vote for option A</li>
|
108 |
+
<li><code>b</code> - Vote for option B</li>
|
109 |
+
</ul>
|
110 |
+
</div>
|
111 |
+
""")
|
112 |
+
|
113 |
+
# Add modal control JavaScript
|
114 |
+
app.load(None, None, js="""
|
115 |
+
function() {
|
116 |
+
function toggleModal(show) {
|
117 |
+
document.querySelector('.shortcuts-modal').classList.toggle('show', show);
|
118 |
+
document.querySelector('.modal-backdrop').classList.toggle('show', show);
|
119 |
+
}
|
120 |
+
|
121 |
+
document.addEventListener('keydown', function(e) {
|
122 |
+
// Only handle shortcuts when not typing in an input
|
123 |
+
if (document.activeElement.tagName === 'INPUT' ||
|
124 |
+
document.activeElement.tagName === 'TEXTAREA') {
|
125 |
+
return;
|
126 |
+
}
|
127 |
+
|
128 |
+
// Check for shift + / or ? key
|
129 |
+
if ((e.key === '/' && e.shiftKey) || e.key === '?') {
|
130 |
+
toggleModal(true);
|
131 |
+
}
|
132 |
+
// Check for escape key
|
133 |
+
else if (e.key === 'Escape') {
|
134 |
+
toggleModal(false);
|
135 |
+
}
|
136 |
+
});
|
137 |
+
|
138 |
+
// Close modal when clicking backdrop
|
139 |
+
document.querySelector('.modal-backdrop').addEventListener('click', function() {
|
140 |
+
toggleModal(false);
|
141 |
+
});
|
142 |
+
|
143 |
+
// Make toggleModal available globally
|
144 |
+
window.toggleModal = toggleModal;
|
145 |
+
}
|
146 |
+
""")
|
app/ui_battle.py
CHANGED
@@ -20,25 +20,28 @@ with gr.Blocks() as battle:
|
|
20 |
with gr.Group():
|
21 |
with gr.Row():
|
22 |
text = gr.Textbox(container=False, show_label=False, placeholder="Enter text to synthesize", lines=1, max_lines=1, scale=9999999, min_width=0)
|
23 |
-
randomt_battle = gr.Button('🎲', scale=0, min_width=0, variant='tool')
|
24 |
with gr.Row():
|
25 |
with gr.Column(scale=10):
|
26 |
model1s = gr.Dropdown(label="Model 1", container=False, show_label=False, choices=AVAILABLE_MODELS.keys(), interactive=True, value=list(AVAILABLE_MODELS.keys())[0])
|
27 |
with gr.Column(scale=10):
|
28 |
model2s = gr.Dropdown(label="Model 2", container=False, show_label=False, choices=AVAILABLE_MODELS.keys(), interactive=True, value=list(AVAILABLE_MODELS.keys())[1])
|
29 |
randomt_battle.click(randomsent_battle, outputs=[text, randomt_battle, model1s, model2s])
|
30 |
-
btn = gr.Button("Synthesize", variant='primary')
|
31 |
with gr.Row(visible=False) as r2:
|
32 |
with gr.Column():
|
33 |
with gr.Group():
|
34 |
-
aud1 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False)
|
35 |
-
abetter = gr.Button("A is better", variant='primary')
|
36 |
prevmodel1 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model A", text_align="center", lines=1, max_lines=1, visible=False)
|
37 |
with gr.Column():
|
38 |
with gr.Group():
|
39 |
-
aud2 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False)
|
40 |
-
bbetter = gr.Button("B is better", variant='primary')
|
41 |
prevmodel2 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model B", text_align="center", lines=1, max_lines=1, visible=False)
|
|
|
|
|
|
|
42 |
outputs = [
|
43 |
text,
|
44 |
btn,
|
@@ -56,4 +59,31 @@ with gr.Blocks() as battle:
|
|
56 |
nxt_outputs = [abetter, bbetter, prevmodel1, prevmodel2]
|
57 |
abetter.click(a_is_better_battle, outputs=nxt_outputs, inputs=[model1, model2, battle_useridstate])
|
58 |
bbetter.click(b_is_better_battle, outputs=nxt_outputs, inputs=[model1, model2, battle_useridstate])
|
59 |
-
battle.load(random_m, outputs=[model1s, model2s])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
with gr.Group():
|
21 |
with gr.Row():
|
22 |
text = gr.Textbox(container=False, show_label=False, placeholder="Enter text to synthesize", lines=1, max_lines=1, scale=9999999, min_width=0)
|
23 |
+
randomt_battle = gr.Button('🎲', scale=0, min_width=0, variant='tool', elem_id="battle-random-button")
|
24 |
with gr.Row():
|
25 |
with gr.Column(scale=10):
|
26 |
model1s = gr.Dropdown(label="Model 1", container=False, show_label=False, choices=AVAILABLE_MODELS.keys(), interactive=True, value=list(AVAILABLE_MODELS.keys())[0])
|
27 |
with gr.Column(scale=10):
|
28 |
model2s = gr.Dropdown(label="Model 2", container=False, show_label=False, choices=AVAILABLE_MODELS.keys(), interactive=True, value=list(AVAILABLE_MODELS.keys())[1])
|
29 |
randomt_battle.click(randomsent_battle, outputs=[text, randomt_battle, model1s, model2s])
|
30 |
+
btn = gr.Button("Synthesize", variant='primary', elem_id="battle-synth-button")
|
31 |
with gr.Row(visible=False) as r2:
|
32 |
with gr.Column():
|
33 |
with gr.Group():
|
34 |
+
aud1 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False, autoplay=True, elem_id="battle-a-audio")
|
35 |
+
abetter = gr.Button("A is better", variant='primary', elem_id="battle-a-button")
|
36 |
prevmodel1 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model A", text_align="center", lines=1, max_lines=1, visible=False)
|
37 |
with gr.Column():
|
38 |
with gr.Group():
|
39 |
+
aud2 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False, elem_id="battle-b-audio")
|
40 |
+
bbetter = gr.Button("B is better", variant='primary', elem_id="battle-b-button")
|
41 |
prevmodel2 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model B", text_align="center", lines=1, max_lines=1, visible=False)
|
42 |
+
aud1.stop(None, None, js="""function() {
|
43 |
+
document.getElementById('battle-b-audio')?.querySelector('button.play-pause-button')?.click();
|
44 |
+
}""")
|
45 |
outputs = [
|
46 |
text,
|
47 |
btn,
|
|
|
59 |
nxt_outputs = [abetter, bbetter, prevmodel1, prevmodel2]
|
60 |
abetter.click(a_is_better_battle, outputs=nxt_outputs, inputs=[model1, model2, battle_useridstate])
|
61 |
bbetter.click(b_is_better_battle, outputs=nxt_outputs, inputs=[model1, model2, battle_useridstate])
|
62 |
+
battle.load(random_m, outputs=[model1s, model2s], js="""function() {
|
63 |
+
document.addEventListener('keydown', function(e) {
|
64 |
+
// Only handle shortcuts if we're on the battle tab
|
65 |
+
if (!document.querySelector('#battle-a-button')?.offsetParent) {
|
66 |
+
// Check if random button is visible
|
67 |
+
if (document.querySelector('#battle-random-button')?.offsetParent) {
|
68 |
+
if (e.key === 'r') {
|
69 |
+
document.getElementById('battle-random-button')?.click();
|
70 |
+
} else if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
|
71 |
+
document.getElementById('battle-synth-button')?.click();
|
72 |
+
}
|
73 |
+
}
|
74 |
+
return;
|
75 |
+
}
|
76 |
+
|
77 |
+
// Only handle A and B keys when not typing in an input
|
78 |
+
if (document.activeElement.tagName === 'INPUT' ||
|
79 |
+
document.activeElement.tagName === 'TEXTAREA') {
|
80 |
+
return;
|
81 |
+
}
|
82 |
+
|
83 |
+
if (e.key.toLowerCase() === 'a') {
|
84 |
+
document.getElementById('battle-a-button')?.click();
|
85 |
+
} else if (e.key.toLowerCase() === 'b') {
|
86 |
+
document.getElementById('battle-b-button')?.click();
|
87 |
+
}
|
88 |
+
});
|
89 |
+
}""")
|
app/ui_vote.py
CHANGED
@@ -20,9 +20,9 @@ with gr.Blocks() as vote:
|
|
20 |
with gr.Group():
|
21 |
with gr.Row():
|
22 |
text = gr.Textbox(container=False, show_label=False, placeholder="Enter text to synthesize", lines=1, max_lines=1, scale=9999999, min_width=0)
|
23 |
-
randomt = gr.Button('🎲', scale=0, min_width=0, variant='tool')
|
24 |
randomt.click(randomsent, outputs=[text, randomt])
|
25 |
-
btn = gr.Button("Synthesize", variant='primary')
|
26 |
model1 = gr.Textbox(interactive=False, lines=1, max_lines=1, visible=False)
|
27 |
#model1 = gr.Textbox(interactive=False, lines=1, max_lines=1, visible=True)
|
28 |
model2 = gr.Textbox(interactive=False, lines=1, max_lines=1, visible=False)
|
@@ -30,14 +30,17 @@ with gr.Blocks() as vote:
|
|
30 |
with gr.Row(visible=False) as r2:
|
31 |
with gr.Column():
|
32 |
with gr.Group():
|
33 |
-
aud1 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False)
|
34 |
-
abetter = gr.Button("A is better", variant='primary')
|
35 |
prevmodel1 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model A", text_align="center", lines=1, max_lines=1, visible=False)
|
36 |
with gr.Column():
|
37 |
with gr.Group():
|
38 |
-
aud2 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False)
|
39 |
-
bbetter = gr.Button("B is better", variant='primary')
|
40 |
prevmodel2 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model B", text_align="center", lines=1, max_lines=1, visible=False)
|
|
|
|
|
|
|
41 |
nxtroundbtn = gr.Button('Next round', visible=False)
|
42 |
# outputs = [text, btn, r2, model1, model2, prevmodel1, aud1, prevmodel2, aud2, abetter, bbetter]
|
43 |
outputs = [
|
@@ -78,3 +81,33 @@ with gr.Blocks() as vote:
|
|
78 |
nxt_outputs = [abetter, bbetter, prevmodel1, prevmodel2, nxtroundbtn]
|
79 |
abetter.click(a_is_better, outputs=nxt_outputs, inputs=[model1, model2, useridstate])
|
80 |
bbetter.click(b_is_better, outputs=nxt_outputs, inputs=[model1, model2, useridstate])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
with gr.Group():
|
21 |
with gr.Row():
|
22 |
text = gr.Textbox(container=False, show_label=False, placeholder="Enter text to synthesize", lines=1, max_lines=1, scale=9999999, min_width=0)
|
23 |
+
randomt = gr.Button('🎲', scale=0, min_width=0, variant='tool', elem_id="vote-random-button")
|
24 |
randomt.click(randomsent, outputs=[text, randomt])
|
25 |
+
btn = gr.Button("Synthesize", variant='primary', elem_id="vote-synth-button")
|
26 |
model1 = gr.Textbox(interactive=False, lines=1, max_lines=1, visible=False)
|
27 |
#model1 = gr.Textbox(interactive=False, lines=1, max_lines=1, visible=True)
|
28 |
model2 = gr.Textbox(interactive=False, lines=1, max_lines=1, visible=False)
|
|
|
30 |
with gr.Row(visible=False) as r2:
|
31 |
with gr.Column():
|
32 |
with gr.Group():
|
33 |
+
aud1 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False, autoplay=True, elem_id="vote-a-audio")
|
34 |
+
abetter = gr.Button("A is better", variant='primary', elem_id="vote-a-button")
|
35 |
prevmodel1 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model A", text_align="center", lines=1, max_lines=1, visible=False)
|
36 |
with gr.Column():
|
37 |
with gr.Group():
|
38 |
+
aud2 = gr.Audio(interactive=False, show_label=False, show_download_button=False, show_share_button=False, elem_id="vote-b-audio")
|
39 |
+
bbetter = gr.Button("B is better", variant='primary', elem_id="vote-b-button")
|
40 |
prevmodel2 = gr.Textbox(interactive=False, show_label=False, container=False, value="Vote to reveal model B", text_align="center", lines=1, max_lines=1, visible=False)
|
41 |
+
aud1.stop(None, None, js="""function() {
|
42 |
+
document.getElementById('vote-b-audio')?.querySelector('button.play-pause-button')?.click();
|
43 |
+
}""")
|
44 |
nxtroundbtn = gr.Button('Next round', visible=False)
|
45 |
# outputs = [text, btn, r2, model1, model2, prevmodel1, aud1, prevmodel2, aud2, abetter, bbetter]
|
46 |
outputs = [
|
|
|
81 |
nxt_outputs = [abetter, bbetter, prevmodel1, prevmodel2, nxtroundbtn]
|
82 |
abetter.click(a_is_better, outputs=nxt_outputs, inputs=[model1, model2, useridstate])
|
83 |
bbetter.click(b_is_better, outputs=nxt_outputs, inputs=[model1, model2, useridstate])
|
84 |
+
|
85 |
+
# Add custom JS for keyboard shortcuts
|
86 |
+
vote.load(None, None, js="""function() {
|
87 |
+
document.addEventListener('keydown', function(e) {
|
88 |
+
// Only handle shortcuts if we're on the vote tab
|
89 |
+
if (!document.querySelector('#vote-a-button')?.offsetParent) {
|
90 |
+
// Check if random button is visible
|
91 |
+
if (document.querySelector('#vote-random-button')?.offsetParent) {
|
92 |
+
if (e.key === 'r') {
|
93 |
+
document.getElementById('vote-random-button')?.click();
|
94 |
+
} else if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
|
95 |
+
document.getElementById('vote-synth-button')?.click();
|
96 |
+
}
|
97 |
+
}
|
98 |
+
return;
|
99 |
+
}
|
100 |
+
|
101 |
+
// Only handle A and B keys when not typing in an input
|
102 |
+
if (document.activeElement.tagName === 'INPUT' ||
|
103 |
+
document.activeElement.tagName === 'TEXTAREA') {
|
104 |
+
return;
|
105 |
+
}
|
106 |
+
|
107 |
+
if (e.key.toLowerCase() === 'a') {
|
108 |
+
document.getElementById('vote-a-button')?.click();
|
109 |
+
} else if (e.key.toLowerCase() === 'b') {
|
110 |
+
document.getElementById('vote-b-button')?.click();
|
111 |
+
}
|
112 |
+
});
|
113 |
+
}""")
|