@karaplay/file-coder 1.4.8 → 1.5.0

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.
Files changed (43) hide show
  1. package/DEMO_ENHANCED.md +168 -0
  2. package/DEMO_FIXED.md +57 -0
  3. package/DEMO_GUIDE.md +204 -0
  4. package/DEMO_README.md +193 -0
  5. package/DEMO_WORKING.md +157 -0
  6. package/RELEASE_v1.4.9.md +144 -0
  7. package/RELEASE_v1.5.0.md +91 -0
  8. package/WHY_DURATION_DECREASES.md +176 -0
  9. package/analyze-cursor-pattern.js +131 -0
  10. package/analyze-emk-cursor.js +169 -0
  11. package/analyze-emk-simple.js +124 -0
  12. package/analyze-tempo-duration.js +243 -0
  13. package/calculate-correct-ratio.js +97 -0
  14. package/check-real-duration.js +69 -0
  15. package/compare-kar-lyrics-timing.js +110 -0
  16. package/demo-client.html +722 -0
  17. package/demo-server.js +184 -0
  18. package/demo-simple.html +712 -0
  19. package/dist/emk-to-kar.browser.js +7 -4
  20. package/dist/emk-to-kar.js +7 -6
  21. package/dist/ncntokar.browser.d.ts +2 -1
  22. package/dist/ncntokar.browser.js +5 -3
  23. package/dist/ncntokar.d.ts +2 -1
  24. package/dist/ncntokar.js +6 -4
  25. package/find-zxio-tempo-ratio.js +118 -0
  26. package/match-cursor-to-lyrics.js +118 -0
  27. package/package.json +6 -2
  28. package/songs/emk/001_original_emk.emk +0 -0
  29. package/songs/fix/001_kar_convert_needtofix_tempo_fast.kar +0 -0
  30. package/songs/fix/001_original_emk.emk +0 -0
  31. package/temp/test_output.kar +0 -0
  32. package/test-all-emk-durations.js +109 -0
  33. package/test-all-emk-final.js +97 -0
  34. package/test-convert-001.js +130 -0
  35. package/analyze-failed-emk.js +0 -156
  36. package/check-gr.js +0 -19
  37. package/debug-emk-blocks.js +0 -123
  38. package/debug-midi-source.js +0 -108
  39. package/debug-tempo.js +0 -135
  40. package/debug-timing.js +0 -159
  41. package/find-midi-in-block.js +0 -96
  42. package/fix-tempo.js +0 -155
  43. package/kar-diagnostic.js +0 -158
package/debug-timing.js DELETED
@@ -1,159 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const { parseMidi } = require('midi-file');
4
-
5
- // Parse KAR file (which is a MIDI file)
6
- function analyzeKarFile(filePath) {
7
- console.log(`\n=== Analyzing: ${path.basename(filePath)} ===`);
8
-
9
- const buffer = fs.readFileSync(filePath);
10
- const midi = parseMidi(buffer);
11
-
12
- console.log(`Format: ${midi.header.format}`);
13
- console.log(`Tracks: ${midi.tracks.length}`);
14
- console.log(`Ticks per beat: ${midi.header.ticksPerBeat}`);
15
-
16
- // Find karaoke track (track with lyric text events)
17
- let karaokeTrackIdx = -1;
18
- let maxTextEvents = 0;
19
-
20
- midi.tracks.forEach((track, idx) => {
21
- const textEvents = track.filter(e => e.type === 'text' && e.text && e.text.trim());
22
- if (textEvents.length > maxTextEvents) {
23
- maxTextEvents = textEvents.length;
24
- karaokeTrackIdx = idx;
25
- }
26
- });
27
-
28
- if (karaokeTrackIdx === -1) {
29
- console.log('No karaoke track found!');
30
- return null;
31
- }
32
-
33
- console.log(`\nKaraoke track index: ${karaokeTrackIdx}`);
34
- console.log(`Text events: ${maxTextEvents}`);
35
-
36
- // Extract timing information
37
- const karaokeTrack = midi.tracks[karaokeTrackIdx];
38
- let absoluteTime = 0;
39
- const timings = [];
40
-
41
- karaokeTrack.forEach((event, idx) => {
42
- absoluteTime += event.deltaTime;
43
-
44
- if (event.type === 'text' && event.text && event.text.trim()) {
45
- timings.push({
46
- index: idx,
47
- deltaTime: event.deltaTime,
48
- absoluteTime: absoluteTime,
49
- text: event.text
50
- });
51
- }
52
- });
53
-
54
- // Show first 20 timing entries
55
- console.log(`\nFirst 20 timing entries:`);
56
- timings.slice(0, 20).forEach((t, i) => {
57
- console.log(`${i+1}. Delta: ${t.deltaTime.toString().padStart(6)}, Absolute: ${t.absoluteTime.toString().padStart(8)}, Text: "${t.text}"`);
58
- });
59
-
60
- // Calculate average delta between events
61
- const deltas = timings.map(t => t.deltaTime).filter(d => d > 0);
62
- const avgDelta = deltas.reduce((a, b) => a + b, 0) / deltas.length;
63
- const minDelta = Math.min(...deltas);
64
- const maxDelta = Math.max(...deltas);
65
-
66
- console.log(`\nDelta statistics:`);
67
- console.log(` Average: ${avgDelta.toFixed(2)}`);
68
- console.log(` Min: ${minDelta}`);
69
- console.log(` Max: ${maxDelta}`);
70
- console.log(` Total events: ${timings.length}`);
71
-
72
- return {
73
- ticksPerBeat: midi.header.ticksPerBeat,
74
- timings: timings,
75
- avgDelta: avgDelta,
76
- minDelta: minDelta,
77
- maxDelta: maxDelta
78
- };
79
- }
80
-
81
- // Compare two KAR files
82
- function compareKarFiles(originalPath, convertedPath) {
83
- console.log('\n' + '='.repeat(80));
84
- console.log('COMPARING TIMING');
85
- console.log('='.repeat(80));
86
-
87
- const original = analyzeKarFile(originalPath);
88
- const converted = analyzeKarFile(convertedPath);
89
-
90
- if (!original || !converted) {
91
- console.log('Cannot compare - one of the files has no karaoke track');
92
- return;
93
- }
94
-
95
- console.log('\n' + '='.repeat(80));
96
- console.log('COMPARISON RESULTS');
97
- console.log('='.repeat(80));
98
-
99
- console.log(`\nTicks per beat:`);
100
- console.log(` Original: ${original.ticksPerBeat}`);
101
- console.log(` Converted: ${converted.ticksPerBeat}`);
102
-
103
- console.log(`\nAverage delta time:`);
104
- console.log(` Original: ${original.avgDelta.toFixed(2)}`);
105
- console.log(` Converted: ${converted.avgDelta.toFixed(2)}`);
106
- console.log(` Ratio: ${(converted.avgDelta / original.avgDelta).toFixed(4)}x`);
107
-
108
- // Compare first 20 events side by side
109
- console.log(`\nFirst 20 events comparison:`);
110
- console.log(`${'Idx'.padEnd(4)} | ${'Original Delta'.padEnd(14)} | ${'Converted Delta'.padEnd(15)} | ${'Ratio'.padEnd(8)} | Text`);
111
- console.log('-'.repeat(100));
112
-
113
- const minLength = Math.min(original.timings.length, converted.timings.length, 20);
114
- for (let i = 0; i < minLength; i++) {
115
- const origDelta = original.timings[i].deltaTime;
116
- const convDelta = converted.timings[i].deltaTime;
117
- const ratio = origDelta > 0 ? (convDelta / origDelta).toFixed(4) : 'N/A';
118
-
119
- console.log(
120
- `${(i+1).toString().padEnd(4)} | ${origDelta.toString().padEnd(14)} | ${convDelta.toString().padEnd(15)} | ${ratio.toString().padEnd(8)} | "${original.timings[i].text}"`
121
- );
122
- }
123
- }
124
-
125
- // Main
126
- const originalKar = './songs/fix/song.kar';
127
- const emkFile = './songs/fix/song.emk';
128
-
129
- console.log('Step 1: Analyzing original KAR file...');
130
- analyzeKarFile(originalKar);
131
-
132
- console.log('\n\nStep 2: Converting EMK to KAR...');
133
- const { convertEmkToKar } = require('./dist/index.js');
134
-
135
- const tempKar = './songs/fix/converted-test.kar';
136
- try {
137
- const result = convertEmkToKar({
138
- inputEmk: emkFile,
139
- outputKar: tempKar,
140
- keepIntermediateFiles: false
141
- });
142
-
143
- console.log(`Conversion ${result.success ? 'SUCCESS' : 'FAILED'}`);
144
- if (result.warnings.length > 0) {
145
- console.log('Warnings:', result.warnings);
146
- }
147
-
148
- console.log('\n\nStep 3: Comparing original vs converted...');
149
- compareKarFiles(originalKar, tempKar);
150
-
151
- // Cleanup
152
- if (fs.existsSync(tempKar)) {
153
- fs.unlinkSync(tempKar);
154
- console.log('\nCleaned up temporary file.');
155
- }
156
- } catch (error) {
157
- console.error('Error during conversion:', error.message);
158
- console.error(error.stack);
159
- }
@@ -1,96 +0,0 @@
1
- const fs = require('fs');
2
- const { inflateSync } = require('zlib');
3
- const { parseMidi } = require('midi-file');
4
-
5
- const XOR_KEY = Buffer.from([0xAF, 0xF2, 0x4C, 0x9C, 0xE9, 0xEA, 0x99, 0x43]);
6
- const MAGIC_SIGNATURE = '.SFDS';
7
- const ZLIB_SECOND_BYTES = new Set([0x01, 0x5E, 0x9C, 0xDA, 0x7D, 0x20, 0xBB]);
8
-
9
- function xorDecrypt(data) {
10
- const decrypted = Buffer.alloc(data.length);
11
- for (let i = 0; i < data.length; i++) {
12
- decrypted[i] = data[i] ^ XOR_KEY[i % XOR_KEY.length];
13
- }
14
- return decrypted;
15
- }
16
-
17
- console.log('Finding MIDI in Block 3...\n');
18
-
19
- const emkBuffer = fs.readFileSync('./songs/emk/failed01.emk');
20
- const decryptedBuffer = xorDecrypt(emkBuffer);
21
-
22
- // Find block 3 (offset 387)
23
- const block3Compressed = decryptedBuffer.subarray(387);
24
- const block3 = inflateSync(block3Compressed);
25
-
26
- console.log(`Block 3 size: ${block3.length} bytes`);
27
- console.log(`First 64 bytes (hex):`);
28
- console.log(block3.subarray(0, 64).toString('hex').match(/.{1,2}/g).join(' '));
29
- console.log('');
30
-
31
- // Search for 'MThd'
32
- const mthdIndex = block3.indexOf('MThd');
33
- console.log(`Index of 'MThd': ${mthdIndex}`);
34
-
35
- if (mthdIndex >= 0) {
36
- console.log(`\nFound 'MThd' at offset ${mthdIndex}!`);
37
- console.log(`\nBytes before 'MThd':`);
38
- console.log(` HEX: ${block3.subarray(0, mthdIndex).toString('hex')}`);
39
- console.log(` ASCII: ${block3.subarray(0, mthdIndex).toString('ascii')}`);
40
- console.log(` Length: ${mthdIndex} bytes`);
41
-
42
- // Extract MIDI starting from MThd
43
- const midiData = block3.subarray(mthdIndex);
44
- console.log(`\nMIDI data size: ${midiData.length} bytes`);
45
- console.log(`First 32 bytes of MIDI:`);
46
- console.log(midiData.subarray(0, 32).toString('hex').match(/.{1,2}/g).join(' '));
47
-
48
- // Try to parse it
49
- try {
50
- const midi = parseMidi(midiData);
51
- console.log(`\n✅ MIDI parsed successfully!`);
52
- console.log(` Format: ${midi.header.format}`);
53
- console.log(` Tracks: ${midi.tracks.length}`);
54
- console.log(` Ticks per beat: ${midi.header.ticksPerBeat}`);
55
-
56
- // Check tempo
57
- let tempoFound = false;
58
- midi.tracks.forEach((track, idx) => {
59
- track.forEach(event => {
60
- if (event.type === 'setTempo' && !tempoFound) {
61
- const bpm = (60000000 / event.microsecondsPerBeat).toFixed(2);
62
- console.log(` Initial tempo: ${bpm} BPM (${event.microsecondsPerBeat} µs/beat) in Track ${idx}`);
63
- tempoFound = true;
64
- }
65
- });
66
- });
67
-
68
- if (!tempoFound) {
69
- console.log(` ⚠️ No tempo events found`);
70
- }
71
-
72
- // Save the extracted MIDI
73
- fs.writeFileSync('./songs/emk/failed01-extracted.mid', midiData);
74
- console.log(`\n✓ Extracted MIDI saved to: ./songs/emk/failed01-extracted.mid`);
75
-
76
- } catch (err) {
77
- console.log(`\n❌ Failed to parse MIDI: ${err.message}`);
78
- }
79
- } else {
80
- console.log('\n❌ No MThd signature found in Block 3');
81
- }
82
-
83
- // Also check what "zxio" might be
84
- console.log('\n' + '='.repeat(60));
85
- console.log('Analysis of header "zxio":');
86
- console.log('='.repeat(60));
87
-
88
- const header = block3.subarray(0, 12);
89
- console.log('Bytes: ' + header.toString('hex').match(/.{1,2}/g).join(' '));
90
- console.log('ASCII: ' + header.toString('ascii'));
91
-
92
- // Could be a custom header or file identification
93
- const possibleMagic = header.subarray(0, 4).toString('ascii');
94
- console.log(`\nFirst 4 bytes as string: "${possibleMagic}"`);
95
- console.log('This might be a custom EMK MIDI format identifier.');
96
- console.log('The standard EMK decoder needs to be updated to handle this format.');
package/fix-tempo.js DELETED
@@ -1,155 +0,0 @@
1
- const fs = require('fs');
2
- const { parseMidi, writeMidi } = require('midi-file');
3
-
4
- /**
5
- * Copy tempo events from source MIDI to target MIDI
6
- */
7
- function copyTempoEvents(sourceMidiPath, targetMidiPath, outputPath) {
8
- console.log('Reading source MIDI (original KAR)...');
9
- const sourceBuffer = fs.readFileSync(sourceMidiPath);
10
- const sourceMidi = parseMidi(sourceBuffer);
11
-
12
- console.log('Reading target MIDI (converted from EMK)...');
13
- const targetBuffer = fs.readFileSync(targetMidiPath);
14
- const targetMidi = parseMidi(targetBuffer);
15
-
16
- // Extract all tempo events from source
17
- const tempoEvents = [];
18
- sourceMidi.tracks.forEach((track, trackIdx) => {
19
- let absoluteTime = 0;
20
- track.forEach((event, eventIdx) => {
21
- absoluteTime += event.deltaTime;
22
- if (event.type === 'setTempo') {
23
- const bpm = 60000000 / event.microsecondsPerBeat;
24
- tempoEvents.push({
25
- trackIdx,
26
- eventIdx,
27
- absoluteTime,
28
- deltaTime: event.deltaTime,
29
- microsecondsPerBeat: event.microsecondsPerBeat,
30
- bpm: bpm.toFixed(2)
31
- });
32
- }
33
- });
34
- });
35
-
36
- console.log(`\nFound ${tempoEvents.length} tempo event(s) in source:`);
37
- tempoEvents.forEach((t, i) => {
38
- console.log(` ${i+1}. Track ${t.trackIdx}, Event ${t.eventIdx}, BPM ${t.bpm}, absolute time: ${t.absoluteTime}`);
39
- });
40
-
41
- // Find and replace tempo events in target
42
- let replaced = 0;
43
- targetMidi.tracks.forEach((track, trackIdx) => {
44
- for (let i = 0; i < track.length; i++) {
45
- const event = track[i];
46
- if (event.type === 'setTempo') {
47
- const oldBpm = (60000000 / event.microsecondsPerBeat).toFixed(2);
48
-
49
- // Replace with first tempo from source
50
- if (tempoEvents.length > 0) {
51
- const newTempo = tempoEvents[0];
52
- event.microsecondsPerBeat = newTempo.microsecondsPerBeat;
53
-
54
- console.log(`\nReplaced tempo in target Track ${trackIdx}, Event ${i}:`);
55
- console.log(` Old: ${oldBpm} BPM`);
56
- console.log(` New: ${newTempo.bpm} BPM`);
57
- replaced++;
58
- }
59
- }
60
- }
61
- });
62
-
63
- if (replaced === 0) {
64
- console.log('\n⚠️ No tempo events found in target to replace');
65
- console.log('Adding tempo event to first track...');
66
-
67
- if (tempoEvents.length > 0 && targetMidi.tracks.length > 0) {
68
- const newTempo = tempoEvents[0];
69
-
70
- // Insert tempo event at the beginning of first track (after deltaTime 0 events)
71
- const firstTrack = targetMidi.tracks[0];
72
- const tempoEvent = {
73
- type: 'setTempo',
74
- deltaTime: 0,
75
- microsecondsPerBeat: newTempo.microsecondsPerBeat
76
- };
77
-
78
- // Insert after any existing deltaTime 0 events
79
- let insertIndex = 0;
80
- while (insertIndex < firstTrack.length && firstTrack[insertIndex].deltaTime === 0) {
81
- insertIndex++;
82
- }
83
-
84
- firstTrack.splice(insertIndex, 0, tempoEvent);
85
- console.log(`Added tempo event: ${newTempo.bpm} BPM to Track 0`);
86
- replaced++;
87
- }
88
- }
89
-
90
- // Write output
91
- console.log(`\nWriting fixed MIDI to: ${outputPath}`);
92
- const outputBytes = writeMidi(targetMidi);
93
- fs.writeFileSync(outputPath, Buffer.from(outputBytes));
94
-
95
- console.log(`✓ Fixed tempo in ${replaced} location(s)`);
96
-
97
- return { replaced, tempoEvents };
98
- }
99
-
100
- // Test with song.emk -> song.kar
101
- console.log('='.repeat(80));
102
- console.log('TEMPO FIX TEST');
103
- console.log('='.repeat(80));
104
- console.log('');
105
-
106
- // First convert EMK to KAR
107
- const { convertEmkToKar } = require('./dist/index.js');
108
- const tempKar = './songs/fix/converted-before-fix.kar';
109
-
110
- console.log('Step 1: Converting EMK to KAR...\n');
111
- convertEmkToKar({
112
- inputEmk: './songs/fix/song.emk',
113
- outputKar: tempKar,
114
- keepIntermediateFiles: false
115
- });
116
-
117
- console.log('\nStep 2: Copying tempo from original KAR...\n');
118
- const fixedKar = './songs/fix/converted-fixed-tempo.kar';
119
- copyTempoEvents(
120
- './songs/fix/song.kar', // source (original)
121
- tempKar, // target (converted)
122
- fixedKar // output
123
- );
124
-
125
- // Verify
126
- console.log('\n' + '='.repeat(80));
127
- console.log('VERIFICATION');
128
- console.log('='.repeat(80));
129
-
130
- const verifyOriginal = parseMidi(fs.readFileSync('./songs/fix/song.kar'));
131
- const verifyFixed = parseMidi(fs.readFileSync(fixedKar));
132
-
133
- console.log('\nOriginal song.kar tempo:');
134
- verifyOriginal.tracks.forEach((track, idx) => {
135
- track.forEach(event => {
136
- if (event.type === 'setTempo') {
137
- const bpm = (60000000 / event.microsecondsPerBeat).toFixed(2);
138
- console.log(` Track ${idx}: ${bpm} BPM`);
139
- }
140
- });
141
- });
142
-
143
- console.log('\nFixed converted KAR tempo:');
144
- verifyFixed.tracks.forEach((track, idx) => {
145
- track.forEach(event => {
146
- if (event.type === 'setTempo') {
147
- const bpm = (60000000 / event.microsecondsPerBeat).toFixed(2);
148
- console.log(` Track ${idx}: ${bpm} BPM`);
149
- }
150
- });
151
- });
152
-
153
- // Cleanup temp file
154
- fs.unlinkSync(tempKar);
155
- console.log('\n✓ Test complete! Fixed file saved as: ' + fixedKar);
package/kar-diagnostic.js DELETED
@@ -1,158 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * KAR File Diagnostic Tool
5
- *
6
- * This tool checks if a KAR file is valid and provides detailed diagnostics
7
- * Usage: node kar-diagnostic.js <path-to-kar-file>
8
- */
9
-
10
- const fs = require('fs');
11
- const path = require('path');
12
-
13
- function checkKarFile(filePath) {
14
- console.log('='.repeat(60));
15
- console.log('KAR File Diagnostic Tool');
16
- console.log('='.repeat(60));
17
- console.log('File:', filePath);
18
- console.log('');
19
-
20
- // Check if file exists
21
- if (!fs.existsSync(filePath)) {
22
- console.error('❌ File not found:', filePath);
23
- process.exit(1);
24
- }
25
-
26
- // Read file
27
- const data = fs.readFileSync(filePath);
28
- console.log('✅ File exists');
29
- console.log(' Size:', data.length, 'bytes');
30
- console.log('');
31
-
32
- // Check MThd header
33
- console.log('--- MIDI Header Check ---');
34
- const expectedHeader = Buffer.from([0x4D, 0x54, 0x68, 0x64]); // MThd
35
- const actualHeader = data.subarray(0, 4);
36
-
37
- console.log('Expected: 4D 54 68 64 (MThd)');
38
- console.log('Actual: ', actualHeader.toString('hex').match(/.{2}/g).join(' '));
39
-
40
- if (Buffer.compare(expectedHeader, actualHeader) === 0) {
41
- console.log('✅ Valid MThd header');
42
- } else {
43
- console.log('❌ Invalid header!');
44
- console.log(' First 20 bytes:', data.subarray(0, 20).toString('hex'));
45
- return false;
46
- }
47
- console.log('');
48
-
49
- // Check MIDI structure
50
- console.log('--- MIDI Structure ---');
51
- const headerLength = data.readUInt32BE(4);
52
- const format = data.readUInt16BE(8);
53
- const numTracks = data.readUInt16BE(10);
54
- const division = data.readUInt16BE(12);
55
-
56
- console.log('Header length:', headerLength, '(expected: 6)');
57
- console.log('Format:', format, '(expected: 0 or 1)');
58
- console.log('Number of tracks:', numTracks);
59
- console.log('Division:', division);
60
-
61
- if (headerLength !== 6) {
62
- console.log('⚠️ Unusual header length');
63
- }
64
- if (format > 2) {
65
- console.log('⚠️ Invalid format');
66
- return false;
67
- }
68
- if (numTracks === 0) {
69
- console.log('❌ No tracks found');
70
- return false;
71
- }
72
- console.log('✅ Valid structure');
73
- console.log('');
74
-
75
- // Check first track
76
- console.log('--- First Track Check ---');
77
- const firstTrackPos = 14;
78
- const firstTrackHeader = data.subarray(firstTrackPos, firstTrackPos + 4).toString('ascii');
79
- const firstTrackLength = data.readUInt32BE(firstTrackPos + 4);
80
-
81
- console.log('Track header:', firstTrackHeader, '(expected: MTrk)');
82
- console.log('Track length:', firstTrackLength);
83
-
84
- if (firstTrackHeader !== 'MTrk') {
85
- console.log('❌ Invalid track header');
86
- return false;
87
- }
88
- console.log('✅ Valid first track');
89
- console.log('');
90
-
91
- // Try parsing with midi-file
92
- console.log('--- Library Compatibility Test ---');
93
- try {
94
- const { parseMidi } = require('midi-file');
95
- const parsed = parseMidi(data);
96
- console.log('✅ midi-file library can parse');
97
- console.log(' Parsed tracks:', parsed.tracks.length);
98
- console.log(' Format:', parsed.header.format);
99
- console.log(' Division:', parsed.header.ticksPerBeat || parsed.header.framesPerSecond);
100
- } catch (e) {
101
- console.log('❌ midi-file library error:', e.message);
102
- return false;
103
- }
104
- console.log('');
105
-
106
- // Check for karaoke content
107
- console.log('--- Karaoke Content Check ---');
108
- const fileContent = data.toString('binary');
109
- const hasKaraoke = fileContent.includes('@T');
110
- const hasWords = fileContent.includes('Words');
111
-
112
- console.log('Contains @T markers:', hasKaraoke ? '✅' : '❌');
113
- console.log('Contains "Words" track:', hasWords ? '✅' : '❌');
114
- console.log('');
115
-
116
- // Summary
117
- console.log('='.repeat(60));
118
- console.log('SUMMARY: File is VALID ✅');
119
- console.log('='.repeat(60));
120
- console.log('');
121
- console.log('If you still get errors when using this file:');
122
- console.log('1. Check file transfer method (use binary mode)');
123
- console.log('2. Verify file size matches:', data.length, 'bytes');
124
- console.log('3. Check first 4 bytes: should be 4D 54 68 64');
125
- console.log('4. Try a different MIDI player/library');
126
- console.log('5. Check if server is re-encoding the file');
127
- console.log('');
128
-
129
- return true;
130
- }
131
-
132
- // Main
133
- const args = process.argv.slice(2);
134
- if (args.length === 0) {
135
- console.error('Usage: node kar-diagnostic.js <path-to-kar-file>');
136
- console.error('');
137
- console.error('Example:');
138
- console.error(' node kar-diagnostic.js output.kar');
139
- console.error(' node kar-diagnostic.js /path/to/song.kar');
140
- process.exit(1);
141
- }
142
-
143
- const filePath = args[0];
144
- try {
145
- checkKarFile(filePath);
146
- } catch (error) {
147
- console.error('');
148
- console.error('❌ Error:', error.message);
149
- console.error('');
150
- console.error('Stack trace:');
151
- console.error(error.stack);
152
- process.exit(1);
153
- }
154
-
155
-
156
-
157
-
158
-