@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.
- package/DEMO_ENHANCED.md +207 -134
- package/DOCUMENTATION_INDEX.md +317 -0
- package/EMK_REFERENCE_DATA.json +190 -0
- package/EMK_SONGS_INFO.md +336 -0
- package/EMK_TEST_SUITE_README.md +456 -0
- package/EMK_TEST_SUITE_SUMMARY.txt +197 -0
- package/README.md +90 -0
- package/RELEASE_v1.5.1.md +190 -0
- package/RELEASE_v1.5.2.md +238 -0
- package/SONG_LIST.txt +268 -0
- package/TEMPO_TRICKS_SUMMARY.md +240 -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/dist/ncntokar.browser.js +13 -1
- package/dist/ncntokar.js +13 -1
- package/package.json +4 -1
- package/verify-emk-reference.js +230 -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
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const { Midi } = require('@tonejs/midi');
|
|
3
|
-
const { convertEmkToKar } = require('./dist/index.js');
|
|
4
|
-
|
|
5
|
-
console.log('='.repeat(80));
|
|
6
|
-
console.log('TEST: All EMK Files Duration Check');
|
|
7
|
-
console.log('='.repeat(80));
|
|
8
|
-
console.log('');
|
|
9
|
-
|
|
10
|
-
// Get all EMK files
|
|
11
|
-
const emkDir = './songs/emk';
|
|
12
|
-
const emkFiles = fs.readdirSync(emkDir).filter(f => f.endsWith('.emk'));
|
|
13
|
-
|
|
14
|
-
console.log(`Found ${emkFiles.length} EMK files\n`);
|
|
15
|
-
|
|
16
|
-
const results = [];
|
|
17
|
-
|
|
18
|
-
emkFiles.forEach((file, idx) => {
|
|
19
|
-
console.log(`[${idx + 1}/${emkFiles.length}] Testing: ${file}`);
|
|
20
|
-
|
|
21
|
-
const inputPath = `${emkDir}/${file}`;
|
|
22
|
-
const tempInput = `./temp/test_${idx}.emk`;
|
|
23
|
-
const tempOutput = `./temp/test_${idx}.kar`;
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
// Copy input
|
|
27
|
-
fs.copyFileSync(inputPath, tempInput);
|
|
28
|
-
|
|
29
|
-
// Convert
|
|
30
|
-
const result = convertEmkToKar({
|
|
31
|
-
inputEmk: tempInput,
|
|
32
|
-
outputKar: tempOutput,
|
|
33
|
-
keepIntermediateFiles: false
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
if (!result.success) {
|
|
37
|
-
console.log(` ❌ Failed: conversion error`);
|
|
38
|
-
results.push({ file, status: 'failed', error: 'conversion' });
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Analyze
|
|
43
|
-
const karBuffer = fs.readFileSync(tempOutput);
|
|
44
|
-
const karMidi = new Midi(karBuffer);
|
|
45
|
-
|
|
46
|
-
const duration = karMidi.duration;
|
|
47
|
-
const minutes = Math.floor(duration / 60);
|
|
48
|
-
const seconds = Math.floor(duration % 60);
|
|
49
|
-
const tempo = karMidi.header.tempos[0].bpm;
|
|
50
|
-
|
|
51
|
-
console.log(` ✅ Success!`);
|
|
52
|
-
console.log(` Duration: ${minutes}:${String(seconds).padStart(2, '0')} (${duration.toFixed(1)}s)`);
|
|
53
|
-
console.log(` Tempo: ${tempo.toFixed(1)} BPM`);
|
|
54
|
-
|
|
55
|
-
results.push({
|
|
56
|
-
file,
|
|
57
|
-
status: 'success',
|
|
58
|
-
duration: duration.toFixed(1),
|
|
59
|
-
tempo: tempo.toFixed(1),
|
|
60
|
-
formatted: `${minutes}:${String(seconds).padStart(2, '0')}`
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// Clean up
|
|
64
|
-
fs.unlinkSync(tempInput);
|
|
65
|
-
fs.unlinkSync(tempOutput);
|
|
66
|
-
|
|
67
|
-
} catch (error) {
|
|
68
|
-
console.log(` ❌ Failed:`, error.message);
|
|
69
|
-
results.push({ file, status: 'failed', error: error.message });
|
|
70
|
-
|
|
71
|
-
// Clean up on error
|
|
72
|
-
try {
|
|
73
|
-
if (fs.existsSync(tempInput)) fs.unlinkSync(tempInput);
|
|
74
|
-
if (fs.existsSync(tempOutput)) fs.unlinkSync(tempOutput);
|
|
75
|
-
} catch (e) {}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
console.log('');
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
console.log('='.repeat(80));
|
|
82
|
-
console.log('SUMMARY');
|
|
83
|
-
console.log('='.repeat(80));
|
|
84
|
-
console.log('');
|
|
85
|
-
|
|
86
|
-
const successful = results.filter(r => r.status === 'success');
|
|
87
|
-
const failed = results.filter(r => r.status === 'failed');
|
|
88
|
-
|
|
89
|
-
console.log(`✅ Successful: ${successful.length}/${results.length}`);
|
|
90
|
-
console.log(`❌ Failed: ${failed.length}/${results.length}`);
|
|
91
|
-
console.log('');
|
|
92
|
-
|
|
93
|
-
if (successful.length > 0) {
|
|
94
|
-
console.log('Successful conversions:');
|
|
95
|
-
successful.forEach(r => {
|
|
96
|
-
console.log(` ${r.file.padEnd(30)} ${r.formatted.padEnd(8)} ${r.tempo.padStart(6)} BPM`);
|
|
97
|
-
});
|
|
98
|
-
console.log('');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (failed.length > 0) {
|
|
102
|
-
console.log('Failed conversions:');
|
|
103
|
-
failed.forEach(r => {
|
|
104
|
-
console.log(` ${r.file.padEnd(30)} ${r.error}`);
|
|
105
|
-
});
|
|
106
|
-
console.log('');
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
console.log('');
|
package/test-convert-001.js
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const { Midi } = require('@tonejs/midi');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
console.log('='.repeat(80));
|
|
6
|
-
console.log('TEST: Converting 001_original_emk.emk to KAR');
|
|
7
|
-
console.log('='.repeat(80));
|
|
8
|
-
console.log('');
|
|
9
|
-
|
|
10
|
-
// Target duration
|
|
11
|
-
const targetMin = 4 * 60 + 42; // 4:42 = 282 seconds
|
|
12
|
-
const targetMax = 4 * 60 + 45; // 4:45 = 285 seconds
|
|
13
|
-
|
|
14
|
-
console.log('🎯 Target Duration: 4:42 - 4:45 (282-285 seconds)');
|
|
15
|
-
console.log('');
|
|
16
|
-
|
|
17
|
-
// Copy file to temp for processing
|
|
18
|
-
const inputPath = './songs/emk/001_original_emk.emk';
|
|
19
|
-
const tempInput = './temp/test_input.emk';
|
|
20
|
-
const tempOutput = './temp/test_output.kar';
|
|
21
|
-
|
|
22
|
-
// Ensure temp dir exists
|
|
23
|
-
if (!fs.existsSync('./temp')) {
|
|
24
|
-
fs.mkdirSync('./temp');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Copy input
|
|
28
|
-
fs.copyFileSync(inputPath, tempInput);
|
|
29
|
-
|
|
30
|
-
console.log('📁 Converting: songs/emk/001_original_emk.emk');
|
|
31
|
-
console.log('');
|
|
32
|
-
|
|
33
|
-
// Use the file-based API
|
|
34
|
-
const { convertEmkToKar } = require('./dist/index.js');
|
|
35
|
-
|
|
36
|
-
const result = convertEmkToKar({
|
|
37
|
-
inputEmk: tempInput,
|
|
38
|
-
outputKar: tempOutput,
|
|
39
|
-
keepIntermediateFiles: false
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
if (!result.success) {
|
|
43
|
-
console.log('❌ Conversion failed!');
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
console.log('✅ Conversion successful!');
|
|
48
|
-
console.log('');
|
|
49
|
-
|
|
50
|
-
// Analyze result
|
|
51
|
-
const karBuffer = fs.readFileSync(tempOutput);
|
|
52
|
-
const karMidi = new Midi(karBuffer);
|
|
53
|
-
|
|
54
|
-
console.log('📊 Converted KAR Info:');
|
|
55
|
-
console.log(' PPQ:', karMidi.header.ppq);
|
|
56
|
-
console.log(' Tempo:', karMidi.header.tempos[0].bpm.toFixed(2), 'BPM');
|
|
57
|
-
console.log(' Duration:', karMidi.duration.toFixed(2), 'seconds');
|
|
58
|
-
console.log(' Duration:', Math.floor(karMidi.duration / 60) + ':' + String(Math.floor(karMidi.duration % 60)).padStart(2, '0'));
|
|
59
|
-
console.log('');
|
|
60
|
-
|
|
61
|
-
// Check if in range
|
|
62
|
-
const duration = karMidi.duration;
|
|
63
|
-
const inRange = duration >= targetMin && duration <= targetMax;
|
|
64
|
-
|
|
65
|
-
console.log('🎯 Duration Check:');
|
|
66
|
-
console.log(' Target: 4:42 - 4:45 (282-285 seconds)');
|
|
67
|
-
console.log(' Got:', duration.toFixed(2), 'seconds');
|
|
68
|
-
console.log(' Status:', inRange ? '✅ IN RANGE!' : '❌ OUT OF RANGE!');
|
|
69
|
-
console.log('');
|
|
70
|
-
|
|
71
|
-
if (!inRange) {
|
|
72
|
-
const diff = duration < targetMin ? targetMin - duration : duration - targetMax;
|
|
73
|
-
console.log(' Difference:', diff.toFixed(2), 'seconds');
|
|
74
|
-
console.log(' Too', duration < targetMin ? 'LONG' : 'SHORT', 'by', diff.toFixed(2), 'seconds');
|
|
75
|
-
console.log('');
|
|
76
|
-
|
|
77
|
-
// Calculate what ratio we need
|
|
78
|
-
const currentTempo = karMidi.header.tempos[0].bpm;
|
|
79
|
-
|
|
80
|
-
// Calculate target tempo for 283.5 seconds (middle of 4:42-4:45)
|
|
81
|
-
const targetDuration = 283.5;
|
|
82
|
-
const targetTempo = currentTempo * (duration / targetDuration);
|
|
83
|
-
|
|
84
|
-
console.log('📐 Correction Needed:');
|
|
85
|
-
console.log(' Current tempo:', currentTempo.toFixed(2), 'BPM');
|
|
86
|
-
console.log(' Target tempo:', targetTempo.toFixed(2), 'BPM');
|
|
87
|
-
console.log(' Tempo adjustment:', (targetTempo / currentTempo).toFixed(4) + 'x');
|
|
88
|
-
console.log('');
|
|
89
|
-
|
|
90
|
-
// Find current ratio being used
|
|
91
|
-
const { decodeEmkServer } = require('./dist/index.js');
|
|
92
|
-
const emkBuffer = fs.readFileSync(inputPath);
|
|
93
|
-
const decoded = decodeEmkServer(emkBuffer);
|
|
94
|
-
const emkMidi = new Midi(decoded.midi);
|
|
95
|
-
const emkTempo = emkMidi.header.tempos[0].bpm;
|
|
96
|
-
const currentRatio = currentTempo / emkTempo;
|
|
97
|
-
const targetRatio = targetTempo / emkTempo;
|
|
98
|
-
|
|
99
|
-
console.log('📊 Ratio Analysis:');
|
|
100
|
-
console.log(' EMK tempo:', emkTempo.toFixed(2), 'BPM');
|
|
101
|
-
console.log(' Current ratio:', currentRatio.toFixed(4) + 'x');
|
|
102
|
-
console.log(' Target ratio:', targetRatio.toFixed(4) + 'x');
|
|
103
|
-
console.log(' Format:', decoded.isZxioFormat ? 'ZXIO' : 'MThd');
|
|
104
|
-
console.log('');
|
|
105
|
-
|
|
106
|
-
console.log('⚙️ ACTION REQUIRED:');
|
|
107
|
-
if (decoded.isZxioFormat) {
|
|
108
|
-
console.log(' Update ZXIO ratio in libs from', currentRatio.toFixed(4) + 'x', 'to', targetRatio.toFixed(4) + 'x');
|
|
109
|
-
const ppq = karMidi.header.ppq;
|
|
110
|
-
const newDivisor = ppq / targetRatio;
|
|
111
|
-
console.log(' Change: ticksPerBeat /', currentRatio.toFixed(2), '→ ticksPerBeat /', targetRatio.toFixed(2));
|
|
112
|
-
console.log(' Or: ticksPerBeat /', newDivisor.toFixed(2));
|
|
113
|
-
} else {
|
|
114
|
-
console.log(' Update MThd ratio in libs from', currentRatio.toFixed(4) + 'x', 'to', targetRatio.toFixed(4) + 'x');
|
|
115
|
-
}
|
|
116
|
-
console.log('');
|
|
117
|
-
|
|
118
|
-
process.exit(1);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
console.log('🎉 SUCCESS! Duration is within target range!');
|
|
122
|
-
console.log('');
|
|
123
|
-
|
|
124
|
-
// Clean up temp input
|
|
125
|
-
fs.unlinkSync(tempInput);
|
|
126
|
-
|
|
127
|
-
// Keep output for testing
|
|
128
|
-
console.log('💾 Saved to: temp/test_output.kar');
|
|
129
|
-
console.log('');
|
|
130
|
-
|