Lab

lane

What every GPU lane shares: placing the canvas on what the view shows, the clips in view with the frames they read, drawing again on a change of inputs, a resize, a scroll or the theme, and whether this browser has WebGL 2.

pnpm add @audio-ui/gl
import { CHUNK_FRAMES, feedChunks, OVERSCAN, groundOf, hasWebGL2, /* … */ } from "@audio-ui/gl/lane";
ExportKindWhat it is
LaneSamplesinterfaceA recording's samples, mixed to one: how many frames it holds, and a reader of the frames from from to to.
LaneSpectrainterfaceA recording's spectra: a column every hop frames, each bin's level to Nyquist over windows of size frames, a byte each, (dB + 150) · 1.6, 0 silence: what gl-spectrogram draws a recording it does not hold from.
CHUNK_FRAMESconstThe frames a renderer keeps its samples in, and a streamed recording is read in: 65536.
ChunkFeedinterfaceWhat a renderer holds of a recording it streams: the chunks handed over, oldest first, and those being read.
ChunkRendererinterfaceWhat a renderer that streams its samples does with them: gl-waveform and gl-spectrogram both do.
feedChunksfunctionHands a renderer the chunks of a streamed recording that ranges read, in frames: those it lacks are read, each handed over once read, and ready draws again.
LaneSegmentinterfaceA stretch of a clip that reads its recording straight on: its two times in the clip, in seconds from its start, and the frames of the recording at them.
LaneClipinterfaceA clip of a lane to draw: where it lies, how long it is, and how it reads its recording.
VisiblePartinterfaceA part of a clip the view shows: its part of the canvas in CSS px from the canvas' left, and the frames of the recording it reads there.
VisibleClipinterfaceA clip the view shows, with its parts and its samples.
OVERSCANconstThe pixels drawn past each edge of the view, so a scroll shows them at once.
groundOffunctionThe nearest background that is not transparent, from element up: what a picture's silence lies over.
hasWebGL2functionWhether this browser can draw with WebGL 2: the lanes that need it say no, and the page draws them another way.
releaseContextfunctionLets go of a lane's own WebGL context once the lane is destroyed: a browser keeps only some sixteen, frees a dropped canvas's only when it collects it, and past that loses the oldest, which may be one a page still draws on.
keepContextfunctionA lane starts on a canvas whose context was being let go of: it keeps it.
sameSamplesfunctionWhether two readings are the same samples: a reader may hand out a new view of the same memory each time, which is not new data to upload.
clearLanefunctionClears the whole canvas of a lane's context.
PlacedinterfaceWhat placeLane gives: where the canvas starts on the lane, in CSS px, where it ends, and the device pixel ratio.
placeLanefunctionSizes the canvas to the stretch of its lane the view shows, and gives that stretch in CSS px from the lane's left with the device pixel ratio, or hides the canvas when none shows: the lane scrolled out of sight to either side, or up or down.
joinPartsfunctionParts that follow one another on the canvas and in the recording, joined into one while every junction between them stays within a quarter of a pixel of where the one line reading them all puts it: a warped clip's stretches, a pixel or two wide zoomed out, are drawn as one part, not hundreds, and stay apart zoomed in, where a junction would show.
partsInViewfunctionThe clips of a lane the view shows, each with its parts, before their samples are read: stretches that read as one line joined (joinParts).
SampleCachetypeWhat a lane has read of its recordings, by source: a recording is read once, not at every paint, since a reader may copy or mix the whole of it each time it is asked.
KeptCachetypeWhat a lane has read of its recordings, by key: what it read, or is reading, and the reader it read it with.
readKeptfunctionWhat read gives for samples, from cache under key when that reader is the one it was read with, else read now and kept: once a promise is settled, the cache holds what it gave, so the next paint has it at once.
readOncefunctionA recording's samples, from the cache when its reader is the one it was read with, else read now and kept.
visibleClipsNowfunctionThe clips of a lane the view shows, as visibleClips gives them, when every recording they read hands its samples at once: what lets a lane draw a zoom in the frame it happens.
visibleClipsfunctionThe clips of a lane the view shows, each with its parts of the canvas and the frames it reads there, and their samples read once a recording.
LaneContextinterfaceWhat a lane hands its painter and its listeners.
LaneOptionsinterfaceWhat a lane is made of.
LaneinterfaceA lane that is drawing.
createLanefunctionStarts a lane on canvas, which sits in the lane's box: placed on the stretch the view shows, drawn again when its inputs change, the lane resizes, the view scrolls a good way or the theme changes, and let go of with destroy.

Usage

createLane starts a lane on a canvas that sits in the lane's box. You give it what to keep between paints, how to draw, and the inputs; it places the canvas, draws in the next frame whenever the inputs change, the lane resizes, the view scrolls a good way or the theme changes, and lets go of everything on destroy.

import { createLane, placeLane, visibleClips } from "@audio-ui/gl/lane";

const lane = createLane({
  canvas,
  create: () => ({ gone: false, renderers: new Map() }), // what the lane keeps
  destroy: (held) => { /* let go of what create made */ },
  inputs: { clips, zoom },
  paint: async ({ body, canvas, held, inputs, view }) => {
    const { clips, zoom } = inputs();
    const placed = placeLane(canvas, body, view); // null when no part of the lane shows
    if (!placed) return false;
    const visible = await visibleClips(clips, placed, zoom); // each with its parts: x, width and frames
    // draw each part into its viewport; return true while some of it is not yet whole
    return false;
  },
});

lane.update({ clips, zoom }); // draws again, keeping what is on the GPU
lane.destroy();

The lane never starts over for a change of inputs, so what is on the GPU stays: a zoom or a cut draws again without the samples going up a second time.

Clips

A LaneClip is a clip of the lane: its id, where it starts and how long it is on the lane in seconds, its gain, the recording's sampleRate and samples ({ frames, read }, mixed to one), the source it reads, and segments, how it reads it. segments(from, to) gives the stretches of the clip between two times of it, each with the frames of the recording at its two ends; a stretch whose frames fall is read backwards.

A recording streamed from a file adds leaves to its samples, what a waveform draws from zoomed out (see waveform): a lane then never reads it whole. feedChunks hands a renderer the chunks of CHUNK_FRAMES frames a view reads, reading those it lacks and letting go of the oldest past what it keeps; readKept reads something of a recording once, kept across paints.

Stretches that follow one another on the canvas and in the recording are drawn as one part while every junction stays within a quarter of a pixel of where that one line puts it (joinParts): a warped clip zoomed out is a part or two, not one per marker, and zoomed in, where a junction would show, its stretches stay apart.

visibleClips gives the clips the view shows, each with its parts of the canvas (x and width in CSS px, the frames read there, lowest first) and its samples, read once a recording whatever its clips. sameSamples tells whether a reader handed out the same memory again, so it is not uploaded twice.

WebGL 2

hasWebGL2 says whether this browser can draw a lane at all, asked once, and a page draws it another way when it cannot. A browser also keeps only some sixteen contexts: a lane takes one, whatever its clips, and lets it go when it is destroyed (releaseContext, and keepContext for a lane started again on the same canvas in the same task); a waveform lane made shared takes none of its own. A lane scrolled out of sight, to either side or up or down, draws nothing until it comes near.

Built by Ouest Labs, the source code is available on GitHub.
RSS