akhaliq HF Staff commited on
Commit
b9eca36
·
verified ·
1 Parent(s): 0fb15b9

Upload index.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. index.js +166 -62
index.js CHANGED
@@ -1,76 +1,180 @@
1
- import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.7.6';
2
-
3
- // Reference the elements that we will need
4
- const status = document.getElementById('status');
5
- const fileUpload = document.getElementById('upload');
6
- const imageContainer = document.getElementById('container');
7
- const example = document.getElementById('example');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
- const EXAMPLE_URL = 'https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/city-streets.jpg';
10
 
11
- // Create a new object detection pipeline
12
- status.textContent = 'Loading model...';
13
- const detector = await pipeline('object-detection', 'Xenova/detr-resnet-50');
14
- status.textContent = 'Ready';
 
 
 
 
 
15
 
16
- example.addEventListener('click', (e) => {
17
- e.preventDefault();
18
- detect(EXAMPLE_URL);
19
  });
20
 
21
- fileUpload.addEventListener('change', function (e) {
22
- const file = e.target.files[0];
23
- if (!file) {
24
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  }
 
26
 
27
- const reader = new FileReader();
28
-
29
- // Set up a callback when the file is loaded
30
- reader.onload = e2 => detect(e2.target.result);
 
31
 
32
- reader.readAsDataURL(file);
33
- });
 
 
 
34
 
 
 
 
 
35
 
36
- // Detect objects in the image
37
- async function detect(img) {
38
- imageContainer.innerHTML = '';
39
- imageContainer.style.backgroundImage = `url(${img})`;
 
 
40
 
41
- status.textContent = 'Analysing...';
42
- const output = await detector(img, {
43
- threshold: 0.5,
44
- percentage: true,
45
- });
46
- status.textContent = '';
47
- output.forEach(renderBox);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
 
50
- // Render a bounding box and label on the image
51
- function renderBox({ box, label }) {
52
- const { xmax, xmin, ymax, ymin } = box;
53
-
54
- // Generate a random color for the box
55
- const color = '#' + Math.floor(Math.random() * 0xFFFFFF).toString(16).padStart(6, 0);
56
-
57
- // Draw the box
58
- const boxElement = document.createElement('div');
59
- boxElement.className = 'bounding-box';
60
- Object.assign(boxElement.style, {
61
- borderColor: color,
62
- left: 100 * xmin + '%',
63
- top: 100 * ymin + '%',
64
- width: 100 * (xmax - xmin) + '%',
65
- height: 100 * (ymax - ymin) + '%',
66
- })
67
-
68
- // Draw label
69
- const labelElement = document.createElement('span');
70
- labelElement.textContent = label;
71
- labelElement.className = 'bounding-box-label';
72
- labelElement.style.backgroundColor = color;
73
-
74
- boxElement.appendChild(labelElement);
75
- imageContainer.appendChild(boxElement);
76
  }
 
 
 
 
 
 
 
 
 
1
+ // Configuration
2
+ const MODEL_ID = 'onnx-community/Supertonic-TTS-ONNX';
3
+ const VOICE_BASE_URL = 'https://huggingface.co/onnx-community/Supertonic-TTS-ONNX/resolve/main/voices/';
4
+
5
+ // DOM Elements
6
+ const generateBtn = document.getElementById('generate-btn');
7
+ const inputText = document.getElementById('input-text');
8
+ const voiceSelect = document.getElementById('voice-select');
9
+ const gpuToggle = document.getElementById('gpu-toggle');
10
+ const deviceLabel = document.getElementById('device-label');
11
+ const statusContainer = document.getElementById('status-container');
12
+ const statusText = document.getElementById('status-text');
13
+ const progressBar = document.getElementById('progress-bar');
14
+ const outputCard = document.getElementById('output-card');
15
+ const audioPlayer = document.getElementById('audio-player');
16
+ const downloadLink = document.getElementById('download-link');
17
+ const errorMsg = document.getElementById('error-msg');
18
+
19
+ // State
20
+ let ttsPipeline = null;
21
+ let currentDevice = 'cpu';
22
+
23
+ // Helper: Check WebGPU support
24
+ async function checkWebGPU() {
25
+ if (!navigator.gpu) {
26
+ gpuToggle.disabled = true;
27
+ deviceLabel.innerText = "WebGPU not supported (CPU only)";
28
+ return false;
29
+ }
30
+ return true;
31
+ }
32
 
33
+ checkWebGPU();
34
 
35
+ // UI Event Listeners
36
+ gpuToggle.addEventListener('change', (e) => {
37
+ const useGPU = e.target.checked;
38
+ currentDevice = useGPU ? 'webgpu' : 'cpu';
39
+ deviceLabel.innerText = useGPU ? 'Run on WebGPU' : 'Run on CPU';
40
+
41
+ // Reset pipeline to force reload with new device setting next time
42
+ ttsPipeline = null;
43
+ });
44
 
45
+ inputText.addEventListener('input', () => {
46
+ document.querySelector('.char-count').innerText = `${inputText.value.length} / 500`;
 
47
  });
48
 
49
+ generateBtn.addEventListener('click', async () => {
50
+ const text = inputText.value.trim();
51
+ if (!text) return;
52
+
53
+ resetUI();
54
+ statusContainer.classList.remove('hidden');
55
+ generateBtn.disabled = true;
56
+
57
+ try {
58
+ // 1. Initialize Pipeline if needed
59
+ if (!ttsPipeline) {
60
+ updateStatus('Loading model... (this may take a moment)', 0);
61
+
62
+ // Import pipeline from window (set in HTML)
63
+ const { pipeline } = window;
64
+
65
+ ttsPipeline = await pipeline('text-to-speech', MODEL_ID, {
66
+ device: currentDevice,
67
+ dtype: 'fp32', // Required for this specific model as per prompt
68
+ progress_callback: (data) => {
69
+ if (data.status === 'progress') {
70
+ updateStatus(`Downloading ${data.file}...`, data.progress);
71
+ } else if (data.status === 'ready') {
72
+ updateStatus('Model ready!', 100);
73
+ }
74
+ }
75
+ });
76
+ }
77
+
78
+ // 2. Generate Audio
79
+ updateStatus('Generating audio...', 100);
80
+ progressBar.classList.add('pulsing'); // Add animation for inference time
81
+
82
+ const voiceFile = voiceSelect.value;
83
+ const speaker_embeddings = `${VOICE_BASE_URL}${voiceFile}`;
84
+
85
+ // Run inference
86
+ const output = await ttsPipeline(text, {
87
+ speaker_embeddings: speaker_embeddings
88
+ });
89
+
90
+ // 3. Process Output
91
+ // output.audio is a Float32Array, output.sampling_rate is a number
92
+ const wavUrl = createWavUrl(output.audio, output.sampling_rate);
93
+
94
+ audioPlayer.src = wavUrl;
95
+ downloadLink.href = wavUrl;
96
+
97
+ outputCard.classList.remove('hidden');
98
+ // Auto-play result
99
+ try {
100
+ await audioPlayer.play();
101
+ } catch (e) {
102
+ console.log("Auto-play blocked by browser policy");
103
+ }
104
+
105
+ } catch (err) {
106
+ console.error(err);
107
+ showError(err.message);
108
+ } finally {
109
+ generateBtn.disabled = false;
110
+ progressBar.classList.remove('pulsing');
111
+ statusContainer.classList.add('hidden');
112
  }
113
+ });
114
 
115
+ // Helper: Update Progress UI
116
+ function updateStatus(text, progressPercent) {
117
+ statusText.innerText = text;
118
+ progressBar.style.width = `${progressPercent}%`;
119
+ }
120
 
121
+ function resetUI() {
122
+ outputCard.classList.add('hidden');
123
+ errorMsg.classList.add('hidden');
124
+ progressBar.style.width = '0%';
125
+ }
126
 
127
+ function showError(msg) {
128
+ errorMsg.innerText = `Error: ${msg}`;
129
+ errorMsg.classList.remove('hidden');
130
+ }
131
 
132
+ // Audio Utility: Convert Float32Array to WAV Blob URL
133
+ function createWavUrl(audioData, sampleRate) {
134
+ const buffer = encodeWAV(audioData, sampleRate);
135
+ const blob = new Blob([buffer], { type: 'audio/wav' });
136
+ return URL.createObjectURL(blob);
137
+ }
138
 
139
+ function encodeWAV(samples, sampleRate) {
140
+ const buffer = new ArrayBuffer(44 + samples.length * 2);
141
+ const view = new DataView(buffer);
142
+
143
+ // RIFF chunk descriptor
144
+ writeString(view, 0, 'RIFF');
145
+ view.setUint32(4, 36 + samples.length * 2, true);
146
+ writeString(view, 8, 'WAVE');
147
+
148
+ // fmt sub-chunk
149
+ writeString(view, 12, 'fmt ');
150
+ view.setUint32(16, 16, true);
151
+ view.setUint16(20, 1, true); // PCM format
152
+ view.setUint16(22, 1, true); // Mono
153
+ view.setUint32(24, sampleRate, true);
154
+ view.setUint32(28, sampleRate * 2, true);
155
+ view.setUint16(32, 2, true);
156
+ view.setUint16(34, 16, true); // 16-bit
157
+
158
+ // data sub-chunk
159
+ writeString(view, 36, 'data');
160
+ view.setUint32(40, samples.length * 2, true);
161
+
162
+ // Write PCM samples
163
+ floatTo16BitPCM(view, 44, samples);
164
+
165
+ return buffer;
166
  }
167
 
168
+ function writeString(view, offset, string) {
169
+ for (let i = 0; i < string.length; i++) {
170
+ view.setUint8(offset + i, string.charCodeAt(i));
171
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  }
173
+
174
+ function floatTo16BitPCM(view, offset, input) {
175
+ for (let i = 0; i < input.length; i++, offset += 2) {
176
+ let s = Math.max(-1, Math.min(1, input[i]));
177
+ s = s < 0 ? s * 0x8000 : s * 0x7FFF;
178
+ view.setInt16(offset, s, true);
179
+ }
180
+ }