Public
Edited
Sep 21, 2023
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
instrument = xylo
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
channelDataToAudioEl(data, sampleRate)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
if (renderBtn === 0) return;
const notes = 16;
const noteOffsets = [0.5];
// const tempoChanges = d3.range(-0.06, 0.07, 0.02);
const tempoChanges = [0.01];
// const wrongNoteIndices = d3.range(1, notes - 2, 2);
const wrongNoteIndices = [3];
const noteDeviations = d3.range(-0.05, 0.06, 0.02);

const imageWidth = 1000;
const imageHeight = 100;
const maxTimeDrawn = notes * d3.max(noteOffsets) + 5;

const zip = new jszip.default();

const count =
noteOffsets.length *
tempoChanges.length *
wrongNoteIndices.length *
noteDeviations.length;
console.log(`rendering ${count} samples`);
let current = 1;

for (const noteOffset of noteOffsets) {
for (const tempoChange of tempoChanges) {
for (const wrongNoteIndex of wrongNoteIndices) {
for (const noteDeviation of noteDeviations) {
if (current % 50 === 0 || current === count)
console.log(`rendering ${current} / ${count}`);
current++;
const name = `n-${notes}_no-${noteOffset.toFixed(
3
)}_tc-${tempoChange.toFixed(
3
)}_wni-${wrongNoteIndex}_nd-${noteDeviation.toFixed(3)}`;
// pattern
const pattern = generatePattern(
notes,
noteOffset,
tempoChange,
wrongNoteIndex,
noteDeviation
);
// render audio
const data = simulate(instrument, pattern);
const buffer = new AudioBuffer({ sampleRate, length: data.length });
buffer.copyToChannel(data, 0);
const blob = new Blob([audioBufferToWav.default(buffer)], {
type: "audio/wav"
});
zip.file(`${name}.wav`, blob);
// render tick image
// use fixed seconds/pixel
const canvas = document.createElement("canvas");
canvas.width = imageWidth;
canvas.height = imageHeight;
const context = canvas.getContext("2d");
context.fillStyle = "white";
context.fillRect(0, 0, imageWidth, imageHeight);
context.fillStyle = "black";
const scaleX = d3
.scaleLinear()
.domain([0, maxTimeDrawn])
.range([10, imageWidth - 10]);
for (const note of pattern) {
context.fillRect(scaleX(note), 0, 2, imageHeight);
}
const imgBlob = await new Promise((resolve) =>
canvas.toBlob(resolve)
);
zip.file(`${name}_ticks.png`, imgBlob);
// render waveform
context.fillStyle = "white";
context.fillRect(0, 0, imageWidth, imageHeight);
context.strokeStyle = "black";
const max = d3.max(data.map((d) => Math.abs(d)));
const scaleY = d3
.scaleLinear()
.domain([max, -max])
.range([0, imageHeight]);
const line = d3
.line()
.x((d, i) => scaleX(i / sampleRate))
.y((d) => scaleY(d))
.context(context);
context.beginPath();
line(data);
context.stroke();
const imgBlob2 = await new Promise((resolve) =>
canvas.toBlob(resolve)
);
zip.file(`${name}_wave.png`, imgBlob2);
}
}
}
}

// download
zip.generateAsync({ type: "blob" }).then((content) => {
// see FileSaver.js
fileSaver.default(content, "example.zip");
});
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more