@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.
@@ -1,108 +0,0 @@
1
- const fs = require('fs');
2
- const { parseMidi } = require('midi-file');
3
- const { decodeEmkServer } = require('./dist/index.js');
4
-
5
- // Check MIDI tempo from EMK
6
- console.log('Step 1: Decode EMK and check MIDI tempo...\n');
7
-
8
- const emkBuffer = fs.readFileSync('./songs/fix/song.emk');
9
- const decoded = decodeEmkServer(emkBuffer);
10
-
11
- console.log('EMK decoded successfully');
12
- console.log('Has MIDI:', !!decoded.midi);
13
- console.log('Has Lyric:', !!decoded.lyric);
14
- console.log('Has Cursor:', !!decoded.cursor);
15
-
16
- if (decoded.midi) {
17
- // Write the decoded MIDI to temp file
18
- fs.writeFileSync('./songs/fix/decoded-midi-temp.mid', decoded.midi);
19
-
20
- // Parse the decoded MIDI
21
- const midi = parseMidi(decoded.midi);
22
-
23
- console.log('\nDecoded MIDI file:');
24
- console.log(' Format:', midi.header.format);
25
- console.log(' Tracks:', midi.tracks.length);
26
- console.log(' Ticks per beat:', midi.header.ticksPerBeat);
27
-
28
- // Find tempo events
29
- const tempoEvents = [];
30
-
31
- midi.tracks.forEach((track, trackIdx) => {
32
- let absoluteTime = 0;
33
-
34
- track.forEach((event) => {
35
- absoluteTime += event.deltaTime;
36
-
37
- if (event.type === 'setTempo') {
38
- const bpm = 60000000 / event.microsecondsPerBeat;
39
- tempoEvents.push({
40
- trackIdx: trackIdx,
41
- absoluteTime: absoluteTime,
42
- microsecondsPerBeat: event.microsecondsPerBeat,
43
- bpm: bpm.toFixed(2)
44
- });
45
- }
46
- });
47
- });
48
-
49
- if (tempoEvents.length === 0) {
50
- console.log('\n⚠️ NO TEMPO EVENTS in decoded MIDI!');
51
- } else {
52
- console.log(`\n Tempo events: ${tempoEvents.length}`);
53
- tempoEvents.forEach((t, i) => {
54
- console.log(` ${i+1}. Track ${t.trackIdx}, BPM: ${t.bpm}, µs/beat: ${t.microsecondsPerBeat}`);
55
- });
56
- }
57
-
58
- // Cleanup
59
- fs.unlinkSync('./songs/fix/decoded-midi-temp.mid');
60
- }
61
-
62
- console.log('\n' + '='.repeat(80));
63
- console.log('Step 2: Check ncntokar conversion process...\n');
64
-
65
- // Check if ncntokar.ts adds any tempo
66
- const ncntokarCode = fs.readFileSync('./src/ncntokar.ts', 'utf8');
67
-
68
- if (ncntokarCode.includes('setTempo')) {
69
- console.log('⚠️ ncntokar.ts contains "setTempo" - it might be modifying tempo!');
70
-
71
- // Find the lines
72
- const lines = ncntokarCode.split('\n');
73
- lines.forEach((line, idx) => {
74
- if (line.includes('setTempo')) {
75
- console.log(` Line ${idx+1}: ${line.trim()}`);
76
- }
77
- });
78
- } else {
79
- console.log('✓ ncntokar.ts does not modify tempo');
80
- }
81
-
82
- console.log('\n' + '='.repeat(80));
83
- console.log('Step 3: Check original song.kar tempo location...\n');
84
-
85
- const originalBuffer = fs.readFileSync('./songs/fix/song.kar');
86
- const originalMidi = parseMidi(originalBuffer);
87
-
88
- console.log('Original song.kar:');
89
- console.log(' Tracks:', originalMidi.tracks.length);
90
-
91
- // Check which tracks have tempo
92
- originalMidi.tracks.forEach((track, trackIdx) => {
93
- const tempoCount = track.filter(e => e.type === 'setTempo').length;
94
- if (tempoCount > 0) {
95
- console.log(` Track ${trackIdx} has ${tempoCount} tempo event(s)`);
96
-
97
- track.forEach((event, eventIdx) => {
98
- if (event.type === 'setTempo') {
99
- const bpm = 60000000 / event.microsecondsPerBeat;
100
- console.log(` Event ${eventIdx}: BPM ${bpm.toFixed(2)}, µs/beat: ${event.microsecondsPerBeat}, deltaTime: ${event.deltaTime}`);
101
- }
102
- });
103
- }
104
- });
105
-
106
- console.log('\n' + '='.repeat(80));
107
- console.log('ANALYSIS COMPLETE');
108
- console.log('='.repeat(80));
package/debug-tempo.js DELETED
@@ -1,135 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const { parseMidi } = require('midi-file');
4
-
5
- // Parse and compare tempo events
6
- function analyzeTempo(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 all tempo events in all tracks
17
- const tempoEvents = [];
18
-
19
- midi.tracks.forEach((track, trackIdx) => {
20
- let absoluteTime = 0;
21
-
22
- track.forEach((event, eventIdx) => {
23
- absoluteTime += event.deltaTime;
24
-
25
- if (event.type === 'setTempo') {
26
- // microsecondsPerBeat = event.microsecondsPerBeat
27
- // BPM = 60000000 / microsecondsPerBeat
28
- const bpm = 60000000 / event.microsecondsPerBeat;
29
-
30
- tempoEvents.push({
31
- trackIdx: trackIdx,
32
- eventIdx: eventIdx,
33
- absoluteTime: absoluteTime,
34
- deltaTime: event.deltaTime,
35
- microsecondsPerBeat: event.microsecondsPerBeat,
36
- bpm: bpm.toFixed(2)
37
- });
38
- }
39
- });
40
- });
41
-
42
- if (tempoEvents.length === 0) {
43
- console.log('\n⚠️ NO TEMPO EVENTS FOUND - USING DEFAULT 120 BPM');
44
- return {
45
- hasTempoEvents: false,
46
- defaultBpm: 120,
47
- tempoEvents: []
48
- };
49
- }
50
-
51
- console.log(`\nTempo events found: ${tempoEvents.length}`);
52
- tempoEvents.forEach((t, i) => {
53
- console.log(`${i+1}. Track ${t.trackIdx}, Delta: ${t.deltaTime}, Absolute: ${t.absoluteTime}, BPM: ${t.bpm}, µs/beat: ${t.microsecondsPerBeat}`);
54
- });
55
-
56
- return {
57
- hasTempoEvents: true,
58
- tempoEvents: tempoEvents
59
- };
60
- }
61
-
62
- // Main
63
- const originalKar = './songs/fix/song.kar';
64
- const emkFile = './songs/fix/song.emk';
65
- const tempKar = './songs/fix/converted-test.kar';
66
-
67
- console.log('Converting EMK to KAR...');
68
- const { convertEmkToKar } = require('./dist/index.js');
69
-
70
- try {
71
- const result = convertEmkToKar({
72
- inputEmk: emkFile,
73
- outputKar: tempKar,
74
- keepIntermediateFiles: false
75
- });
76
-
77
- console.log(`Conversion ${result.success ? 'SUCCESS' : 'FAILED'}`);
78
-
79
- console.log('\n' + '='.repeat(80));
80
- console.log('TEMPO COMPARISON');
81
- console.log('='.repeat(80));
82
-
83
- const originalTempo = analyzeTempo(originalKar);
84
- const convertedTempo = analyzeTempo(tempKar);
85
-
86
- console.log('\n' + '='.repeat(80));
87
- console.log('COMPARISON RESULTS');
88
- console.log('='.repeat(80));
89
-
90
- console.log(`\nOriginal has tempo events: ${originalTempo.hasTempoEvents}`);
91
- console.log(`Converted has tempo events: ${convertedTempo.hasTempoEvents}`);
92
-
93
- if (originalTempo.hasTempoEvents && !convertedTempo.hasTempoEvents) {
94
- console.log('\n🔴 PROBLEM FOUND: Original has tempo events but converted file does NOT!');
95
- console.log('This will cause the converted file to play at default 120 BPM instead of the intended tempo.');
96
- console.log('\nOriginal tempo events:');
97
- originalTempo.tempoEvents.forEach((t, i) => {
98
- console.log(` ${i+1}. Track ${t.trackIdx}, BPM: ${t.bpm} (${t.microsecondsPerBeat} µs/beat)`);
99
- });
100
- } else if (!originalTempo.hasTempoEvents && !convertedTempo.hasTempoEvents) {
101
- console.log('\n✓ Both files use default tempo (120 BPM)');
102
- } else if (originalTempo.hasTempoEvents && convertedTempo.hasTempoEvents) {
103
- console.log('\n✓ Both files have tempo events');
104
-
105
- // Compare tempos
106
- if (originalTempo.tempoEvents.length !== convertedTempo.tempoEvents.length) {
107
- console.log(`\n⚠️ Warning: Different number of tempo events (${originalTempo.tempoEvents.length} vs ${convertedTempo.tempoEvents.length})`);
108
- }
109
-
110
- // Compare first tempo
111
- const origFirst = originalTempo.tempoEvents[0];
112
- const convFirst = convertedTempo.tempoEvents[0];
113
-
114
- if (origFirst && convFirst) {
115
- console.log(`\nFirst tempo comparison:`);
116
- console.log(` Original: ${origFirst.bpm} BPM`);
117
- console.log(` Converted: ${convFirst.bpm} BPM`);
118
-
119
- if (origFirst.bpm !== convFirst.bpm) {
120
- console.log(` 🔴 TEMPO MISMATCH!`);
121
- } else {
122
- console.log(` ✓ Tempo matches`);
123
- }
124
- }
125
- }
126
-
127
- // Cleanup
128
- if (fs.existsSync(tempKar)) {
129
- fs.unlinkSync(tempKar);
130
- console.log('\nCleaned up temporary file.');
131
- }
132
- } catch (error) {
133
- console.error('Error:', error.message);
134
- console.error(error.stack);
135
- }
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
- }
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);