@karaplay/file-coder 1.5.0 β 1.5.1
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.
- package/DEMO_ENHANCED.md +207 -134
- package/README.md +90 -0
- package/RELEASE_v1.5.1.md +190 -0
- package/debug-duration.js +124 -0
- package/demo-libs/KarFile.js +391 -0
- package/demo-libs/MIDIEvents.js +325 -0
- package/demo-libs/MIDIFile.js +450 -0
- package/demo-libs/MIDIFileHeader.js +144 -0
- package/demo-libs/MIDIFileTrack.js +111 -0
- package/demo-libs/TextEncoding.js +275 -0
- package/demo-libs/UTF8.js +151 -0
- package/demo-server.js +78 -1
- package/demo-simple.html +287 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -1
- package/dist/kar-validator.d.ts +66 -0
- package/dist/kar-validator.js +152 -0
- package/manual-test-validator.js +79 -0
- package/package.json +1 -1
- package/test-demo-player.sh +63 -0
- package/test-duration-fix.sh +53 -0
- package/analyze-emk-cursor.js +0 -169
- package/analyze-emk-simple.js +0 -124
- package/check-real-duration.js +0 -69
- package/temp/test_output.kar +0 -0
- package/test-all-emk-durations.js +0 -109
- package/test-convert-001.js +0 -130
package/DEMO_ENHANCED.md
CHANGED
|
@@ -1,168 +1,241 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
## π― New Features Added
|
|
4
|
-
|
|
5
|
-
### 1. **EMK File Analysis** (Before Conversion)
|
|
6
|
-
When you select an EMK file, it now automatically analyzes:
|
|
7
|
-
- β
Title and Artist (Thai encoding)
|
|
8
|
-
- β
Format type (ZXIO or MThd)
|
|
9
|
-
- β
**Original Tempo** (BPM)
|
|
10
|
-
- β
**Original Duration** (seconds and minutes)
|
|
11
|
-
- β
PPQ (Ticks per beat)
|
|
12
|
-
- β
Number of tracks
|
|
13
|
-
- β
Number of notes
|
|
14
|
-
- β
Lyric and cursor data size
|
|
15
|
-
|
|
16
|
-
### 2. **Comparison Table** (EMK vs KAR)
|
|
17
|
-
After conversion, see a detailed comparison:
|
|
18
|
-
- π Format changes
|
|
19
|
-
- π Tempo ratio (e.g., 2.78x for ZXIO, 4x for MThd)
|
|
20
|
-
- π Duration changes
|
|
21
|
-
- π Tempo verification (β
or β οΈ)
|
|
22
|
-
- π Track and note count
|
|
23
|
-
- π Visual indicators for increases/decreases
|
|
24
|
-
|
|
25
|
-
## π How to Use
|
|
26
|
-
|
|
27
|
-
### Step 1: Open Demo
|
|
28
|
-
```
|
|
29
|
-
http://localhost:3000/demo-simple.html
|
|
30
|
-
```
|
|
1
|
+
# Demo Page Enhanced - MIDI Player
|
|
31
2
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- **Wait for automatic analysis** (1-2 seconds)
|
|
35
|
-
- See original EMK info displayed
|
|
3
|
+
**Date:** 2026-01-13
|
|
4
|
+
**Version:** v1.5.0 with MIDI Player
|
|
36
5
|
|
|
37
|
-
|
|
38
|
-
- Click "Convert to KAR"
|
|
39
|
-
- Wait for conversion
|
|
40
|
-
- See converted KAR info
|
|
6
|
+
## π΅ New Feature: MIDI Player
|
|
41
7
|
|
|
42
|
-
###
|
|
43
|
-
- Automatically displays after conversion
|
|
44
|
-
- Compare original vs converted values
|
|
45
|
-
- Verify tempo ratio is correct
|
|
8
|
+
### Added to `demo-simple.html`
|
|
46
9
|
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
10
|
+
#### 1. **UI Components**
|
|
11
|
+
- βΆοΈ **Play Button** - Start MIDI playback
|
|
12
|
+
- βΈοΈ **Pause Button** - Pause playback (currently restarts on resume)
|
|
13
|
+
- βΉοΈ **Stop Button** - Stop and reset playback
|
|
14
|
+
- **Progress Bar** - Visual playback progress
|
|
15
|
+
- **Time Display** - Current time / Total duration
|
|
16
|
+
- **Status Display** - Shows player state and messages
|
|
50
17
|
|
|
51
|
-
|
|
18
|
+
#### 2. **Libraries Used**
|
|
19
|
+
```html
|
|
20
|
+
<!-- MIDI Parser -->
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/midi-parser-js@4.0.4/src/main.min.js"></script>
|
|
22
|
+
|
|
23
|
+
<!-- Soundfont Player for audio synthesis -->
|
|
24
|
+
<script src="https://cdn.jsdelivr.net/npm/soundfont-player@0.12.0/dist/soundfont-player.min.js"></script>
|
|
25
|
+
```
|
|
52
26
|
|
|
27
|
+
#### 3. **Features**
|
|
28
|
+
- β
**Auto-initialize** - Loads instrument on first play
|
|
29
|
+
- β
**Parse MIDI** - Converts base64 KAR data to playable MIDI
|
|
30
|
+
- β
**Tempo-aware** - Respects MIDI tempo events
|
|
31
|
+
- β
**Real-time progress** - Updates every 100ms
|
|
32
|
+
- β
**Multi-track support** - Plays all MIDI tracks simultaneously
|
|
33
|
+
- β
**Note scheduling** - Uses Web Audio API for precise timing
|
|
34
|
+
- β
**Piano soundfont** - Uses Acoustic Grand Piano (MusyngKite)
|
|
35
|
+
|
|
36
|
+
#### 4. **How It Works**
|
|
37
|
+
|
|
38
|
+
**Step 1: Initialization**
|
|
39
|
+
```javascript
|
|
40
|
+
await initMidiPlayer();
|
|
41
|
+
// Creates AudioContext
|
|
42
|
+
// Loads soundfont from: https://gleitz.github.io/midi-js-soundfonts/
|
|
53
43
|
```
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
β PPQ β 96 β Same β Unchanged β
|
|
62
|
-
β Tracks β 25 β Same + Karaoke β +1 (lyrics) β
|
|
63
|
-
β Notes β 6313 β 6313 β Same β
|
|
64
|
-
β Tempo Ratio β - β 2.78x β β
Correct β
|
|
65
|
-
ββββββββββββββββββββββββ΄ββββββββββββββββββ΄ββββββββββββββββββ΄ββββββββββββββββββ
|
|
44
|
+
|
|
45
|
+
**Step 2: Parse MIDI**
|
|
46
|
+
```javascript
|
|
47
|
+
parseMidiData(base64Data);
|
|
48
|
+
// Decodes base64 β Uint8Array
|
|
49
|
+
// Parses with MidiParser.js
|
|
50
|
+
// Extracts tempo, PPQ, notes
|
|
66
51
|
```
|
|
67
52
|
|
|
68
|
-
|
|
53
|
+
**Step 3: Schedule Notes**
|
|
54
|
+
```javascript
|
|
55
|
+
// Converts ticks to seconds
|
|
56
|
+
ticksToSeconds(ticks, tempo, ppq);
|
|
57
|
+
|
|
58
|
+
// Schedules note events
|
|
59
|
+
instrument.play(midiNote, scheduleTime, options);
|
|
60
|
+
```
|
|
69
61
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
62
|
+
**Step 4: Playback Control**
|
|
63
|
+
```javascript
|
|
64
|
+
playMidi() // Start playback
|
|
65
|
+
pauseMidi() // Stop all notes
|
|
66
|
+
stopMidi() // Reset to beginning
|
|
67
|
+
```
|
|
74
68
|
|
|
75
|
-
|
|
76
|
-
- Tempo: **178.09 BPM** (2.78x)
|
|
77
|
-
- Duration: **~2.11 minutes** (126 seconds)
|
|
78
|
-
- Tempo Ratio: β
**Correct (2.78x)**
|
|
69
|
+
## π― Usage
|
|
79
70
|
|
|
80
|
-
###
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
71
|
+
### 1. Convert EMK to KAR
|
|
72
|
+
1. Select an EMK file from the list
|
|
73
|
+
2. Click "π Convert to KAR"
|
|
74
|
+
3. Wait for conversion to complete
|
|
84
75
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
76
|
+
### 2. Play Converted KAR
|
|
77
|
+
1. After successful conversion, MIDI Player appears
|
|
78
|
+
2. Click "βΆοΈ Play" to start playback
|
|
79
|
+
3. Use "βΈοΈ Pause" or "βΉοΈ Stop" to control playback
|
|
89
80
|
|
|
90
|
-
|
|
81
|
+
### 3. Monitor Playback
|
|
82
|
+
- **Progress Bar** - Shows current position
|
|
83
|
+
- **Time Display** - Shows current time / total duration
|
|
84
|
+
- **Status** - Shows player state (Playing, Paused, Stopped)
|
|
91
85
|
|
|
92
|
-
|
|
93
|
-
Analyze EMK file before conversion
|
|
86
|
+
## π§ Technical Details
|
|
94
87
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
### MIDI Processing
|
|
89
|
+
|
|
90
|
+
**Tempo Calculation:**
|
|
91
|
+
```javascript
|
|
92
|
+
microsecondsPerBeat = (data[0] << 16) | (data[1] << 8) | data[2];
|
|
100
93
|
```
|
|
101
94
|
|
|
102
|
-
**
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
"success": true,
|
|
106
|
-
"emkInfo": {
|
|
107
|
-
"format": "ZXIO",
|
|
108
|
-
"title": "ΰΈΰΈΰΈΰΈ£ΰΈ°ΰΈΰΈΰΈ",
|
|
109
|
-
"artist": "ΰΈΰΈΈΰΉΰΈ ΰΈ¨ΰΈΈΰΈ ΰΈΰΈ²ΰΈΰΈΰΈΰΉ",
|
|
110
|
-
"tempo": "64.00",
|
|
111
|
-
"duration": "351.56",
|
|
112
|
-
"durationMinutes": "5.86",
|
|
113
|
-
"ppq": 96,
|
|
114
|
-
"tracks": 25,
|
|
115
|
-
"notes": 6313,
|
|
116
|
-
"hasLyrics": 3456,
|
|
117
|
-
"hasCursor": 3742
|
|
118
|
-
}
|
|
119
|
-
}
|
|
95
|
+
**Time Conversion:**
|
|
96
|
+
```javascript
|
|
97
|
+
seconds = (ticks Γ microsecondsPerBeat) / (ppq Γ 1000000)
|
|
120
98
|
```
|
|
121
99
|
|
|
122
|
-
|
|
100
|
+
**Note Events:**
|
|
101
|
+
- **Type 9 (Note On):** data[0] = note, data[1] = velocity
|
|
102
|
+
- **Type 8 (Note Off):** data[0] = note
|
|
103
|
+
- **Type 255, Meta 81:** Set Tempo event
|
|
104
|
+
|
|
105
|
+
### Audio Synthesis
|
|
106
|
+
|
|
107
|
+
**Soundfont:**
|
|
108
|
+
- Source: `https://gleitz.github.io/midi-js-soundfonts/`
|
|
109
|
+
- Format: MusyngKite (high quality)
|
|
110
|
+
- Instrument: Acoustic Grand Piano
|
|
111
|
+
|
|
112
|
+
**Web Audio API:**
|
|
113
|
+
- Uses `AudioContext` for precise timing
|
|
114
|
+
- Schedules notes in advance
|
|
115
|
+
- Supports dynamic gain (velocity)
|
|
123
116
|
|
|
124
|
-
|
|
125
|
-
- β
**Correct (2.78x)** - ZXIO format with correct tempo
|
|
126
|
-
- β
**Correct (~4x)** - MThd format with correct tempo
|
|
127
|
-
- β οΈ **Unexpected** - Tempo ratio doesn't match expected value
|
|
117
|
+
## π Testing
|
|
128
118
|
|
|
129
|
-
###
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
119
|
+
### Test File: `001_original_emk.emk`
|
|
120
|
+
|
|
121
|
+
**Expected Results:**
|
|
122
|
+
```
|
|
123
|
+
Format: ZXIO
|
|
124
|
+
Tempo: 79 BPM
|
|
125
|
+
Duration: 4:43 (283 seconds)
|
|
126
|
+
Tracks: 25
|
|
127
|
+
Notes: 6313
|
|
128
|
+
```
|
|
133
129
|
|
|
134
|
-
|
|
130
|
+
**Playback:**
|
|
131
|
+
- β
Should play piano notes
|
|
132
|
+
- β
Should follow correct tempo (79 BPM)
|
|
133
|
+
- β
Should complete in ~4:43
|
|
134
|
+
- β
Progress bar should update smoothly
|
|
135
|
+
- β
Time display should increment correctly
|
|
135
136
|
|
|
136
|
-
|
|
137
|
-
1. **Before/After Comparison** - See exact changes from conversion
|
|
138
|
-
2. **Tempo Verification** - Confirm correct ratio is applied
|
|
139
|
-
3. **Duration Accuracy** - Verify music length is correct
|
|
140
|
-
4. **Format Detection** - Ensure ZXIO vs MThd is identified correctly
|
|
137
|
+
## π Known Limitations
|
|
141
138
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
3. **Quality Assurance** - Demonstrates accurate metadata preservation
|
|
139
|
+
1. **Pause/Resume**
|
|
140
|
+
- Currently restarts playback from beginning
|
|
141
|
+
- Full resume from pause position requires state tracking
|
|
146
142
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
3. **Format Info** - Identify which conversion path was used
|
|
143
|
+
2. **Instrument**
|
|
144
|
+
- Only uses piano soundfont
|
|
145
|
+
- Does not respect MIDI program changes (instrument selection)
|
|
151
146
|
|
|
152
|
-
|
|
147
|
+
3. **Performance**
|
|
148
|
+
- Large MIDI files (>10,000 notes) may cause lag
|
|
149
|
+
- All notes are scheduled at once
|
|
153
150
|
|
|
154
|
-
**
|
|
151
|
+
4. **Browser Support**
|
|
152
|
+
- Requires Web Audio API support
|
|
153
|
+
- Tested on Chrome, Firefox, Safari
|
|
154
|
+
|
|
155
|
+
## π Future Improvements
|
|
156
|
+
|
|
157
|
+
1. **Lyrics Display**
|
|
158
|
+
- Show synchronized lyrics during playback
|
|
159
|
+
- Highlight current lyric line
|
|
160
|
+
|
|
161
|
+
2. **Multiple Instruments**
|
|
162
|
+
- Load different soundfonts per track
|
|
163
|
+
- Respect MIDI program changes
|
|
164
|
+
|
|
165
|
+
3. **Better Pause**
|
|
166
|
+
- Resume from exact pause position
|
|
167
|
+
- Maintain note states
|
|
168
|
+
|
|
169
|
+
4. **Visualization**
|
|
170
|
+
- Piano roll display
|
|
171
|
+
- Waveform visualization
|
|
172
|
+
- Spectrum analyzer
|
|
173
|
+
|
|
174
|
+
5. **Playback Controls**
|
|
175
|
+
- Speed control (0.5x, 1x, 2x)
|
|
176
|
+
- Volume control
|
|
177
|
+
- Seek bar (click to jump)
|
|
178
|
+
|
|
179
|
+
## π Code Structure
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
demo-simple.html
|
|
183
|
+
βββ UI Components
|
|
184
|
+
β βββ Play/Pause/Stop buttons
|
|
185
|
+
β βββ Progress bar
|
|
186
|
+
β βββ Time display
|
|
187
|
+
β βββ Status panel
|
|
188
|
+
β
|
|
189
|
+
βββ MIDI Player Object
|
|
190
|
+
β βββ ac: AudioContext
|
|
191
|
+
β βββ instrument: Soundfont instance
|
|
192
|
+
β βββ midiData: Parsed MIDI
|
|
193
|
+
β βββ State: isPlaying, isPaused, etc.
|
|
194
|
+
β
|
|
195
|
+
βββ Functions
|
|
196
|
+
βββ initMidiPlayer() - Initialize audio
|
|
197
|
+
βββ parseMidiData() - Parse MIDI from base64
|
|
198
|
+
βββ playMidi() - Start playback
|
|
199
|
+
βββ pauseMidi() - Pause playback
|
|
200
|
+
βββ stopMidi() - Stop playback
|
|
201
|
+
βββ ticksToSeconds() - Convert MIDI ticks to time
|
|
155
202
|
```
|
|
156
|
-
|
|
203
|
+
|
|
204
|
+
## β
Verification
|
|
205
|
+
|
|
206
|
+
### Server Running
|
|
207
|
+
```bash
|
|
208
|
+
npm run demo
|
|
209
|
+
# Server: http://localhost:3000
|
|
210
|
+
# Demo: http://localhost:3000/demo-simple.html
|
|
157
211
|
```
|
|
158
212
|
|
|
159
|
-
|
|
160
|
-
1.
|
|
161
|
-
2.
|
|
162
|
-
3. Convert to KAR
|
|
163
|
-
4.
|
|
164
|
-
|
|
213
|
+
### Test Conversion
|
|
214
|
+
1. Open: `http://localhost:3000/demo-simple.html`
|
|
215
|
+
2. Select: `001_original_emk.emk`
|
|
216
|
+
3. Click: "π Convert to KAR"
|
|
217
|
+
4. Verify: Tempo = 79 BPM, Duration = 4:43
|
|
218
|
+
|
|
219
|
+
### Test Playback
|
|
220
|
+
1. Click: "βΆοΈ Play"
|
|
221
|
+
2. Verify: Piano notes play
|
|
222
|
+
3. Verify: Progress bar moves
|
|
223
|
+
4. Verify: Time display updates
|
|
224
|
+
5. Click: "βΉοΈ Stop"
|
|
225
|
+
6. Verify: Playback stops, progress resets
|
|
165
226
|
|
|
166
227
|
---
|
|
167
228
|
|
|
168
|
-
|
|
229
|
+
## π Status: β
Complete
|
|
230
|
+
|
|
231
|
+
**Demo Page Features:**
|
|
232
|
+
- β
EMK file list
|
|
233
|
+
- β
File selection
|
|
234
|
+
- β
EMK analysis (before conversion)
|
|
235
|
+
- β
EMK to KAR conversion
|
|
236
|
+
- β
Metadata display
|
|
237
|
+
- β
Comparison table (EMK vs KAR)
|
|
238
|
+
- β
KAR file download
|
|
239
|
+
- β
**MIDI Player** (NEW!)
|
|
240
|
+
|
|
241
|
+
**Ready for testing and demonstration!** π
|
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@ A comprehensive library for encoding/decoding karaoke files (.emk, .kar, MIDI) w
|
|
|
14
14
|
- βοΈ Next.js compatible (both client and server side)
|
|
15
15
|
- π **NEW**: Browser-compatible API for client-side processing
|
|
16
16
|
- πΉπ Thai language support (TIS-620 encoding)
|
|
17
|
+
- π― **v1.5.0**: Accurate tempo handling for ZXIO format (1.24x ratio)
|
|
18
|
+
- β±οΈ **NEW**: Tempo validation and duration accuracy
|
|
17
19
|
- β
**FIXED v1.3.2**: Client-side EMK decoder now works correctly!
|
|
18
20
|
- β
131 tests - 100% pass rate (including integration tests with real files)
|
|
19
21
|
|
|
@@ -83,6 +85,94 @@ const results = convertEmkToKarBatch(
|
|
|
83
85
|
|
|
84
86
|
See [EMK_TO_KAR_WORKFLOW.md](./EMK_TO_KAR_WORKFLOW.md) for complete documentation.
|
|
85
87
|
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## β±οΈ Tempo Handling & Duration Accuracy
|
|
91
|
+
|
|
92
|
+
### Understanding EMK Tempo Conversion
|
|
93
|
+
|
|
94
|
+
EMK files use non-standard MIDI timing that requires tempo adjustment during conversion. This library **automatically** handles tempo correction based on the EMK format:
|
|
95
|
+
|
|
96
|
+
#### ZXIO Format (v1.5.0+)
|
|
97
|
+
```
|
|
98
|
+
Original EMK: 64 BPM β Converted KAR: 79 BPM
|
|
99
|
+
Tempo Ratio: 1.24x (ticksPerBeat / 77.42)
|
|
100
|
+
Duration: 4:43 β
Accurate!
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### MThd Format
|
|
104
|
+
```
|
|
105
|
+
Tempo Ratio: 4x, 8x, or 20x (auto-detected)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Validating Conversion Accuracy
|
|
109
|
+
|
|
110
|
+
Use `@tonejs/midi` to verify tempo and duration:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { Midi } from '@tonejs/midi';
|
|
114
|
+
import { convertEmkToKar } from '@karaplay/file-coder';
|
|
115
|
+
import * as fs from 'fs';
|
|
116
|
+
|
|
117
|
+
// Convert EMK to KAR
|
|
118
|
+
const result = convertEmkToKar({
|
|
119
|
+
inputEmk: 'song.emk',
|
|
120
|
+
outputKar: 'song.kar'
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Validate with Tone.js
|
|
124
|
+
const karBuffer = fs.readFileSync('song.kar');
|
|
125
|
+
const midi = new Midi(karBuffer);
|
|
126
|
+
|
|
127
|
+
console.log('β
Validation Results:');
|
|
128
|
+
console.log(` Tempo: ${midi.header.tempos[0].bpm.toFixed(2)} BPM`);
|
|
129
|
+
console.log(` Duration: ${(midi.duration / 60).toFixed(2)} minutes`);
|
|
130
|
+
console.log(` PPQ: ${midi.header.ppq}`);
|
|
131
|
+
console.log(` Tracks: ${midi.tracks.length}`);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### π§ Troubleshooting Tempo Issues
|
|
135
|
+
|
|
136
|
+
**Problem:** Converted KAR plays too fast or too slow
|
|
137
|
+
**Solution:** The library automatically applies correct tempo ratios:
|
|
138
|
+
|
|
139
|
+
| Format | Original BPM | Ratio | Converted BPM | Status |
|
|
140
|
+
|--------|--------------|-------|---------------|--------|
|
|
141
|
+
| ZXIO | 64 BPM | 1.24x | 79 BPM | β
v1.5.0+ |
|
|
142
|
+
| MThd | 160 BPM | 4x | 640 BPM | β
Auto |
|
|
143
|
+
| MThd | 142 BPM | 8x | 1136 BPM | β
Auto |
|
|
144
|
+
|
|
145
|
+
**Important Notes:**
|
|
146
|
+
1. β
**Tempo is automatically corrected** - no manual adjustment needed
|
|
147
|
+
2. β
**Use @tonejs/midi for accurate duration** - it handles tempo events correctly
|
|
148
|
+
3. β οΈ **Don't use karaoke-player's getTickResolution()** - it can give incorrect timing
|
|
149
|
+
4. β
**Duration decrease is normal** - faster tempo = shorter playback time
|
|
150
|
+
|
|
151
|
+
### Best Practices for MIDI Playback
|
|
152
|
+
|
|
153
|
+
When implementing a player for converted KAR files:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { Midi } from '@tonejs/midi';
|
|
157
|
+
|
|
158
|
+
// β
CORRECT: Use Tone.js for timing
|
|
159
|
+
const midi = new Midi(karBuffer);
|
|
160
|
+
const duration = midi.duration; // Accurate!
|
|
161
|
+
|
|
162
|
+
midi.tracks.forEach(track => {
|
|
163
|
+
track.notes.forEach(note => {
|
|
164
|
+
const time = note.time; // β
Correct time in seconds
|
|
165
|
+
const noteNum = note.midi; // MIDI note number
|
|
166
|
+
const velocity = note.velocity;
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// β INCORRECT: Don't manually calculate from ticks
|
|
171
|
+
// const time = ticks * tickResolution; // May be wrong!
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
86
176
|
### NCN to KAR Conversion
|
|
87
177
|
|
|
88
178
|
```typescript
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Release v1.5.1 - Tempo Validation & Documentation
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-01-13
|
|
4
|
+
|
|
5
|
+
## π― New Features: Tempo Validation Utilities
|
|
6
|
+
|
|
7
|
+
### Added Validation Functions
|
|
8
|
+
|
|
9
|
+
This release adds comprehensive tempo and duration validation utilities to ensure conversion accuracy.
|
|
10
|
+
|
|
11
|
+
#### 1. **`validateKarTempo()`** - Validate KAR File Accuracy
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { validateKarTempo } from '@karaplay/file-coder';
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
|
|
17
|
+
const karBuffer = fs.readFileSync('song.kar');
|
|
18
|
+
const validation = validateKarTempo(karBuffer);
|
|
19
|
+
|
|
20
|
+
console.log(`Valid: ${validation.valid}`);
|
|
21
|
+
console.log(`Tempo: ${validation.tempo} BPM`);
|
|
22
|
+
console.log(`Duration: ${validation.durationMinutes}`);
|
|
23
|
+
console.log(`Warnings: ${validation.warnings.length}`);
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Returns:**
|
|
27
|
+
- `valid`: boolean - Overall validation status
|
|
28
|
+
- `tempo`: number - Tempo in BPM
|
|
29
|
+
- `duration`: number - Duration in seconds
|
|
30
|
+
- `durationMinutes`: string - Formatted as "M:SS"
|
|
31
|
+
- `ppq`: number - Ticks per beat
|
|
32
|
+
- `trackCount`: number - Number of tracks
|
|
33
|
+
- `noteCount`: number - Total notes
|
|
34
|
+
- `format`: number - MIDI format (0, 1, or 2)
|
|
35
|
+
- `warnings`: string[] - Non-critical issues
|
|
36
|
+
- `errors`: string[] - Critical issues
|
|
37
|
+
|
|
38
|
+
#### 2. **`compareEmkKarTempo()`** - Compare EMK vs KAR
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { compareEmkKarTempo } from '@karaplay/file-coder';
|
|
42
|
+
|
|
43
|
+
const emkBuffer = fs.readFileSync('song.emk');
|
|
44
|
+
const karBuffer = fs.readFileSync('song.kar');
|
|
45
|
+
|
|
46
|
+
const comparison = compareEmkKarTempo(emkBuffer, karBuffer);
|
|
47
|
+
|
|
48
|
+
console.log(`Format: ${comparison.format}`); // "ZXIO" or "MThd"
|
|
49
|
+
console.log(`Tempo Ratio: ${comparison.tempoRatio}x`); // 1.24x for ZXIO
|
|
50
|
+
console.log(`Expected: ${comparison.expectedRatio}x`); // 1.24x
|
|
51
|
+
console.log(`Match: ${comparison.ratioMatch}`); // true
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Returns:**
|
|
55
|
+
- `emkTempo`: number - Original EMK tempo
|
|
56
|
+
- `karTempo`: number - Converted KAR tempo
|
|
57
|
+
- `tempoRatio`: number - Actual ratio applied
|
|
58
|
+
- `emkDuration`: number - EMK duration
|
|
59
|
+
- `karDuration`: number - KAR duration
|
|
60
|
+
- `durationRatio`: number - Duration change
|
|
61
|
+
- `format`: string - "ZXIO" or "MThd"
|
|
62
|
+
- `expectedRatio`: number - Expected ratio for format
|
|
63
|
+
- `ratioMatch`: boolean - Whether ratio is correct
|
|
64
|
+
|
|
65
|
+
## π Enhanced Documentation
|
|
66
|
+
|
|
67
|
+
### New README Section: "Tempo Handling & Duration Accuracy"
|
|
68
|
+
|
|
69
|
+
Added comprehensive guide covering:
|
|
70
|
+
|
|
71
|
+
1. **Understanding EMK Tempo Conversion**
|
|
72
|
+
- ZXIO format: 1.24x ratio
|
|
73
|
+
- MThd format: 4x, 8x, or 20x (auto-detected)
|
|
74
|
+
|
|
75
|
+
2. **Validating Conversion Accuracy**
|
|
76
|
+
- Using @tonejs/midi for verification
|
|
77
|
+
- Duration and tempo checks
|
|
78
|
+
|
|
79
|
+
3. **Troubleshooting Tempo Issues**
|
|
80
|
+
- Common problems and solutions
|
|
81
|
+
- Format-specific ratios
|
|
82
|
+
|
|
83
|
+
4. **Best Practices for MIDI Playback**
|
|
84
|
+
- β
Use Tone.js for timing (accurate!)
|
|
85
|
+
- β Don't use karaoke-player's getTickResolution() (can be wrong!)
|
|
86
|
+
|
|
87
|
+
### Example Usage
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { Midi } from '@tonejs/midi';
|
|
91
|
+
import { convertEmkToKar, validateKarTempo } from '@karaplay/file-coder';
|
|
92
|
+
|
|
93
|
+
// Convert
|
|
94
|
+
const result = convertEmkToKar({
|
|
95
|
+
inputEmk: 'song.emk',
|
|
96
|
+
outputKar: 'song.kar'
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Validate
|
|
100
|
+
const karBuffer = fs.readFileSync('song.kar');
|
|
101
|
+
const midi = new Midi(karBuffer);
|
|
102
|
+
|
|
103
|
+
console.log(`β
Tempo: ${midi.header.tempos[0].bpm.toFixed(2)} BPM`);
|
|
104
|
+
console.log(`β
Duration: ${(midi.duration / 60).toFixed(2)} minutes`);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## π§ Technical Details
|
|
108
|
+
|
|
109
|
+
### Why Use @tonejs/midi?
|
|
110
|
+
|
|
111
|
+
**Problem:** `karaoke-player`'s `getTickResolution()` can calculate incorrect timing:
|
|
112
|
+
```
|
|
113
|
+
karaoke-player: 5,208 ms/tick β (660x too high!)
|
|
114
|
+
Tone.js: 7.87 ms/tick β
(correct!)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Solution:** Always use `@tonejs/midi` for:
|
|
118
|
+
- Accurate tempo event handling
|
|
119
|
+
- Correct duration calculations
|
|
120
|
+
- Reliable timing information
|
|
121
|
+
|
|
122
|
+
### Tempo Ratios
|
|
123
|
+
|
|
124
|
+
| Format | Original BPM | Ratio | Converted BPM | Duration Example |
|
|
125
|
+
|--------|--------------|-------|---------------|------------------|
|
|
126
|
+
| ZXIO | 64 BPM | 1.24x | 79 BPM | 4:43 β
|
|
|
127
|
+
| MThd | 160 BPM | 4x | 640 BPM | Auto β
|
|
|
128
|
+
| MThd | 142 BPM | 8x | 1136 BPM | Auto β
|
|
|
129
|
+
|
|
130
|
+
## β
Testing
|
|
131
|
+
|
|
132
|
+
**Test Results:**
|
|
133
|
+
```
|
|
134
|
+
Test 1: validateKarTempo()
|
|
135
|
+
β
Valid: true
|
|
136
|
+
β
Tempo: 87.00 BPM
|
|
137
|
+
β
Duration: 4:16
|
|
138
|
+
|
|
139
|
+
Test 2: compareEmkKarTempo() - ZXIO
|
|
140
|
+
β
Format: ZXIO
|
|
141
|
+
β
Tempo: 64 β 79 BPM (1.24x)
|
|
142
|
+
β
Expected: 1.24x, Match: YES β
|
|
143
|
+
|
|
144
|
+
Test 3: Duration Accuracy
|
|
145
|
+
β
Expected: 283s (4:43)
|
|
146
|
+
β
Actual: 283.5s (4:43)
|
|
147
|
+
β
Difference: 0.5s - PASS
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## π What Changed
|
|
151
|
+
|
|
152
|
+
### New Files
|
|
153
|
+
- `src/kar-validator.ts` - Validation utilities
|
|
154
|
+
- `tests/kar-validator.test.ts` - Test suite
|
|
155
|
+
- `RELEASE_v1.5.1.md` - This file
|
|
156
|
+
|
|
157
|
+
### Updated Files
|
|
158
|
+
- `README.md` - Added "Tempo Handling & Duration Accuracy" section
|
|
159
|
+
- `src/index.ts` - Exported validator functions
|
|
160
|
+
- `package.json` - Version bump to 1.5.1
|
|
161
|
+
|
|
162
|
+
## π Migration from v1.5.0
|
|
163
|
+
|
|
164
|
+
No breaking changes! Simply update:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
npm install @karaplay/file-coder@1.5.1
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
New validator functions are optional - existing code continues to work.
|
|
171
|
+
|
|
172
|
+
## π‘ Key Takeaways
|
|
173
|
+
|
|
174
|
+
1. β
**Automatic tempo correction** - no manual adjustment needed
|
|
175
|
+
2. β
**Use @tonejs/midi** for accurate timing
|
|
176
|
+
3. β
**New validation utilities** for quality assurance
|
|
177
|
+
4. β
**Comprehensive documentation** for tempo handling
|
|
178
|
+
5. β οΈ **Duration decrease is normal** - faster tempo = shorter playback
|
|
179
|
+
|
|
180
|
+
## π Documentation
|
|
181
|
+
|
|
182
|
+
- [README.md](./README.md) - Main documentation
|
|
183
|
+
- [EMK_TO_KAR_WORKFLOW.md](./EMK_TO_KAR_WORKFLOW.md) - Conversion workflow
|
|
184
|
+
- [RELEASE_v1.5.0.md](./RELEASE_v1.5.0.md) - Previous release (ZXIO fix)
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
**Status:** β
Ready for Production
|
|
189
|
+
|
|
190
|
+
**Recommended Action:** Update to get validation utilities and improved documentation
|