@karaplay/file-coder 1.5.0 → 1.5.2

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.
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Verify EMK to KAR Conversions Against Reference Data
4
+ *
5
+ * This script:
6
+ * 1. Reads EMK_REFERENCE_DATA.json
7
+ * 2. Converts each EMK file
8
+ * 3. Compares results with reference data
9
+ * 4. Reports any discrepancies
10
+ *
11
+ * Usage:
12
+ * node verify-emk-reference.js
13
+ * node verify-emk-reference.js --verbose
14
+ * node verify-emk-reference.js --file=001.emk
15
+ */
16
+
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+ const { convertEmkToKar, validateKarTempo, compareEmkKarTempo } = require('./dist/index.js');
20
+
21
+ // Parse arguments
22
+ const args = process.argv.slice(2);
23
+ const verbose = args.includes('--verbose');
24
+ const fileArg = args.find(arg => arg.startsWith('--file='));
25
+ const targetFile = fileArg ? fileArg.split('=')[1] : null;
26
+
27
+ // Load reference data
28
+ const referenceFile = path.join(__dirname, 'EMK_REFERENCE_DATA.json');
29
+ if (!fs.existsSync(referenceFile)) {
30
+ console.error('❌ Reference data not found!');
31
+ console.error(' Run: node analyze-all-emk.js');
32
+ process.exit(1);
33
+ }
34
+
35
+ const referenceData = JSON.parse(fs.readFileSync(referenceFile, 'utf8'));
36
+
37
+ console.log('='.repeat(80));
38
+ console.log('VERIFY EMK CONVERSIONS AGAINST REFERENCE DATA');
39
+ console.log('='.repeat(80));
40
+ console.log('');
41
+ console.log(`Reference Data: ${referenceData.generatedAt}`);
42
+ console.log(`Library Version: ${referenceData.version}`);
43
+ console.log(`Total Songs: ${referenceData.totalFiles}`);
44
+ console.log('');
45
+
46
+ // Filter songs to test
47
+ let songsToTest = referenceData.songs.filter(s => s.status === 'success');
48
+ if (targetFile) {
49
+ songsToTest = songsToTest.filter(s => s.filename === targetFile);
50
+ if (songsToTest.length === 0) {
51
+ console.error(`❌ File not found in reference data: ${targetFile}`);
52
+ process.exit(1);
53
+ }
54
+ }
55
+
56
+ console.log(`Testing ${songsToTest.length} songs...`);
57
+ console.log('');
58
+
59
+ const emkDir = path.join(__dirname, 'songs/emk');
60
+ const tempDir = path.join(__dirname, 'temp');
61
+
62
+ if (!fs.existsSync(tempDir)) {
63
+ fs.mkdirSync(tempDir, { recursive: true });
64
+ }
65
+
66
+ const results = {
67
+ passed: 0,
68
+ failed: 0,
69
+ warnings: 0,
70
+ details: []
71
+ };
72
+
73
+ songsToTest.forEach((refSong, idx) => {
74
+ const testNum = idx + 1;
75
+ const totalTests = songsToTest.length;
76
+
77
+ console.log(`[${testNum}/${totalTests}] Testing: ${refSong.filename}`);
78
+
79
+ if (verbose) {
80
+ console.log(` Reference: ${refSong.title} - ${refSong.artist}`);
81
+ console.log(` Expected Duration: ${refSong.durationFormatted} (${refSong.duration.toFixed(2)}s)`);
82
+ console.log(` Expected Tempo: ${refSong.karTempo.toFixed(2)} BPM`);
83
+ }
84
+
85
+ try {
86
+ const inputEmk = path.join(emkDir, refSong.filename);
87
+ const outputKar = path.join(tempDir, refSong.filename.replace('.emk', '_verify.kar'));
88
+
89
+ // Convert
90
+ const result = convertEmkToKar({
91
+ inputEmk: inputEmk,
92
+ outputKar: outputKar,
93
+ keepIntermediateFiles: false
94
+ });
95
+
96
+ if (!result.success) {
97
+ console.log(` ❌ FAIL: Conversion failed`);
98
+ results.failed++;
99
+ results.details.push({
100
+ file: refSong.filename,
101
+ status: 'failed',
102
+ reason: 'Conversion failed'
103
+ });
104
+ console.log('');
105
+ return;
106
+ }
107
+
108
+ // Validate
109
+ const emkBuffer = fs.readFileSync(inputEmk);
110
+ const karBuffer = fs.readFileSync(outputKar);
111
+
112
+ const validation = validateKarTempo(karBuffer);
113
+ const comparison = compareEmkKarTempo(emkBuffer, karBuffer);
114
+
115
+ // Compare with reference
116
+ const issues = [];
117
+
118
+ // Check tempo
119
+ const tempoDiff = Math.abs(validation.tempo - refSong.karTempo);
120
+ if (tempoDiff > 0.1) {
121
+ issues.push(`Tempo mismatch: expected ${refSong.karTempo.toFixed(2)}, got ${validation.tempo.toFixed(2)}`);
122
+ }
123
+
124
+ // Check duration (±2 seconds tolerance)
125
+ const durationDiff = Math.abs(validation.duration - refSong.duration);
126
+ if (durationDiff > 2) {
127
+ issues.push(`Duration mismatch: expected ${refSong.duration.toFixed(2)}s, got ${validation.duration.toFixed(2)}s (diff: ${durationDiff.toFixed(2)}s)`);
128
+ }
129
+
130
+ // Check tempo ratio (allow 0.1 tolerance)
131
+ const ratioDiff = Math.abs(comparison.tempoRatio - refSong.expectedRatio);
132
+ if (ratioDiff > 0.1) {
133
+ issues.push(`Tempo ratio mismatch: expected ${refSong.expectedRatio}x, got ${comparison.tempoRatio.toFixed(2)}x`);
134
+ }
135
+
136
+ // Check format
137
+ if (comparison.format !== refSong.format) {
138
+ issues.push(`Format mismatch: expected ${refSong.format}, got ${comparison.format}`);
139
+ }
140
+
141
+ // Report results
142
+ if (issues.length === 0) {
143
+ console.log(` ✅ PASS: All checks passed`);
144
+ if (verbose) {
145
+ console.log(` Duration: ${validation.duration.toFixed(2)}s (expected ${refSong.duration.toFixed(2)}s)`);
146
+ console.log(` Tempo: ${validation.tempo.toFixed(2)} BPM (expected ${refSong.karTempo.toFixed(2)} BPM)`);
147
+ console.log(` Ratio: ${comparison.tempoRatio.toFixed(2)}x (expected ${refSong.expectedRatio}x)`);
148
+ }
149
+ results.passed++;
150
+ results.details.push({
151
+ file: refSong.filename,
152
+ status: 'passed'
153
+ });
154
+ } else {
155
+ // Check if they're warnings or failures
156
+ const hasFailure = issues.some(i => i.includes('mismatch') && !i.includes('Duration'));
157
+
158
+ if (hasFailure) {
159
+ console.log(` ❌ FAIL: ${issues.length} issue(s) found`);
160
+ results.failed++;
161
+ results.details.push({
162
+ file: refSong.filename,
163
+ status: 'failed',
164
+ issues: issues
165
+ });
166
+ } else {
167
+ console.log(` ⚠️ WARNING: ${issues.length} minor issue(s)`);
168
+ results.warnings++;
169
+ results.details.push({
170
+ file: refSong.filename,
171
+ status: 'warning',
172
+ issues: issues
173
+ });
174
+ }
175
+
176
+ if (verbose || hasFailure) {
177
+ issues.forEach(issue => console.log(` - ${issue}`));
178
+ }
179
+ }
180
+
181
+ // Clean up
182
+ if (fs.existsSync(outputKar)) {
183
+ fs.unlinkSync(outputKar);
184
+ }
185
+
186
+ } catch (error) {
187
+ console.log(` ❌ ERROR: ${error.message}`);
188
+ results.failed++;
189
+ results.details.push({
190
+ file: refSong.filename,
191
+ status: 'error',
192
+ error: error.message
193
+ });
194
+ }
195
+
196
+ console.log('');
197
+ });
198
+
199
+ // Print summary
200
+ console.log('='.repeat(80));
201
+ console.log('SUMMARY');
202
+ console.log('='.repeat(80));
203
+ console.log('');
204
+ console.log(`✅ Passed: ${results.passed}/${songsToTest.length}`);
205
+ console.log(`⚠️ Warnings: ${results.warnings}/${songsToTest.length}`);
206
+ console.log(`❌ Failed: ${results.failed}/${songsToTest.length}`);
207
+ console.log('');
208
+
209
+ // Exit code
210
+ const exitCode = results.failed > 0 ? 1 : 0;
211
+
212
+ if (exitCode === 0) {
213
+ console.log('🎉 All tests passed!');
214
+ } else {
215
+ console.log('❌ Some tests failed. Review the output above.');
216
+ console.log('');
217
+ console.log('Failed files:');
218
+ results.details.filter(d => d.status === 'failed' || d.status === 'error').forEach(d => {
219
+ console.log(` - ${d.file}`);
220
+ if (d.issues) {
221
+ d.issues.forEach(issue => console.log(` ${issue}`));
222
+ }
223
+ if (d.error) {
224
+ console.log(` Error: ${d.error}`);
225
+ }
226
+ });
227
+ }
228
+
229
+ console.log('');
230
+ process.exit(exitCode);
@@ -1,169 +0,0 @@
1
- const fs = require('fs');
2
- const { Midi } = require('@tonejs/midi');
3
- const { decodeEmkServer } = require('./dist/index.js');
4
-
5
- console.log('='.repeat(80));
6
- console.log('ANALYZING EMK CURSOR DATA FOR CORRECT TEMPO');
7
- console.log('='.repeat(80));
8
- console.log('');
9
-
10
- // Decode EMK
11
- const emkBuffer = fs.readFileSync('./songs/fix/001_original_emk.emk');
12
- const decoded = decodeEmkServer(emkBuffer);
13
-
14
- console.log('📁 EMK File Info:');
15
- console.log(' Format:', decoded.isZxioFormat ? 'ZXIO' : 'MThd');
16
- console.log(' Has MIDI:', decoded.midi ? 'Yes' : 'No');
17
- console.log(' Has Lyric:', decoded.lyric ? 'Yes' : 'No');
18
- console.log(' Has Cursor:', decoded.cursor ? 'Yes' : 'No');
19
- console.log('');
20
-
21
- // Parse MIDI
22
- const midi = new Midi(decoded.midi);
23
- console.log('📊 MIDI Info:');
24
- console.log(' PPQ:', midi.header.ppq);
25
- console.log(' Tempo:', midi.header.tempos[0].bpm.toFixed(2), 'BPM');
26
- console.log(' Duration:', midi.duration.toFixed(2), 'seconds');
27
- console.log(' Total Ticks:', midi.header.tempos[0].ticks);
28
- console.log('');
29
-
30
- // Find last note and last tempo event
31
- let maxTicks = 0;
32
- midi.tracks.forEach(track => {
33
- track.notes.forEach(note => {
34
- const noteTicks = note.ticks + (note.durationTicks || 0);
35
- if (noteTicks > maxTicks) maxTicks = noteTicks;
36
- });
37
- });
38
-
39
- // Find last tempo/event
40
- let lastEventTicks = 0;
41
- midi.tracks.forEach(track => {
42
- let currentTicks = 0;
43
- track.forEach(event => {
44
- currentTicks += event.deltaTime;
45
- if (currentTicks > lastEventTicks) lastEventTicks = currentTicks;
46
- });
47
- });
48
-
49
- console.log('🎵 MIDI Ticks Info:');
50
- console.log(' Last Note Ticks:', maxTicks);
51
- console.log(' Last Event Ticks:', lastEventTicks);
52
- console.log('');
53
-
54
- // Analyze cursor data
55
- const cursorBuffer = decoded.cursor;
56
- console.log('⏱️ Cursor Data:');
57
- console.log(' Buffer size:', cursorBuffer.length, 'bytes');
58
- console.log(' Cursor values:', cursorBuffer.length / 2, 'values (2 bytes each)');
59
- console.log('');
60
-
61
- // Read cursor values
62
- const cursorValues = [];
63
- for (let i = 0; i < cursorBuffer.length; i += 2) {
64
- cursorValues.push(cursorBuffer.readUInt16LE(i));
65
- }
66
-
67
- // Find min, max, average
68
- const maxCursor = Math.max(...cursorValues);
69
- const minCursor = Math.min(...cursorValues.filter(v => v > 0));
70
- const avgCursor = cursorValues.reduce((a, b) => a + b, 0) / cursorValues.length;
71
-
72
- console.log(' Min cursor (non-zero):', minCursor);
73
- console.log(' Max cursor:', maxCursor);
74
- console.log(' Average cursor:', avgCursor.toFixed(2));
75
- console.log('');
76
-
77
- // Cursor represents ticks in the final KAR
78
- // If cursor max = X and we multiply by ratio Y, final ticks = X * Y
79
- // Final duration = (X * Y) / (PPQ * final_BPM / 60)
80
-
81
- console.log('🔍 Cursor to Ticks Analysis:');
82
- console.log('');
83
-
84
- // Test different cursor multipliers
85
- const ppq = midi.header.ppq;
86
- const targetDuration = 285; // 4:45 in seconds
87
-
88
- console.log(' Target duration: 4:45 (285 seconds)');
89
- console.log('');
90
-
91
- // Cursor multiply ratios to test
92
- const ratios = [1, 2, 3, 4, 5, 6];
93
-
94
- console.log(' Testing cursor multiply ratios:');
95
- console.log('');
96
-
97
- ratios.forEach(cursorRatio => {
98
- const finalTicks = maxCursor * cursorRatio;
99
-
100
- // For each cursor ratio, calculate what tempo would give us 4:45
101
- const requiredTempo = (finalTicks / targetDuration) * (60 / ppq);
102
-
103
- // Calculate tempo ratio from original EMK tempo
104
- const emkTempo = midi.header.tempos[0].bpm;
105
- const tempoRatio = requiredTempo / emkTempo;
106
-
107
- console.log(` Cursor ×${cursorRatio}:`);
108
- console.log(` Final ticks: ${finalTicks}`);
109
- console.log(` Required tempo: ${requiredTempo.toFixed(2)} BPM`);
110
- console.log(` Tempo ratio: ${tempoRatio.toFixed(2)}x (from ${emkTempo} BPM)`);
111
-
112
- // Check if this makes sense
113
- if (Math.abs(tempoRatio - 1.23) < 0.1) {
114
- console.log(` ✅ MATCHES! This gives us 4:45 duration`);
115
- }
116
- console.log('');
117
- });
118
-
119
- // Now let's check: what if we DON'T multiply cursor?
120
- console.log('📐 Analysis: Cursor as Direct Ticks (no multiply)');
121
- console.log('');
122
-
123
- const cursorAsTicks = maxCursor;
124
- const tempoFor285 = (cursorAsTicks / targetDuration) * (60 / ppq);
125
- const ratioFor285 = tempoFor285 / midi.header.tempos[0].bpm;
126
-
127
- console.log(' Max cursor value:', cursorAsTicks);
128
- console.log(' If cursor = ticks directly:');
129
- console.log(' Required tempo:', tempoFor285.toFixed(2), 'BPM');
130
- console.log(' Tempo ratio:', ratioFor285.toFixed(2) + 'x');
131
- console.log('');
132
-
133
- // Check with cursor × 4
134
- const cursor4x = maxCursor * 4;
135
- const tempoFor285_4x = (cursor4x / targetDuration) * (60 / ppq);
136
- const ratioFor285_4x = tempoFor285_4x / midi.header.tempos[0].bpm;
137
-
138
- console.log(' If cursor × 4:');
139
- console.log(' Final ticks:', cursor4x);
140
- console.log(' Required tempo:', tempoFor285_4x.toFixed(2), 'BPM');
141
- console.log(' Tempo ratio:', ratioFor285_4x.toFixed(2) + 'x');
142
- console.log('');
143
-
144
- console.log('🎯 CONCLUSION:');
145
- console.log('');
146
- console.log(' For song duration 4:45 (285 seconds):');
147
- console.log('');
148
-
149
- if (ratioFor285_4x >= 1 && ratioFor285_4x <= 2) {
150
- console.log(' ✅ Cursor should be multiplied by 4');
151
- console.log(' ✅ Tempo ratio should be:', ratioFor285_4x.toFixed(2) + 'x');
152
- console.log(' ✅ Final tempo:', tempoFor285_4x.toFixed(2), 'BPM');
153
- console.log('');
154
- console.log(' Current (wrong):');
155
- console.log(' Tempo ratio: 2.78x (ZXIO formula)');
156
- console.log(' Final tempo: 178.09 BPM');
157
- console.log(' Duration: 2:11 ❌');
158
- console.log('');
159
- console.log(' Should be:');
160
- console.log(' Tempo ratio:', ratioFor285_4x.toFixed(2) + 'x');
161
- console.log(' Final tempo:', tempoFor285_4x.toFixed(2), 'BPM');
162
- console.log(' Duration: 4:45 ✅');
163
- } else if (ratioFor285 >= 1 && ratioFor285 <= 2) {
164
- console.log(' ✅ Cursor should NOT be multiplied (use raw values)');
165
- console.log(' ✅ Tempo ratio should be:', ratioFor285.toFixed(2) + 'x');
166
- console.log(' ✅ Final tempo:', tempoFor285.toFixed(2), 'BPM');
167
- }
168
-
169
- console.log('');
@@ -1,124 +0,0 @@
1
- const fs = require('fs');
2
- const { Midi } = require('@tonejs/midi');
3
- const { decodeEmkServer } = require('./dist/index.js');
4
- const { parseMidi } = require('midi-file');
5
-
6
- console.log('='.repeat(80));
7
- console.log('ANALYZING EMK FOR CORRECT TEMPO - Simple Version');
8
- console.log('='.repeat(80));
9
- console.log('');
10
-
11
- // Decode EMK
12
- const emkBuffer = fs.readFileSync('./songs/fix/001_original_emk.emk');
13
- const decoded = decodeEmkServer(emkBuffer);
14
-
15
- console.log('📁 EMK File Info:');
16
- console.log(' Format:', decoded.isZxioFormat ? 'ZXIO' : 'MThd');
17
- console.log('');
18
-
19
- // Parse with midi-file to get raw ticks
20
- const rawMidi = parseMidi(decoded.midi);
21
- console.log('📊 Raw MIDI Info:');
22
- console.log(' PPQ:', rawMidi.header.ticksPerBeat);
23
- console.log('');
24
-
25
- // Find max ticks
26
- let maxTicks = 0;
27
- rawMidi.tracks.forEach((track, idx) => {
28
- let currentTicks = 0;
29
- track.forEach(event => {
30
- currentTicks += event.deltaTime;
31
- if (currentTicks > maxTicks) maxTicks = currentTicks;
32
- });
33
- });
34
-
35
- console.log(' Max MIDI ticks:', maxTicks);
36
- console.log('');
37
-
38
- // Get tempo
39
- let tempo = null;
40
- rawMidi.tracks.forEach(track => {
41
- track.forEach(event => {
42
- if (event.type === 'setTempo' && !tempo) {
43
- tempo = (60000000 / event.microsecondsPerBeat).toFixed(2);
44
- }
45
- });
46
- });
47
-
48
- console.log(' Current tempo:', tempo, 'BPM');
49
- console.log('');
50
-
51
- // Parse cursor
52
- const cursorBuffer = decoded.cursor;
53
- const cursorValues = [];
54
- for (let i = 0; i < cursorBuffer.length; i += 2) {
55
- cursorValues.push(cursorBuffer.readUInt16LE(i));
56
- }
57
-
58
- const maxCursor = Math.max(...cursorValues);
59
- console.log('⏱️ Cursor Data:');
60
- console.log(' Max cursor value:', maxCursor);
61
- console.log('');
62
-
63
- // Target duration
64
- const targetDuration = 285; // 4 minutes 45 seconds
65
- const ppq = rawMidi.header.ticksPerBeat;
66
-
67
- console.log('🎯 Target: 4:45 (285 seconds)');
68
- console.log('');
69
-
70
- // Calculate what we need
71
- console.log('📐 Calculations:');
72
- console.log('');
73
-
74
- // Scenario 1: Don't multiply cursor (use raw)
75
- console.log(' [1] If cursor = ticks (no multiply):');
76
- const finalTicks1 = maxCursor;
77
- const reqTempo1 = (finalTicks1 / targetDuration) * (60 / ppq);
78
- const ratio1 = reqTempo1 / parseFloat(tempo);
79
- console.log(' Final ticks:', finalTicks1);
80
- console.log(' Required tempo:', reqTempo1.toFixed(2), 'BPM');
81
- console.log(' Tempo ratio:', ratio1.toFixed(2) + 'x');
82
- const dur1 = (finalTicks1 / (ppq * reqTempo1 / 60));
83
- console.log(' Duration:', dur1.toFixed(2), 'seconds =', (dur1/60).toFixed(2), 'minutes');
84
- console.log('');
85
-
86
- // Scenario 2: Multiply cursor by 4
87
- console.log(' [2] If cursor × 4:');
88
- const finalTicks2 = maxCursor * 4;
89
- const reqTempo2 = (finalTicks2 / targetDuration) * (60 / ppq);
90
- const ratio2 = reqTempo2 / parseFloat(tempo);
91
- console.log(' Final ticks:', finalTicks2);
92
- console.log(' Required tempo:', reqTempo2.toFixed(2), 'BPM');
93
- console.log(' Tempo ratio:', ratio2.toFixed(2) + 'x');
94
- const dur2 = (finalTicks2 / (ppq * reqTempo2 / 60));
95
- console.log(' Duration:', dur2.toFixed(2), 'seconds =', (dur2/60).toFixed(2), 'minutes');
96
- console.log('');
97
-
98
- // Current implementation (ZXIO)
99
- console.log(' [3] Current (ZXIO 2.78x, cursor × 4):');
100
- const currentTempo = parseFloat(tempo) * 2.78;
101
- const currentTicks = maxCursor * 4;
102
- const currentDur = (currentTicks / (ppq * currentTempo / 60));
103
- console.log(' Tempo:', currentTempo.toFixed(2), 'BPM');
104
- console.log(' Ticks:', currentTicks);
105
- console.log(' Duration:', currentDur.toFixed(2), 'seconds =', (currentDur/60).toFixed(2), 'minutes ❌');
106
- console.log('');
107
-
108
- // Recommendation
109
- console.log('✅ RECOMMENDATION:');
110
- console.log('');
111
-
112
- if (Math.abs(ratio2 - 1.23) < 0.3 && Math.abs(dur2 - 285) < 5) {
113
- console.log(' Use: Cursor × 4, Tempo ratio', ratio2.toFixed(2) + 'x');
114
- console.log(' Final tempo:', reqTempo2.toFixed(2), 'BPM');
115
- console.log(' Duration: 4:45 ✅');
116
- console.log('');
117
- console.log(' ZXIO should use ratio:', ratio2.toFixed(2) + 'x (NOT 2.78x)');
118
- } else if (Math.abs(ratio1 - 1.23) < 0.3 && Math.abs(dur1 - 285) < 5) {
119
- console.log(' Use: No cursor multiply, Tempo ratio', ratio1.toFixed(2) + 'x');
120
- console.log(' Final tempo:', reqTempo1.toFixed(2), 'BPM');
121
- console.log(' Duration: 4:45 ✅');
122
- }
123
-
124
- console.log('');
@@ -1,69 +0,0 @@
1
- const fs = require('fs');
2
- const { Midi } = require('@tonejs/midi');
3
-
4
- console.log('='.repeat(80));
5
- console.log('CHECKING REAL DURATION - 001.emk');
6
- console.log('='.repeat(80));
7
- console.log('');
8
-
9
- // Check original KAR (the reference file)
10
- const originalKarPath = './songs/fix/001_kar_convert_needtofix_tempo_fast.kar';
11
- console.log('📁 Original KAR (Reference):');
12
- console.log(' File:', originalKarPath);
13
- console.log(' Note: Filename says "needtofix_tempo_fast" - might be WRONG!');
14
- console.log('');
15
-
16
- const karBuffer = fs.readFileSync(originalKarPath);
17
- const karMidi = new Midi(karBuffer);
18
-
19
- console.log(' Tempo:', karMidi.header.tempos[0].bpm.toFixed(2), 'BPM');
20
- console.log(' Duration:', karMidi.duration.toFixed(2), 'seconds');
21
- console.log(' Duration:', (karMidi.duration / 60).toFixed(2), 'minutes');
22
- console.log('');
23
-
24
- // Check EMK MIDI
25
- const { decodeEmkServer } = require('./dist/index.js');
26
- const emkBuffer = fs.readFileSync('./songs/fix/001_original_emk.emk');
27
- const decoded = decodeEmkServer(emkBuffer);
28
- const emkMidi = new Midi(decoded.midi);
29
-
30
- console.log('📁 EMK MIDI (Original):');
31
- console.log(' Tempo:', emkMidi.header.tempos[0].bpm.toFixed(2), 'BPM');
32
- console.log(' Duration:', emkMidi.duration.toFixed(2), 'seconds');
33
- console.log(' Duration:', (emkMidi.duration / 60).toFixed(2), 'minutes');
34
- console.log('');
35
-
36
- // Calculate what tempo should be for 4:45 (285 seconds)
37
- const targetDuration = 285; // 4 minutes 45 seconds
38
- const emkDuration = emkMidi.duration;
39
- const emkTempo = emkMidi.header.tempos[0].bpm;
40
-
41
- const requiredRatio = emkDuration / targetDuration;
42
- const requiredTempo = emkTempo * requiredRatio;
43
-
44
- console.log('🎯 For REAL song duration (4:45):');
45
- console.log(' Target Duration:', targetDuration, 'seconds (4:45)');
46
- console.log(' Required Ratio:', requiredRatio.toFixed(2) + 'x');
47
- console.log(' Required Tempo:', requiredTempo.toFixed(2), 'BPM');
48
- console.log('');
49
-
50
- console.log('📊 COMPARISON:');
51
- console.log('');
52
- console.log(' Current implementation:');
53
- console.log(' Ratio: 2.78x (ZXIO formula)');
54
- console.log(' Tempo: 178.09 BPM');
55
- console.log(' Duration: 126 seconds (2:06)');
56
- console.log(' Status: ❌ TOO FAST! (Should be 4:45)');
57
- console.log('');
58
- console.log(' Correct implementation:');
59
- console.log(' Ratio:', requiredRatio.toFixed(2) + 'x');
60
- console.log(' Tempo:', requiredTempo.toFixed(2), 'BPM');
61
- console.log(' Duration: 285 seconds (4:45)');
62
- console.log(' Status: ✅ CORRECT!');
63
- console.log('');
64
-
65
- console.log('⚠️ CONCLUSION:');
66
- console.log(' The reference KAR file (001_kar_convert_needtofix_tempo_fast.kar)');
67
- console.log(' is WRONG! It has tempo that is TOO FAST (178 BPM).');
68
- console.log(' The real song should be 4:45, not 2:11!');
69
- console.log('');
Binary file