keithhon commited on
Commit
dd98c36
1 Parent(s): 977d7b7

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +158 -0
app.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, sys
2
+ import tempfile
3
+ import gradio as gr
4
+ import numpy as np
5
+ from typing import Tuple, List
6
+
7
+ # Setup and installation
8
+ os.system("git clone https://github.com/neonbjb/tortoise-tts.git")
9
+ os.system("cd tortoise-tts")
10
+ os.system("git reset --hard 8c0b3855bfb5312adf2b000b52cf5cfa2830c310")
11
+ sys.path.append("./tortoise-tts/")
12
+ os.system("pip install -r ./tortoise-tts/requirements.txt")
13
+ os.system("python ./tortoise-tts/setup.py install")
14
+
15
+ import torch
16
+ import torchaudio
17
+ import torch.nn as nn
18
+ import torch.nn.functional as F
19
+
20
+ from tortoise.api import TextToSpeech
21
+ from tortoise.utils.audio import load_audio, load_voice
22
+
23
+
24
+ # Download and instantiate model
25
+ tts = TextToSpeech()
26
+
27
+
28
+ # Display parameters
29
+ VOICES = ["random","train_atkins","train_daws","train_dotrice","train_dreams","train_empire","train_grace","train_kennard","train_lescault","train_mouse","angie","applejack","daniel","deniro","emma","freeman","geralt","halle","jlaw","lj","mol","myself","pat","pat2","rainbow","snakes","tim_reynolds","tom","weaver","william"]
30
+ DEFAULT_VOICE = "random"
31
+ PRESETS = ["ultra_fast", "fast", "standard", "high_quality"]
32
+ DEFAULT_PRESET = "fast"
33
+ DEFAULT_TEXT = "Hello, world!"
34
+
35
+ README = """# TorToiSe
36
+ Tortoise is a text-to-speech model developed by James Betker. It is capable of zero-shot voice cloning from a small set of voice samples. GitHub repo: [neonbjb/tortoise-tts](https://github.com/neonbjb/tortoise-tts).
37
+
38
+ ## Usage
39
+ 1. Select a model preset and type the text to speak.
40
+ 2. Load a voice - either by choosing a preset, uploading audio files, or recording via microphone. Select the option to split audio into chunks if the clips are much longer than 10 seconds each. Follow the guidelines in the [voice customization guide](https://github.com/neonbjb/tortoise-tts#voice-customization-guide).
41
+ 3. Click **Generate**, and wait - it's called *tortoise* for a reason!
42
+ """
43
+
44
+ TORTOISE_SR_IN = 22050
45
+ TORTOISE_SR_OUT = 24000
46
+
47
+ def chunk_audio(t: torch.Tensor, sample_rate: int, chunk_duration_sec: int) -> List[torch.Tensor]:
48
+ duration = t.shape[1] / sample_rate
49
+ num_chunks = 1 + int(duration/chunk_duration_sec)
50
+ chunks = [t[:,(sample_rate*chunk_duration_sec*i):(sample_rate*chunk_duration_sec*(i+1))] for i in range(num_chunks)]
51
+ # remove 0-width chunks
52
+ chunks = [chunk for chunk in chunks if chunk.shape[1]>0]
53
+ return chunks
54
+
55
+ def tts_main(voice_samples: List[torch.Tensor], text: str, model_preset: str) -> str:
56
+ gen = tts.tts_with_preset(
57
+ text,
58
+ voice_samples=voice_samples,
59
+ conditioning_latents=None,
60
+ preset=model_preset
61
+ )
62
+ torchaudio.save("generated.wav", gen.squeeze(0).cpu(), TORTOISE_SR_OUT)
63
+ return "generated.wav"
64
+
65
+ def tts_from_preset(voice: str, text, model_preset):
66
+ voice_samples, _ = load_voice(voice)
67
+ return tts_main(voice_samples, text, model_preset)
68
+
69
+ def tts_from_files(files: List[tempfile._TemporaryFileWrapper], do_chunk, text, model_preset):
70
+ voice_samples = [load_audio(f.name, TORTOISE_SR_IN) for f in files]
71
+ if do_chunk:
72
+ voice_samples = [chunk for t in voice_samples for chunk in chunk_audio(t, TORTOISE_SR_IN, 10)]
73
+ return tts_main(voice_samples, text, model_preset)
74
+
75
+ def tts_from_recording(recording: Tuple[int, np.ndarray], do_chunk, text, model_preset):
76
+ sample_rate, audio = recording
77
+ # normalize- https://github.com/neonbjb/tortoise-tts/blob/main/tortoise/utils/audio.py#L16
78
+ norm_fix = 1
79
+ if audio.dtype == np.int32:
80
+ norm_fix = 2**31
81
+ elif audio.dtype == np.int16:
82
+ norm_fix = 2**15
83
+ audio = torch.FloatTensor(audio.T) / norm_fix
84
+ if len(audio.shape) > 1:
85
+ # convert to mono
86
+ audio = torch.mean(audio, axis=0).unsqueeze(0)
87
+ audio = torchaudio.transforms.Resample(sample_rate, TORTOISE_SR_IN)(audio)
88
+ if do_chunk:
89
+ voice_samples = chunk_audio(audio, TORTOISE_SR_IN, 10)
90
+ else:
91
+ voice_samples = [audio]
92
+ return tts_main(voice_samples, text, model_preset)
93
+
94
+ def tts_from_url(audio_url, start_time, end_time, do_chunk, text, model_preset):
95
+ os.system(f"yt-dlp -x --audio-format mp3 --force-overwrites {audio_url} -o audio.mp3")
96
+ audio = load_audio("audio.mp3", TORTOISE_SR_IN)
97
+ audio = audio[:,start_time*TORTOISE_SR_IN:end_time*TORTOISE_SR_IN]
98
+ if do_chunk:
99
+ voice_samples = chunk_audio(audio, TORTOISE_SR_IN, 10)
100
+ else:
101
+ voice_samples = [audio]
102
+ return tts_main(voice_samples, text, model_preset)
103
+
104
+
105
+ with gr.Blocks() as demo:
106
+
107
+ gr.Markdown(README)
108
+
109
+ preset = gr.Dropdown(PRESETS, label="Model preset", value=DEFAULT_PRESET)
110
+ text = gr.Textbox(label="Text to speak", value=DEFAULT_TEXT)
111
+ do_chunk_label = "Split audio into chunks? (for audio much longer than 10 seconds.)"
112
+ do_chunk_default = True
113
+
114
+ with gr.Tab("Choose preset voice"):
115
+ inp1 = gr.Dropdown(VOICES, value=DEFAULT_VOICE, label="Preset voice")
116
+ btn1 = gr.Button("Generate")
117
+
118
+ with gr.Tab("Upload audio"):
119
+ inp2 = gr.File(file_count="multiple")
120
+ do_chunk2 = gr.Checkbox(label=do_chunk_label, value=do_chunk_default)
121
+ btn2 = gr.Button("Generate")
122
+
123
+ with gr.Tab("Record audio"):
124
+ inp3 = gr.Audio(source="microphone")
125
+ do_chunk3 = gr.Checkbox(label=do_chunk_label, value=do_chunk_default)
126
+ btn3 = gr.Button("Generate")
127
+
128
+ # with gr.Tab("From YouTube"):
129
+ # inp4 = gr.Textbox(label="URL")
130
+ # do_chunk4 = gr.Checkbox(label=do_chunk_label, value=do_chunk_default)
131
+ # start_time = gr.Number(label="Start time (seconds)", precision=0)
132
+ # end_time = gr.Number(label="End time (seconds)", precision=0)
133
+ # btn4 = gr.Button("Generate")
134
+
135
+ audio_out = gr.Audio()
136
+
137
+ btn1.click(
138
+ tts_from_preset,
139
+ [inp1, text, preset],
140
+ [audio_out],
141
+ )
142
+ btn2.click(
143
+ tts_from_files,
144
+ [inp2, do_chunk2, text, preset],
145
+ [audio_out],
146
+ )
147
+ btn3.click(
148
+ tts_from_recording,
149
+ [inp3, do_chunk3, text, preset],
150
+ [audio_out],
151
+ )
152
+ # btn4.click(
153
+ # tts_from_url,
154
+ # [inp4, start_time, end_time, do_chunk4, text, preset],
155
+ # [audio_out],
156
+ # )
157
+
158
+ demo.launch()