gagndeep commited on
Commit
d1b390e
·
verified ·
1 Parent(s): 9767f98

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +623 -19
index.html CHANGED
@@ -1,19 +1,623 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>VibeVoice Realtime 0.5B - Voice Conversion</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ :root {
11
+ --primary: #ff4785;
12
+ --primary-dark: #ff2d75;
13
+ --secondary: #1e1e1e;
14
+ --secondary-light: #2d2d2d;
15
+ --accent: #00d4aa;
16
+ --text: #f8f9fa;
17
+ --text-secondary: #adb5bd;
18
+ }
19
+
20
+ body {
21
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
22
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
23
+ min-height: 100vh;
24
+ }
25
+
26
+ .gradient-border {
27
+ background: linear-gradient(45deg, var(--primary), var(--accent));
28
+ padding: 3px;
29
+ border-radius: 12px;
30
+ }
31
+
32
+ .gradient-border-inner {
33
+ background: var(--secondary);
34
+ border-radius: 9px;
35
+ height: 100%;
36
+ }
37
+
38
+ .btn-primary {
39
+ background: linear-gradient(45deg, var(--primary), var(--primary-dark));
40
+ transition: all 0.3s ease;
41
+ }
42
+
43
+ .btn-primary:hover {
44
+ transform: translateY(-2px);
45
+ box-shadow: 0 10px 20px rgba(255, 71, 133, 0.3);
46
+ }
47
+
48
+ .audio-visualizer {
49
+ display: flex;
50
+ align-items: center;
51
+ justify-content: center;
52
+ height: 100px;
53
+ background: rgba(255, 255, 255, 0.05);
54
+ border-radius: 8px;
55
+ margin: 15px 0;
56
+ }
57
+
58
+ .bar {
59
+ background: linear-gradient(to top, var(--accent), var(--primary));
60
+ width: 4px;
61
+ height: 20px;
62
+ margin: 0 2px;
63
+ border-radius: 2px;
64
+ animation: equalize 1.5s infinite alternate;
65
+ }
66
+
67
+ @keyframes equalize {
68
+ 0% { height: 10px; }
69
+ 25% { height: 30px; }
70
+ 50% { height: 15px; }
71
+ 75% { height: 25px; }
72
+ 100% { height: 20px; }
73
+ }
74
+
75
+ .voice-preset {
76
+ transition: all 0.3s ease;
77
+ cursor: pointer;
78
+ }
79
+
80
+ .voice-preset:hover {
81
+ transform: scale(1.05);
82
+ background: rgba(255, 255, 255, 0.1);
83
+ }
84
+
85
+ .voice-preset.active {
86
+ border: 2px solid var(--accent);
87
+ background: rgba(0, 212, 170, 0.2);
88
+ }
89
+
90
+ .loading-spinner {
91
+ width: 40px;
92
+ height: 40px;
93
+ border: 4px solid rgba(255, 255, 255, 0.1);
94
+ border-radius: 50%;
95
+ border-top-color: var(--accent);
96
+ animation: spin 1s ease-in-out infinite;
97
+ }
98
+
99
+ @keyframes spin {
100
+ to { transform: rotate(360deg); }
101
+ }
102
+
103
+ .toggle-switch {
104
+ position: relative;
105
+ display: inline-block;
106
+ width: 60px;
107
+ height: 30px;
108
+ }
109
+
110
+ .toggle-switch input {
111
+ opacity: 0;
112
+ width: 0;
113
+ height: 0;
114
+ }
115
+
116
+ .slider {
117
+ position: absolute;
118
+ cursor: pointer;
119
+ top: 0;
120
+ left: 0;
121
+ right: 0;
122
+ bottom: 0;
123
+ background-color: #ccc;
124
+ transition: .4s;
125
+ border-radius: 30px;
126
+ }
127
+
128
+ .slider:before {
129
+ position: absolute;
130
+ content: "";
131
+ height: 22px;
132
+ width: 22px;
133
+ left: 4px;
134
+ bottom: 4px;
135
+ background-color: white;
136
+ transition: .4s;
137
+ border-radius: 50%;
138
+ }
139
+
140
+ input:checked + .slider {
141
+ background-color: var(--accent);
142
+ }
143
+
144
+ input:checked + .slider:before {
145
+ transform: translateX(30px);
146
+ }
147
+
148
+ .param-slider {
149
+ -webkit-appearance: none;
150
+ width: 100%;
151
+ height: 6px;
152
+ border-radius: 3px;
153
+ background: rgba(255, 255, 255, 0.2);
154
+ outline: none;
155
+ }
156
+
157
+ .param-slider::-webkit-slider-thumb {
158
+ -webkit-appearance: none;
159
+ appearance: none;
160
+ width: 18px;
161
+ height: 18px;
162
+ border-radius: 50%;
163
+ background: var(--accent);
164
+ cursor: pointer;
165
+ }
166
+
167
+ .param-slider::-moz-range-thumb {
168
+ width: 18px;
169
+ height: 18px;
170
+ border-radius: 50%;
171
+ background: var(--accent);
172
+ cursor: pointer;
173
+ }
174
+
175
+ .output-waveform {
176
+ background: rgba(255, 255, 255, 0.05);
177
+ border-radius: 8px;
178
+ height: 120px;
179
+ position: relative;
180
+ overflow: hidden;
181
+ }
182
+
183
+ .waveform-line {
184
+ position: absolute;
185
+ bottom: 0;
186
+ width: 100%;
187
+ height: 0;
188
+ background: linear-gradient(to right, var(--primary), var(--accent));
189
+ transition: height 0.1s ease;
190
+ }
191
+
192
+ @media (max-width: 768px) {
193
+ .gradient-border {
194
+ padding: 2px;
195
+ border-radius: 8px;
196
+ }
197
+
198
+ .gradient-border-inner {
199
+ border-radius: 6px;
200
+ }
201
+
202
+ .voice-presets-grid {
203
+ grid-template-columns: repeat(2, 1fr);
204
+ }
205
+ }
206
+ </style>
207
+ </head>
208
+ <body class="text-white">
209
+ <!-- Header -->
210
+ <header class="p-4 border-b border-gray-700">
211
+ <div class="max-w-6xl mx-auto flex justify-between items-center">
212
+ <div class="flex items-center space-x-2">
213
+ <i class="fas fa-microphone-alt text-2xl text-accent"></i>
214
+ <h1 class="text-xl font-bold">VibeVoice Realtime 0.5B</h1>
215
+ </div>
216
+ <div class="text-sm text-text-secondary">
217
+ Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" class="text-accent hover:underline">anycoder</a>
218
+ </div>
219
+ </div>
220
+ </header>
221
+
222
+ <!-- Main Content -->
223
+ <main class="max-w-6xl mx-auto p-4">
224
+ <div class="gradient-border mb-6">
225
+ <div class="gradient-border-inner p-6">
226
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
227
+ <!-- Input Section -->
228
+ <div class="lg:col-span-2">
229
+ <div class="bg-secondary-light rounded-lg p-6 mb-6">
230
+ <h2 class="text-lg font-semibold mb-4 flex items-center">
231
+ <i class="fas fa-upload mr-2"></i>
232
+ Input Audio
233
+ </h2>
234
+
235
+ <div class="audio-upload-area border-2 border-dashed border-gray-600 rounded-lg p-8 text-center cursor-pointer hover:border-accent transition-colors">
236
+ <i class="fas fa-cloud-upload-alt text-4xl text-gray-400 mb-4"></i>
237
+ <p class="text-text-secondary mb-2">Click to upload or drag and drop</p>
238
+ <p class="text-sm text-gray-500">MP3, WAV, OGG (Max 10MB)</p>
239
+ <input type="file" id="audioUpload" accept="audio/*" class="hidden">
240
+ </div>
241
+
242
+ <div class="audio-visualizer mt-6" id="inputVisualizer">
243
+ <!-- Bars will be generated by JavaScript -->
244
+ </div>
245
+
246
+ <div class="flex justify-center mt-4">
247
+ <button class="btn-primary px-6 py-2 rounded-lg font-medium flex items-center" id="recordBtn">
248
+ <i class="fas fa-microphone mr-2"></i>
249
+ Record Voice
250
+ </button>
251
+ </div>
252
+ </div>
253
+
254
+ <!-- Voice Presets -->
255
+ <div class="bg-secondary-light rounded-lg p-6">
256
+ <h2 class="text-lg font-semibold mb-4 flex items-center">
257
+ <i class="fas fa-user-circle mr-2"></i>
258
+ Voice Presets
259
+ </h2>
260
+
261
+ <div class="voice-presets-grid grid grid-cols-3 gap-4 mb-6">
262
+ <div class="voice-preset active p-4 rounded-lg border border-transparent text-center" data-voice="male-1">
263
+ <i class="fas fa-male text-3xl text-accent mb-2"></i>
264
+ <p class="font-medium">Male 1</p>
265
+ </div>
266
+ <div class="voice-preset p-4 rounded-lg border border-transparent text-center" data-voice="female-1">
267
+ <i class="fas fa-female text-3xl text-primary mb-2"></i>
268
+ <p class="font-medium">Female 1</p>
269
+ </div>
270
+ <div class="voice-preset p-4 rounded-lg border border-transparent text-center" data-voice="child">
271
+ <i class="fas fa-child text-3xl text-yellow-400 mb-2"></i>
272
+ <p class="font-medium">Child</p>
273
+ </div>
274
+ <div class="voice-preset p-4 rounded-lg border border-transparent text-center" data-voice="male-2">
275
+ <i class="fas fa-male text-3xl text-blue-400 mb-2"></i>
276
+ <p class="font-medium">Male 2</p>
277
+ </div>
278
+ <div class="voice-preset p-4 rounded-lg border border-transparent text-center" data-voice="female-2">
279
+ <i class="fas fa-female text-3xl text-purple-400 mb-2"></i>
280
+ <p class="font-medium">Female 2</p>
281
+ </div>
282
+ <div class="voice-preset p-4 rounded-lg border border-transparent text-center" data-voice="robot">
283
+ <i class="fas fa-robot text-3xl text-green-400 mb-2"></i>
284
+ <p class="font-medium">Robot</p>
285
+ </div>
286
+ </div>
287
+
288
+ <div class="mb-4">
289
+ <label class="block text-sm font-medium mb-2">Custom Voice Reference</label>
290
+ <input type="file" accept="audio/*" class="w-full p-2 bg-secondary rounded border border-gray-600">
291
+ </div>
292
+ </div>
293
+ </div>
294
+
295
+ <!-- Parameters & Output Section -->
296
+ <div class="lg:col-span-1">
297
+ <div class="bg-secondary-light rounded-lg p-6 mb-6">
298
+ <h2 class="text-lg font-semibold mb-4 flex items-center">
299
+ <i class="fas fa-sliders-h mr-2"></i>
300
+ Conversion Parameters
301
+ </h2>
302
+
303
+ <div class="space-y-6">
304
+ <div>
305
+ <label class="block text-sm font-medium mb-2">Pitch Adjustment</label>
306
+ <input type="range" class="param-slider" min="-12" max="12" value="0" id="pitchSlider">
307
+ <div class="flex justify-between text-sm text-text-secondary mt-1">
308
+ <span>-12</span>
309
+ <span>0</span>
310
+ <span>+12</span>
311
+ </div>
312
+ </div>
313
+
314
+ <div>
315
+ <label class="block text-sm font-medium mb-2">Speed</label>
316
+ <input type="range" class="param-slider" min="0.5" max="2" step="0.1" value="1" id="speedSlider">
317
+ <div class="flex justify-between text-sm text-text-secondary mt-1">
318
+ <span>0.5x</span>
319
+ <span>1x</span>
320
+ <span>2x</span>
321
+ </div>
322
+ </div>
323
+
324
+ <div>
325
+ <label class="block text-sm font-medium mb-2">Emotion Intensity</label>
326
+ <input type="range" class="param-slider" min="0" max="100" value="50" id="emotionSlider">
327
+ <div class="flex justify-between text-sm text-text-secondary mt-1">
328
+ <span>Neutral</span>
329
+ <span>Intense</span>
330
+ </div>
331
+ </div>
332
+
333
+ <div class="flex items-center justify-between">
334
+ <label class="text-sm font-medium">Real-time Processing</label>
335
+ <label class="toggle-switch">
336
+ <input type="checkbox" id="realtimeToggle">
337
+ <span class="slider"></span>
338
+ </label>
339
+ </div>
340
+
341
+ <div class="flex items-center justify-between">
342
+ <label class="text-sm font-medium">Noise Reduction</label>
343
+ <label class="toggle-switch">
344
+ <input type="checkbox" id="noiseReductionToggle" checked>
345
+ <span class="slider"></span>
346
+ </label>
347
+ </div>
348
+ </div>
349
+ </div>
350
+
351
+ <!-- Output Section -->
352
+ <div class="bg-secondary-light rounded-lg p-6">
353
+ <h2 class="text-lg font-semibold mb-4 flex items-center">
354
+ <i class="fas fa-volume-up mr-2"></i>
355
+ Output
356
+ </h2>
357
+
358
+ <div class="output-waveform mb-4" id="outputWaveform">
359
+ <div class="waveform-line" id="waveformLine"></div>
360
+ </div>
361
+
362
+ <div class="flex justify-center mb-4">
363
+ <button class="btn-primary px-8 py-3 rounded-lg font-medium flex items-center" id="convertBtn">
364
+ <i class="fas fa-magic mr-2"></i>
365
+ Convert Voice
366
+ </button>
367
+ </div>
368
+
369
+ <div class="flex justify-center space-x-4">
370
+ <button class="bg-gray-600 hover:bg-gray-500 px-4 py-2 rounded-lg flex items-center transition-colors" id="playBtn" disabled>
371
+ <i class="fas fa-play mr-2"></i>
372
+ Play
373
+ </button>
374
+ <button class="bg-gray-600 hover:bg-gray-500 px-4 py-2 rounded-lg flex items-center transition-colors" id="downloadBtn" disabled>
375
+ <i class="fas fa-download mr-2"></i>
376
+ Download
377
+ </button>
378
+ </div>
379
+
380
+ <div class="mt-4 text-center" id="statusMessage">
381
+ <p class="text-text-secondary text-sm">Ready to convert</p>
382
+ </div>
383
+ </div>
384
+ </div>
385
+ </div>
386
+ </div>
387
+ </div>
388
+ </main>
389
+
390
+ <!-- Footer -->
391
+ <footer class="p-4 border-t border-gray-700 text-center text-text-secondary text-sm">
392
+ <p>VibeVoice Realtime 0.5B - Advanced Voice Conversion Model</p>
393
+ <p class="mt-1">This is a simulation of the Hugging Face Space interface</p>
394
+ </footer>
395
+
396
+ <script>
397
+ // DOM Elements
398
+ const audioUpload = document.getElementById('audioUpload');
399
+ const audioUploadArea = document.querySelector('.audio-upload-area');
400
+ const recordBtn = document.getElementById('recordBtn');
401
+ const convertBtn = document.getElementById('convertBtn');
402
+ const playBtn = document.getElementById('playBtn');
403
+ const downloadBtn = document.getElementById('downloadBtn');
404
+ const statusMessage = document.getElementById('statusMessage');
405
+ const inputVisualizer = document.getElementById('inputVisualizer');
406
+ const outputWaveform = document.getElementById('outputWaveform');
407
+ const waveformLine = document.getElementById('waveformLine');
408
+ const voicePresets = document.querySelectorAll('.voice-preset');
409
+ const pitchSlider = document.getElementById('pitchSlider');
410
+ const speedSlider = document.getElementById('speedSlider');
411
+ const emotionSlider = document.getElementById('emotionSlider');
412
+ const realtimeToggle = document.getElementById('realtimeToggle');
413
+ const noiseReductionToggle = document.getElementById('noiseReductionToggle');
414
+
415
+ // State
416
+ let isRecording = false;
417
+ let audioContext;
418
+ let mediaRecorder;
419
+ let audioChunks = [];
420
+ let audioBlob;
421
+ let audioUrl;
422
+ let isProcessing = false;
423
+
424
+ // Initialize
425
+ document.addEventListener('DOMContentLoaded', () => {
426
+ // Set up event listeners
427
+ audioUploadArea.addEventListener('click', () => audioUpload.click());
428
+ audioUpload.addEventListener('change', handleAudioUpload);
429
+ recordBtn.addEventListener('click', toggleRecording);
430
+ convertBtn.addEventListener('click', convertVoice);
431
+ playBtn.addEventListener('click', playAudio);
432
+ downloadBtn.addEventListener('click', downloadAudio);
433
+
434
+ // Voice preset selection
435
+ voicePresets.forEach(preset => {
436
+ preset.addEventListener('click', () => {
437
+ voicePresets.forEach(p => p.classList.remove('active'));
438
+ preset.classList.add('active');
439
+ });
440
+ });
441
+
442
+ // Generate visualizer bars
443
+ generateVisualizerBars();
444
+
445
+ // Initialize audio context
446
+ audioContext = new (window.AudioContext || window.webkitAudioContext)();
447
+ });
448
+
449
+ // Generate visualizer bars
450
+ function generateVisualizerBars() {
451
+ for (let i = 0; i < 20; i++) {
452
+ const bar = document.createElement('div');
453
+ bar.className = 'bar';
454
+ bar.style.animationDelay = `${i * 0.05}s`;
455
+ inputVisualizer.appendChild(bar);
456
+ }
457
+ }
458
+
459
+ // Handle audio upload
460
+ function handleAudioUpload(e) {
461
+ const file = e.target.files[0];
462
+ if (!file) return;
463
+
464
+ if (file.size > 10 * 1024 * 1024) {
465
+ alert('File size exceeds 10MB limit');
466
+ return;
467
+ }
468
+
469
+ audioBlob = file;
470
+ audioUrl = URL.createObjectURL(file);
471
+ updateStatus('Audio uploaded successfully');
472
+ enableConvertButton();
473
+ }
474
+
475
+ // Toggle recording
476
+ function toggleRecording() {
477
+ if (isRecording) {
478
+ stopRecording();
479
+ } else {
480
+ startRecording();
481
+ }
482
+ }
483
+
484
+ // Start recording
485
+ function startRecording() {
486
+ navigator.mediaDevices.getUserMedia({ audio: true })
487
+ .then(stream => {
488
+ mediaRecorder = new MediaRecorder(stream);
489
+ audioChunks = [];
490
+
491
+ mediaRecorder.ondataavailable = e => {
492
+ audioChunks.push(e.data);
493
+ };
494
+
495
+ mediaRecorder.onstop = () => {
496
+ audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
497
+ audioUrl = URL.createObjectURL(audioBlob);
498
+ updateStatus('Recording saved');
499
+ enableConvertButton();
500
+ };
501
+
502
+ mediaRecorder.start();
503
+ isRecording = true;
504
+ recordBtn.innerHTML = '<i class="fas fa-stop mr-2"></i> Stop Recording';
505
+ recordBtn.classList.add('bg-red-500');
506
+ recordBtn.classList.remove('btn-primary');
507
+ updateStatus('Recording...');
508
+ })
509
+ .catch(err => {
510
+ console.error('Error accessing microphone:', err);
511
+ updateStatus('Microphone access denied');
512
+ });
513
+ }
514
+
515
+ // Stop recording
516
+ function stopRecording() {
517
+ if (mediaRecorder && isRecording) {
518
+ mediaRecorder.stop();
519
+ mediaRecorder.stream.getTracks().forEach(track => track.stop());
520
+ isRecording = false;
521
+ recordBtn.innerHTML = '<i class="fas fa-microphone mr-2"></i> Record Voice';
522
+ recordBtn.classList.remove('bg-red-500');
523
+ recordBtn.classList.add('btn-primary');
524
+ }
525
+ }
526
+
527
+ // Enable convert button
528
+ function enableConvertButton() {
529
+ convertBtn.disabled = false;
530
+ convertBtn.classList.remove('opacity-50', 'cursor-not-allowed');
531
+ }
532
+
533
+ // Convert voice (simulated)
534
+ function convertVoice() {
535
+ if (!audioBlob || isProcessing) return;
536
+
537
+ isProcessing = true;
538
+ updateStatus('Processing voice conversion...');
539
+
540
+ // Disable buttons during processing
541
+ convertBtn.disabled = true;
542
+ playBtn.disabled = true;
543
+ downloadBtn.disabled = true;
544
+
545
+ // Simulate processing
546
+ setTimeout(() => {
547
+ // Generate random waveform for output
548
+ animateWaveform();
549
+
550
+ // Enable play and download buttons
551
+ playBtn.disabled = false;
552
+ downloadBtn.disabled = false;
553
+
554
+ updateStatus('Conversion complete!');
555
+ isProcessing = false;
556
+ convertBtn.disabled = false;
557
+ }, 3000);
558
+ }
559
+
560
+ // Animate waveform
561
+ function animateWaveform() {
562
+ let height = 0;
563
+ const interval = setInterval(() => {
564
+ height = Math.random() * 80 + 20;
565
+ waveformLine.style.height = `${height}%`;
566
+
567
+ if (height >= 90) {
568
+ clearInterval(interval);
569
+ }
570
+ }, 100);
571
+ }
572
+
573
+ // Play audio
574
+ function playAudio() {
575
+ if (!audioUrl) return;
576
+
577
+ const audio = new Audio(audioUrl);
578
+ audio.play();
579
+ updateStatus('Playing converted audio');
580
+ }
581
+
582
+ // Download audio
583
+ function downloadAudio() {
584
+ if (!audioBlob) return;
585
+
586
+ const url = URL.createObjectURL(audioBlob);
587
+ const a = document.createElement('a');
588
+ a.href = url;
589
+ a.download = 'converted-voice.wav';
590
+ document.body.appendChild(a);
591
+ a.click();
592
+ document.body.removeChild(a);
593
+ URL.revokeObjectURL(url);
594
+ updateStatus('Download started');
595
+ }
596
+
597
+ // Update status message
598
+ function updateStatus(message) {
599
+ statusMessage.innerHTML = `<i class="fas fa-info-circle mr-1"></i> ${message}`;
600
+ }
601
+
602
+ // Handle drag and drop
603
+ audioUploadArea.addEventListener('dragover', (e) => {
604
+ e.preventDefault();
605
+ audioUploadArea.classList.add('border-accent');
606
+ });
607
+
608
+ audioUploadArea.addEventListener('dragleave', () => {
609
+ audioUploadArea.classList.remove('border-accent');
610
+ });
611
+
612
+ audioUploadArea.addEventListener('drop', (e) => {
613
+ e.preventDefault();
614
+ audioUploadArea.classList.remove('border-accent');
615
+
616
+ if (e.dataTransfer.files.length) {
617
+ audioUpload.files = e.dataTransfer.files;
618
+ handleAudioUpload({ target: audioUpload });
619
+ }
620
+ });
621
+ </script>
622
+ </body>
623
+ </html>