-
Notifications
You must be signed in to change notification settings - Fork 1
/
cabbr.js
174 lines (169 loc) · 6.45 KB
/
cabbr.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
const fs = require('fs');
const ini = require('ini');
const os = require('os');
const cpuCores = os.availableParallelism ? os.availableParallelism() : undefined;
const config = ini.parse(fs.readFileSync('./config.ini', 'utf-8'));
// Get variables from ini
var {type, seconds, sampleRate} = config.General;
var {upscale, resample, resampleMethod, bits} = config.Quality;
var {tLength, workers} = config.Advanced;
var {reportEvery, invalidSamples} = config.Misc;
var skipNaNs = false;
(()=>{ // change variables into their proper types
type = parseInt(type);
seconds = parseFloat(seconds);
sampleRate = parseInt(sampleRate);
upscale = parseFloat(upscale);
resample = parseInt(resample);
tLength = parseInt(tLength);
workers = workers=='max'?(cpuCores?(console.log('Using CPU core count: %d workers',cpuCores),cpuCores):(console.log('The "max" settings requires at least Node.JS v18.14.0. Using 1 worker'),1)):parseInt(workers);
reportEvery = parseFloat(reportEvery);
invalidSamples = parseInt(invalidSamples);
bits = parseInt(bits);
skipNaNs = invalidSamples == 1;
})(); // this is so you can retract it
const pcm = require('./pcm.js');
const { Worker } = require('worker_threads');
var waveResampler;
if(resample > 0 && resample != sampleRate) waveResampler = require('wave-resampler');
var data = [];
var expr = fs.readFileSync('expr.txt','utf8');
if(expr.trim().substring(0,20) == 'eval(unescape(escape') {
console.log('Optimizing...');
expr = expr.trim().replace(
/^eval\(unescape\(escape`([^]*?)`.replace\(\/u\(\.\.\)\/g,["']\$1%["']\)\)\)$/,
(match, m1) => unescape(escape(m1).replace(/u(..)/g, '$1%')));
fs.writeFileSync('expr_optimized.txt',expr,'utf8');
console.log('Optimized version saved as expr_optimized.txt');
}
if(upscale > 0 && upscale !== 1) {
sampleRate *= upscale;
expr = 't/='+upscale+','+expr;
console.log('Upscaled x'+upscale+' (new sample rate: '+sampleRate+')');
}
var stereo = false;
var stereoTester = new Worker('./worker',{workerData:{stereoTest:true,expr}});
stereoTester.on('message',m=>{
stereo = m;
if(m) {
console.log("Stereo detected");
seconds *= 2;
expr = 't/=2,'+expr;
}
process();
});
async function process() {
const length = tLength > 0 ? tLength : seconds*sampleRate;
var workerArray = [];
var parts = [];
var part = length/workers;
var percents = [0,0,0,0];
console.log('Processing...\nProgress:\nWorkers:');
console.time('Processing');
var workersFinished = 0;
for (var i = 0; i<workers; i++) {
workerArray.push(new Worker('./worker',{argv:[part*i,part*(i+1),i+1],workerData:{stereoTest:false,sampleRate,reportEvery,type,expr,stereo,skipNaNs}}));
workerArray[workerArray.length-1].on('message',m=>{
if(typeof m === 'string') {
if(m.endsWith('%')) {
let d = m.split(';');
let number = parseInt(d[0]);
percents[number-1] = parseFloat(d[1].substring(0,d[1].length-1));
let percentage = 0;
percents.forEach(workerPercentage=> percentage += workerPercentage/workers);
console.log('\x1b[2AProgress: %s %d\% \nWorkers : %d/%d',progressBar(percentage),percentage.toFixed(1),workersFinished,workers);
} else {
console.log(m);
}
} else parts[m[0]] = m[1];
});
workerArray[workerArray.length-1].on('exit',()=>{
workersFinished++;
if(workersFinished == workers) {
console.log('\x1b[2FProgress: %s 100.0\%\nWorkers : %d/%d\n\x1b[1F',progressBar(100),workersFinished,workers);
console.timeEnd('Processing');
console.log('Merging...');
let partCount = 0;
parts.forEach(part=>{
part.forEach(item=>{data.push(item);});
partCount++;
console.log('Part #'+partCount+' merged');
});
switch(invalidSamples) {
case 2:
let num = -1;
console.log('Searching for an invalid sample...');
for(let s = 0; num == -1 && s < data.length; s++) {
if(isNaN(data[s])) {
num = s;
console.log('Invalid sample found at '+num);
}
}
var spliced = data.splice(num,data.length-num+1);
if(num==-1) console.log('No invalid samples were found'); else console.log('Removed '+spliced.length+' sample(s).');
break;
case 3:
let nm = 0;
let lastSample = 127;
console.log('Searching for invalid samples...');
for(let s = 0; s < data.length; s++) {
if(isNaN(data[s])) {
nm++;
if(!isNaN(data[s-1])) lastSample = data[s-1];
data[s] = lastSample;
}
}
if(nm) console.log('Found and replaced '+nm+' invalid sample(s).'); else console.log('Found no invalid samples.');
break;
}
if(waveResampler) {
console.log('Resampling...');
if(stereo) {
var ch1 = [];
var ch2 = [];
for(let o = 0; o < data.length; o+=2) {
ch1.push(data[o]);
ch2.push(data[o+1]);
}
console.log('Left ear...');
ch1 = [...waveResampler.resample(ch1, sampleRate, resample,{method:resampleMethod,LPF:false})];
console.log('Right ear...');
ch2 = [...waveResampler.resample(ch2, sampleRate, resample,{method:resampleMethod,LPF:false})];
console.log('Merging channels...');
var m = [];
data = [];
for(let o = 0; o < ch1.length; o++) {
data.push(ch1[o]);
data.push(ch2[o]);
}
} else data = [...waveResampler.resample(data, sampleRate, resample,{method:resampleMethod,LPF:false})];
console.log('Resampled from '+sampleRate+'hz to '+resample+'hz sample rate');
}
if(bits == 16) {
console.log('Converting to 16 bits...');
var newData = [];
for(let o = 0; o < data.length; o++) {
let the = Math.round(data[o]*256-32768);
newData[o*2] = convertIt(the,0);
newData[o*2+1] = convertIt(the,1);
}
data = newData;
} else if(bits != 8) console.log('Invalid amount of bits, defaulting to 8. (no change)');
console.log('Writing...');
var the = new pcm({channels: stereo?2:1, rate: waveResampler?resample:sampleRate, depth: bits===16?16:8});
var wave = the.toWav(data);
fs.writeFileSync('out.wav',Buffer.from(wave));
}
});
}
}
function progressBar(percentage) {
return '\x1b[106;30m['+(''.padEnd((percentage/100)*60,'#').padEnd(60,'.').replaceAll('#','\x1b[42;32m#\x1b[0m').replaceAll('.','\x1b[41;31m.\x1b[0m'))+'\x1b[106;30m]\x1b[0m';
}
function convertIt(int16,num) {
if(num === 0) {
return int16%256;
} else if(num === 1) {
return Math.floor(int16/256)%256;
}
}