Offexb commited on
Commit
3a585d6
Β·
verified Β·
1 Parent(s): ee3ebe0

Add permanent saved voices + mobile-friendly premium UI

Browse files

Adds two cleaned, normalised 20s reference voices to assets/ so no upload is needed. Quick-pick buttons and one-tap examples use them; the reference is preloaded by default. Shorter labels and CSS media queries fix cramped mobile rendering.

Files changed (4) hide show
  1. .gitattributes +2 -0
  2. app.py +36 -24
  3. assets/voice_1.wav +3 -0
  4. assets/voice_2.wav +3 -0
.gitattributes CHANGED
@@ -43,3 +43,5 @@ training_data/19535.png filter=lfs diff=lfs merge=lfs -text
43
  training_data/19536.png filter=lfs diff=lfs merge=lfs -text
44
  training_data/19540.png filter=lfs diff=lfs merge=lfs -text
45
  training_data/19542.png filter=lfs diff=lfs merge=lfs -text
 
 
 
43
  training_data/19536.png filter=lfs diff=lfs merge=lfs -text
44
  training_data/19540.png filter=lfs diff=lfs merge=lfs -text
45
  training_data/19542.png filter=lfs diff=lfs merge=lfs -text
46
+ assets/voice_1.wav filter=lfs diff=lfs merge=lfs -text
47
+ assets/voice_2.wav filter=lfs diff=lfs merge=lfs -text
app.py CHANGED
@@ -111,18 +111,13 @@ def _patch_numpy_compat() -> None:
111
  np.bool8 = np.bool_
112
 
113
 
114
- def _download_example_voice() -> Optional[str]:
115
- """Fetch one sample reference voice so users can try the app instantly."""
116
- try:
117
- from huggingface_hub import hf_hub_download
118
-
119
- return hf_hub_download(
120
- repo_id="IndexTeam/IndexTTS-2-Demo",
121
- repo_type="space",
122
- filename="examples/voice_02.wav",
123
- )
124
- except Exception:
125
- return None
126
 
127
 
128
  def _warm_up() -> None:
@@ -148,9 +143,6 @@ def _warm_up() -> None:
148
  _warmup_thread = threading.Thread(target=_warm_up, daemon=True)
149
  _warmup_thread.start()
150
 
151
- EXAMPLE_VOICE = _download_example_voice()
152
-
153
-
154
  def _find_config(model_dir: Path) -> str:
155
  candidates = [
156
  model_dir / "config.yaml",
@@ -475,6 +467,16 @@ CUSTOM_CSS = """
475
  .quota-note {font-size: 0.78rem; color: #64748b; margin-top: 6px; line-height: 1.5;}
476
  .tips-card {font-size: 0.92rem; line-height: 1.7;}
477
  footer {display: none !important;}
 
 
 
 
 
 
 
 
 
 
478
  """
479
 
480
  HEADER_HTML = """
@@ -503,25 +505,33 @@ with gr.Blocks(title="Offex Voice Studio β€” AI Voice Cloning", theme=gr.themes.
503
  with gr.Row():
504
  with gr.Column(scale=5):
505
  reference_audio = gr.Audio(
506
- label="🎧 Reference voice (5–15s of clean speech works best)",
507
  sources=["upload", "microphone"],
508
  type="filepath",
 
509
  )
510
- with gr.Accordion("🎭 Emotion reference (optional β€” big quality boost)", open=False):
 
 
 
 
 
 
 
511
  emotion_audio = gr.Audio(
512
- label="Emotion reference (same voice, desired emotion)",
513
  sources=["upload", "microphone"],
514
  type="filepath",
515
  )
516
  with gr.Column(scale=7):
517
  text = gr.Textbox(
518
- label="πŸ“ Text to speak (English)",
519
  placeholder="Type the text you want in the cloned voice...",
520
  lines=5,
521
  max_lines=12,
522
  )
523
  permission = gr.Checkbox(
524
- label="βœ… I have permission to use this reference voice",
525
  value=False,
526
  )
527
  generate = gr.Button("✨ Generate Voice", variant="primary", size="lg", elem_id="generate-btn")
@@ -574,14 +584,16 @@ with gr.Blocks(title="Offex Voice Studio β€” AI Voice Cloning", theme=gr.themes.
574
  with gr.Accordion("πŸ’‘ Tips for the most accurate clone", open=False):
575
  gr.Markdown(TIPS_MD)
576
 
577
- if EXAMPLE_VOICE:
 
 
578
  gr.Examples(
579
  examples=[
580
- [EXAMPLE_VOICE, "Hello there! This is my cloned voice, speaking naturally and clearly."],
581
- [EXAMPLE_VOICE, "Welcome to Offex Voice Studio. Today is a wonderful day to create something amazing."],
582
  ],
583
  inputs=[reference_audio, text],
584
- label="Try it instantly with a sample voice",
585
  )
586
 
587
 
 
111
  np.bool8 = np.bool_
112
 
113
 
114
+ ASSETS_DIR = Path(__file__).resolve().parent / "assets"
115
+ MY_VOICES = [
116
+ ("πŸŽ™οΈ My Voice 1", ASSETS_DIR / "voice_1.wav"),
117
+ ("πŸŽ™οΈ My Voice 2", ASSETS_DIR / "voice_2.wav"),
118
+ ]
119
+ MY_VOICES = [(label, str(path)) for label, path in MY_VOICES if path.is_file()]
120
+ DEFAULT_VOICE = MY_VOICES[0][1] if MY_VOICES else None
 
 
 
 
 
121
 
122
 
123
  def _warm_up() -> None:
 
143
  _warmup_thread = threading.Thread(target=_warm_up, daemon=True)
144
  _warmup_thread.start()
145
 
 
 
 
146
  def _find_config(model_dir: Path) -> str:
147
  candidates = [
148
  model_dir / "config.yaml",
 
467
  .quota-note {font-size: 0.78rem; color: #64748b; margin-top: 6px; line-height: 1.5;}
468
  .tips-card {font-size: 0.92rem; line-height: 1.7;}
469
  footer {display: none !important;}
470
+ #foot {text-align: center; color: #64748b; margin-top: 20px; font-size: 0.85rem;}
471
+ #foot span {opacity: .75;}
472
+ @media (max-width: 768px) {
473
+ #hero {padding: 18px 16px; border-radius: 14px;}
474
+ #hero h1 {font-size: 1.35rem;}
475
+ #hero p {font-size: 0.85rem;}
476
+ .gradio-container {padding: 8px !important;}
477
+ .quota-row {flex-direction: column; gap: 1px;}
478
+ #generate-btn {height: 46px !important; font-size: 0.95rem !important;}
479
+ }
480
  """
481
 
482
  HEADER_HTML = """
 
505
  with gr.Row():
506
  with gr.Column(scale=5):
507
  reference_audio = gr.Audio(
508
+ label="Reference voice",
509
  sources=["upload", "microphone"],
510
  type="filepath",
511
+ value=DEFAULT_VOICE,
512
  )
513
+ if MY_VOICES:
514
+ gr.Markdown("**⚑ Quick-pick β€” no upload needed:**")
515
+ with gr.Row():
516
+ for label, path in MY_VOICES:
517
+ gr.Button(label, size="sm").click(
518
+ fn=lambda p=path: p, outputs=reference_audio
519
+ )
520
+ with gr.Accordion("🎭 Emotion reference (optional β€” big boost)", open=False):
521
  emotion_audio = gr.Audio(
522
+ label="Emotion reference",
523
  sources=["upload", "microphone"],
524
  type="filepath",
525
  )
526
  with gr.Column(scale=7):
527
  text = gr.Textbox(
528
+ label="Text to speak",
529
  placeholder="Type the text you want in the cloned voice...",
530
  lines=5,
531
  max_lines=12,
532
  )
533
  permission = gr.Checkbox(
534
+ label="βœ… I have permission to use this voice",
535
  value=False,
536
  )
537
  generate = gr.Button("✨ Generate Voice", variant="primary", size="lg", elem_id="generate-btn")
 
584
  with gr.Accordion("πŸ’‘ Tips for the most accurate clone", open=False):
585
  gr.Markdown(TIPS_MD)
586
 
587
+ gr.HTML("<div id='foot'>Built with <span>❀️</span> in India · Powered by IndexTTS-2 on ZeroGPU</div>")
588
+
589
+ if MY_VOICES:
590
  gr.Examples(
591
  examples=[
592
+ [MY_VOICES[0][1], "Hello there! This is my cloned voice, speaking naturally and clearly."],
593
+ [MY_VOICES[-1][1], "Welcome to Offex Voice Studio. Today is a wonderful day to create something amazing."],
594
  ],
595
  inputs=[reference_audio, text],
596
+ label="One-tap examples with your saved voices",
597
  )
598
 
599
 
assets/voice_1.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:539b877c780d3c13645f8dad702269a94125121cc23819cc291dbe5daa720767
3
+ size 1764044
assets/voice_2.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:acb7378e322eb12d8836281972e6f0c9ff1ea8ef1aee9d8dc2e670f8622f2c0d
3
+ size 1764044