Skip to content

Instantly share code, notes, and snippets.

@ronwnor
Last active April 17, 2023 07:16
Show Gist options
  • Select an option

  • Save ronwnor/0249ab784918aabe164b82a9e1ad3a81 to your computer and use it in GitHub Desktop.

Select an option

Save ronwnor/0249ab784918aabe164b82a9e1ad3a81 to your computer and use it in GitHub Desktop.
generate audio from functions!
{
window.AudioContext = window.AudioContext || window.webkitAudioContext;
const audioContext = new AudioContext();
const BuiltIn = require("core/math/builtin");
// returns the selected expression as a js function
const parsedSelectedExpression = () => {
// from the great fireflame
const computeContext = () => {
// Emulate what happens in the web worker
const Context = require("core/math/context").Context,
context = new Context(),
changeSet = {
isCompleteState: true,
statements: {},
};
for (let stmt of Calc.controller.getAllItemModels()) {
if (stmt.type !== "expression" && stmt.type !== "table") continue;
changeSet.statements[stmt.id] = stmt;
}
context.processChangeSet(changeSet);
context.updateAnalysis();
return context;
}
// get the selected expression from the computed thing
let f = computeContext()
.analysis[Calc.selectedExpressionId]
.concreteTree
.getCompiledFunction();
// replace _C[0], _C[1] stuff with the actual lists
let src = f.source.replace(/_C\[(.*?)\]/g, e => '[' + f.constants[e.match(/\[(.*?)\]/)[1]] + ']');
// return it
let parsedFunc;
eval(`parsedFunc = (${f.args[0]}) => {${src}}`);
return parsedFunc;
}
let isPlaying = false; // toggle
let bufferSource;
// generates the audio buffer? from the function
const generateAudioFromExpression = () => {
if(isPlaying){
bufferSource.stop();
isPlaying = false;
return;
}
let parsedFunc = parsedSelectedExpression();
let bounds = Calc.graphpaperBounds.mathCoordinates;
// audio part, from https://stackoverflow.com/a/34709510
let buf = new Float32Array(audioContext.sampleRate * (bounds.right - bounds.left));
for(let i=0; i<buf.length; i++){
buf[i] = parsedFunc((i/buf.length)*(bounds.right - bounds.left) + bounds.left);
}
let buffer = audioContext.createBuffer(1, buf.length, audioContext.sampleRate);
buffer.copyToChannel(buf, 0)
bufferSource = audioContext.createBufferSource();
bufferSource.buffer = buffer;
bufferSource.connect(audioContext.destination);
bufferSource.start(0);
isPlaying = true;
bufferSource.onended = () => isPlaying = false;
}
// divs and css and stuff
const div = document.createElement('div');
div.innerText = "音";
div.style = `
position: absolute;
bottom: 0;
right: -1px;
padding: 20px 7px 6px 20px;
z-index: 3;
cursor: pointer;
text-align: left;
opacity: 0.4;
font-size: 115%;
font-weight: bold;
`;
div.onmousedown = () =>
div.style.opacity = 1;
div.onmouseup = () => {
div.style.opacity = .8;
generateAudioFromExpression();
}
div.onmouseenter = () => {
div.style.opacity = .8;
}
div.onmouseleave = () => {
div.style.opacity = .2;
}
Calc.controller.dispatcher.register(() => {
if(Calc.controller.focusLocation?.type == 'expression')
document
.querySelector(`div[expr-id="${Calc.controller.focusLocation.id}"] div.dcg-top-level-icon`)
.append(div);
});
}
@ronwnor
Copy link
Author

ronwnor commented Feb 22, 2023

*wip*
one x unit on the graph equals one second of audio.
try not to play graphs with amplitude greater than 1. you will suffer.
doesn't support tables for some reason, but regular lists should work fine

example usage:
image

@ronwnor
Copy link
Author

ronwnor commented Apr 17, 2023

goddamn this is broken now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment