> ## Documentation Index
> Fetch the complete documentation index at: https://rockboxzig.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# TypeScript

> One typed API for the Rockbox engine across Bun, Deno, and Node.js.

TypeScript bindings for **metadata** parsing (40+ formats), the **DSP**
pipeline (EQ, tone, surround, compressor, ReplayGain, resampler), and a
queue-based **player** with crossfade, shuffle/repeat, and the full Rockbox
DSP chain (EQ presets, tone, crossfeed, surround, compressor, …). One typed
API, three runtimes.

```sh theme={"theme":{"light":"catppuccin-latte","dark":"min-dark"}}
npm  install rockbox-ffi        # Node.js  (also pulls in koffi)
bun  add     rockbox-ffi        # Bun
deno add npm:rockbox-ffi        # Deno   (or import npm:rockbox-ffi/deno)
```

Import the entry point for your runtime — all three expose the identical API:

| Runtime | Import | Notes |
| - | - | - |
| Bun | `import … from "rockbox-ffi/bun"` | uses built-in `bun:ffi` |
| Deno | `import … from "npm:rockbox-ffi/deno"` | run with `--allow-ffi --allow-read --allow-env` |
| Node.js | `import … from "rockbox-ffi/node"` | uses [`koffi`](https://koffi.dev) (a dependency) |

> [!TIP]
>
> Prefer one import that resolves the right backend at runtime? Use
> `import { load } from "rockbox-ffi"` — it returns a promise for the correct
> runtime backend.

The published package bundles a prebuilt native library per platform. From a
repo checkout, point at your own build with `ROCKBOX_FFI_LIB`.

## Quick start

```ts theme={"theme":{"light":"catppuccin-latte","dark":"min-dark"}}
import {
  metadata,
  Dsp,
  Player,
  sineStereo,
  DspReplayGainMode,
  ReplayGainMode,
  CrossfadeMode,
  EqPreset,
  RepeatMode,
} from "rockbox-ffi/bun"; // or /node, or npm:rockbox-ffi/deno

// --- metadata --------------------------------------------------------
const meta = metadata.read("song.flac");
console.log(meta.artist, "—", meta.title, `${meta.duration_ms} ms`);
console.log(metadata.probe("track.opus")); // "Opus"  (extension guess, no I/O)

// --- DSP: process interleaved stereo Int16 ---------------------------
const dsp = new Dsp(44_100);
dsp.eqEnable(true);
dsp.setEqBand(0, /*cutoffHz*/ 60, /*q*/ 0.7, /*gainDb*/ 3.0);
dsp.setReplaygain(DspReplayGainMode.TRACK, /*noclip*/ true, /*preampDb*/ 0.0);
dsp.setReplaygainGains(/*trackGainDb*/ -6.02); // −6 dB ≈ half amplitude

const input = sineStereo(1_000, 1.0, 44_100); // 1 s of a 1 kHz test tone
const output: Int16Array = dsp.process(input);
dsp.close();

// --- playback (needs an audio output device) -------------------------
// Mutating Player methods return `this`, so setup fluently chains.
// Queue entries may be local paths, http(s):// files, or live-radio /
// streaming URLs.
const player = new Player({ volume: 0.8, crossfadeMode: CrossfadeMode.ALWAYS });
player
  .setReplaygain(ReplayGainMode.TRACK, 0.0, true)
  .setQueue(["a.flac", "https://example.com/b.mp3", "http://radio.example/live"])
  .setEqPreset(EqPreset.BassBoost) // one of 21 presets
  .setShuffle(true)
  .setRepeat(RepeatMode.All)
  .play();

console.log(player.status()); // { state: "playing", index: 0, ... }
console.log(player.dspSettings()); // full DSP-chain snapshot
```

## Decode a file to PCM

`Decoder` streams one audio file through the Rockbox codec engine, yielding
interleaved-stereo S16LE PCM (`Int16Array`) one chunk at a time — no output
device required. Same `Metadata` shape as `metadata.read`.

```ts theme={"theme":{"light":"catppuccin-latte","dark":"min-dark"}}
import { Decoder } from "rockbox-ffi/bun"; // or /node, or npm:rockbox-ffi/deno

using dec = new Decoder("song.flac"); // freed at end of scope
console.log(dec.metadata().title);

for (let chunk; (chunk = dec.nextChunk()) !== null; ) {
  // chunk.samples: interleaved-stereo Int16Array; chunk.sampleRate: Hz
  writePcm(chunk.samples);
}

const { done, code } = dec.finished(); // code 0 = clean end, negative = codec error
```

Seek with `dec.seekMs(ms)` and read the codec's position with `dec.elapsedMs()`.

> [!WARNING]
>
> Codec state is **process-wide — only one `Decoder` decodes at a time**.
> Constructing a second `Decoder` blocks until the first is closed (via
> `close()`, or automatically at the end of a `using` scope).

## API

Everything is fully typed — your editor has the details.

**`metadata`**

| Function | Returns | Description |
| - | - | - |
| `metadata.read(path)` | `Metadata` | Parse tags, duration, ReplayGain, album-art/cue offsets |
| `metadata.probe(filename)` | `string \| null` | Codec label from the extension, without opening the file |

**`Dsp`** — interleaved-S16LE-stereo processor. Construct with a sample rate,
feed it `Int16Array`s, and `close()` (or `using`) when done.

| Method | Description |
| - | - |
| `process(samples: Int16Array): Int16Array` | Run stereo S16 frames through the pipeline |
| `setInputFrequency(hz)` | Change input rate (engages the resampler) |
| `eqEnable(on)` / `setEqBand(band, cutoffHz, q, gainDb)` | 10-band EQ (band 0 low-shelf, 9 high-shelf) |
| `setEqPrecut(db)` | Negative pre-gain to avoid EQ clipping |

**`Decoder`** — streaming file decoder. Construct with a path, pull PCM with
`nextChunk()`, and `close()` (or `using`) when done. Only one may decode at a
time (see [Decode a file to PCM](#decode-a-file-to-pcm)).

| Method | Description |
| - | - |
| `metadata()` | Tags + stream properties (same shape as `metadata.read`) |
| `nextChunk(): { samples, sampleRate } \| null` | Next interleaved-stereo `Int16Array`; `null` at end of track |
| `finished(): { done, code }` | `done` once ended; `code` 0 = clean end, negative = codec error |
| `seekMs(ms)` / `elapsedMs()` | Seek by / read position in milliseconds |

**`Player`** — queue, transport, crossfade, shuffle/repeat, the full DSP
chain, and `status()`. Every mutating method returns `this`, so calls chain
fluently (see the quick start); getters and `close()` return their own values.

| Method | Description |
| - | - |
| `setQueue(paths)` / `enqueue(path)` | Set / append. Entries: local path, `http(s)://`, or stream URL |
| `insert(paths, position?, index?)` | Insert at an `InsertPosition` |
| `queue()` | The current queue as an array of paths/URLs |
| `play()` / `pause()` / `toggle()` / `stop()` | Transport controls |
| `next()` / `previous()` / `skipTo(index)` / `seekMs(ms)` | Navigate within the queue |
| `setVolume(v)` / `volume()` / `sampleRate()` | Volume + output-rate access |
| `setBalance(balance)` / `balance()` | Stereo balance, −100 (full left) to +100 (full right); 0 = centre |
| `setShuffle(on)` / `isShuffleEnabled()` | Queue shuffle |
| `setRepeat(mode)` / `repeat()` | `RepeatMode` (Off / One / All) |
| `setCrossfade(mode, …)` / `setReplaygain(mode, preampDb, noclip)` | Crossfade + `ReplayGainMode` |
| `setEqEnabled(on)` / `isEqEnabled()` | Toggle the graphic EQ |
| `setEqPreset(preset)` | Apply one of 21 `EqPreset` presets |
| `setEqBand(band, cutoffHz, q, gainDb)` / `setEqPrecut(db)` | Per-band EQ (plain units) + pre-gain |
| `setTone(bassDb, trebleDb, bassHz, trebleHz)` | Bass/treble tone controls at given cutoffs |
| `setBass(db)` / `setTreble(db)` / `setBassCutoff(hz)` / `setTrebleCutoff(hz)` | Individual tone setters |
| `setCrossfeed(mode, directGain, crossGain, hfGain, hfCutoff)` | `CrossfeedMode` (Off / Meier / Custom) |
| `setSurround(delayMs, balance, cutoffLowHz, cutoffHighHz)` | Surround/soundstage effect |
| `setChannelMode(mode)` / `setStereoWidth(percent)` | `ChannelMode` + stereo-width control |
| `setBassEnhancement(strength, precut)` / `setFatigueReduction(strength)` | Bass boost + listening-fatigue reduction |
| `setCompressor(thresholdDb, makeupGain, ratio, knee, attackMs, releaseMs)` | Dynamic-range compressor |
| `setDither(on)` / `setPitch(ratio)` | Dithering + pitch/tempo ratio |
| `dspSettings()` | The current DSP-chain settings as a plain object |
| `status()` | Playback state, index, elapsed, and current track metadata |
| `resume()` / `saveResume()` / `clearResume()` | Persist / restore the queue + exact position |
| `importM3u(path, position?, index?)` / `loadM3u(path)` / `exportM3u(path)` | Playlist (.m3u/.m3u8) import / load / export |

**Enums**

Exported constant objects to pass to the setters above:
`DspReplayGainMode`, `ReplayGainMode`, `CrossfadeMode`, `MixMode`,
`CrossfeedMode`, `InsertPosition`, `ChannelConfig`, `ChannelMode`, `EqPreset`,
and `RepeatMode`.

> [!TIP]
>
> The `EqPreset` presets, tone/crossfeed/surround/compressor controls, and
> their value ranges mirror Rockbox's own sound-settings menu — see the
> [official Rockbox manual](https://download.rockbox.org/daily/manual/rockbox-ipodvideo/rockbox-buildch6.html)
> for what each effect does.

> [!WARNING]
>
> `Dsp.setReplaygain` and `Player.setReplaygain` use **different mode
> integers**. Pass the exported enums — `DspReplayGainMode` for the DSP,
> `ReplayGainMode` for the player. See [the overview](https://rockboxzig.mintlify.site/bindings/overview.md#the-one-thing-to-know-two-replaygain-encodings).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.