{
clear;
const isWaveform = measure === "Waveform";
const height = width * 0.618;
const marginLeft = 0;
const marginRight = 0;
const marginTop = isWaveform ? 50 : 0;
const marginBottom = isWaveform ? 50 : 0;
const frameRate = 10;
const urls = [
await FileAttachment("tears.wav").url(),
await FileAttachment("takerimba.wav").url(),
await FileAttachment("blip@1.wav").url(),
await FileAttachment("punch.wav").url()
];
const players = urls.map((url) => new Tone.Player(url).toDestination());
const analyzers = players.map((player) => {
const analyzer = new (isWaveform ? Tone.Waveform : Tone.FFT)(256);
player.connect(analyzer);
return analyzer;
});
const cancelURL = await FileAttachment("cancel.wav").url();
const cancel = new Tone.Player(cancelURL).toDestination();
const colors =
schemes === "Categorical"
? d3.schemeObservable10
: urls
.map((_, i) => {
const l = urls.length;
const step = 1 / (l + 1);
return (i + 1) * step;
})
.map(d3.interpolateCool);
const [state, dispose] = cm
.flow()
.let("data", [])
.derive("paths", paths)
.on("loop", loop, { frameRate })
.on("keydown", keydown)
.join();
invalidation.then(() => dispose());
await Tone.loaded;
function loop(state) {
for (let i = 0; i < players.length; i++) {
const player = players[i];
if (player.state === "started") {
const analyzer = analyzers[i];
const id = Math.random();
const data = Array.from(analyzer.getValue());
if (data.every((d) => d === 0 || d === -Infinity)) return;
const indexed = data.map((d, j) => ({
index: j,
name: `${i}-${id}`,
value: d
}));
state.data = [...state.data, ...indexed];
}
}
}
function keydown(d, { key }) {
switch (key) {
case "a":
players[0].start();
break;
case "s":
players[1].start();
break;
case "d":
players[2].start();
break;
case "f":
players[3].start();
break;
case "c":
state.data = []; // Clear Screen
cancel.start();
break;
}
}
function paths({ data }) {
const series = d3
.stack()
.offset(isWaveform ? d3.stackOffsetDiverging : null)
.keys(d3.union(data.map((d) => d.name)))
.value(([, D], key) => D.get(key).value)(
d3.index(
data,
(d) => d.index,
(d) => d.name
)
);
const x = d3
.scaleLinear()
.domain(d3.extent(data, (d) => d.index))
.range([marginLeft, width - marginRight]);
const y = d3
.scaleLinear()
.domain(d3.extent(series.flat(2)))
.rangeRound([height - marginBottom, marginTop]);
const color = d3
.scaleOrdinal()
.domain(series.map((d) => d.key.split("-")[0]))
.range(colors);
const area = d3
.area()
.x((d) => x(d.data[0]))
.y0((d) => y(d[0]))
.y1((d) => y(d[1]));
return series.map((d) =>
svg.path({
d: area(d),
fill: color(d.key.split("-")[0])
})
);
}
const node = svg.svg({ width, height, viewBox: [0, 0, width, height] }, [
svg.rect({ x: 0, y: 0, width, height, fill: "black" }),
cm.$(() => state.paths)
]);
yield node;
fullscreen(node, { center: true });
}