Cracked-User commited on
Commit
11e0218
Β·
verified Β·
1 Parent(s): eacb2b2

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +13 -0
  2. app.py +172 -0
  3. requirements.txt +6 -0
README.md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Andrew TTS
3
+ emoji: πŸŽ™οΈ
4
+ colorFrom: yellow
5
+ colorTo: red
6
+ sdk: gradio
7
+ sdk_version: 3.50.2
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
11
+
12
+ # Andrew TTS
13
+ Microsoft Edge TTS with Andrew voice + audio enhancement.
app.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import edge_tts
3
+ import asyncio
4
+ import numpy as np
5
+ import tempfile
6
+ import os
7
+ from scipy import signal
8
+ from scipy.io import wavfile
9
+ from pydub import AudioSegment
10
+
11
+ VOICE = "en-US-AndrewMultilingualNeural"
12
+ CHUNK_SIZE = 5000
13
+
14
+ # ── TTS ───────────────────────────────────────────────────────────────────────
15
+ async def generate_chunks(text):
16
+ chunks = [text[i:i+CHUNK_SIZE] for i in range(0, len(text), CHUNK_SIZE)]
17
+ parts = []
18
+ for i, chunk in enumerate(chunks):
19
+ tmp = tempfile.mktemp(suffix='.mp3')
20
+ comm = edge_tts.Communicate(chunk, VOICE)
21
+ await comm.save(tmp)
22
+ parts.append(tmp)
23
+ return parts
24
+
25
+ def merge_audio(parts):
26
+ combined = AudioSegment.empty()
27
+ for p in parts:
28
+ combined += AudioSegment.from_file(p)
29
+ os.remove(p)
30
+ return combined
31
+
32
+ # ── Audio Enhancer ────────────────────────────────────────────────────────────
33
+ def enhance_audio(audio):
34
+ audio = audio.set_channels(1).set_frame_rate(44100)
35
+ samples = np.array(audio.get_array_of_samples()).astype(np.float64)
36
+ samples = samples / (np.max(np.abs(samples)) + 1e-9)
37
+ sr = 44100
38
+
39
+ # Bass boost 100-300Hz +4dB
40
+ b, a = signal.butter(2, [100/(sr/2), 300/(sr/2)], btype='band')
41
+ bass = signal.lfilter(b, a, samples)
42
+ samples = samples + bass * (10**(4/20) - 1)
43
+
44
+ # Cut harshness 4-8kHz -3dB
45
+ b, a = signal.butter(2, [4000/(sr/2), 8000/(sr/2)], btype='band')
46
+ harsh = signal.lfilter(b, a, samples)
47
+ samples = samples - harsh * (1 - 10**(-3/20))
48
+
49
+ # Proximity effect
50
+ b, a = signal.butter(1, 200/(sr/2), btype='low')
51
+ proximity = signal.lfilter(b, a, samples)
52
+ samples = samples + proximity * 0.15
53
+
54
+ # De-esser
55
+ b, a = signal.butter(2, [6000/(sr/2), min(10000/(sr/2), 0.99)], btype='band')
56
+ sibilance = signal.lfilter(b, a, samples)
57
+ sib_env = np.abs(sibilance)
58
+ threshold = np.percentile(sib_env, 85)
59
+ mask = sib_env > threshold
60
+ reduction = np.where(mask, sibilance * 0.4, 0.0)
61
+ samples = samples - reduction
62
+
63
+ # Soft-knee compression
64
+ thr_lin = 10 ** (-18/20)
65
+ knee_lin = 10 ** (6/20)
66
+ makeup = 10 ** (4/20)
67
+ ratio = 3.0
68
+ abs_s = np.abs(samples)
69
+ comp = samples.copy()
70
+ above = abs_s > thr_lin * knee_lin
71
+ in_knee = (abs_s > thr_lin) & ~above
72
+ if np.any(above):
73
+ g = thr_lin * (abs_s[above] / thr_lin) ** (1/ratio)
74
+ comp[above] = np.sign(samples[above]) * g
75
+ if np.any(in_knee):
76
+ kf = (abs_s[in_knee] - thr_lin) / (thr_lin * (knee_lin - 1))
77
+ kr = 1 + (ratio - 1) * kf ** 2
78
+ g = thr_lin * (abs_s[in_knee] / thr_lin) ** (1/kr)
79
+ comp[in_knee] = np.sign(samples[in_knee]) * g
80
+ samples = comp * makeup
81
+
82
+ # Normalize
83
+ samples = samples / (np.max(np.abs(samples)) + 1e-9) * 0.95
84
+ samples_int = (samples * 32767).astype(np.int16)
85
+
86
+ enhanced = AudioSegment(
87
+ samples_int.tobytes(),
88
+ frame_rate=sr,
89
+ sample_width=2,
90
+ channels=1
91
+ )
92
+ return enhanced.set_channels(2)
93
+
94
+ # ── Main function ─────────────────────────────────────────────────────────────
95
+ def generate(text, enhance):
96
+ if not text.strip():
97
+ return None, '❌ Please enter some text!'
98
+
99
+ try:
100
+ parts = asyncio.run(generate_chunks(text))
101
+ combined = merge_audio(parts)
102
+
103
+ if enhance:
104
+ combined = enhance_audio(combined)
105
+
106
+ out_path = tempfile.mktemp(suffix='.mp3')
107
+ combined.export(out_path, format='mp3', bitrate='192k')
108
+ return out_path, 'βœ… Done! Click play or download below.'
109
+
110
+ except Exception as e:
111
+ return None, '❌ Error: ' + str(e)
112
+
113
+ # ── CSS ───────────────────────────────────────────────────────────────────────
114
+ css = """
115
+ @import url('https://fonts.googleapis.com/css2?family=Syne:wght@700;800&family=DM+Sans:wght@300;400;500&display=swap');
116
+ * { font-family: 'DM Sans', sans-serif; }
117
+ body, .gradio-container { background: #07070f !important; color: #e8e8f5 !important; }
118
+ .gradio-container { max-width: 800px !important; margin: 0 auto !important; }
119
+ h1 { font-family: 'Syne', sans-serif !important; font-weight: 800 !important;
120
+ font-size: 2.2rem !important;
121
+ background: linear-gradient(135deg, #ffd700, #ff8c00, #ff4500) !important;
122
+ -webkit-background-clip: text !important; -webkit-text-fill-color: transparent !important;
123
+ text-align: center !important; margin-bottom: 0.2rem !important; }
124
+ .subtitle { text-align:center; color:#5a5a7a; font-size:0.88rem; margin-bottom:1.5rem; }
125
+ label { font-family:'Syne',sans-serif !important; font-weight:700 !important;
126
+ font-size:0.78rem !important; color:#ffd700 !important;
127
+ letter-spacing:0.06em !important; text-transform:uppercase !important; }
128
+ textarea { background:#0e0e1c !important; border:1px solid #2a2a3a !important;
129
+ border-radius:10px !important; color:#e8e8f5 !important; padding:12px !important; font-size:0.95rem !important; }
130
+ textarea:focus { border-color:#ffd700 !important; outline:none !important; }
131
+ .block { background:#0d0d1a !important; border:1px solid #1a1a2e !important;
132
+ border-radius:14px !important; padding:1.2rem !important; }
133
+ .btn-generate { background:linear-gradient(135deg,#b8860b,#ff8c00) !important;
134
+ border:none !important; border-radius:12px !important; color:white !important;
135
+ font-family:'Syne',sans-serif !important; font-weight:700 !important;
136
+ font-size:1.05rem !important; padding:16px !important; width:100% !important; }
137
+ .btn-generate:hover { opacity:0.85 !important; transform:translateY(-1px) !important; }
138
+ .tip { background:#0e0e1c; border-left:3px solid #ffd700; border-radius:0 8px 8px 0;
139
+ padding:10px 14px; font-size:0.82rem; color:#9ca3af; margin:0.5rem 0; }
140
+ """
141
+
142
+ # ── GUI ───────────────────────────────────────────────────────────────────────
143
+ with gr.Blocks(css=css, title='Andrew TTS') as demo:
144
+ gr.HTML('<h1>πŸŽ™οΈ Andrew TTS</h1>')
145
+ gr.HTML('<p class="subtitle">Microsoft Andrew voice Β· Auto enhanced Β· Download MP3</p>')
146
+
147
+ with gr.Row():
148
+ with gr.Column(scale=2):
149
+ text_input = gr.Textbox(
150
+ label='Your Text',
151
+ placeholder='Paste your novel chapter or any text here...',
152
+ lines=12
153
+ )
154
+ enhance_toggle = gr.Checkbox(
155
+ label='✨ Enhance audio (warmer voice, de-essed, compressed)',
156
+ value=True
157
+ )
158
+ generate_btn = gr.Button('πŸŽ™οΈ Generate Audio', elem_classes='btn-generate')
159
+
160
+ with gr.Column(scale=1):
161
+ gr.HTML('<div class="tip">πŸ“Œ Tips:<br><br>β€’ Works best with chapters under 50,000 characters<br><br>β€’ Enhancement adds warmth and depth<br><br>β€’ Audio saves as MP3 192kbps<br><br>β€’ You can close your PC β€” this runs on the server!</div>')
162
+ status_box = gr.Textbox(label='Status', interactive=False, lines=2)
163
+
164
+ audio_out = gr.Audio(label='Output Audio', type='filepath')
165
+
166
+ generate_btn.click(
167
+ fn=generate,
168
+ inputs=[text_input, enhance_toggle],
169
+ outputs=[audio_out, status_box]
170
+ )
171
+
172
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ edge-tts
2
+ pydub==0.25.1
3
+ pyaudioop
4
+ scipy
5
+ numpy
6
+ gradio==3.50.2