Hezu06 commited on
Commit
c46caa2
·
1 Parent(s): 9246010

refactor music generation logic in index.html to improve BPM handling and enhance user feedback

Browse files
Files changed (1) hide show
  1. index.html +164 -100
index.html CHANGED
@@ -88,130 +88,194 @@
88
  <div id="status">Chưa kết nối... Mở nRF Connect trên điện thoại để chuẩn bị!</div>
89
  </div>
90
 
91
- <script>
92
- let currentBpm = 0;
93
- let detectedCharUUID = null;
94
- let bpmHistory = [];
95
-
96
- const bpmDisplay = document.getElementById('bpm-display');
97
- const statusText = document.getElementById('status');
98
- const btnConnect = document.getElementById('btn-connect');
99
-
100
- // ================================
101
- // HELPER: SMOOTH BPM
102
- // ================================
103
- function smoothBpm(newVal) {
104
- bpmHistory.push(newVal);
105
- if (bpmHistory.length > 5) bpmHistory.shift();
106
-
107
- return Math.round(
108
- bpmHistory.reduce((a, b) => a + b, 0) / bpmHistory.length
109
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  }
111
 
112
- // ================================
113
- // HELPER: PARSE DATA
114
- // ================================
115
- function handleAutoData(event, uuid) {
116
- const value = event.target.value;
117
 
118
- let raw = [];
119
- for (let i = 0; i < value.byteLength; i++) {
120
- raw.push(value.getUint8(i));
121
- }
122
-
123
- console.log("📡 UUID:", uuid, "DATA:", raw);
124
-
125
- // ===== AUTO DETECT BPM =====
126
- for (let i = 0; i < raw.length; i++) {
127
- let v = raw[i];
128
 
129
- // heuristic: BPM range
130
- if (v >= 40 && v <= 180) {
131
 
132
- let newBpm = smoothBpm(v);
133
 
134
- // filter noise (tránh nhảy lung tung)
135
- if (currentBpm !== 0 && Math.abs(newBpm - currentBpm) > 40) {
136
- continue;
137
- }
138
-
139
- currentBpm = newBpm;
140
 
141
- bpmDisplay.innerText = `${currentBpm} BPM`;
142
- bpmDisplay.style.color = "#e94560";
143
 
144
- statusText.innerText = `Đã detect BPM từ UUID: ${uuid}`;
145
- statusText.style.color = "#4ecca3";
146
 
147
- detectedCharUUID = uuid;
 
148
 
149
- break;
150
- }
151
  }
152
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
 
154
- // ================================
155
- // MAIN: AUTO SCAN BLE
156
- // ================================
157
- btnConnect.addEventListener('click', async () => {
158
- try {
159
- statusText.innerText = "Đang mở BLE scanner (auto)...";
160
-
161
- const device = await navigator.bluetooth.requestDevice({
162
- acceptAllDevices: true,
163
- optionalServices: [
164
- 'battery_service',
165
- 'device_information',
166
- 'heart_rate'
167
- ]
168
- });
169
-
170
- statusText.innerText = `Đã chọn: ${device.name || "Unknown"} → đang connect...`;
171
-
172
- const server = await device.gatt.connect();
173
 
174
- const services = await server.getPrimaryServices();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
- statusText.innerText = "Đang scan toàn bộ services... (check console)";
 
 
 
 
 
177
 
178
- for (const service of services) {
179
- console.log("🔵 Service:", service.uuid);
 
 
180
 
181
- const characteristics = await service.getCharacteristics();
 
 
 
 
 
182
 
183
- for (const char of characteristics) {
184
- console.log(" ⚪ Char:", char.uuid, char.properties);
185
 
186
- // chỉ lấy char có notify / indicate
187
- if (char.properties.notify || char.properties.indicate) {
188
- try {
189
- await char.startNotifications();
190
 
191
- char.addEventListener(
192
- 'characteristicvaluechanged',
193
- (event) => handleAutoData(event, char.uuid)
194
- );
195
 
196
- console.log(" ✅ Subscribed:", char.uuid);
197
- } catch (err) {
198
- console.log(" ❌ Cannot subscribe:", char.uuid);
199
- }
200
- }
201
- }
202
- }
203
 
204
- statusText.innerText = "Đã subscribe toàn bộ notify characteristics. Đang chờ BPM...";
205
- btnConnect.innerText = "✓ BLE Connected (Auto Mode)";
206
- btnConnect.style.backgroundColor = "#4ecca3";
207
- btnConnect.style.color = "#1a1a2e";
 
208
 
209
- } catch (error) {
210
- console.error("❌ BLE ERROR:", error);
211
- statusText.innerText = "Lỗi BLE hoặc user cancel!";
212
- statusText.style.color = "#ff4d4d";
213
  }
214
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  </script>
216
  </body>
217
  </html>
 
88
  <div id="status">Chưa kết nối... Mở nRF Connect trên điện thoại để chuẩn bị!</div>
89
  </div>
90
 
91
+ <script>
92
+ let currentBpm = 0;
93
+ let detectedCharUUID = null;
94
+ let bpmHistory = [];
95
+ let currentAudio = null;
96
+
97
+ // DOM
98
+ const bpmDisplay = document.getElementById('bpm-display');
99
+ const statusText = document.getElementById('status');
100
+ const btnConnect = document.getElementById('btn-connect');
101
+ const btnGenerate = document.getElementById('btn-generate');
102
+ const btnStop = document.getElementById('btn-stop');
103
+
104
+ // ================================
105
+ // SMOOTH BPM
106
+ // ================================
107
+ function smoothBpm(newVal) {
108
+ bpmHistory.push(newVal);
109
+ if (bpmHistory.length > 5) bpmHistory.shift();
110
+
111
+ return Math.round(
112
+ bpmHistory.reduce((a, b) => a + b, 0) / bpmHistory.length
113
+ );
114
+ }
115
+
116
+ // ================================
117
+ // AUTO DETECT DATA
118
+ // ================================
119
+ function handleAutoData(event, uuid) {
120
+ const value = event.target.value;
121
+
122
+ let raw = [];
123
+ for (let i = 0; i < value.byteLength; i++) {
124
+ raw.push(value.getUint8(i));
125
  }
126
 
127
+ console.log("📡 UUID:", uuid, "DATA:", raw);
 
 
 
 
128
 
129
+ for (let i = 0; i < raw.length; i++) {
130
+ let v = raw[i];
 
 
 
 
 
 
 
 
131
 
132
+ if (v >= 40 && v <= 180) {
 
133
 
134
+ let newBpm = smoothBpm(v);
135
 
136
+ if (currentBpm !== 0 && Math.abs(newBpm - currentBpm) > 40) {
137
+ continue;
138
+ }
 
 
 
139
 
140
+ currentBpm = newBpm;
 
141
 
142
+ bpmDisplay.innerText = `${currentBpm} BPM`;
143
+ bpmDisplay.style.color = "#e94560";
144
 
145
+ statusText.innerText = `Đã detect BPM (${uuid})`;
146
+ statusText.style.color = "#4ecca3";
147
 
148
+ detectedCharUUID = uuid;
149
+ break;
150
  }
151
  }
152
+ }
153
+
154
+ // ================================
155
+ // AUTO SCAN BLE
156
+ // ================================
157
+ btnConnect.addEventListener('click', async () => {
158
+ try {
159
+ statusText.innerText = "Đang scan BLE...";
160
+
161
+ const device = await navigator.bluetooth.requestDevice({
162
+ acceptAllDevices: true,
163
+ optionalServices: [
164
+ 'battery_service',
165
+ 'device_information',
166
+ 'heart_rate'
167
+ ]
168
+ });
169
+
170
+ const server = await device.gatt.connect();
171
+ const services = await server.getPrimaryServices();
172
+
173
+ for (const service of services) {
174
+ console.log("🔵 Service:", service.uuid);
175
+
176
+ const characteristics = await service.getCharacteristics();
177
+
178
+ for (const char of characteristics) {
179
+ console.log("⚪ Char:", char.uuid, char.properties);
180
+
181
+ if (char.properties.notify || char.properties.indicate) {
182
+ try {
183
+ await char.startNotifications();
184
+
185
+ char.addEventListener(
186
+ 'characteristicvaluechanged',
187
+ (event) => handleAutoData(event, char.uuid)
188
+ );
189
+
190
+ console.log("✅ Subscribed:", char.uuid);
191
+ } catch (err) {
192
+ console.log("❌ Cannot subscribe:", char.uuid);
193
+ }
194
+ }
195
+ }
196
+ }
197
 
198
+ statusText.innerText = "Đã kết nối! Đang chờ BPM...";
199
+ btnConnect.innerText = "✓ BLE Connected";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
 
201
+ } catch (error) {
202
+ console.error(error);
203
+ statusText.innerText = "Lỗi BLE!";
204
+ statusText.style.color = "#ff4d4d";
205
+ }
206
+ });
207
+
208
+ // ================================
209
+ // GENERATE MUSIC (GIỮ NGUYÊN LOGIC)
210
+ // ================================
211
+ btnGenerate.addEventListener('click', async () => {
212
+ if (currentBpm === 0) {
213
+ alert("Chưa có BPM!");
214
+ return;
215
+ }
216
 
217
+ // stop audio
218
+ if (currentAudio) {
219
+ currentAudio.pause();
220
+ currentAudio.currentTime = 0;
221
+ bpmDisplay.style.animation = "none";
222
+ }
223
 
224
+ statusText.innerText = `Đang gửi ${currentBpm} BPM cho AI...`;
225
+ btnGenerate.innerText = "AI đang sáng tác...";
226
+ btnGenerate.disabled = true;
227
+ btnStop.disabled = true;
228
 
229
+ try {
230
+ const response = await fetch('/generate-vibe', {
231
+ method: 'POST',
232
+ headers: { 'Content-Type': 'application/json' },
233
+ body: JSON.stringify({ bpm: currentBpm })
234
+ });
235
 
236
+ const data = await response.json();
 
237
 
238
+ if (data.status === "success") {
239
+ currentAudio = new Audio(data.audio_url);
240
+ currentAudio.play();
 
241
 
242
+ bpmDisplay.style.animation = "pulse 1s infinite";
 
 
 
243
 
244
+ statusText.innerText = "🎵 Đang phát nhạc...";
245
+ btnStop.disabled = false;
 
 
 
 
 
246
 
247
+ currentAudio.onended = () => {
248
+ bpmDisplay.style.animation = "none";
249
+ btnStop.disabled = true;
250
+ statusText.innerText = "Nhạc đã kết thúc.";
251
+ };
252
 
253
+ } else {
254
+ statusText.innerText = "Lỗi AI!";
 
 
255
  }
256
+
257
+ } catch (error) {
258
+ console.error(error);
259
+ statusText.innerText = "Mất kết nối server!";
260
+ } finally {
261
+ btnGenerate.innerText = "2. Sinh nhạc AI";
262
+ btnGenerate.disabled = false;
263
+ }
264
+ });
265
+
266
+ // ================================
267
+ // STOP MUSIC
268
+ // ================================
269
+ btnStop.addEventListener('click', () => {
270
+ if (currentAudio) {
271
+ currentAudio.pause();
272
+ currentAudio.currentTime = 0;
273
+
274
+ bpmDisplay.style.animation = "none";
275
+ statusText.innerText = "Đã dừng nhạc.";
276
+ btnStop.disabled = true;
277
+ }
278
+ });
279
  </script>
280
  </body>
281
  </html>