Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

request: scriptable offline renderer #116

Open
M0ahgra opened this issue Nov 11, 2024 · 7 comments
Open

request: scriptable offline renderer #116

M0ahgra opened this issue Nov 11, 2024 · 7 comments

Comments

@M0ahgra
Copy link

M0ahgra commented Nov 11, 2024

i wish there was a script-compatible offline version of beepbox that could be sent a .json or song hash, and then just render an audio file automatically. as im writing this im in the process of trying to hack together this exact idea from the source code, so i can use it in conjunction with python scripts to automatically create low quality previews of my .json beepbox songs.

im not sure where to put this idea, im not sure if anyone cares, but maybe someone other than me could benefit from it.

@leovoel
Copy link

leovoel commented Nov 12, 2024

It would definitely be convenient to have this as part of, say, the NPM package. But you can put something like that together yourself without too much work, today (this is meant to be executed with Node.js, for clarity):

import * as fs from "node:fs/promises";
import { Synth } from "beepbox"; // npm install beepbox
import wavefile from "wavefile"; // npm install wavefile
const { WaveFile } = wavefile; // See https://github.com/rochars/wavefile/pull/30 for why this.
// Could also pull the wav code from editor/ExportPrompt.ts, that works too.

async function main(argc, argv) {
    if (argc !== 4) return console.log(`Usage:\n  ${argv[0]} ${argv[1]} input.txt output.wav`);
    const songPath = argv[2];
    const outputPath = argv[3];

    const samplesPerSecond = 48000; // or 44100
    const bitDepth = "16";
    const channelCount = 2;

    const songString = await fs.readFile(songPath, { encoding: "utf-8" });
    const synth = new Synth(songString); // songString can either be JSON data or the #... portion of song URLs.
    synth.samplesPerSecond = samplesPerSecond;
    synth.loopRepeatCount = 0;

    // See https://github.com/johnnesky/beepbox/blob/main/editor/ExportPrompt.ts#L194
    const song = synth.song;
    const enableIntro = song.loopStart !== 0;
    const enableOutro = song.loopStart + song.loopLength !== song.barCount;
    if (!enableIntro) for (let i = 0; i < song.loopStart; i++) synth.goToNextBar();
    const songDurationInSamples = Math.ceil(synth.getSamplesPerBar() * synth.getTotalBars(enableIntro, enableOutro));

    const samplesLeft = new Float32Array(songDurationInSamples);
    const samplesRight = new Float32Array(songDurationInSamples);
    synth.synthesize(samplesLeft, samplesRight, songDurationInSamples);

    const wav = new WaveFile();
    wav.fromScratch(channelCount, samplesPerSecond, "32f", [samplesLeft, samplesRight]);
    wav.toBitDepth(bitDepth);

    await fs.writeFile(outputPath, wav.toBuffer());
}
main(process.argv.length, process.argv);

...which is maybe why this doesn't already exist?

@M0ahgra
Copy link
Author

M0ahgra commented Nov 12, 2024

where did u get that?

@leovoel
Copy link

leovoel commented Nov 12, 2024

I just wrote it right now. I work with BeepBox's source code a lot so I knew what to look for.

It's definitely inconvenient to have to set this up manually if you need it, but I don't know if johnnesky wants to maintain a command-line interface for BeepBox at this point in time.

@M0ahgra
Copy link
Author

M0ahgra commented Nov 12, 2024

I just wrote it right now. I work with BeepBox's source code a lot so I knew what to look for.

It's definitely inconvenient to have to set this up manually if you need it, but I don't know if johnnesky wants to maintain a command-line interface for BeepBox at this point in time.

i would love a cli for beepbox just fyi. the way im trying to do it is i stripped out alot of the player code down to just whats needed, but the big problem is the code has several things in it that inherently tie it up with a web browser, which i dont want to use.

@M0ahgra
Copy link
Author

M0ahgra commented Nov 12, 2024

the player code includes the synth code, and the synth code makes references to web api stuff, its kinda hard to remove the 'web' from javascript/typescript.

@leovoel
Copy link

leovoel commented Nov 12, 2024

What else do you need from the player? The only thing that it does, besides synthesizing the song on the fly, is visualize it.

Is the issue getting just the synthesizer code? You could grab just that then -- in the script I posted above, I rely on the NPM package. But the synth code can also be found here: https://www.beepbox.co/beepbox_synth.js. It's not really meant for use with Node.js (...do you need to run the JavaScript code in some other environment?), since it has all the code wrapped in an IIFE and sets a global variable, which works fine in the browser, but Node expects modules to be CommonJS ones or ES modules.

This still has "web" dependencies inside it (Web Audio stuff specifically), but those will only run if you try to use the built-in methods to play songs. If you instead use the synthesize method like I did above then that shouldn't be an issue.

@M0ahgra
Copy link
Author

M0ahgra commented Nov 13, 2024

i want to just run the synth code just to generate the audio data, no playing, then just save it to a file. and have this b scriptable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants