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

# Kotlin

> Rockbox DSP, metadata, and playback on the JVM via the Java Foreign Function & Memory API — no JNI.

Kotlin/JVM bindings with **no JNI and no native glue to compile**: they use the
Java **Foreign Function & Memory API** (JEP 454, stable since **JDK 22**) to
locate `librockbox_ffi` at runtime and bind every function to a `MethodHandle`
downcall.

```kotlin theme={"theme":{"light":"catppuccin-latte","dark":"min-dark"}}
// build.gradle.kts
dependencies {
    implementation("io.github.tsirysndr:rockbox-ffi:0.2.0")
}
```

Requires a **JDK 22+**. The published jar bundles a prebuilt `librockbox_ffi`
for every OS/arch — it extracts the matching one at load time, so consumers
need no Rust toolchain. `ROCKBOX_FFI_LIB` overrides; a repo checkout falls back
to `target/release`.

<Note>
  FFM's restricted native calls need `--enable-native-access=ALL-UNNAMED` on
  the launch command (the project's Gradle tasks wire this in).
</Note>

## Quick start

```kotlin theme={"theme":{"light":"catppuccin-latte","dark":"min-dark"}}
import org.rockbox.ffi.*

// metadata
val meta = Metadata.read("/music/song.flac")   // Map<String, Any?>
println(meta["title"])
Metadata.probe("song.flac")                     // "FLAC"

// DSP (interleaved stereo int16)
Dsp(44_100).use { dsp ->
    dsp.eqEnable(true)
    dsp.setEqBand(band = 0, cutoffHz = 100, q = 0.7f, gainDb = 3.0f)
    val out: ShortArray = dsp.process(samples)
}

// Player (queue + transport + DSP + shuffle/repeat)
Player(Player.Config().apply { volume = 0.8f }).use { player ->
    // Sources may be local paths, http(s):// URLs, or live-radio / streaming URLs.
    player.setQueue(listOf("/music/a.flac", "https://example.com/b.mp3"))
    player.enqueue("http://stream.example.org/radio")

    // DSP chain
    player.setEqEnabled(true)
    player.setEqPreset(EqPreset.BASS_BOOST)     // one of 21 built-in presets

    // Playback modes
    player.setShuffle(true)
    player.setRepeat(RepeatMode.ALL)

    player.play()
    println(player.status()["state"])
    println(player.dspSettings())               // Map<String, Any?>
}
```

<Note>
  Player setters follow Kotlin idiom — they return `Unit`, so they do **not**
  chain. Construct with `Player(Config().apply { volume = 0.8f })` and call
  each setter on its own line.
</Note>

## Decode a file to PCM

`Decoder` streams a file through the Rockbox codec engine, yielding
interleaved-stereo S16LE chunks one at a time — no output device required.

```kotlin theme={"theme":{"light":"catppuccin-latte","dark":"min-dark"}}
import org.rockbox.ffi.*

Decoder("/music/song.flac").use { dec ->
    println(dec.metadata()["title"])            // same shape as Metadata.read

    while (true) {
        val chunk = dec.nextChunk() ?: break     // null at end of track
        val (samples: ShortArray, rate: Int) = chunk
        // feed `samples` (interleaved stereo int16) at `rate` Hz to your sink
    }

    val (done, code) = dec.finished()            // code 0 = clean end, <0 = codec error
    check(done && code == 0) { "decode failed: $code" }
}

// seeking is supported mid-stream
Decoder("/music/song.flac").use { dec ->
    dec.seekMs(30_000)
    dec.nextChunk()
    println(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, so always
  keep decoders inside a `use { }` block (or call `close()`) before opening the
  next one.
</Warning>

## API surface

| Type       | Selected members                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Metadata` | `read(path)` → `Map<String, Any?>`, `probe(path)` → codec name                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `Dsp`      | `eqEnable(on)`, `setEqBand(band, cutoffHz, q, gainDb)`, `process(samples)`, `setReplaygain(mode, …)`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `Decoder`  | `Decoder(path)` (AutoCloseable), `metadata()` → `Map<String, Any?>`, `nextChunk()` → `Pair<ShortArray, Int>?`, `seekMs(ms)`, `elapsedMs()`, `finished()` → `Pair<Boolean, Int>`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `Player`   | **Sources / queue**: `setQueue(paths)`, `enqueue(path)`, `insert(paths, position, index)`, `queue()`, `loadM3u` / `importM3u` / `exportM3u` — every path may be a local file, an `http(s)://` URL, or a live-radio / streaming URL. **Transport**: `play`, `pause`, `toggle`, `stop`, `next`, `previous`, `skipTo`, `seekMs`. **Volume / balance / crossfade / replaygain**: `setVolume`, `setBalance(balance)` / `balance` (−100 … +100, 0 = centre), `setCrossfade`, `setReplaygain`. **Shuffle / repeat**: `setShuffle(on)`, `isShuffleEnabled()`, `setRepeat(mode)`, `repeat()`. **DSP chain**: `setEqEnabled` / `isEqEnabled`, `setEqPreset`, `setEqBand`, `setEqPrecut`, `setTone`, `setBass`, `setTreble`, `setBassCutoff`, `setTrebleCutoff`, `setCrossfeed`, `setSurround`, `setChannelMode`, `setStereoWidth`, `setBassEnhancement`, `setFatigueReduction`, `setCompressor`, `setDither`, `setPitch`, `dspSettings()` → `Map`. **Status / resume**: `status()`, `resume`, `saveResume`, `clearResume`. |

### Enums

* `EqPreset` — 21 built-in presets: `FLAT`, `ACOUSTIC`, `BASS_BOOST`, `BASS_REDUCER`, `CLASSICAL`, `DANCE`, `DEEP`, `ELECTRONIC`, `HIP_HOP`, `JAZZ`, `LATIN`, `LOUDNESS`, `LOUNGE`, `PIANO`, `POP`, `RNB`, `ROCK`, `SMALL_SPEAKERS`, `TREBLE_BOOST`, `TREBLE_REDUCER`, `VOCAL_BOOST`.
* `RepeatMode` — `OFF`, `ONE`, `ALL`.
* `CrossfeedMode` — `OFF`, `MEIER`, `CUSTOM`.
* `ChannelMode` — `STEREO`, `MONO`, `CUSTOM`, `MONO_LEFT`, `MONO_RIGHT`, `KARAOKE`, `SWAP`.
* `CrossfadeMode` — `OFF`, `AUTO_SKIP`, `MANUAL_SKIP`, `SHUFFLE`, `SHUFFLE_OR_MANUAL`, `ALWAYS`; `MixMode` — `CROSSFADE`, `MIX`.
* `InsertPosition` — `PREPEND`, `INSERT`, `INSERT_NEXT`, `INSERT_LAST`, `INSERT_SHUFFLED`, `INSERT_LAST_SHUFFLED`, `REPLACE`, `INDEX`.
* `ReplayGainMode` (player) — `OFF`, `TRACK`, `ALBUM`; `DspReplayGainMode` (`Dsp`) — `TRACK`, `ALBUM`, `SHUFFLE`, `OFF`.

<Tip>
  The DSP chain mirrors Rockbox's own **Sound Settings** — parametric EQ,
  crossfeed, tone controls, channel modes, surround, and the compressor. For
  what each knob does and sensible ranges, see the official
  [Rockbox manual](https://download.rockbox.org/daily/manual/rockbox-ipodvideo/rockbox-buildch6.html).
</Tip>

## Notes

* Rich values (metadata, player status) come back as `Map<String, Any?>`,
  parsed from the ABI's JSON with `org.json`.
* Sample buffers are `ShortArray` of interleaved-stereo signed 16-bit samples.
* Native memory is freed automatically — handles are `AutoCloseable`
  (`use { }`), and every `char*` / `int16*` the ABI returns is freed inside the
  binding.

<Warning>
  **Two ReplayGain encodings** — `DspReplayGainMode` (TRACK=0, ALBUM=1,
  SHUFFLE=2, OFF=3) for `Dsp`, `ReplayGainMode` (OFF=0, TRACK=1, ALBUM=2) for
  `Player`. See [the overview](/bindings/overview#the-one-thing-to-know-two-replaygain-encodings).
</Warning>
