Skip to main content
Clojure bindings with no JNI and no native glue to compile: they call the Java Foreign Function & Memory API (JEP 454, stable since JDK 22) through interop to locate librockbox_ffi at runtime and bind every function to a MethodHandle downcall.
Requires a JDK 22+ and the Clojure CLI. The published jar bundles a prebuilt librockbox_ffi for every OS/arch and extracts the matching one at load time — no Rust toolchain needed. ROCKBOX_FFI_LIB overrides; a repo checkout falls back to target/release.

Quick start

Player API

Every mutating player fn takes the handle p first and returns p, so a full session — queue, DSP chain, shuffle/repeat, transport — threads with ->. Getters/queries (volume, status, queue, dsp-settings, shuffle-enabled?, repeat, eq-enabled?, …) return their values as usual.

Sources

set-queue, enqueue, and insert accept local file paths, finite http(s):// URLs, and live-radio / streaming URLs in the same list — mix them freely. dsp-settings returns a keywordized snapshot of the whole chain.

Stereo balance

set-balance / balance pan the output between the channels: the value ranges from -100 (full left) to +100 (full right), with 0 = centre.

Enums

Every enum arg accepts either the keyword or the raw int (rockbox.ffi.enums):
The DSP chain mirrors Rockbox’s own sound settings. For what each control does (EQ bands, crossfeed, tone/bass/treble, compressor, dithering, pitch, …) see the official Rockbox manual — Sound Settings.

Decoder API

rockbox.ffi.decoder streams an audio file to interleaved-stereo S16LE PCM one chunk at a time through the Rockbox codec engine — useful for feeding your own output sink, resampler, or analysis pipeline.
  • open takes a path and returns a handle; free (or with-decoder) releases it when you’re done.
  • metadata returns the tags + stream properties as a keyword-keyed map (same shape as metadata/read).
  • next-chunk yields [samples sample-rate]samples is an interleaved stereo short-array — or nil at end of track.
  • seek-ms requests a seek; elapsed-ms is the codec’s last reported position.
  • finished returns [done code]: code is 0 on a clean end and negative on a codec error (valid only once done is true).
Codec state is process-wide — only one decoder decodes at a time. Opening a second decoder blocks until the first is freed, so wrap each in with-decoder (or free it) before starting the next.

Notes

  • Rich values (metadata, player status, dsp-settings) come back as maps with keyword keys, parsed with clojure.data.json.
  • Native memory is freed automatically — with-dsp / with-player free their handle, and every char* / int16* the ABI returns is freed inside the binding.
  • Enum arguments accept either a keyword or the raw int (see src/rockbox/ffi/enums.clj).
Two ReplayGain encodingsdsp-replaygain-mode (:track 0, :album 1, :shuffle 2, :off 3) for rockbox.ffi.dsp, replaygain-mode (:off 0, :track 1, :album 2) for rockbox.ffi.player. See the overview.
The FFM downcalls need --enable-native-access=ALL-UNNAMED, wired into the project’s deps.edn aliases. This binding lives under rockbox.ffi.* and coexists cleanly with the separate rockbox-clj gRPC SDK (which owns the bare rockbox.* namespaces).