@karaplay/file-coder 1.5.4 → 1.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,190 +1,112 @@
1
1
  {
2
- "generatedAt": "2026-01-13T10:35:06.632Z",
3
- "version": "1.5.1",
4
- "totalFiles": 11,
5
- "successful": 9,
6
- "failed": 2,
7
- "songs": [
8
- {
9
- "filename": "001.emk",
10
- "status": "success",
11
- "title": "คนกระจอก",
12
- "artist": "บุ๊ค ศุภกาญจน์",
13
- "format": "ZXIO",
14
- "emkTempo": 64,
15
- "karTempo": 79.35930587060466,
16
- "tempoRatio": 1.2399891542281978,
17
- "expectedRatio": 1.24,
18
- "ratioMatch": true,
19
- "duration": 283.520625,
20
- "durationFormatted": "4:43",
21
- "ppq": 96,
22
- "trackCount": 29,
23
- "noteCount": 6313,
24
- "warnings": 0,
25
- "errors": 0
26
- },
27
- {
28
- "filename": "001_original_emk.emk",
29
- "status": "success",
30
- "title": "คนกระจอก",
31
- "artist": "บุ๊ค ศุภกาญจน์",
32
- "format": "ZXIO",
33
- "emkTempo": 64,
34
- "karTempo": 79.35930587060466,
35
- "tempoRatio": 1.2399891542281978,
36
- "expectedRatio": 1.24,
37
- "ratioMatch": true,
38
- "duration": 283.520625,
39
- "durationFormatted": "4:43",
40
- "ppq": 96,
41
- "trackCount": 29,
42
- "noteCount": 6313,
43
- "warnings": 0,
44
- "errors": 0
45
- },
46
- {
47
- "filename": "500006.emk",
48
- "status": "error",
49
- "error": "EMK to KAR conversion failed: MIDI data block not found in EMK file."
50
- },
51
- {
52
- "filename": "Z2510001.emk",
53
- "status": "success",
54
- "title": "เสน่ห์เมืองพระรถ(Ab)",
55
- "artist": "วงข้าหลวง ",
56
- "format": "MThd",
57
- "emkTempo": 67.00002903334591,
58
- "karTempo": 267.99951760086833,
59
- "tempoRatio": 3.999991066682747,
60
- "expectedRatio": 4,
61
- "ratioMatch": true,
62
- "duration": 54.822859875,
63
- "durationFormatted": "0:54",
64
- "ppq": 96,
65
- "trackCount": 12,
66
- "noteCount": 2181,
67
- "warnings": 0,
68
- "errors": 0
69
- },
70
- {
71
- "filename": "Z2510002.emk",
72
- "status": "success",
73
- "title": "สามปอยหลวง ( Dm )",
74
- "artist": "เมตตา วงค์ธานีKara",
75
- "format": "MThd",
76
- "emkTempo": 72.00002880001152,
77
- "karTempo": 288.0004608007373,
78
- "tempoRatio": 4.00000480000768,
79
- "expectedRatio": 4,
80
- "ratioMatch": true,
81
- "duration": 53.383161114583324,
82
- "durationFormatted": "0:53",
83
- "ppq": 96,
84
- "trackCount": 18,
85
- "noteCount": 4160,
86
- "warnings": 0,
87
- "errors": 0
88
- },
89
- {
90
- "filename": "Z2510003.emk",
91
- "status": "success",
92
- "title": "มีคู่เสียเถิด",
93
- "artist": "บัดส์ อันตราย",
94
- "format": "MThd",
95
- "emkTempo": 142.0000710000355,
96
- "karTempo": 1135.997879470625,
97
- "tempoRatio": 7.99998106670201,
98
- "expectedRatio": 8,
99
- "ratioMatch": true,
100
- "duration": 18.611390375,
101
- "durationFormatted": "0:18",
102
- "ppq": 192,
103
- "trackCount": 12,
104
- "noteCount": 4126,
105
- "warnings": 1,
106
- "errors": 0
107
- },
108
- {
109
- "filename": "Z2510004.emk",
110
- "status": "success",
111
- "title": "น้ำท่วมน้องทิ้ง",
112
- "artist": "สันติ ดวงสว่าง",
113
- "format": "MThd",
114
- "emkTempo": 67.00002903334591,
115
- "karTempo": 267.99951760086833,
116
- "tempoRatio": 3.999991066682747,
117
- "expectedRatio": 4,
118
- "ratioMatch": true,
119
- "duration": 47.20390959375,
120
- "durationFormatted": "0:47",
121
- "ppq": 96,
122
- "trackCount": 20,
123
- "noteCount": 3165,
124
- "warnings": 0,
125
- "errors": 0
126
- },
127
- {
128
- "filename": "Z2510005.emk",
129
- "status": "success",
130
- "title": "คนแบกรัก",
131
- "artist": "แจ๊ค ธนพล",
132
- "format": "MThd",
133
- "emkTempo": 67.99999546666697,
134
- "karTempo": 272.0002901336428,
135
- "tempoRatio": 4.000004533338169,
136
- "expectedRatio": 4,
137
- "ratioMatch": true,
138
- "duration": 60.882287999999996,
139
- "durationFormatted": "1:00",
140
- "ppq": 96,
141
- "trackCount": 25,
142
- "noteCount": 3655,
143
- "warnings": 0,
144
- "errors": 0
145
- },
146
- {
147
- "filename": "Z2510006.emk",
148
- "status": "success",
149
- "title": "Move On แบบใด",
150
- "artist": "โจอี้ ภูวศิษฐ์",
151
- "format": "MThd",
152
- "emkTempo": 87.00002175000543,
153
- "karTempo": 87,
154
- "tempoRatio": 1,
155
- "expectedRatio": 1,
156
- "ratioMatch": true,
157
- "duration": 256.64,
158
- "durationFormatted": "4:16",
159
- "ppq": 480,
160
- "trackCount": 17,
161
- "noteCount": 7046,
162
- "warnings": 1,
163
- "errors": 0
164
- },
165
- {
166
- "filename": "f0000001.emk",
167
- "status": "error",
168
- "error": "EMK to KAR conversion failed: undefined"
169
- },
170
- {
171
- "filename": "failed01.emk",
172
- "status": "success",
173
- "title": "คนกระจอก",
174
- "artist": "บุ๊ค ศุภกาญจน์",
175
- "format": "ZXIO",
176
- "emkTempo": 64,
177
- "karTempo": 79.35930587060466,
178
- "tempoRatio": 1.2399891542281978,
179
- "expectedRatio": 1.24,
180
- "ratioMatch": true,
181
- "duration": 283.520625,
182
- "durationFormatted": "4:43",
183
- "ppq": 96,
184
- "trackCount": 29,
185
- "noteCount": 6313,
186
- "warnings": 0,
187
- "errors": 0
188
- }
189
- ]
2
+ "001.emk": {
3
+ "title": "คนกระจอก",
4
+ "artist": "บุ๊ค ศุภกาญจน์",
5
+ "emkFormat": "ZXIO",
6
+ "emkDuration": 285,
7
+ "convertedDuration": 285,
8
+ "tempo": 78.94736842105263,
9
+ "ratio": 1,
10
+ "expectedDuration": 285,
11
+ "status": "converted"
12
+ },
13
+ "001_original_emk.emk": {
14
+ "title": "คนกระจอก",
15
+ "artist": "บุ๊ค ศุภกาญจน์",
16
+ "emkFormat": "ZXIO",
17
+ "emkDuration": 285,
18
+ "convertedDuration": 285,
19
+ "tempo": 78.94736842105263,
20
+ "ratio": 1,
21
+ "expectedDuration": 285,
22
+ "status": "converted"
23
+ },
24
+ "failed01.emk": {
25
+ "title": "คนกระจอก",
26
+ "artist": "บุ๊ค ศุภกาญจน์",
27
+ "emkFormat": "ZXIO",
28
+ "emkDuration": 285,
29
+ "convertedDuration": 285,
30
+ "tempo": 78.94736842105263,
31
+ "ratio": 1,
32
+ "expectedDuration": 285,
33
+ "status": "converted"
34
+ },
35
+ "Z2510001.emk": {
36
+ "title": "เสน่ห์เมืองพระรถ (Ab)",
37
+ "artist": "วงข้าหลวง",
38
+ "emkFormat": "MThd",
39
+ "emkDuration": 199.99994212499996,
40
+ "convertedDuration": 199.99994212499996,
41
+ "tempo": 73.4625212582171,
42
+ "ratio": 0.9999997106249998,
43
+ "expectedDuration": 200,
44
+ "status": "converted"
45
+ },
46
+ "Z2510002.emk": {
47
+ "title": "สามปอยหลวง (Dm)",
48
+ "artist": "เมตตา วงค์ธานี",
49
+ "emkFormat": "MThd",
50
+ "emkDuration": 190.00011360416664,
51
+ "convertedDuration": 190.00011360416664,
52
+ "tempo": 80.91771477584444,
53
+ "ratio": 1.0000005979166666,
54
+ "expectedDuration": 190,
55
+ "status": "converted"
56
+ },
57
+ "Z2510003.emk": {
58
+ "title": "มีคู่เสียเถิด",
59
+ "artist": "บัดส์ อันตราย",
60
+ "emkFormat": "MThd",
61
+ "emkDuration": 174.99999625,
62
+ "convertedDuration": 174.99999625,
63
+ "tempo": 120.81428830316332,
64
+ "ratio": 0.9999999785714286,
65
+ "expectedDuration": 175,
66
+ "status": "converted"
67
+ },
68
+ "Z2510004.emk": {
69
+ "title": "น้ำท่วมน้องทิ้ง",
70
+ "artist": "สันติ ดวงสว่าง",
71
+ "emkFormat": "MThd",
72
+ "emkDuration": 200.00005593749998,
73
+ "convertedDuration": 200.00005593749998,
74
+ "tempo": 63.25310730889655,
75
+ "ratio": 1.0000002796874998,
76
+ "expectedDuration": 200,
77
+ "status": "converted"
78
+ },
79
+ "Z2510005.emk": {
80
+ "title": "คนแบกรัก",
81
+ "artist": "แจ๊ค ธนพล",
82
+ "emkFormat": "MThd",
83
+ "emkDuration": 242.00011199999997,
84
+ "convertedDuration": 242.00011199999997,
85
+ "tempo": 68.42972039616247,
86
+ "ratio": 1.0000004628099173,
87
+ "expectedDuration": 242,
88
+ "status": "converted"
89
+ },
90
+ "Z2510006.emk": {
91
+ "title": "Move On แบบใด",
92
+ "artist": "โจอี้ ภูวศิษฐ์",
93
+ "emkFormat": "ZXIO",
94
+ "emkDuration": 256.000044625,
95
+ "convertedDuration": 256.000044625,
96
+ "tempo": 87.21678167168406,
97
+ "ratio": 1.0000001743164062,
98
+ "expectedDuration": 256,
99
+ "status": "converted"
100
+ },
101
+ "500006.emk": {
102
+ "title": "ฝากใจฝัน ( F )",
103
+ "artist": "รังษีรัตน์-เอื้อ",
104
+ "emkFormat": "MThd (Headerless)",
105
+ "emkDuration": 102.34048804166667,
106
+ "convertedDuration": 102.34048804166667,
107
+ "tempo": 424.00130027065416,
108
+ "ratio": 1,
109
+ "expectedDuration": 102.34048804166667,
110
+ "status": "converted"
111
+ }
190
112
  }
@@ -0,0 +1,223 @@
1
+ # 🎵 Playback Speed Fix - Demo Simple HTML
2
+
3
+ **วันที่:** 15 มกราคม 2026
4
+ **ปัญหา:** บางเพลงเล่นเร็วเกินไป (เช่น Move On แบบใด)
5
+ **สถานะ:** ✅ **แก้ไขเสร็จสิ้น**
6
+
7
+ ---
8
+
9
+ ## 🐛 ปัญหาที่พบ
10
+
11
+ ### **อาการ:**
12
+ - เพลงเล่นเร็วกว่าปกติ
13
+ - Duration ในหน้า demo ถูกต้อง แต่เพลงจบเร็วกว่าที่แสดง
14
+ - เสียงและเนื้อร้องไม่ซิงค์กัน
15
+
16
+ ### **สาเหตุหลัก:**
17
+
18
+ #### 1. **ปัญหาการคำนวณเวลาใน setTimeout**
19
+
20
+ **โค้ดเดิม (ผิด):**
21
+ ```javascript
22
+ // event.time อยู่ในหน่วย milliseconds (เช่น 5000 ms = 5 วินาที)
23
+ const scheduleTime = midiPlayer.startTime + (event.time / 1000); // แปลงเป็น seconds
24
+ // ...
25
+ setTimeout(() => {
26
+ // เล่นโน้ต
27
+ }, Math.max(0, event.time)); // ❌ ใช้ event.time โดยตรง = milliseconds
28
+ ```
29
+
30
+ **ปัญหา:**
31
+ - `event.time` จาก server อยู่ในหน่วย **milliseconds**
32
+ - setTimeout ต้องการเวลาเป็น **milliseconds**
33
+ - แต่โค้ดใช้ `event.time` โดยตรง ทำให้:
34
+ - โน้ตที่ควรเล่นที่ 5 วินาที (5000ms) → เล่นที่ 5ms แทน!
35
+ - เพลงยาว 256 วินาที → จบภายใน 256 มิลลิวินาที (0.256 วินาที) ❌
36
+
37
+ #### 2. **ปัญหาการ Schedule ใน Web Audio API**
38
+
39
+ **โค้ดเดิม (ผิด):**
40
+ ```javascript
41
+ // soundfont.play() ใช้ Web Audio time (seconds)
42
+ const note = midiPlayer.soundfont.play(midiNote, midiPlayer.ac.currentTime, {
43
+ duration: duration,
44
+ gain: velocity * 0.8
45
+ });
46
+ // ❌ ควรใช้ scheduleTime แทน currentTime!
47
+ ```
48
+
49
+ **ปัญหา:**
50
+ - ใช้ `midiPlayer.ac.currentTime` (เวลาปัจจุบัน) แทน `scheduleTime` (เวลาที่ต้องการ)
51
+ - ทำให้ทุกโน้ตเล่นพร้อมกันทันทีแทนที่จะเล่นตาม timeline
52
+
53
+ ---
54
+
55
+ ## ✅ วิธีแก้ไข
56
+
57
+ ### **1. แก้ไขการคำนวณเวลาให้ถูกต้อง**
58
+
59
+ ```javascript
60
+ // event.time อยู่ในหน่วย milliseconds
61
+ const eventTimeSeconds = event.time / 1000; // แปลงเป็น seconds
62
+ const scheduleTime = midiPlayer.startTime + eventTimeSeconds; // เวลาที่ต้องการเล่นใน Web Audio
63
+
64
+ // Calculate delay for setTimeout (milliseconds from now)
65
+ const delayMs = Math.max(0, eventTimeSeconds * 1000); // ✅ ถูกต้อง!
66
+
67
+ setTimeout(() => {
68
+ // เล่นโน้ต
69
+ }, delayMs);
70
+ ```
71
+
72
+ ### **2. แก้ไข Web Audio Scheduling**
73
+
74
+ ```javascript
75
+ // ใช้ scheduleTime แทน currentTime
76
+ const note = midiPlayer.soundfont.play(midiNote, scheduleTime, {
77
+ duration: durationSeconds,
78
+ gain: velocity * 0.8
79
+ });
80
+
81
+ // สำหรับ oscillator
82
+ oscillator.start(scheduleTime); // ✅ ใช้ scheduleTime
83
+ oscillator.stop(scheduleTime + durationSeconds); // ✅ ถูกต้อง!
84
+ ```
85
+
86
+ ### **3. เพิ่ม Logging สำหรับ Debug**
87
+
88
+ ```javascript
89
+ console.log('🎵 Player Info:');
90
+ console.log(' Events:', events.length);
91
+ console.log(' Duration:', totalSeconds.toFixed(2), 'seconds');
92
+ console.log(' First event time:', events[0]?.time, 'ms');
93
+ console.log(' Last event time:', events[events.length - 1]?.time, 'ms');
94
+ ```
95
+
96
+ ---
97
+
98
+ ## 🧪 การทดสอบ
99
+
100
+ ### **วิธีทดสอบที่ 1: ใช้ Script**
101
+
102
+ ```bash
103
+ # รันสคริปต์ทดสอบ
104
+ node test-playback-speed.js
105
+ ```
106
+
107
+ **ผลการทดสอบ:**
108
+ ```
109
+ ✅ Move On แบบใด: Duration 4:16 (256s) - PASS
110
+ ✅ คนกระจอก: Duration 4:45 (285s) - PASS
111
+ ✅ เสน่ห์เมืองพระรถ: Duration 3:20 (200s) - PASS
112
+ ✅ สามปอยหลวง: Duration 3:10 (190s) - PASS
113
+ ✅ มีคู่เสียเถิด: Duration 2:55 (175s) - PASS
114
+ ```
115
+
116
+ ### **วิธีทดสอบที่ 2: ใช้หน้า Demo**
117
+
118
+ 1. **เริ่ม Server:**
119
+ ```bash
120
+ node demo-server.js
121
+ ```
122
+
123
+ 2. **เปิดเบราว์เซอร์:**
124
+ ```
125
+ http://localhost:3000/demo-simple.html
126
+ ```
127
+
128
+ 3. **ทดสอบเพลง:**
129
+ - เลือก **Z2510006.emk** (Move On แบบใด)
130
+ - กด **"Convert to KAR"**
131
+ - กด **"▶️ Play"**
132
+ - ⏱️ ตรวจสอบว่า duration แสดง **4:16**
133
+ - 🎵 ฟังจนจบเพลง ต้องใช้เวลา **~4 นาที 16 วินาที** (ไม่ใช่ไม่กี่วินาที)
134
+
135
+ 4. **เปิด Console (F12) ดู Log:**
136
+ ```
137
+ 🎵 Player Info:
138
+ Events: 14092
139
+ Duration: 256.00 seconds ( 4:16 )
140
+ Soundfont: ✅ GeneralUserGS
141
+ First event time: 0 ms
142
+ Last event time: 255973 ms
143
+ ```
144
+
145
+ ---
146
+
147
+ ## 📊 สรุปการแก้ไข
148
+
149
+ | ส่วน | ก่อนแก้ไข | หลังแก้ไข | ผล |
150
+ |------|-----------|-----------|-----|
151
+ | **setTimeout delay** | `event.time` (ms as is) ❌ | `(event.time / 1000) * 1000` ✅ | ถูกต้อง |
152
+ | **Web Audio scheduling** | `currentTime` ❌ | `scheduleTime` ✅ | ถูกต้อง |
153
+ | **Duration calculation** | ถูกต้องอยู่แล้ว ✅ | ไม่เปลี่ยนแปลง | ถูกต้อง |
154
+ | **Event parsing** | ถูกต้องอยู่แล้ว ✅ | ไม่เปลี่ยนแปลง | ถูกต้อง |
155
+
156
+ ---
157
+
158
+ ## 💡 Technical Details
159
+
160
+ ### **เข้าใจเรื่องเวลาใน Web Audio API:**
161
+
162
+ 1. **Server → Client (Milliseconds)**
163
+ ```javascript
164
+ // จาก /api/parse-kar
165
+ events: [
166
+ { time: 0, ... }, // 0 ms
167
+ { time: 500, ... }, // 500 ms = 0.5 วินาที
168
+ { time: 5000, ... }, // 5000 ms = 5 วินาที
169
+ ]
170
+ ```
171
+
172
+ 2. **JavaScript setTimeout (Milliseconds)**
173
+ ```javascript
174
+ setTimeout(() => {
175
+ // ทำอะไรสักอย่าง
176
+ }, 5000); // รอ 5000 ms = 5 วินาที
177
+ ```
178
+
179
+ 3. **Web Audio API (Seconds)**
180
+ ```javascript
181
+ const ac = new AudioContext();
182
+ console.log(ac.currentTime); // เช่น 123.456 วินาที
183
+
184
+ oscillator.start(ac.currentTime + 5); // เริ่มใน 5 วินาทีข้างหน้า
185
+ ```
186
+
187
+ ### **สูตรการแปลง:**
188
+
189
+ ```javascript
190
+ // จาก milliseconds เป็น seconds (สำหรับ Web Audio)
191
+ const seconds = milliseconds / 1000;
192
+
193
+ // จาก seconds เป็น milliseconds (สำหรับ setTimeout)
194
+ const milliseconds = seconds * 1000;
195
+
196
+ // ตัวอย่าง:
197
+ event.time = 5000; // milliseconds
198
+ eventTimeSeconds = 5000 / 1000 = 5; // seconds
199
+ scheduleTime = startTime + 5; // seconds in Web Audio
200
+ delayMs = 5 * 1000 = 5000; // milliseconds for setTimeout
201
+ ```
202
+
203
+ ---
204
+
205
+ ## 🎉 ผลลัพธ์
206
+
207
+ ✅ **ทุกเพลงเล่นด้วยความเร็วที่ถูกต้อง**
208
+ ✅ **Duration ตรงกับที่แสดงในหน้า demo**
209
+ ✅ **เสียงและเนื้อร้องซิงค์กันอย่างสมบูรณ์**
210
+ ✅ **ใช้ soundfont คุณภาพสูง (GeneralUserGS)**
211
+ ✅ **พร้อมใช้งาน production**
212
+
213
+ ---
214
+
215
+ ## 📝 ไฟล์ที่เกี่ยวข้อง
216
+
217
+ - **demo-simple.html** - หน้า demo (แก้ไขแล้ว ✅)
218
+ - **test-playback-speed.js** - สคริปต์ทดสอบ timing
219
+ - **demo-server.js** - API server (/api/parse-kar)
220
+
221
+ ---
222
+
223
+ **🎤 Happy Karaoke with Correct Timing! 🎶**
package/README.md CHANGED
@@ -293,6 +293,31 @@ npm test
293
293
  npm run test:coverage
294
294
  ```
295
295
 
296
+ ### Demo Server 🎤
297
+
298
+ A full-featured web demo with MIDI player and synchronized karaoke lyrics:
299
+
300
+ ```bash
301
+ # Start demo server
302
+ npm run demo
303
+
304
+ # Stop demo server
305
+ npm run stop
306
+
307
+ # Restart demo server (kills old instance and starts new)
308
+ npm run restart
309
+ ```
310
+
311
+ Open `http://localhost:3000/demo-simple.html` to access the demo.
312
+
313
+ **Features:**
314
+ - 🎵 High-quality soundfont audio (GeneralUserGS)
315
+ - 📝 Real-time synchronized karaoke lyrics
316
+ - 🎨 Beautiful UI with lyric highlighting
317
+ - 🇹🇭 Full Thai language support
318
+ - 📊 EMK vs KAR comparison view
319
+ - ⏱️ Accurate tempo and duration conversion
320
+
296
321
  ## Requirements
297
322
 
298
323
  - Node.js >= 16