|
|
import gradio as gr |
|
|
|
|
|
|
|
|
voices = [ |
|
|
{"name": "Jessica", "gender": "Female", "country": "US", "file": "jessica.mp3"}, |
|
|
{"name": "Michael", "gender": "Male", "country": "UK", "file": "michael.mp3"}, |
|
|
{"name": "Sophia", "gender": "Female", "country": "Canada", "file": "sophia.mp3"}, |
|
|
{"name": "David", "gender": "Male", "country": "Australia", "file": "david.mp3"}, |
|
|
{"name": "Emma", "gender": "Female", "country": "Germany", "file": "emma.mp3"}, |
|
|
{"name": "Lucas", "gender": "Male", "country": "France", "file": "lucas.mp3"}, |
|
|
{"name": "Liam", "gender": "Male", "country": "India", "file": "liam.mp3"}, |
|
|
{"name": "Olivia", "gender": "Female", "country": "Brazil", "file": "olivia.mp3"}, |
|
|
{"name": "Noah", "gender": "Male", "country": "Japan", "file": "noah.mp3"}, |
|
|
{"name": "Ava", "gender": "Female", "country": "South Korea", "file": "ava.mp3"}, |
|
|
] |
|
|
|
|
|
|
|
|
def play_audio(file): |
|
|
return f"voices/{file}" |
|
|
|
|
|
|
|
|
custom_css = """ |
|
|
h2 { |
|
|
text-align: center; |
|
|
color: #ffffff; |
|
|
font-size: 26px; |
|
|
margin-bottom: 20px; |
|
|
} |
|
|
body { |
|
|
background-color: #121212; |
|
|
font-family: Arial, sans-serif; |
|
|
} |
|
|
.voice-container { |
|
|
display: grid; |
|
|
grid-template-columns: repeat(2, 1fr); /* Default: 2 per row */ |
|
|
gap: 15px; |
|
|
padding: 20px; |
|
|
} |
|
|
@media (min-width: 1024px) { |
|
|
.voice-container { |
|
|
grid-template-columns: repeat(3, 1fr); /* 3 per row on PC */ |
|
|
} |
|
|
} |
|
|
.voice-box { |
|
|
background: #1e1e1e; |
|
|
border-radius: 12px; |
|
|
padding: 15px; |
|
|
text-align: center; |
|
|
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.3); |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
justify-content: center; |
|
|
align-items: center; |
|
|
width: 100%; |
|
|
max-width: 220px; |
|
|
height: 220px; |
|
|
margin: auto; |
|
|
transition: transform 0.2s ease-in-out; |
|
|
} |
|
|
.voice-box:hover { |
|
|
transform: scale(1.05); |
|
|
} |
|
|
.play-btn { |
|
|
background-color: #FF5722 !important; |
|
|
color: white; |
|
|
border-radius: 50%; |
|
|
width: 60px; |
|
|
height: 60px; |
|
|
font-size: 26px; |
|
|
border: none; |
|
|
cursor: pointer; |
|
|
margin-bottom: 10px; |
|
|
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.2); |
|
|
transition: background 0.3s ease-in-out; |
|
|
position: relative; |
|
|
} |
|
|
.play-btn.playing { |
|
|
background-color: #4CAF50 !important; /* Green when playing */ |
|
|
} |
|
|
.play-btn.playing::after { |
|
|
content: ""; |
|
|
position: absolute; |
|
|
top: -5px; |
|
|
left: -5px; |
|
|
width: 70px; |
|
|
height: 70px; |
|
|
border-radius: 50%; |
|
|
border: 4px solid #2196F3; /* Blue ring */ |
|
|
animation: pulse 1s infinite alternate; |
|
|
} |
|
|
@keyframes pulse { |
|
|
0% { transform: scale(1); opacity: 0.7; } |
|
|
100% { transform: scale(1.2); opacity: 1; } |
|
|
} |
|
|
.voice-info { |
|
|
color: #ffffff; |
|
|
text-align: center; |
|
|
} |
|
|
.voice-info b { |
|
|
font-weight: bold; |
|
|
font-size: 16px; |
|
|
} |
|
|
.voice-info span { |
|
|
font-size: 14px; |
|
|
color: #bbbbbb; |
|
|
} |
|
|
""" |
|
|
|
|
|
|
|
|
playing_states = {voice["file"]: False for voice in voices} |
|
|
|
|
|
|
|
|
def toggle_audio(file): |
|
|
global playing_states |
|
|
playing_states[file] = not playing_states[file] |
|
|
return (f"voices/{file}" if playing_states[file] else None), gr.update(elem_classes="play-btn playing" if playing_states[file] else "play-btn") |
|
|
|
|
|
|
|
|
with gr.Blocks(css=custom_css) as demo: |
|
|
gr.Markdown("<h2>🎤 Human-Like Voices (Pro Plan)</h2>") |
|
|
|
|
|
with gr.Column(elem_classes="voice-container"): |
|
|
for voice in voices: |
|
|
with gr.Column(elem_classes="voice-box"): |
|
|
|
|
|
audio = gr.Audio(None, autoplay=False, elem_id=voice["name"], visible=False) |
|
|
|
|
|
btn = gr.Button("â–¶", elem_classes="play-btn") |
|
|
|
|
|
info = gr.Markdown(f"<div class='voice-info'><b>{voice['name']}</b><br><span>{voice['gender']} | {voice['country']}</span></div>") |
|
|
|
|
|
btn.click(fn=toggle_audio, inputs=[], outputs=[audio, btn]) |
|
|
|
|
|
|
|
|
demo.launch() |
|
|
|