@karaplay/file-coder 1.4.7 → 1.4.9

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/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
-
package/test-final.js DELETED
@@ -1,175 +0,0 @@
1
- const fs = require('fs');
2
- const { parseMidi } = require('midi-file');
3
- const { convertEmkToKar } = require('./dist/index.js');
4
-
5
- console.log('='.repeat(80));
6
- console.log('FINAL TEMPO FIX VERIFICATION');
7
- console.log('='.repeat(80));
8
- console.log('');
9
-
10
- // Convert EMK to KAR with automatic tempo fix
11
- const outputKar = './songs/fix/final-converted.kar';
12
- console.log('Step 1: Converting EMK to KAR with auto tempo fix...\n');
13
-
14
- const result = convertEmkToKar({
15
- inputEmk: './songs/fix/song.emk',
16
- outputKar: outputKar,
17
- keepIntermediateFiles: false
18
- });
19
-
20
- console.log(`\nConversion: ${result.success ? '✅ SUCCESS' : '❌ FAILED'}`);
21
- console.log(`Warnings: ${result.warnings.length}`);
22
- result.warnings.forEach(w => console.log(` - ${w}`));
23
-
24
- // Compare timing and tempo
25
- console.log('\n' + '='.repeat(80));
26
- console.log('COMPARISON');
27
- console.log('='.repeat(80));
28
-
29
- const originalMidi = parseMidi(fs.readFileSync('./songs/fix/song.kar'));
30
- const convertedMidi = parseMidi(fs.readFileSync(outputKar));
31
-
32
- // Check tempo
33
- console.log('\n1. Tempo:');
34
- let originalTempo = null;
35
- let convertedTempo = null;
36
-
37
- originalMidi.tracks.forEach(track => {
38
- track.forEach(event => {
39
- if (event.type === 'setTempo') {
40
- originalTempo = (60000000 / event.microsecondsPerBeat).toFixed(2);
41
- }
42
- });
43
- });
44
-
45
- convertedMidi.tracks.forEach(track => {
46
- track.forEach(event => {
47
- if (event.type === 'setTempo') {
48
- convertedTempo = (60000000 / event.microsecondsPerBeat).toFixed(2);
49
- }
50
- });
51
- });
52
-
53
- console.log(` Original: ${originalTempo} BPM`);
54
- console.log(` Converted: ${convertedTempo} BPM`);
55
- console.log(` Difference: ${((convertedTempo / originalTempo - 1) * 100).toFixed(2)}%`);
56
-
57
- // Check timing (first 10 lyric events)
58
- console.log('\n2. Timing (first 10 karaoke events):');
59
-
60
- function getKaraokeTrack(midi) {
61
- let maxTextEvents = 0;
62
- let karaokeTrack = null;
63
-
64
- midi.tracks.forEach(track => {
65
- const textEvents = track.filter(e => e.type === 'text');
66
- if (textEvents.length > maxTextEvents) {
67
- maxTextEvents = textEvents.length;
68
- karaokeTrack = track;
69
- }
70
- });
71
-
72
- return karaokeTrack;
73
- }
74
-
75
- const originalKaraokeTrack = getKaraokeTrack(originalMidi);
76
- const convertedKaraokeTrack = getKaraokeTrack(convertedMidi);
77
-
78
- let originalAbsolute = 0;
79
- let convertedAbsolute = 0;
80
- let originalTimes = [];
81
- let convertedTimes = [];
82
-
83
- originalKaraokeTrack.forEach(event => {
84
- originalAbsolute += event.deltaTime;
85
- if (event.type === 'text') {
86
- originalTimes.push(originalAbsolute);
87
- }
88
- });
89
-
90
- convertedKaraokeTrack.forEach(event => {
91
- convertedAbsolute += event.deltaTime;
92
- if (event.type === 'text') {
93
- convertedTimes.push(convertedAbsolute);
94
- }
95
- });
96
-
97
- console.log(' Idx | Original | Converted | Match');
98
- console.log(' ' + '-'.repeat(40));
99
-
100
- for (let i = 0; i < Math.min(10, originalTimes.length, convertedTimes.length); i++) {
101
- const match = originalTimes[i] === convertedTimes[i] ? '✓' : '✗';
102
- console.log(` ${(i+1).toString().padStart(3)} | ${originalTimes[i].toString().padStart(8)} | ${convertedTimes[i].toString().padStart(9)} | ${match}`);
103
- }
104
-
105
- // Calculate playback duration
106
- console.log('\n3. Playback Duration Estimate:');
107
-
108
- function calculateDuration(midi) {
109
- const ticksPerBeat = midi.header.ticksPerBeat;
110
- let microsecondsPerBeat = 500000; // default 120 BPM
111
-
112
- // Get tempo
113
- midi.tracks.forEach(track => {
114
- track.forEach(event => {
115
- if (event.type === 'setTempo') {
116
- microsecondsPerBeat = event.microsecondsPerBeat;
117
- }
118
- });
119
- });
120
-
121
- // Get last event time
122
- let maxTicks = 0;
123
- midi.tracks.forEach(track => {
124
- let currentTicks = 0;
125
- track.forEach(event => {
126
- currentTicks += event.deltaTime;
127
- if (currentTicks > maxTicks) {
128
- maxTicks = currentTicks;
129
- }
130
- });
131
- });
132
-
133
- // Calculate duration
134
- const microsecondsPerTick = microsecondsPerBeat / ticksPerBeat;
135
- const durationMicroseconds = maxTicks * microsecondsPerTick;
136
- const durationSeconds = durationMicroseconds / 1000000;
137
-
138
- return {
139
- ticks: maxTicks,
140
- seconds: durationSeconds,
141
- minutes: Math.floor(durationSeconds / 60),
142
- remainingSeconds: Math.floor(durationSeconds % 60)
143
- };
144
- }
145
-
146
- const originalDuration = calculateDuration(originalMidi);
147
- const convertedDuration = calculateDuration(convertedMidi);
148
-
149
- console.log(` Original: ${originalDuration.minutes}:${originalDuration.remainingSeconds.toString().padStart(2, '0')} (${originalDuration.ticks} ticks)`);
150
- console.log(` Converted: ${convertedDuration.minutes}:${convertedDuration.remainingSeconds.toString().padStart(2, '0')} (${convertedDuration.ticks} ticks)`);
151
-
152
- const durationDiff = Math.abs(originalDuration.seconds - convertedDuration.seconds);
153
- console.log(` Time difference: ${durationDiff.toFixed(1)} seconds`);
154
-
155
- // Summary
156
- console.log('\n' + '='.repeat(80));
157
- console.log('SUMMARY');
158
- console.log('='.repeat(80));
159
-
160
- const tempoOk = Math.abs(convertedTempo / originalTempo - 1) < 0.05; // within 5%
161
- const timingOk = originalTimes[5] === convertedTimes[5]; // check 6th event
162
- const durationOk = durationDiff < 10; // within 10 seconds
163
-
164
- console.log(`\n✓ Tempo fix implemented: ${tempoOk ? '✅ PASS' : '❌ FAIL'} (within 5%)`);
165
- console.log(`✓ Timing accuracy: ${timingOk ? '✅ PASS' : '❌ FAIL'} (exact match)`);
166
- console.log(`✓ Duration accuracy: ${durationOk ? '✅ PASS' : '❌ FAIL'} (within 10s)`);
167
-
168
- if (tempoOk && timingOk && durationOk) {
169
- console.log('\n🎉 ALL TESTS PASSED! Tempo fix is working correctly.');
170
- } else {
171
- console.log('\n⚠️ Some tests failed. Review the results above.');
172
- }
173
-
174
- console.log(`\n✓ Converted file saved: ${outputKar}`);
175
- console.log('');