rockbox_ffi (Gleam)
Gleam bindings for the Rockbox DSP, metadata, and playback
engine (Erlang target), via an erl_nif shim over the librockbox_ffi
C ABI.
📖 Sound settings reference — the equalizer, tone, crossfeed, compressor and other DSP controls mirror Rockbox’s own. See the official Rockbox manual — Sound Settings.
Setup
gleam add rockbox_ffi
Requires OTP 27+ (JSON is decoded with the built-in json module plus
gleam/dynamic/decode — no gleam_json dependency).
How the native code is delivered
This package contains only the Gleam wrappers — no C shim and no .so. The
native code lives in a separate, shared package,
rockbox_ffi_nif (a rebar3 Hex package that the Elixir binding
depends on too), which gleam add rockbox_ffi pulls in automatically.
On the first load of the NIF, rockbox_ffi_nif’s Erlang loader
(rockbox_ffi_nif.erl) detects the host triple, downloads the matching
prebuilt rockbox_ffi_nif-<target>.so from its GitHub release into your user
cache, and verifies it against a shipped sha256 manifest. (The .so statically
links the Rust engine and is far too large to bundle in a Hex tarball.)
Prebuilt targets:
| Target | Tier |
|---|---|
aarch64-apple-darwin | supported |
x86_64-apple-darwin | supported |
x86_64-linux-gnu | supported |
aarch64-linux-gnu | supported |
x86_64-unknown-freebsd | best-effort |
x86_64-unknown-netbsd | best-effort |
On any other platform (musl/Alpine, Windows, or a glibc older than the CI
runner’s) the download has no matching artifact and load_nif will fail —
build from source instead.
Building from source
A from-source build needs the full monorepo checkout (the Cargo workspace
and include/ header are not in any Hex package). The native code builds in the
shared rockbox_ffi_nif package, which this binding uses via a sibling path
dependency (../erlang):
# Build the shared NIF once; the loader prefers this local .so over a download.
cargo build --release -p rockbox-ffi
cd bindings/erlang && make && cd ../gleam
gleam test
Usage
import rockbox/metadata
import rockbox/dsp
import rockbox/player
import gleam/option.{None, Some}
// --- metadata ---
let assert Ok(meta) = metadata.read("song.flac")
meta.artist // "…"
meta.duration_ms // 122324
metadata.probe("track.opus") // Some("Opus")
// --- DSP (interleaved stereo int16 BitArray) ---
let d = dsp.new(44_100)
dsp.eq_enable(d, True)
dsp.set_eq_band(d, 0, 60, 0.7, 3.0)
dsp.set_replaygain(d, 0, True, 0.0) // 0 = track (DSP-native)
dsp.set_replaygain_gains(d, Some(-6.02), None, None, None)
let out = dsp.process(d, pcm) // BitArray in/out
// --- playback (needs an output device) ---
let p = player.with_config(player.Config(..player.default_config(), volume: 0.8))
player.set_replaygain(p, 1, 0.0, True) // 1 = track (player)
player.set_queue(p, ["a.flac", "b.mp3"])
player.play(p)
player.status(p) // Status(state: "playing", index: Some(0), ...)
Dsp and Player are opaque NIF resources, freed by the BEAM garbage
collector — no explicit close.
Two ReplayGain encodings
The DSP and player use different mode integers (a quirk of the C ABI):
dsp.set_replaygain→0track,1album,2shuffle,3offplayer.set_replaygain→0off,1track,2album
Shared native package
The C shim and Erlang NIF loader are not in this package — they live in the
shared rockbox_ffi_nif package, which the Elixir binding
(bindings/elixir/) depends on as well. This package carries only the Gleam
rockbox/* wrappers and declares rockbox_ffi_nif as a dependency.
Releasing (maintainers)
Because the native code is shared, publish rockbox_ffi_nif first, then
this package.
- Native NIFs — bump
{vsn, ...}inbindings/erlang/src/rockbox_ffi_nif.app.src(and the mirroredversioninbindings/erlang/gleam.toml), then run thebindings-erlang-release.ymlworkflow (push anerlang-v<version>tag or dispatch it) to build + upload the per-target.sofiles, and publish the package to Hex locally:bindings/scripts/publish-erlang.sh - This package — bump
versioningleam.toml(Hex versions are immutable), then publish locally (gleam publishneeds an interactive Hex login that can’t run in CI):
The script temporarily rewrites thegleam hex authenticate # or: export HEXPM_API_KEY=... bindings/scripts/publish-gleam.sh # swaps the path dep for the Hex version, then gleam publishrockbox_ffi_nif = { path = "../erlang" }line ingleam.tomlto the released Hex version requirement for publishing, then restores it. Pass--tag/--repoto override, or--dry-runto preview.