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

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).
</Warning>

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

<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](/bindings/overview#the-one-thing-to-know-two-replaygain-encodings).
</Warning>
