Chasyxx

bytebeat renderer in js (update 2)

May 28th, 2023 (edited)
742
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2. Bytebeat renderer
  3. You'll need node.js. If you don't have it, install it at https://nodejs.org/
  4. find the flag list below
  5. */
  6. const fs = require('fs');
  7. const chyx = {
  8.     /*bit*/        "bitC": function (x, y, z) { return x & y ? z : 0 },
  9.     /*bit reverse*/"br": function (x, size = 8) {
  10.         if (size > 32) { throw new Error("br() Size cannot be greater than 32") } else {
  11.             let result = 0;
  12.             for (let idx = 0; idx < (size - 0); idx++) {
  13.                 result += chyx.bitC(x, 2 ** idx, 2 ** (size - (idx + 1)))
  14.             }
  15.             return result
  16.         }
  17.     },
  18.     /*sin that loops every 128 "steps", instead of every pi steps*/"sinf": function (x) { return Math.sin(x / (128 / Math.PI)) },
  19.     /*cos that loops every 128 "steps", instead of every pi steps*/"cosf": function (x) { return Math.cos(x / (128 / Math.PI)) },
  20.     /*tan that loops every 128 "steps", instead of every pi steps*/"tanf": function (x) { return Math.tan(x / (128 / Math.PI)) },
  21.     /*converts t into a string composed of it's bits, regex's that*/"regG": function (t, X) { return X.test(t.toString(2)) }
  22.     /*corrupt sound"crpt": function(x,y=8) {return chyx.br(chyx.br(x,y)+t,y)^chyx.br(t,y)},
  23.     decorrupt sound"decrpt": function(x,y=8) {return chyx.br(chyx.br(x^chyx.br(t,y),y)-t,y)},*/
  24. }
  25.  
  26. const outputFile = './output.wav';
  27. const inputFile = './input.txt';
  28.  
  29. const bitsPerSample = 8;
  30.  
  31. function number32to4chars(input) {
  32.     const buffer = Buffer.alloc(4);
  33.     buffer.writeUInt32LE(input, 0);
  34.     return buffer;
  35. }
  36.  
  37. function number16to2chars(input) {
  38.     const buffer = Buffer.alloc(2);
  39.     buffer.writeUInt16LE(input, 0);
  40.     return buffer;
  41. }
  42.  
  43. function indicator(val, max, barSize = 20) {
  44.     const first = `[${''.padStart(Math.floor((val / max) * barSize), '#').padEnd(barSize, ' ')}]`
  45.     const second = `(${String(val).padStart(12, ".")} / ${String(max).padEnd(12, ".")})`;
  46.     return first + second
  47. }
  48.  
  49. function main() {
  50.     if (process.argv.indexOf('--usage') !== -1) {
  51.         const filePath = require('path').dirname(__filename).replace(/^\w:\\Users\\(.+?)(?:\\OneDrive)?\\Desktop/i, '($1\'s desktop)').replace(/^\w:\\Users\\(.+?)/i, '($1\'s folder)')
  52.         console.log(`       Flag list\r
  53.         --rate <samplerate>\r
  54.         --length <seconds>\r
  55.         --mode <float, byte, or signed. case sensitive>\r
  56.         --stereo to specify the code is stereo. Double check this is used correctly!\r
  57.         --chyx to include the chyxPack extension from the CHYX composer. Non-standard!\r
  58.        
  59.         Instructions
  60.         The program takes in an input.txt file fom the directory it's in. If it can't find this file,\r
  61.         it will probably crash. Ensure you have an input.txt file in: \r
  62.         \x1b[96m${filePath}\x1b[0m\r
  63.         and it has valid bytebeat JS code.\r
  64.         the program will throw an error if the sample rate or length flags are not specified.\r
  65.         Mode does not need to be specified, as it defaults to bytebeat ("byte")\r
  66.         --chyx adds the custom functions found on https://chasyxx.github.io/EnBeat_NEW .\r
  67.         These functions are exotic and non standard.`)
  68.         return 0;
  69.     }
  70.     console.log("\x1b[92mUse flag --usage to see usage information. The discord may not be up to date, so be sure to check!\x1b[0m")
  71.     const modeIndex = process.argv.indexOf('--mode');
  72.     let getvalues = function () { };
  73.     const lengthIndex = process.argv.indexOf('--length');
  74.     const rateIndex = process.argv.indexOf('--rate');
  75.     const sizeIndex = process.argv.indexOf('--size');
  76.     const stereo = process.argv.indexOf('--stereo') !== -1;
  77.     const useChyx = process.argv.indexOf('--chyx') !== -1;
  78.     let bufferSize = null; // Number of samples to buffer before flushing to the main buffer
  79.  
  80.     let rateValue, lengthValue, trueLength;
  81.  
  82.     if (rateIndex !== -1) {
  83.         rateValue = parseInt(parseInt(process.argv[rateIndex + 1]));
  84.         console.log('Sample rate: %d', rateValue);
  85.     } else {
  86.         throw new Error("Required samplerate parameter. Flag is --rate (number)");
  87.     }
  88.  
  89.     if (sizeIndex !== -1) {
  90.         bufferSize = parseInt(parseInt(process.argv[sizeIndex + 1]));
  91.         console.log("buffer size %d", bufferSize)
  92.     }
  93.  
  94.     if (modeIndex !== -1) {
  95.         switch (process.argv[modeIndex + 1]) {
  96.             case 'byte':
  97.                 getvalues = (x) => (x & 255);
  98.                 break;
  99.             case 'signed':
  100.                 getvalues = (x) => ((x + 128) & 255);
  101.                 break;
  102.             case 'float':
  103.                 getvalues = (x) => Math.max(Math.min((x * 128 + 128), 255), 0);
  104.                 break;
  105.             default: throw new Error("Invalid mode. Use byte, signed, or float")
  106.         }
  107.     } else {
  108.         getvalues = (x) => (x & 255)
  109.     }
  110.  
  111.     if (lengthIndex !== -1) {
  112.         lengthValue = parseFloat(process.argv[lengthIndex + 1]);
  113.         trueLength = Math.floor(lengthValue * rateValue);
  114.         bufferSize ??= Math.min(Math.floor(trueLength / 40), 5e4);
  115.         console.log('Length in seconds: %d', lengthValue);
  116.         console.log('Samples to generate: %d', trueLength);
  117.     } else {
  118.         throw new Error("Required length parameter. Flag is --length (number)");
  119.     }
  120.  
  121.     if (stereo) {
  122.         console.log("Code is marked as stereo.")
  123.     }
  124.     const globalVars = {};
  125.  
  126.     const codeString = fs.readFileSync(inputFile, { encoding: "ascii" });
  127.     const codeLength = codeString.length;
  128.     let codeFunc = null;
  129.     let lastValue = stereo ? [0, 0] : 0;
  130.     let buffer = Buffer.alloc(0);
  131.     let tempBuffer = Buffer.alloc(0);
  132.  
  133.     const params = Object.getOwnPropertyNames(Math);
  134.     const values = params.map(k => Math[k]);
  135.     const chyxNames = Object.getOwnPropertyNames(chyx);
  136.     const chyxFuncs = chyxNames.map(k => chyx[k]);
  137.     params.push('int', 'window');
  138.     values.push(Math.floor, globalVars);
  139.     if (useChyx) {
  140.         console.log("ChyxPak library included. \x1b[33mThis is non-standard!\x1b[0m")
  141.         params.push(...chyxNames)
  142.         values.push(...chyxFuncs)
  143.     }
  144.     let i;
  145.     console.log(codeLength > 256 ? `Code squelched, ${codeLength > 1000 ? `${codeLength / 1000} KB` : `${codeLength}b`}` : `Code: ${codeString}`);
  146.     console.log(`\nsong progress:     [$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$](------------ / ------------)\nsubBuffer progress:[$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$](------------ / ------------)`);
  147.  
  148.     try {
  149.         codeFunc = new Function(...params, 't', `return 0,\n${codeString || 0};`).bind(globalVars, ...values);
  150.         try {
  151.             codeFunc(0)
  152.         } catch (e) {
  153.             console.log(`\x1b[3A\x1b[91mRuntime error at t=0: ${e.message}\x1b[0m\x1b[2B`)
  154.             const exec = /^(.+) is not defined$/.exec(e.message)
  155.             if (exec!==null && !useChyx) {
  156.                 if(chyxNames.indexOf(exec[0].replace(/ is not defined$/,""))!==-1) {
  157.                 console.log("Maybe try using the --chyx flag?")
  158.                 return 1;
  159.                 }
  160.             }
  161.         }
  162.     } catch (e) {
  163.         console.error("Creation error: " + e.message);
  164.         const exec = /^(.+) is not defined$/.exec(e.message)
  165.         if (exec!==null && !useChyx) {
  166.             if(chyxNames.indexOf(exec[0].replace(/ is not defined$/,""))!==-1) {
  167.             console.log("Maybe try using the --chyx flag?")
  168.             }
  169.         }
  170.         return 1;
  171.     }
  172.     if (stereo) {
  173.         for (i = 0; i <= trueLength; i++) {
  174.             if ((i % 100) == 0) console.log("\x1b[2Asong progress:     %s\nsubBuffer progess: %s", indicator(i, trueLength, 40), indicator(tempBuffer.length, bufferSize, 40));
  175.             let result = NaN;
  176.             try {
  177.                 result = codeFunc(i)
  178.             } catch (e) {
  179.                 if(e instanceof Error) {
  180.                     console.log(`\x1b[3A\x1b[91mRuntime error at t=${i}: ${e.message}\x1b[0m\x1b[2B`)
  181.                 }else{
  182.                     console.log(`\x1b[3A\x1b[93mThrown at t=${i}: ${e}\x1b[0m\x1b[2B`)
  183.                 }
  184.             }
  185.             lastValue[0] = isNaN(result[0]) ? lastValue[0] : getvalues(result[0]) & 255;
  186.             lastValue[1] = isNaN(result[1]) ? lastValue[1] : getvalues(result[1]) & 255;
  187.             tempBuffer = Buffer.concat([tempBuffer, Buffer.from([lastValue[0]]), Buffer.from([lastValue[1]])]);
  188.  
  189.             // Flush the tempBuffer to the main buffer when it reaches the bufferSize
  190.             if (tempBuffer.length >= bufferSize) {
  191.                 buffer = Buffer.concat([buffer, tempBuffer]);
  192.                 tempBuffer = Buffer.alloc(0);
  193.             }
  194.         }
  195.     } else {
  196.         for (i = 0; i <= trueLength; i++) {
  197.             if ((i % 100) == 0) console.log("\x1b[2Asong progress:     %s\nsubBuffer progess: %s", indicator(i, trueLength, 40), indicator(tempBuffer.length, bufferSize, 40));
  198.             let result = NaN;
  199.             try {
  200.                 result = getvalues(codeFunc(i))
  201.             } catch (e) {
  202.                 if(e instanceof Error) {
  203.                     console.log(`\x1b[3A\x1b[91mRuntime error at t=${i}: ${e.message}\x1b[0m\x1b[2B`)
  204.                 }else{
  205.                     console.log(`\x1b[3A\x1b[93mThrown at t=${i}: ${e}\x1b[0m\x1b[2B`)
  206.                 }
  207.             }
  208.             lastValue = isNaN(result) ? lastValue : result & 255;
  209.             tempBuffer = Buffer.concat([tempBuffer, Buffer.from([lastValue])]);
  210.  
  211.             // Flush the tempBuffer to the main buffer when it reaches the bufferSize
  212.             if (tempBuffer.length >= bufferSize) {
  213.                 buffer = Buffer.concat([buffer, tempBuffer]);
  214.                 tempBuffer = Buffer.alloc(0);
  215.             }
  216.         }
  217.     }
  218.  
  219.     // Flush any remaining samples in the tempBuffer to the main buffer
  220.     buffer = Buffer.concat([buffer, tempBuffer]);
  221.  
  222.     console.log('\x1b[1AProcessing complete. Generating file.                                                    ');
  223.  
  224.     const header = Buffer.concat([
  225.         Buffer.from('RIFF', 'ascii'),                                           // RIFF
  226.         number32to4chars(buffer.length + 36),                                   // filesize - 8
  227.         Buffer.from('WAVEfmt ', 'ascii'),                                       // .wav file, begin formatting info
  228.         number32to4chars(16),                                                   // length of formatting info
  229.         number16to2chars(1),                                                    // Marker for PCM data
  230.         number16to2chars(stereo ? 2 : 1),                                       // Channel No.
  231.         number32to4chars(rateValue),                                            // Sample rate
  232.         number32to4chars(rateValue * (stereo ? 2 : 1) * (bitsPerSample / 8)),   // (Sample rate * Number of channels * Bits per sample) / 8
  233.         number16to2chars((bitsPerSample * (stereo ? 2 : 1)) / 8),               // (Bits per sample * Number of channels) / 8  
  234.         number16to2chars(bitsPerSample),                                        // bits per sample
  235.         Buffer.from('data', 'ascii'),                                           // Begin data block
  236.         number32to4chars(buffer.length),                                        // How long is this block?
  237.     ]);
  238.  
  239.     const final = Buffer.concat([header, buffer]);
  240.  
  241.     fs.writeFileSync(outputFile, final);
  242.  
  243.     console.log('Done, wrote %d samples.', trueLength);
  244.     return 0;
  245. }
  246.  
  247. main();
  248.  
Advertisement
Add Comment
Please, Sign In to add comment