Michael Hu commited on
Commit
f5647fb
·
1 Parent(s): a09d95d

add some files

Browse files
Files changed (11) hide show
  1. VERSION +1 -0
  2. dev/Test Threads.py +378 -0
  3. dev/Test copy.py +94 -0
  4. dev/Test money.py +27 -0
  5. dev/Test num.py +47 -0
  6. dev/Test.py +64 -0
  7. start-cpu.ps1 +13 -0
  8. start-cpu.sh +26 -0
  9. start-gpu.ps1 +13 -0
  10. start-gpu.sh +18 -0
  11. start-gpu_mac.sh +21 -0
VERSION ADDED
@@ -0,0 +1 @@
 
 
1
+ 0.3.0
dev/Test Threads.py ADDED
@@ -0,0 +1,378 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # Compatible with both Windows and Linux
3
+ """
4
+ Kokoro TTS Race Condition Test
5
+
6
+ This script creates multiple concurrent requests to a Kokoro TTS service
7
+ to reproduce a race condition where audio outputs don't match the requested text.
8
+ Each thread generates a simple numbered sentence, which should make mismatches
9
+ easy to identify through listening.
10
+
11
+ To run:
12
+ python kokoro_race_condition_test.py --threads 8 --iterations 5 --url http://localhost:8880
13
+ """
14
+
15
+ import argparse
16
+ import base64
17
+ import concurrent.futures
18
+ import json
19
+ import os
20
+ import sys
21
+ import time
22
+ import wave
23
+ from pathlib import Path
24
+
25
+ import requests
26
+
27
+
28
+ def setup_args():
29
+ """Parse command line arguments"""
30
+ parser = argparse.ArgumentParser(description="Test Kokoro TTS for race conditions")
31
+ parser.add_argument(
32
+ "--url",
33
+ default="http://localhost:8880",
34
+ help="Base URL of the Kokoro TTS service",
35
+ )
36
+ parser.add_argument(
37
+ "--threads", type=int, default=8, help="Number of concurrent threads to use"
38
+ )
39
+ parser.add_argument(
40
+ "--iterations", type=int, default=5, help="Number of iterations per thread"
41
+ )
42
+ parser.add_argument("--voice", default="af_heart", help="Voice to use for TTS")
43
+ parser.add_argument(
44
+ "--output-dir",
45
+ default="./tts_test_output",
46
+ help="Directory to save output files",
47
+ )
48
+ parser.add_argument("--debug", action="store_true", help="Enable debug logging")
49
+ return parser.parse_args()
50
+
51
+
52
+ def generate_test_sentence(thread_id, iteration):
53
+ """Generate a simple test sentence with numbers to make mismatches easily identifiable"""
54
+ return (
55
+ f"This is test sentence number {thread_id}-{iteration}. "
56
+ f"If you hear this sentence, you should hear the numbers {thread_id}-{iteration}."
57
+ )
58
+
59
+
60
+ def log_message(message, debug=False, is_error=False):
61
+ """Log messages with timestamps"""
62
+ timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
63
+ prefix = "[ERROR]" if is_error else "[INFO]"
64
+ if is_error or debug:
65
+ print(f"{prefix} {timestamp} - {message}")
66
+ sys.stdout.flush() # Ensure logs are visible in Docker output
67
+
68
+
69
+ def request_tts(url, test_id, text, voice, output_dir, debug=False):
70
+ """Request TTS from the Kokoro API and save the WAV output"""
71
+ start_time = time.time()
72
+ output_file = os.path.join(output_dir, f"test_{test_id}.wav")
73
+ text_file = os.path.join(output_dir, f"test_{test_id}.txt")
74
+
75
+ # Log output paths for debugging
76
+ log_message(f"Thread {test_id}: Text will be saved to: {text_file}", debug)
77
+ log_message(f"Thread {test_id}: Audio will be saved to: {output_file}", debug)
78
+
79
+ # Save the text for later comparison
80
+ try:
81
+ with open(text_file, "w") as f:
82
+ f.write(text)
83
+ log_message(f"Thread {test_id}: Successfully saved text file", debug)
84
+ except Exception as e:
85
+ log_message(
86
+ f"Thread {test_id}: Error saving text file: {str(e)}", debug, is_error=True
87
+ )
88
+
89
+ # Make the TTS request
90
+ try:
91
+ log_message(f"Thread {test_id}: Requesting TTS for: '{text}'", debug)
92
+
93
+ response = requests.post(
94
+ f"{url}/v1/audio/speech",
95
+ json={
96
+ "model": "kokoro",
97
+ "input": text,
98
+ "voice": voice,
99
+ "response_format": "wav",
100
+ },
101
+ headers={"Accept": "audio/wav"},
102
+ timeout=60, # Increase timeout to 60 seconds
103
+ )
104
+
105
+ log_message(
106
+ f"Thread {test_id}: Response status code: {response.status_code}", debug
107
+ )
108
+ log_message(
109
+ f"Thread {test_id}: Response content type: {response.headers.get('Content-Type', 'None')}",
110
+ debug,
111
+ )
112
+ log_message(
113
+ f"Thread {test_id}: Response content length: {len(response.content)} bytes",
114
+ debug,
115
+ )
116
+
117
+ if response.status_code != 200:
118
+ log_message(
119
+ f"Thread {test_id}: API error: {response.status_code} - {response.text}",
120
+ debug,
121
+ is_error=True,
122
+ )
123
+ return False
124
+
125
+ # Check if we got valid audio data
126
+ if (
127
+ len(response.content) < 100
128
+ ): # Sanity check - WAV files should be larger than this
129
+ log_message(
130
+ f"Thread {test_id}: Received suspiciously small audio data: {len(response.content)} bytes",
131
+ debug,
132
+ is_error=True,
133
+ )
134
+ log_message(
135
+ f"Thread {test_id}: Content (base64): {base64.b64encode(response.content).decode('utf-8')}",
136
+ debug,
137
+ is_error=True,
138
+ )
139
+ return False
140
+
141
+ # Save the audio output with explicit error handling
142
+ try:
143
+ with open(output_file, "wb") as f:
144
+ bytes_written = f.write(response.content)
145
+ log_message(
146
+ f"Thread {test_id}: Wrote {bytes_written} bytes to {output_file}",
147
+ debug,
148
+ )
149
+
150
+ # Verify the WAV file exists and has content
151
+ if os.path.exists(output_file):
152
+ file_size = os.path.getsize(output_file)
153
+ log_message(
154
+ f"Thread {test_id}: Verified file exists with size: {file_size} bytes",
155
+ debug,
156
+ )
157
+
158
+ # Validate WAV file by reading its headers
159
+ try:
160
+ with wave.open(output_file, "rb") as wav_file:
161
+ channels = wav_file.getnchannels()
162
+ sample_width = wav_file.getsampwidth()
163
+ framerate = wav_file.getframerate()
164
+ frames = wav_file.getnframes()
165
+ log_message(
166
+ f"Thread {test_id}: Valid WAV file - channels: {channels}, "
167
+ f"sample width: {sample_width}, framerate: {framerate}, frames: {frames}",
168
+ debug,
169
+ )
170
+ except Exception as wav_error:
171
+ log_message(
172
+ f"Thread {test_id}: Invalid WAV file: {str(wav_error)}",
173
+ debug,
174
+ is_error=True,
175
+ )
176
+ else:
177
+ log_message(
178
+ f"Thread {test_id}: File was not created: {output_file}",
179
+ debug,
180
+ is_error=True,
181
+ )
182
+ except Exception as save_error:
183
+ log_message(
184
+ f"Thread {test_id}: Error saving audio file: {str(save_error)}",
185
+ debug,
186
+ is_error=True,
187
+ )
188
+ return False
189
+
190
+ end_time = time.time()
191
+ log_message(
192
+ f"Thread {test_id}: Saved output to {output_file} (time: {end_time - start_time:.2f}s)",
193
+ debug,
194
+ )
195
+ return True
196
+
197
+ except requests.exceptions.Timeout:
198
+ log_message(f"Thread {test_id}: Request timed out", debug, is_error=True)
199
+ return False
200
+ except Exception as e:
201
+ log_message(f"Thread {test_id}: Exception: {str(e)}", debug, is_error=True)
202
+ return False
203
+
204
+
205
+ def worker_task(thread_id, args):
206
+ """Worker task for each thread"""
207
+ for i in range(args.iterations):
208
+ iteration = i + 1
209
+ test_id = f"{thread_id:02d}_{iteration:02d}"
210
+ text = generate_test_sentence(thread_id, iteration)
211
+ success = request_tts(
212
+ args.url, test_id, text, args.voice, args.output_dir, args.debug
213
+ )
214
+
215
+ if not success:
216
+ log_message(
217
+ f"Thread {thread_id}: Iteration {iteration} failed",
218
+ args.debug,
219
+ is_error=True,
220
+ )
221
+
222
+ # Small delay between iterations to avoid overwhelming the API
223
+ time.sleep(0.1)
224
+
225
+
226
+ def run_test(args):
227
+ """Run the test with the specified parameters"""
228
+ # Ensure output directory exists and check permissions
229
+ os.makedirs(args.output_dir, exist_ok=True)
230
+
231
+ # Test write access to the output directory
232
+ test_file = os.path.join(args.output_dir, "write_test.txt")
233
+ try:
234
+ with open(test_file, "w") as f:
235
+ f.write("Testing write access\n")
236
+ os.remove(test_file)
237
+ log_message(
238
+ f"Successfully verified write access to output directory: {args.output_dir}"
239
+ )
240
+ except Exception as e:
241
+ log_message(
242
+ f"Warning: Cannot write to output directory {args.output_dir}: {str(e)}",
243
+ is_error=True,
244
+ )
245
+ log_message(f"Current directory: {os.getcwd()}", is_error=True)
246
+ log_message(f"Directory contents: {os.listdir('.')}", is_error=True)
247
+
248
+ # Test connection to Kokoro TTS service
249
+ try:
250
+ response = requests.get(f"{args.url}/health", timeout=5)
251
+ if response.status_code == 200:
252
+ log_message(f"Successfully connected to Kokoro TTS service at {args.url}")
253
+ else:
254
+ log_message(
255
+ f"Warning: Kokoro TTS service health check returned status {response.status_code}",
256
+ is_error=True,
257
+ )
258
+ except Exception as e:
259
+ log_message(
260
+ f"Warning: Cannot connect to Kokoro TTS service at {args.url}: {str(e)}",
261
+ is_error=True,
262
+ )
263
+
264
+ # Record start time
265
+ start_time = time.time()
266
+ log_message(
267
+ f"Starting test with {args.threads} threads, {args.iterations} iterations per thread"
268
+ )
269
+
270
+ # Create and start worker threads
271
+ with concurrent.futures.ThreadPoolExecutor(max_workers=args.threads) as executor:
272
+ futures = []
273
+ for thread_id in range(1, args.threads + 1):
274
+ futures.append(executor.submit(worker_task, thread_id, args))
275
+
276
+ # Wait for all tasks to complete
277
+ for future in concurrent.futures.as_completed(futures):
278
+ try:
279
+ future.result()
280
+ except Exception as e:
281
+ log_message(
282
+ f"Thread execution failed: {str(e)}", args.debug, is_error=True
283
+ )
284
+
285
+ # Record end time and print summary
286
+ end_time = time.time()
287
+ total_time = end_time - start_time
288
+ total_requests = args.threads * args.iterations
289
+ log_message(f"Test completed in {total_time:.2f} seconds")
290
+ log_message(f"Total requests: {total_requests}")
291
+ log_message(f"Average time per request: {total_time / total_requests:.2f} seconds")
292
+ log_message(f"Requests per second: {total_requests / total_time:.2f}")
293
+ log_message(f"Output files saved to: {os.path.abspath(args.output_dir)}")
294
+ log_message(
295
+ "To verify, listen to the audio files and check if they match the text files"
296
+ )
297
+ log_message(
298
+ "If you hear audio describing a different test number than the filename, you've found a race condition"
299
+ )
300
+
301
+
302
+ def analyze_audio_files(output_dir):
303
+ """Provide summary of the generated audio files"""
304
+ # Look for both WAV and TXT files
305
+ wav_files = list(Path(output_dir).glob("*.wav"))
306
+ txt_files = list(Path(output_dir).glob("*.txt"))
307
+
308
+ log_message(f"Found {len(wav_files)} WAV files and {len(txt_files)} TXT files")
309
+
310
+ if len(wav_files) == 0:
311
+ log_message(
312
+ "No WAV files found! This indicates the TTS service requests may be failing.",
313
+ is_error=True,
314
+ )
315
+ log_message(
316
+ "Check the connection to the TTS service and the response status codes above.",
317
+ is_error=True,
318
+ )
319
+
320
+ file_stats = []
321
+ for wav_path in wav_files:
322
+ try:
323
+ with wave.open(str(wav_path), "rb") as wav_file:
324
+ frames = wav_file.getnframes()
325
+ rate = wav_file.getframerate()
326
+ duration = frames / rate
327
+
328
+ # Get corresponding text
329
+ text_path = wav_path.with_suffix(".txt")
330
+ if text_path.exists():
331
+ with open(text_path, "r") as text_file:
332
+ text = text_file.read().strip()
333
+ else:
334
+ text = "N/A"
335
+
336
+ file_stats.append(
337
+ {"filename": wav_path.name, "duration": duration, "text": text}
338
+ )
339
+ except Exception as e:
340
+ log_message(f"Error analyzing {wav_path}: {str(e)}", False, is_error=True)
341
+
342
+ # Print summary table
343
+ if file_stats:
344
+ log_message("\nAudio File Summary:")
345
+ log_message(f"{'Filename':<20}{'Duration':<12}{'Text':<60}")
346
+ log_message("-" * 92)
347
+ for stat in file_stats:
348
+ log_message(
349
+ f"{stat['filename']:<20}{stat['duration']:<12.2f}{stat['text'][:57] + '...' if len(stat['text']) > 60 else stat['text']:<60}"
350
+ )
351
+
352
+ # List missing WAV files where text files exist
353
+ missing_wavs = set(p.stem for p in txt_files) - set(p.stem for p in wav_files)
354
+ if missing_wavs:
355
+ log_message(
356
+ f"\nFound {len(missing_wavs)} text files without corresponding WAV files:",
357
+ is_error=True,
358
+ )
359
+ for stem in sorted(list(missing_wavs))[:10]: # Limit to 10 for readability
360
+ log_message(f" - {stem}.txt (no WAV file)", is_error=True)
361
+ if len(missing_wavs) > 10:
362
+ log_message(f" ... and {len(missing_wavs) - 10} more", is_error=True)
363
+
364
+
365
+ if __name__ == "__main__":
366
+ args = setup_args()
367
+ run_test(args)
368
+ analyze_audio_files(args.output_dir)
369
+
370
+ log_message("\nNext Steps:")
371
+ log_message("1. Listen to the generated audio files")
372
+ log_message("2. Verify if each audio correctly says its ID number")
373
+ log_message(
374
+ "3. Check for any mismatches between the audio content and the text files"
375
+ )
376
+ log_message(
377
+ "4. If mismatches are found, you've successfully reproduced the race condition"
378
+ )
dev/Test copy.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import json
3
+
4
+ import pydub
5
+ import requests
6
+
7
+ text = """Delving into the Abyss: A Deeper Exploration of Meaning in 5 Seconds of Summer's "Jet Black Heart"
8
+
9
+ 5 Seconds of Summer, initially perceived as purveyors of upbeat, radio-friendly pop-punk, embarked on a significant artistic evolution with their album Sounds Good Feels Good. Among its tracks, "Jet Black Heart" stands out as a powerful testament to this shift, moving beyond catchy melodies and embracing a darker, more emotionally complex sound. Released in 2015, the song transcends the typical themes of youthful exuberance and romantic angst, instead plunging into the depths of personal turmoil and the corrosive effects of inner darkness on interpersonal relationships. "Jet Black Heart" is not merely a song about heartbreak; it is a raw and vulnerable exploration of internal struggle, self-destructive patterns, and the precarious flicker of hope that persists even in the face of profound emotional chaos. Through potent metaphors, starkly honest lyrics, and a sonic landscape that mirrors its thematic weight, the song offers a profound meditation on the human condition, grappling with the shadows that reside within us all and their far-reaching consequences.
10
+
11
+ The very title, "Jet Black Heart," immediately establishes the song's central motif: an intrinsic darkness residing within the narrator's emotional core. The phrase "jet black" is not simply a descriptor of color; it evokes a sense of absolute darkness, a void devoid of light, and a profound absence of hope. This is not a heart merely bruised by external circumstances, but one fundamentally shaded by internal struggles, suggesting a chronic condition of emotional pain. The opening lines, "Everybody's got their demons, even wide awake or dreaming," acknowledge the universality of inner conflict, a shared human experience of battling internal anxieties and insecurities. However, the designation of a "jet black heart" elevates this struggle to a more profound and potentially entrenched level. It suggests a darkness that is not fleeting or situational, but rather a deeply ingrained aspect of the narrator's being, casting a long shadow over their life and relationships. This internal darkness is further amplified by the subsequent metaphor, "there's a hurricane underneath it." The imagery of a hurricane is intensely evocative, conjuring images of destructive force, uncontrollable chaos, and overwhelming power. This "hurricane" represents the tumultuous emotions and internal disorder raging beneath the surface of the narrator’s composed exterior. It is a maelstrom of pain, anxiety, and self-doubt that threatens to erupt and engulf everything in its path. Crucially, this internal hurricane is not merely passive suffering; it is actively "trying to keep us apart," revealing the insidious way in which these inner demons sabotage connections and erect formidable barriers to genuine intimacy and meaningful relationships.
12
+
13
+ Expanding on this internal struggle, "Jet Black Heart" delves into the narrator's self-destructive patterns, particularly within the realm of romantic relationships. The lyrics "See a war, I wanna fight it, See a match, I wanna strike it" paint a stark picture of a deeply ingrained tendency towards conflict and destruction. This is not simply a reactive response to external aggression, but rather an active seeking out of discord, a subconscious drive to ignite conflict even in peaceful situations. This behavior can be interpreted as a manifestation of their inner turmoil, a projection of their internal chaos onto their external world. Perhaps the narrator, accustomed to internal strife, unconsciously recreates this turbulence in their relationships, finding a perverse sense of familiarity or even control within the chaos. This destructive impulse is further emphasized by the line "Every fire I've ignited faded to gray." The imagery of fire, initially representing passion, intensity, or perhaps even anger, ultimately devolving into "gray" underscores a recurring cycle of destructive behavior that culminates in emptiness and disappointment. The color gray, often associated with neutrality, lifelessness, and a lack of vibrancy, perfectly encapsulates the emotional aftermath of these self-inflicted relational fires. The initial spark of connection or excitement is inevitably extinguished, leaving behind a landscape of emotional flatness and a profound sense of failure in sustaining meaningful bonds. Further solidifying this theme of self-sabotage is the powerful phrase "I write with a poison pen." This metaphor extends beyond mere hurtful words, encompassing actions, behaviors, and the narrator's overall negative influence on their relationships. The "poison pen" suggests a deliberate, albeit perhaps unconscious, act of inflicting harm, highlighting the narrator's painful awareness of their own damaging tendencies and their capacity to erode the very connections they seemingly desire.
14
+
15
+ However, amidst this pervasive darkness and self-destructive cycle, "Jet Black Heart" subtly introduces a fragile glimmer of hope, a faint light flickering in the abyss. The pivotal moment of vulnerability and potential transformation arrives with the plaintive plea, "But now that I'm broken, now that you're knowing, caught up in a moment, can you see inside?" This is a desperate and profoundly vulnerable call for understanding, a raw and unfiltered exposure of the "jet black heart" after reaching a critical breaking point. The narrator, stripped bare by the weight of their own struggles and the consequences of their self-destructive behavior, finally seeks empathy and genuine connection. The admission of being "broken" is not a declaration of defeat, but rather a necessary precursor to potential healing. It is in this state of vulnerability, in the raw aftermath of emotional collapse, that the narrator dares to ask, "Can you see inside?" This question is laden with yearning, a desperate hope that someone, perhaps a partner in the strained relationship, can perceive beyond the surface darkness and recognize the wounded humanity beneath the "jet black heart." It is a plea for acceptance, not despite the darkness, but perhaps even because of it, a hope that vulnerability will be met not with judgment or rejection, but with compassion and understanding. Despite the acknowledgement of their "poison pen" and destructive tendencies, the narrator also recognizes a paradoxical source of potential redemption within the very relationship that is strained by their inner darkness: "these chemicals moving between us are the reason to start again." The ambiguous term "chemicals" can be interpreted on multiple levels. It could symbolize the complex and often volatile dynamics of human connection, the unpredictable and sometimes turbulent interplay of emotions and personalities in a relationship. Alternatively, "chemicals" might allude to a more literal, perhaps even neurochemical, imbalance within the narrator, suggesting that the very forces driving their darkness might also hold the key to transformation. Crucially, the phrase "reason to start again" emphasizes the potential for renewal and redemption, not a guaranteed outcome. It is a tentative step towards hope, acknowledging that the path forward will be fraught with challenges, but that the possibility of healing and rebuilding remains, however fragile.
16
+
17
+ The concluding verses of "Jet Black Heart" further solidify this nascent theme of potential transformation and tentative redemption. "The blood in my veins is made up of mistakes" is a powerful and profoundly honest admission of past errors and a crucial acceptance of human imperfection. This acknowledgement of fallibility is essential for personal growth and relational healing. By owning their mistakes, the narrator begins to dismantle the cycle of self-blame and self-destruction, paving the way for a more compassionate and forgiving self-perception. The subsequent lines, "let's forget who we are and dive into the dark, as we burst into color, returning to life," present a radical and transformative vision of shared vulnerability and mutual healing. The call to "forget who we are" is not an invitation to erase individual identity, but rather a suggestion to shed the constructed personas, ego-driven defenses, and pre-conceived notions that often hinder genuine connection. It is about stripping away the masks and embracing a state of raw, unfiltered vulnerability. The imperative to "dive into the dark" is perhaps the most challenging and transformative element of the song. It is a call to confront the pain, to face the demons, and to embrace the shared vulnerability that lies at the heart of genuine intimacy. This shared descent into darkness is not an act of succumbing to despair, but rather a courageous journey towards healing, suggesting that true connection and growth can only emerge from acknowledging and confronting the deepest, most painful aspects of ourselves and each other. The subsequent image of "bursting into color, returning to life" provides a powerful counterpoint to the prevailing darkness, symbolizing transformation, healing, and a vibrant renewal of life and connection. "Bursting into color" evokes a sense of vibrancy, joy, and emotional richness that stands in stark contrast to the "jet black" and "gray" imagery prevalent throughout the song. This suggests that by confronting and embracing the darkness, there is a possibility of emerging transformed, experiencing a rebirth and a renewed sense of purpose and joy in life. "Returning to life" further reinforces this idea of resurrection and revitalization, implying that the journey through darkness is not an end in itself, but rather a necessary passage towards a fuller, more authentic, and more vibrant existence.
18
+
19
+ Beyond the lyrical content, the musical elements of "Jet Black Heart" contribute significantly to its overall meaning and emotional impact. Compared to 5 Seconds of Summer's earlier, more upbeat work, "Jet Black Heart" adopts a heavier, more brooding sonic landscape. The driving rhythm, the prominent bassline, and the raw, emotive vocal delivery all mirror the thematic weight of the lyrics, creating an atmosphere of intense emotionality and vulnerability. The song's structure, building from a quiet, introspective beginning to a powerful, anthemic chorus, reflects the narrator's journey from internal struggle to a desperate plea for connection and ultimately a tentative hope for transformation.
20
+
21
+ In conclusion, "Jet Black Heart" by 5 Seconds of Summer is far more than a typical pop song; it is a poignant and deeply resonant exploration of inner darkness, self-destructive tendencies, and the fragile yet persistent hope for human connection and redemption. Through its powerful central metaphor of the "jet black heart," its unflinching portrayal of internal turmoil, and its subtle yet potent message of vulnerability and potential transformation, the song resonates with anyone who has grappled with their own inner demons and the complexities of human relationships. It is a reminder that even in the deepest darkness, a flicker of hope can endure, and that true healing and connection often emerge from the courageous act of confronting and sharing our most vulnerable selves. "Jet Black Heart" stands as a testament to 5 Seconds of Summer's artistic growth, showcasing their capacity to delve into profound emotional territories and create music that is not only catchy and engaging but also deeply meaningful and emotionally resonant, solidifying their position as a band capable of capturing the complexities of the human experience."""
22
+
23
+ """Delving into the Abyss: A Deeper Exploration of Meaning in 5 Seconds of Summer's "Jet Black Heart"
24
+
25
+ 5 Seconds of Summer, initially perceived as purveyors of upbeat, radio-friendly pop-punk, embarked on a significant artistic evolution with their album Sounds Good Feels Good. Among its tracks, "Jet Black Heart" stands out as a powerful testament to this shift, moving beyond catchy melodies and embracing a darker, more emotionally complex sound. Released in 2015, the song transcends the typical themes of youthful exuberance and romantic angst, instead plunging into the depths of personal turmoil and the corrosive effects of inner darkness on interpersonal relationships. "Jet Black Heart" is not merely a song about heartbreak; it is a raw and vulnerable exploration of internal struggle, self-destructive patterns, and the precarious flicker of hope that persists even in the face of profound emotional chaos."""
26
+
27
+
28
+ Type = "wav"
29
+ response = requests.post(
30
+ "http://localhost:8880/dev/captioned_speech",
31
+ json={
32
+ "model": "kokoro",
33
+ "input": text,
34
+ "voice": "af_heart+af_sky",
35
+ "speed": 1.0,
36
+ "response_format": Type,
37
+ "stream": True,
38
+ },
39
+ stream=True,
40
+ )
41
+
42
+ f = open(f"outputstream.{Type}", "wb")
43
+ for chunk in response.iter_lines(decode_unicode=True):
44
+ if chunk:
45
+ temp_json = json.loads(chunk)
46
+ if temp_json["timestamps"] != []:
47
+ chunk_json = temp_json
48
+
49
+ # Decode base 64 stream to bytes
50
+ chunk_audio = base64.b64decode(temp_json["audio"].encode("utf-8"))
51
+
52
+ # Process streaming chunks
53
+ f.write(chunk_audio)
54
+
55
+ # Print word level timestamps
56
+ last_chunks = {
57
+ "start_time": chunk_json["timestamps"][-10]["start_time"],
58
+ "end_time": chunk_json["timestamps"][-3]["end_time"],
59
+ "word": " ".join([X["word"] for X in chunk_json["timestamps"][-10:-3]]),
60
+ }
61
+
62
+ print(f"CUTTING TO {last_chunks['word']}")
63
+
64
+ audioseg = pydub.AudioSegment.from_file(f"outputstream.{Type}", format=Type)
65
+ audioseg = audioseg[last_chunks["start_time"] * 1000 : last_chunks["end_time"] * 1000]
66
+ audioseg.export(f"outputstreamcut.{Type}", format=Type)
67
+
68
+
69
+ """
70
+ response = requests.post(
71
+ "http://localhost:8880/dev/captioned_speech",
72
+ json={
73
+ "model": "kokoro",
74
+ "input": text,
75
+ "voice": "af_heart+af_sky",
76
+ "speed": 1.0,
77
+ "response_format": Type,
78
+ "stream": False,
79
+ },
80
+ stream=True
81
+ )
82
+
83
+ with open(f"outputnostream.{Type}", "wb") as f:
84
+ audio_json=json.loads(response.content)
85
+
86
+ # Decode base 64 stream to bytes
87
+ chunk_audio=base64.b64decode(audio_json["audio"].encode("utf-8"))
88
+
89
+ # Process streaming chunks
90
+ f.write(chunk_audio)
91
+
92
+ # Print word level timestamps
93
+ print(audio_json["timestamps"])
94
+ """
dev/Test money.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import json
3
+
4
+ import requests
5
+
6
+ text = """the administration has offered up a platter of repression for more than a year and is still slated to lose $400 million.
7
+
8
+ Columbia is the largest private landowner in New York City and boasts an endowment of $14.8 billion;"""
9
+
10
+
11
+ Type = "wav"
12
+
13
+ response = requests.post(
14
+ "http://localhost:8880/v1/audio/speech",
15
+ json={
16
+ "model": "kokoro",
17
+ "input": text,
18
+ "voice": "af_heart+af_sky",
19
+ "speed": 1.0,
20
+ "response_format": Type,
21
+ "stream": False,
22
+ },
23
+ stream=True,
24
+ )
25
+
26
+ with open(f"outputnostreammoney.{Type}", "wb") as f:
27
+ f.write(response.content)
dev/Test num.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+
3
+ import inflect
4
+ from text_to_num import text2num
5
+ from torch import mul
6
+
7
+ INFLECT_ENGINE = inflect.engine()
8
+
9
+
10
+ def conditional_int(number: float, threshold: float = 0.00001):
11
+ if abs(round(number) - number) < threshold:
12
+ return int(round(number))
13
+ return number
14
+
15
+
16
+ def handle_money(m: re.Match[str]) -> str:
17
+ """Convert money expressions to spoken form"""
18
+
19
+ bill = "dollar" if m.group(2) == "$" else "pound"
20
+ coin = "cent" if m.group(2) == "$" else "pence"
21
+ number = m.group(3)
22
+
23
+ multiplier = m.group(4)
24
+ try:
25
+ number = float(number)
26
+ except:
27
+ return m.group()
28
+
29
+ if m.group(1) == "-":
30
+ number *= -1
31
+
32
+ if number % 1 == 0 or multiplier != "":
33
+ text_number = f"{INFLECT_ENGINE.number_to_words(conditional_int(number))}{multiplier} {INFLECT_ENGINE.plural(bill, count=number)}"
34
+ else:
35
+ sub_number = int(str(number).split(".")[-1].ljust(2, "0"))
36
+
37
+ text_number = f"{INFLECT_ENGINE.number_to_words(int(round(number)))} {INFLECT_ENGINE.plural(bill, count=number)} and {INFLECT_ENGINE.number_to_words(sub_number)} {INFLECT_ENGINE.plural(coin, count=sub_number)}"
38
+
39
+ return text_number
40
+
41
+
42
+ text = re.sub(
43
+ r"(?i)(-?)([$£])(\d+(?:\.\d+)?)((?: hundred| thousand| (?:[bm]|tr|quadr)illion)*)\b",
44
+ handle_money,
45
+ "he administration has offered up a platter of repression for more than a year and is still slated to lose -$5.3 billion",
46
+ )
47
+ print(text)
dev/Test.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import json
3
+
4
+ import requests
5
+
6
+ text = """Delving into the Abyss: A Deeper Exploration of Meaning in 5 Seconds of Summer's "Jet Black Heart"
7
+
8
+ 5 Seconds of Summer, initially perceived as purveyors of upbeat, radio-friendly pop-punk, embarked on a significant artistic evolution with their album Sounds Good Feels Good. Among its tracks, "Jet Black Heart" stands out as a powerful testament to this shift, moving beyond catchy melodies and embracing a darker, more emotionally complex sound. Released in 2015, the song transcends the typical themes of youthful exuberance and romantic angst, instead plunging into the depths of personal turmoil and the corrosive effects of inner darkness on interpersonal relationships. "Jet Black Heart" is not merely a song about heartbreak; it is a raw and vulnerable exploration of internal struggle, self-destructive patterns, and the precarious flicker of hope that persists even in the face of profound emotional chaos. Through potent metaphors, starkly honest lyrics, and a sonic landscape that mirrors its thematic weight, the song offers a profound meditation on the human condition, grappling with the shadows that reside within us all and their far-reaching consequences.
9
+
10
+ The very title, "Jet Black Heart," immediately establishes the song's central motif: an intrinsic darkness residing within the narrator's emotional core. The phrase "jet black" is not simply a descriptor of color; it evokes a sense of absolute darkness, a void devoid of light, and a profound absence of hope. This is not a heart merely bruised by external circumstances, but one fundamentally shaded by internal struggles, suggesting a chronic condition of emotional pain. The opening lines, "Everybody's got their demons, even wide awake or dreaming," acknowledge the universality of inner conflict, a shared human experience of battling internal anxieties and insecurities. However, the designation of a "jet black heart" elevates this struggle to a more profound and potentially entrenched level. It suggests a darkness that is not fleeting or situational, but rather a deeply ingrained aspect of the narrator's being, casting a long shadow over their life and relationships. This internal darkness is further amplified by the subsequent metaphor, "there's a hurricane underneath it." The imagery of a hurricane is intensely evocative, conjuring images of destructive force, uncontrollable chaos, and overwhelming power. This "hurricane" represents the tumultuous emotions and internal disorder raging beneath the surface of the narrator’s composed exterior. It is a maelstrom of pain, anxiety, and self-doubt that threatens to erupt and engulf everything in its path. Crucially, this internal hurricane is not merely passive suffering; it is actively "trying to keep us apart," revealing the insidious way in which these inner demons sabotage connections and erect formidable barriers to genuine intimacy and meaningful relationships.
11
+
12
+ Expanding on this internal struggle, "Jet Black Heart" delves into the narrator's self-destructive patterns, particularly within the realm of romantic relationships. The lyrics "See a war, I wanna fight it, See a match, I wanna strike it" paint a stark picture of a deeply ingrained tendency towards conflict and destruction. This is not simply a reactive response to external aggression, but rather an active seeking out of discord, a subconscious drive to ignite conflict even in peaceful situations. This behavior can be interpreted as a manifestation of their inner turmoil, a projection of their internal chaos onto their external world. Perhaps the narrator, accustomed to internal strife, unconsciously recreates this turbulence in their relationships, finding a perverse sense of familiarity or even control within the chaos. This destructive impulse is further emphasized by the line "Every fire I've ignited faded to gray." The imagery of fire, initially representing passion, intensity, or perhaps even anger, ultimately devolving into "gray" underscores a recurring cycle of destructive behavior that culminates in emptiness and disappointment. The color gray, often associated with neutrality, lifelessness, and a lack of vibrancy, perfectly encapsulates the emotional aftermath of these self-inflicted relational fires. The initial spark of connection or excitement is inevitably extinguished, leaving behind a landscape of emotional flatness and a profound sense of failure in sustaining meaningful bonds. Further solidifying this theme of self-sabotage is the powerful phrase "I write with a poison pen." This metaphor extends beyond mere hurtful words, encompassing actions, behaviors, and the narrator's overall negative influence on their relationships. The "poison pen" suggests a deliberate, albeit perhaps unconscious, act of inflicting harm, highlighting the narrator's painful awareness of their own damaging tendencies and their capacity to erode the very connections they seemingly desire.
13
+
14
+ However, amidst this pervasive darkness and self-destructive cycle, "Jet Black Heart" subtly introduces a fragile glimmer of hope, a faint light flickering in the abyss. The pivotal moment of vulnerability and potential transformation arrives with the plaintive plea, "But now that I'm broken, now that you're knowing, caught up in a moment, can you see inside?" This is a desperate and profoundly vulnerable call for understanding, a raw and unfiltered exposure of the "jet black heart" after reaching a critical breaking point. The narrator, stripped bare by the weight of their own struggles and the consequences of their self-destructive behavior, finally seeks empathy and genuine connection. The admission of being "broken" is not a declaration of defeat, but rather a necessary precursor to potential healing. It is in this state of vulnerability, in the raw aftermath of emotional collapse, that the narrator dares to ask, "Can you see inside?" This question is laden with yearning, a desperate hope that someone, perhaps a partner in the strained relationship, can perceive beyond the surface darkness and recognize the wounded humanity beneath the "jet black heart." It is a plea for acceptance, not despite the darkness, but perhaps even because of it, a hope that vulnerability will be met not with judgment or rejection, but with compassion and understanding. Despite the acknowledgement of their "poison pen" and destructive tendencies, the narrator also recognizes a paradoxical source of potential redemption within the very relationship that is strained by their inner darkness: "these chemicals moving between us are the reason to start again." The ambiguous term "chemicals" can be interpreted on multiple levels. It could symbolize the complex and often volatile dynamics of human connection, the unpredictable and sometimes turbulent interplay of emotions and personalities in a relationship. Alternatively, "chemicals" might allude to a more literal, perhaps even neurochemical, imbalance within the narrator, suggesting that the very forces driving their darkness might also hold the key to transformation. Crucially, the phrase "reason to start again" emphasizes the potential for renewal and redemption, not a guaranteed outcome. It is a tentative step towards hope, acknowledging that the path forward will be fraught with challenges, but that the possibility of healing and rebuilding remains, however fragile.
15
+
16
+ The concluding verses of "Jet Black Heart" further solidify this nascent theme of potential transformation and tentative redemption. "The blood in my veins is made up of mistakes" is a powerful and profoundly honest admission of past errors and a crucial acceptance of human imperfection. This acknowledgement of fallibility is essential for personal growth and relational healing. By owning their mistakes, the narrator begins to dismantle the cycle of self-blame and self-destruction, paving the way for a more compassionate and forgiving self-perception. The subsequent lines, "let's forget who we are and dive into the dark, as we burst into color, returning to life," present a radical and transformative vision of shared vulnerability and mutual healing. The call to "forget who we are" is not an invitation to erase individual identity, but rather a suggestion to shed the constructed personas, ego-driven defenses, and pre-conceived notions that often hinder genuine connection. It is about stripping away the masks and embracing a state of raw, unfiltered vulnerability. The imperative to "dive into the dark" is perhaps the most challenging and transformative element of the song. It is a call to confront the pain, to face the demons, and to embrace the shared vulnerability that lies at the heart of genuine intimacy. This shared descent into darkness is not an act of succumbing to despair, but rather a courageous journey towards healing, suggesting that true connection and growth can only emerge from acknowledging and confronting the deepest, most painful aspects of ourselves and each other. The subsequent image of "bursting into color, returning to life" provides a powerful counterpoint to the prevailing darkness, symbolizing transformation, healing, and a vibrant renewal of life and connection. "Bursting into color" evokes a sense of vibrancy, joy, and emotional richness that stands in stark contrast to the "jet black" and "gray" imagery prevalent throughout the song. This suggests that by confronting and embracing the darkness, there is a possibility of emerging transformed, experiencing a rebirth and a renewed sense of purpose and joy in life. "Returning to life" further reinforces this idea of resurrection and revitalization, implying that the journey through darkness is not an end in itself, but rather a necessary passage towards a fuller, more authentic, and more vibrant existence.
17
+
18
+ Beyond the lyrical content, the musical elements of "Jet Black Heart" contribute significantly to its overall meaning and emotional impact. Compared to 5 Seconds of Summer's earlier, more upbeat work, "Jet Black Heart" adopts a heavier, more brooding sonic landscape. The driving rhythm, the prominent bassline, and the raw, emotive vocal delivery all mirror the thematic weight of the lyrics, creating an atmosphere of intense emotionality and vulnerability. The song's structure, building from a quiet, introspective beginning to a powerful, anthemic chorus, reflects the narrator's journey from internal struggle to a desperate plea for connection and ultimately a tentative hope for transformation.
19
+
20
+ In conclusion, "Jet Black Heart" by 5 Seconds of Summer is far more than a typical pop song; it is a poignant and deeply resonant exploration of inner darkness, self-destructive tendencies, and the fragile yet persistent hope for human connection and redemption. Through its powerful central metaphor of the "jet black heart," its unflinching portrayal of internal turmoil, and its subtle yet potent message of vulnerability and potential transformation, the song resonates with anyone who has grappled with their own inner demons and the complexities of human relationships. It is a reminder that even in the deepest darkness, a flicker of hope can endure, and that true healing and connection often emerge from the courageous act of confronting and sharing our most vulnerable selves. "Jet Black Heart" stands as a testament to 5 Seconds of Summer's artistic growth, showcasing their capacity to delve into profound emotional territories and create music that is not only catchy and engaging but also deeply meaningful and emotionally resonant, solidifying their position as a band capable of capturing the complexities of the human experience."""
21
+
22
+ text = """Delving into the Abyss: A Deeper Exploration of Meaning in 5 Seconds of Summer's "Jet Black Heart"
23
+
24
+ 5 Seconds of Summer, initially perceived as purveyors of upbeat, radio-friendly pop-punk, embarked on a significant artistic evolution with their album Sounds Good Feels Good. Among its tracks, "Jet Black Heart" stands out as a powerful testament to this shift, moving beyond catchy melodies and embracing a darker, more emotionally complex sound. Released in 2015, the song transcends the typical themes of youthful exuberance and romantic angst, instead plunging into the depths of personal turmoil and the corrosive effects of inner darkness on interpersonal relationships. "Jet Black Heart" is not merely a song about heartbreak; it is a raw and vulnerable exploration of internal struggle, self-destructive patterns, and the precarious flicker of hope that persists even in the face of profound emotional chaos."""
25
+
26
+
27
+ Type = "wav"
28
+
29
+
30
+ response = requests.post(
31
+ "http://localhost:8880/v1/audio/speech",
32
+ json={
33
+ "model": "kokoro",
34
+ "input": text,
35
+ "voice": "af_heart+af_sky",
36
+ "speed": 1.0,
37
+ "response_format": Type,
38
+ "stream": True,
39
+ },
40
+ stream=True,
41
+ )
42
+
43
+
44
+ f = open(f"outputstream.{Type}", "wb")
45
+ for chunk in response.iter_content():
46
+ if chunk:
47
+ # Process streaming chunks
48
+ f.write(chunk)
49
+
50
+ response = requests.post(
51
+ "http://localhost:8880/v1/audio/speech",
52
+ json={
53
+ "model": "kokoro",
54
+ "input": text,
55
+ "voice": "af_heart+af_sky",
56
+ "speed": 1.0,
57
+ "response_format": Type,
58
+ "stream": False,
59
+ },
60
+ stream=True,
61
+ )
62
+
63
+ with open(f"outputnostream.{Type}", "wb") as f:
64
+ f.write(response.content)
start-cpu.ps1 ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ $env:PHONEMIZER_ESPEAK_LIBRARY="C:\Program Files\eSpeak NG\libespeak-ng.dll"
2
+ $env:PYTHONUTF8=1
3
+ $Env:PROJECT_ROOT="$pwd"
4
+ $Env:USE_GPU="false"
5
+ $Env:USE_ONNX="false"
6
+ $Env:PYTHONPATH="$Env:PROJECT_ROOT;$Env:PROJECT_ROOT/api"
7
+ $Env:MODEL_DIR="src/models"
8
+ $Env:VOICES_DIR="src/voices/v1_0"
9
+ $Env:WEB_PLAYER_PATH="$Env:PROJECT_ROOT/web"
10
+
11
+ uv pip install -e ".[cpu]"
12
+ uv run --no-sync python docker/scripts/download_model.py --output api/src/models/v1_0
13
+ uv run --no-sync uvicorn api.src.main:app --host 0.0.0.0 --port 8880
start-cpu.sh ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Get project root directory
4
+ PROJECT_ROOT=$(pwd)
5
+
6
+ # Set environment variables
7
+ export USE_GPU=false
8
+ export USE_ONNX=false
9
+ export PYTHONPATH=$PROJECT_ROOT:$PROJECT_ROOT/api
10
+ export MODEL_DIR=src/models
11
+ export VOICES_DIR=src/voices/v1_0
12
+ export WEB_PLAYER_PATH=$PROJECT_ROOT/web
13
+ # Set the espeak-ng data path to your location
14
+ export ESPEAK_DATA_PATH=/usr/lib/x86_64-linux-gnu/espeak-ng-data
15
+
16
+ # Run FastAPI with CPU extras using uv run
17
+ # Note: espeak may still require manual installation,
18
+ uv pip install -e ".[cpu]"
19
+ uv run --no-sync python docker/scripts/download_model.py --output api/src/models/v1_0
20
+
21
+ # Apply the misaki patch to fix possible EspeakWrapper issue in older versions
22
+ # echo "Applying misaki patch..."
23
+ # python scripts/fix_misaki.py
24
+
25
+ # Start the server
26
+ uv run --no-sync uvicorn api.src.main:app --host 0.0.0.0 --port 8880
start-gpu.ps1 ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ $env:PHONEMIZER_ESPEAK_LIBRARY="C:\Program Files\eSpeak NG\libespeak-ng.dll"
2
+ $env:PYTHONUTF8=1
3
+ $Env:PROJECT_ROOT="$pwd"
4
+ $Env:USE_GPU="true"
5
+ $Env:USE_ONNX="false"
6
+ $Env:PYTHONPATH="$Env:PROJECT_ROOT;$Env:PROJECT_ROOT/api"
7
+ $Env:MODEL_DIR="src/models"
8
+ $Env:VOICES_DIR="src/voices/v1_0"
9
+ $Env:WEB_PLAYER_PATH="$Env:PROJECT_ROOT/web"
10
+
11
+ uv pip install -e ".[gpu]"
12
+ uv run --no-sync python docker/scripts/download_model.py --output api/src/models/v1_0
13
+ uv run --no-sync uvicorn api.src.main:app --host 0.0.0.0 --port 8880
start-gpu.sh ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Get project root directory
4
+ PROJECT_ROOT=$(pwd)
5
+
6
+ # Set environment variables
7
+ export USE_GPU=true
8
+ export USE_ONNX=false
9
+ export PYTHONPATH=$PROJECT_ROOT:$PROJECT_ROOT/api
10
+ export MODEL_DIR=src/models
11
+ export VOICES_DIR=src/voices/v1_0
12
+ export WEB_PLAYER_PATH=$PROJECT_ROOT/web
13
+
14
+ # Run FastAPI with GPU extras using uv run
15
+ # Note: espeak may still require manual installation,
16
+ uv pip install -e ".[gpu]"
17
+ uv run --no-sync python docker/scripts/download_model.py --output api/src/models/v1_0
18
+ uv run --no-sync uvicorn api.src.main:app --host 0.0.0.0 --port 8880
start-gpu_mac.sh ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Get project root directory
4
+ PROJECT_ROOT=$(pwd)
5
+
6
+ # Set other environment variables
7
+ export USE_GPU=true
8
+ export USE_ONNX=false
9
+ export PYTHONPATH=$PROJECT_ROOT:$PROJECT_ROOT/api
10
+ export MODEL_DIR=src/models
11
+ export VOICES_DIR=src/voices/v1_0
12
+ export WEB_PLAYER_PATH=$PROJECT_ROOT/web
13
+
14
+ export DEVICE_TYPE=mps
15
+ # Enable MPS fallback for unsupported operations
16
+ export PYTORCH_ENABLE_MPS_FALLBACK=1
17
+
18
+ # Run FastAPI with GPU extras using uv run
19
+ uv pip install -e .
20
+ uv run --no-sync python docker/scripts/download_model.py --output api/src/models/v1_0
21
+ uv run --no-sync uvicorn api.src.main:app --host 0.0.0.0 --port 8880