# 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.

```bash
npm install @audio-ui/gl
```

```ts
import { CHUNK_FRAMES, feedChunks, OVERSCAN, groundOf, hasWebGL2, /* … */ } from "@audio-ui/gl/lane";
```

| Export | Kind | What it is |
| --- | --- | --- |
| `LaneSamples` | interface | A recording's samples, mixed to one: how many frames it holds, and a reader of the frames from `from` to `to`. |
| `LaneSpectra` | interface | A 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_FRAMES` | const | The frames a renderer keeps its samples in, and a streamed recording is read in: 65536. |
| `ChunkFeed` | interface | What a renderer holds of a recording it streams: the chunks handed over, oldest first, and those being read. |
| `ChunkRenderer` | interface | What a renderer that streams its samples does with them: `gl-waveform` and `gl-spectrogram` both do. |
| `feedChunks` | function | Hands 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. |
| `LaneSegment` | interface | A 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. |
| `LaneClip` | interface | A clip of a lane to draw: where it lies, how long it is, and how it reads its recording. |
| `VisiblePart` | interface | A 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. |
| `VisibleClip` | interface | A clip the view shows, with its parts and its samples. |
| `OVERSCAN` | const | The pixels drawn past each edge of the view, so a scroll shows them at once. |
| `groundOf` | function | The nearest background that is not transparent, from `element` up: what a picture's silence lies over. |
| `hasWebGL2` | function | Whether this browser can draw with WebGL 2: the lanes that need it say no, and the page draws them another way. |
| `releaseContext` | function | Lets 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. |
| `keepContext` | function | A lane starts on a canvas whose context was being let go of: it keeps it. |
| `sameSamples` | function | Whether 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. |
| `clearLane` | function | Clears the whole canvas of a lane's context. |
| `Placed` | interface | What `placeLane` gives: where the canvas starts on the lane, in CSS px, where it ends, and the device pixel ratio. |
| `placeLane` | function | Sizes 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. |
| `joinParts` | function | Parts 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. |
| `partsInView` | function | The clips of a lane the view shows, each with its parts, before their samples are read: stretches that read as one line joined (`joinParts`). |
| `SampleCache` | type | What 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. |
| `KeptCache` | type | What a lane has read of its recordings, by key: what it read, or is reading, and the reader it read it with. |
| `readKept` | function | What `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. |
| `readOnce` | function | A recording's samples, from the cache when its reader is the one it was read with, else read now and kept. |
| `visibleClipsNow` | function | The 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. |
| `visibleClips` | function | The 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. |
| `LaneContext` | interface | What a lane hands its painter and its listeners. |
| `LaneOptions` | interface | What a lane is made of. |
| `Lane` | interface | A lane that is drawing. |
| `createLane` | function | Starts 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`.

```ts
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 `start`s 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](/docs/gl/waveform#streamed-recordings)): 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.
