Public
Edited
Dec 3, 2023
Importers
23 stars
Insert cell
Insert cell
Insert cell
viewof manual = sample({
loop: false,
gain: 0.4,
speed: 3,
audioContext: audioContext,
arrayBuffer: await FileAttachment("Crash-Cymbal-1.wav").arrayBuffer()
})
Insert cell
{
manual.playing = audioContext.currentTime + 0.5;
manual.playing = audioContext.currentTime + 1;
}
Insert cell
Insert cell
cymbal
Insert cell
Insert cell
Insert cell
Insert cell
// We can write to the property and the UI updates
// The properties are enumerable
{
writable_demo.start = 0.5* Math.sin(now/300) + 0.5
writable_demo.end = 0.5*Math.sin(now/300) + 0.6
writable_demo.gain = (now / 1000) % 1
return Object.keys(writable_demo).concat(Object.values(writable_demo))
}
Insert cell
Insert cell
viewof writable_demo = sample({
loop: true,
audioContext: audioContext,
arrayBuffer: await FileAttachment("Crash-Cymbal-1.wav").arrayBuffer()
})
Insert cell
writable_demo
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof waveform = waveformSelect({
audioBuffer: await new AudioContext().decodeAudioData(await FileAttachment("Crash-Cymbal-1.wav").arrayBuffer()),
selectionStart: 10000,
selectionEnd: 50000,
})
Insert cell
function waveformSelect({
audioBuffer,
width = 250,
height = 50,
selectionStart = 0,
selectionEnd = audioBuffer.length
} = {}) {
const data = audioBuffer.getChannelData(0);
const density = seriesDensity(width, height)
.yDomain([-1, 1])
.arcLengthNormalize(false)
.xDomain([0, data.length]);
let plot = densityPlot(density)
.drawAxes(false)
.background("white")
.color(() => () => ({r:0, g:0, b:0, opacity:1}));
const waveform = plot([data]);
let start = null;
function mousedown(evt) {
const rect = ui.getBoundingClientRect()
const x = evt.clientX - rect.left
start = x;
setSelectionStart(x * px2x)
return false;
}
function mousemove(evt) {
if (!evt.buttons) start = null;
if (start) {
const rect = ui.getBoundingClientRect()
const x = evt.clientX - rect.left
setSelectionEnd(x * px2x)
}
}
let selectionStartRect, selectionEndRect;
const px2x = data.length * 1.0 / width;
const x2px = width * 1.0 / data.length;
function setSelectionStart(start) {
selectionStart = start;
selectionStartRect.setAttribute("width", start*x2px)
notifyInput()
}
function setSelectionEnd(end) {
selectionEnd = end;
selectionEndRect.setAttribute("x", end*x2px)
notifyInput()
}
const value = {
selectionStart, selectionEnd,
setSelectionStart, setSelectionEnd
};
function notifyInput() {
value.selectionStart = selectionStart;
value.selectionEnd = selectionEnd;
ui.dispatchEvent(new CustomEvent("input"));
}
const overlay = svg`<svg
viewBox="0 0 ${width} ${height}"
width="${width}px"
height="${height}px"
preserveAspectRatio="none"
fill="rgba(0, 0, 0, 0.3)"
stroke="none"
style="position: absolute"
>
<rect class="wselect-start"
x="0"
width=${selectionStart*x2px}
height=${height} />
<rect class="wselect-end"
x=${selectionEnd * x2px}
width=${width}
height=${height} />
</svg>`
const ui = html`<span>
${overlay}
${waveform}
</span>`
selectionStartRect = ui.querySelector(".wselect-start");
selectionEndRect = ui.querySelector(".wselect-end");
ui.addEventListener('mousedown', mousedown);
document.addEventListener('mousemove', mousemove);
invalidation.then(() => {
ui.removeEventListener('mousedown', mousedown);
document.removeEventListener('mousemove', mousemove);
})
ui.value = value;
return ui;
}
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