rockbox-ffi 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -98,10 +98,10 @@ details.
98
98
 
99
99
  ### `metadata`
100
100
 
101
- | Function | Returns | Description | |
102
- | -------------------------- | ---------- | ------------------------------------------------------- | -------------------------------------------------------- |
103
- | `metadata.read(path)` | `Metadata` | Parse tags, duration, ReplayGain, album-art/cue offsets | |
104
- | `metadata.probe(filename)` | `string \ | null` | Codec label from the extension, without opening the file |
101
+ | Function | Returns | Description |
102
+ | -------------------------- | ---------------- | -------------------------------------------------------- |
103
+ | `metadata.read(path)` | `Metadata` | Parse tags, duration, ReplayGain, album-art/cue offsets |
104
+ | `metadata.probe(filename)` | `string \| null` | Codec label from the extension, without opening the file |
105
105
 
106
106
  ### `Dsp`
107
107
 
package/dist/bun.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bun.d.ts","sourceRoot":"","sources":["../src/bun.ts"],"names":[],"mappings":"AAeA,OAAO,EAAW,UAAU,EAAE,MAAM,UAAU,CAAC;AA4E/C,eAAO,MAAQ,UAAU,gBAAE,QAAQ;;;GAAE,GAAG;;;;;;;;;;;;;;;;;;;;;GAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;CAAQ,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,cAAc,YAAY,CAAC;AAC3B,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"bun.d.ts","sourceRoot":"","sources":["../src/bun.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAW,UAAU,EAAE,MAAM,UAAU,CAAC;AA+F/C,eAAO,MAAQ,UAAU,gBAAE,QAAQ;;;GAAE,GAAG;;;;;;;;;;;;;;;;;;;;;GAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;CAAQ,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,cAAc,YAAY,CAAC;AAC3B,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
package/dist/bun.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import {
2
2
  SPEC,
3
+ platformLibName,
4
+ platformPackageName,
3
5
  resolveLibPath
4
- } from "./chunk-ewj9g332.js";
6
+ } from "./chunk-sycd0w1e.js";
5
7
  import {
6
8
  ChannelConfig,
7
9
  CrossfadeMode,
@@ -21,8 +23,18 @@ import {
21
23
  toArrayBuffer
22
24
  } from "bun:ffi";
23
25
  import { existsSync } from "node:fs";
26
+ import { createRequire } from "node:module";
24
27
  import { dirname, join } from "node:path";
25
28
  import { fileURLToPath } from "node:url";
29
+ function bundledLibPath() {
30
+ try {
31
+ const require2 = createRequire(import.meta.url);
32
+ const pkg = platformPackageName(process.platform, process.arch);
33
+ return require2.resolve(`${pkg}/${platformLibName(process.platform)}`);
34
+ } catch {
35
+ return;
36
+ }
37
+ }
26
38
  var TOK = {
27
39
  void: FFIType.void,
28
40
  ptr: FFIType.ptr,
@@ -38,7 +50,7 @@ var TOK = {
38
50
  };
39
51
  function makeRaw() {
40
52
  const here = dirname(fileURLToPath(import.meta.url));
41
- const libPath = resolveLibPath(here, (k) => Bun.env[k], existsSync, join, dirname);
53
+ const libPath = resolveLibPath(here, (k) => Bun.env[k], existsSync, join, dirname, bundledLibPath());
42
54
  const symbols = {};
43
55
  for (const [name, spec] of Object.entries(SPEC)) {
44
56
  symbols[name] = { args: spec.args.map((t) => TOK[t]), returns: TOK[spec.ret] };
@@ -93,4 +105,4 @@ export {
93
105
  ChannelConfig
94
106
  };
95
107
 
96
- //# debugId=9A1F3395E268D15D64756E2164756E21
108
+ //# debugId=05552B80C9EDDD7A64756E2164756E21
package/dist/bun.js.map CHANGED
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/bun.ts"],
4
4
  "sourcesContent": [
5
- "// Bun backend + full API. Import from here under Bun:\n// import { Dsp, Player, metadata } from \"rockbox-ffi/bun\";\n\nimport {\n CString,\n dlopen,\n FFIType,\n type Pointer,\n ptr,\n toArrayBuffer,\n} from \"bun:ffi\";\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { makeApi, sineStereo } from \"./api.ts\";\nimport { resolveLibPath, SPEC, type Raw, type Tok } from \"./ffi.ts\";\n\nconst TOK: Record<Tok, FFIType> = {\n void: FFIType.void,\n ptr: FFIType.ptr,\n u32: FFIType.u32,\n i32: FFIType.i32,\n u64: FFIType.u64,\n usize: FFIType.u64,\n f32: FFIType.f32,\n bool: FFIType.bool,\n strbuf: FFIType.ptr,\n i16in: FFIType.ptr,\n outsize: FFIType.ptr,\n};\n\nfunction makeRaw(): Raw {\n const here = dirname(fileURLToPath(import.meta.url));\n const libPath = resolveLibPath(\n here,\n (k) => Bun.env[k],\n existsSync,\n join,\n dirname,\n );\n\n const symbols: Record<string, { args: FFIType[]; returns: FFIType }> = {};\n for (const [name, spec] of Object.entries(SPEC)) {\n symbols[name] = { args: spec.args.map((t) => TOK[t]), returns: TOK[spec.ret] };\n }\n const lib = dlopen(libPath, symbols);\n const sym = lib.symbols as Record<string, (...a: any[]) => any>;\n\n // Keep marshalled input buffers alive across the (synchronous) call by\n // holding a reference on the caller's stack — Bun's `ptr()` does not copy.\n const enc = new TextEncoder();\n\n return {\n sym,\n cstr(s: string) {\n const buf = enc.encode(s + \"\\0\");\n // Attach to a WeakRef-free holder: return the pointer but stash the\n // buffer on the returned Number via closure is impossible, so we rely\n // on synchronous FFI — the buffer is created fresh here and the call\n // happens immediately after in the same tick.\n return ptr(buf);\n },\n i16in(a: Int16Array) {\n return ptr(a);\n },\n sizeOut() {\n const arr = new BigUint64Array(1);\n return { arg: ptr(arr), value: () => Number(arr[0]) };\n },\n takeString(p: unknown) {\n if (p === null || p === 0) return null;\n // Bun pointers are numbers at runtime; the typed API wants `Pointer`.\n const str = new CString(p as Pointer).toString();\n sym.rb_string_free(p);\n return str;\n },\n takeI16(p: unknown, len: number) {\n const ab = toArrayBuffer(p as Pointer, 0, len * 2);\n const copy = new Int16Array(len);\n copy.set(new Int16Array(ab));\n sym.rb_buffer_free(p, len);\n return copy;\n },\n isNull(p: unknown) {\n return p === null || p === 0 || p === undefined;\n },\n };\n}\n\nconst api = makeApi(makeRaw());\nexport const { abiVersion, metadata, Dsp, Player } = api;\nexport { sineStereo };\nexport * from \"./enums.ts\";\nexport type { Metadata, PlayerStatus, PlayerConfig } from \"./types.ts\";\n"
5
+ "// Bun backend + full API. Import from here under Bun:\n// import { Dsp, Player, metadata } from \"rockbox-ffi/bun\";\n\nimport {\n CString,\n dlopen,\n FFIType,\n type Pointer,\n ptr,\n toArrayBuffer,\n} from \"bun:ffi\";\nimport { existsSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { makeApi, sineStereo } from \"./api.ts\";\nimport {\n platformLibName,\n platformPackageName,\n resolveLibPath,\n SPEC,\n type Raw,\n type Tok,\n} from \"./ffi.ts\";\n\n/** Resolve the prebuilt binary from the per-platform npm package, if present. */\nfunction bundledLibPath(): string | undefined {\n try {\n const require = createRequire(import.meta.url);\n const pkg = platformPackageName(process.platform, process.arch);\n return require.resolve(`${pkg}/${platformLibName(process.platform)}`);\n } catch {\n return undefined;\n }\n}\n\nconst TOK: Record<Tok, FFIType> = {\n void: FFIType.void,\n ptr: FFIType.ptr,\n u32: FFIType.u32,\n i32: FFIType.i32,\n u64: FFIType.u64,\n usize: FFIType.u64,\n f32: FFIType.f32,\n bool: FFIType.bool,\n strbuf: FFIType.ptr,\n i16in: FFIType.ptr,\n outsize: FFIType.ptr,\n};\n\nfunction makeRaw(): Raw {\n const here = dirname(fileURLToPath(import.meta.url));\n const libPath = resolveLibPath(\n here,\n (k) => Bun.env[k],\n existsSync,\n join,\n dirname,\n bundledLibPath(),\n );\n\n const symbols: Record<string, { args: FFIType[]; returns: FFIType }> = {};\n for (const [name, spec] of Object.entries(SPEC)) {\n symbols[name] = { args: spec.args.map((t) => TOK[t]), returns: TOK[spec.ret] };\n }\n const lib = dlopen(libPath, symbols);\n const sym = lib.symbols as Record<string, (...a: any[]) => any>;\n\n // Keep marshalled input buffers alive across the (synchronous) call by\n // holding a reference on the caller's stack — Bun's `ptr()` does not copy.\n const enc = new TextEncoder();\n\n return {\n sym,\n cstr(s: string) {\n const buf = enc.encode(s + \"\\0\");\n // Attach to a WeakRef-free holder: return the pointer but stash the\n // buffer on the returned Number via closure is impossible, so we rely\n // on synchronous FFI — the buffer is created fresh here and the call\n // happens immediately after in the same tick.\n return ptr(buf);\n },\n i16in(a: Int16Array) {\n return ptr(a);\n },\n sizeOut() {\n const arr = new BigUint64Array(1);\n return { arg: ptr(arr), value: () => Number(arr[0]) };\n },\n takeString(p: unknown) {\n if (p === null || p === 0) return null;\n // Bun pointers are numbers at runtime; the typed API wants `Pointer`.\n const str = new CString(p as Pointer).toString();\n sym.rb_string_free(p);\n return str;\n },\n takeI16(p: unknown, len: number) {\n const ab = toArrayBuffer(p as Pointer, 0, len * 2);\n const copy = new Int16Array(len);\n copy.set(new Int16Array(ab));\n sym.rb_buffer_free(p, len);\n return copy;\n },\n isNull(p: unknown) {\n return p === null || p === 0 || p === undefined;\n },\n };\n}\n\nconst api = makeApi(makeRaw());\nexport const { abiVersion, metadata, Dsp, Player } = api;\nexport { sineStereo };\nexport * from \"./enums.ts\";\nexport type { Metadata, PlayerStatus, PlayerConfig } from \"./types.ts\";\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;AAGA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA;AACA;AACA;AAKA,IAAM,MAA4B;AAAA,EAChC,MAAM,QAAQ;AAAA,EACd,KAAK,QAAQ;AAAA,EACb,KAAK,QAAQ;AAAA,EACb,KAAK,QAAQ;AAAA,EACb,KAAK,QAAQ;AAAA,EACb,OAAO,QAAQ;AAAA,EACf,KAAK,QAAQ;AAAA,EACb,MAAM,QAAQ;AAAA,EACd,QAAQ,QAAQ;AAAA,EAChB,OAAO,QAAQ;AAAA,EACf,SAAS,QAAQ;AACnB;AAEA,SAAS,OAAO,GAAQ;AAAA,EACtB,MAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;AAAA,EACnD,MAAM,UAAU,eACd,MACA,CAAC,MAAM,IAAI,IAAI,IACf,YACA,MACA,OACF;AAAA,EAEA,MAAM,UAAiE,CAAC;AAAA,EACxE,YAAY,MAAM,SAAS,OAAO,QAAQ,IAAI,GAAG;AAAA,IAC/C,QAAQ,QAAQ,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC,MAAM,IAAI,EAAE,GAAG,SAAS,IAAI,KAAK,KAAK;AAAA,EAC/E;AAAA,EACA,MAAM,MAAM,OAAO,SAAS,OAAO;AAAA,EACnC,MAAM,MAAM,IAAI;AAAA,EAIhB,MAAM,MAAM,IAAI;AAAA,EAEhB,OAAO;AAAA,IACL;AAAA,IACA,IAAI,CAAC,GAAW;AAAA,MACd,MAAM,MAAM,IAAI,OAAO,IAAI,MAAI;AAAA,MAK/B,OAAO,IAAI,GAAG;AAAA;AAAA,IAEhB,KAAK,CAAC,GAAe;AAAA,MACnB,OAAO,IAAI,CAAC;AAAA;AAAA,IAEd,OAAO,GAAG;AAAA,MACR,MAAM,MAAM,IAAI,eAAe,CAAC;AAAA,MAChC,OAAO,EAAE,KAAK,IAAI,GAAG,GAAG,OAAO,MAAM,OAAO,IAAI,EAAE,EAAE;AAAA;AAAA,IAEtD,UAAU,CAAC,GAAY;AAAA,MACrB,IAAI,MAAM,QAAQ,MAAM;AAAA,QAAG,OAAO;AAAA,MAElC,MAAM,MAAM,IAAI,QAAQ,CAAY,EAAE,SAAS;AAAA,MAC/C,IAAI,eAAe,CAAC;AAAA,MACpB,OAAO;AAAA;AAAA,IAET,OAAO,CAAC,GAAY,KAAa;AAAA,MAC/B,MAAM,KAAK,cAAc,GAAc,GAAG,MAAM,CAAC;AAAA,MACjD,MAAM,OAAO,IAAI,WAAW,GAAG;AAAA,MAC/B,KAAK,IAAI,IAAI,WAAW,EAAE,CAAC;AAAA,MAC3B,IAAI,eAAe,GAAG,GAAG;AAAA,MACzB,OAAO;AAAA;AAAA,IAET,MAAM,CAAC,GAAY;AAAA,MACjB,OAAO,MAAM,QAAQ,MAAM,KAAK,MAAM;AAAA;AAAA,EAE1C;AAAA;AAGF,IAAM,MAAM,QAAQ,QAAQ,CAAC;AACtB,MAAQ,YAAY,UAAU,KAAK,WAAW;",
8
- "debugId": "9A1F3395E268D15D64756E2164756E21",
7
+ "mappings": ";;;;;;;;;;;;;;;;;AAGA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA;AACA;AACA;AACA;AAaA,SAAS,cAAc,GAAuB;AAAA,EAC5C,IAAI;AAAA,IACF,MAAM,WAAU,cAAc,YAAY,GAAG;AAAA,IAC7C,MAAM,MAAM,oBAAoB,QAAQ,UAAU,QAAQ,IAAI;AAAA,IAC9D,OAAO,SAAQ,QAAQ,GAAG,OAAO,gBAAgB,QAAQ,QAAQ,GAAG;AAAA,IACpE,MAAM;AAAA,IACN;AAAA;AAAA;AAIJ,IAAM,MAA4B;AAAA,EAChC,MAAM,QAAQ;AAAA,EACd,KAAK,QAAQ;AAAA,EACb,KAAK,QAAQ;AAAA,EACb,KAAK,QAAQ;AAAA,EACb,KAAK,QAAQ;AAAA,EACb,OAAO,QAAQ;AAAA,EACf,KAAK,QAAQ;AAAA,EACb,MAAM,QAAQ;AAAA,EACd,QAAQ,QAAQ;AAAA,EAChB,OAAO,QAAQ;AAAA,EACf,SAAS,QAAQ;AACnB;AAEA,SAAS,OAAO,GAAQ;AAAA,EACtB,MAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;AAAA,EACnD,MAAM,UAAU,eACd,MACA,CAAC,MAAM,IAAI,IAAI,IACf,YACA,MACA,SACA,eAAe,CACjB;AAAA,EAEA,MAAM,UAAiE,CAAC;AAAA,EACxE,YAAY,MAAM,SAAS,OAAO,QAAQ,IAAI,GAAG;AAAA,IAC/C,QAAQ,QAAQ,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC,MAAM,IAAI,EAAE,GAAG,SAAS,IAAI,KAAK,KAAK;AAAA,EAC/E;AAAA,EACA,MAAM,MAAM,OAAO,SAAS,OAAO;AAAA,EACnC,MAAM,MAAM,IAAI;AAAA,EAIhB,MAAM,MAAM,IAAI;AAAA,EAEhB,OAAO;AAAA,IACL;AAAA,IACA,IAAI,CAAC,GAAW;AAAA,MACd,MAAM,MAAM,IAAI,OAAO,IAAI,MAAI;AAAA,MAK/B,OAAO,IAAI,GAAG;AAAA;AAAA,IAEhB,KAAK,CAAC,GAAe;AAAA,MACnB,OAAO,IAAI,CAAC;AAAA;AAAA,IAEd,OAAO,GAAG;AAAA,MACR,MAAM,MAAM,IAAI,eAAe,CAAC;AAAA,MAChC,OAAO,EAAE,KAAK,IAAI,GAAG,GAAG,OAAO,MAAM,OAAO,IAAI,EAAE,EAAE;AAAA;AAAA,IAEtD,UAAU,CAAC,GAAY;AAAA,MACrB,IAAI,MAAM,QAAQ,MAAM;AAAA,QAAG,OAAO;AAAA,MAElC,MAAM,MAAM,IAAI,QAAQ,CAAY,EAAE,SAAS;AAAA,MAC/C,IAAI,eAAe,CAAC;AAAA,MACpB,OAAO;AAAA;AAAA,IAET,OAAO,CAAC,GAAY,KAAa;AAAA,MAC/B,MAAM,KAAK,cAAc,GAAc,GAAG,MAAM,CAAC;AAAA,MACjD,MAAM,OAAO,IAAI,WAAW,GAAG;AAAA,MAC/B,KAAK,IAAI,IAAI,WAAW,EAAE,CAAC;AAAA,MAC3B,IAAI,eAAe,GAAG,GAAG;AAAA,MACzB,OAAO;AAAA;AAAA,IAET,MAAM,CAAC,GAAY;AAAA,MACjB,OAAO,MAAM,QAAQ,MAAM,KAAK,MAAM;AAAA;AAAA,EAE1C;AAAA;AAGF,IAAM,MAAM,QAAQ,QAAQ,CAAC;AACtB,MAAQ,YAAY,UAAU,KAAK,WAAW;",
8
+ "debugId": "05552B80C9EDDD7A64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -71,10 +71,23 @@ var SPEC = {
71
71
  rb_player_status_json: { args: ["ptr"], ret: "ptr" }
72
72
  };
73
73
  var LIB_NAMES = ["librockbox_ffi.dylib", "librockbox_ffi.so", "rockbox_ffi.dll"];
74
- function resolveLibPath(startDir, env, exists, join, dirname) {
74
+ function platformLibName(platform) {
75
+ if (platform === "darwin")
76
+ return "librockbox_ffi.dylib";
77
+ if (platform === "win32")
78
+ return "rockbox_ffi.dll";
79
+ return "librockbox_ffi.so";
80
+ }
81
+ function platformPackageName(platform, arch) {
82
+ const cpu = arch === "x64" ? "x64" : arch === "arm64" ? "arm64" : arch;
83
+ return `@rockbox-ffi/${platform}-${cpu}`;
84
+ }
85
+ function resolveLibPath(startDir, env, exists, join, dirname, bundled) {
75
86
  const override = env("ROCKBOX_FFI_LIB");
76
87
  if (override)
77
88
  return override;
89
+ if (bundled && exists(bundled))
90
+ return bundled;
78
91
  let dir = startDir;
79
92
  const tried = [];
80
93
  for (let i = 0;i < 40; i++) {
@@ -94,6 +107,6 @@ function resolveLibPath(startDir, env, exists, join, dirname) {
94
107
  `));
95
108
  }
96
109
 
97
- export { SPEC, resolveLibPath };
110
+ export { SPEC, platformLibName, platformPackageName, resolveLibPath };
98
111
 
99
- //# debugId=EBB87DA589FF378164756E2164756E21
112
+ //# debugId=7F5CAD4AD56C7E0E64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/ffi.ts"],
4
4
  "sourcesContent": [
5
- "// Runtime-agnostic FFI description: one symbol table with abstract type\n// tokens that each backend (bun.ts / deno.ts) maps to its own FFI types.\n\n/** Abstract argument/return type tokens used by the symbol spec. */\nexport type Tok =\n | \"void\"\n | \"ptr\" // opaque handle / returned pointer\n | \"u32\"\n | \"i32\"\n | \"u64\"\n | \"usize\"\n | \"f32\"\n | \"bool\"\n | \"strbuf\" // input: a JS string marshalled as `const char *`\n | \"i16in\" // input: an Int16Array marshalled as `const int16_t *`\n | \"outsize\"; // input: a `size_t *` out-parameter\n\nexport interface SymSpec {\n args: Tok[];\n ret: Tok;\n}\n\n// Mirrors include/rockbox_ffi.h. Keep in sync with the C ABI.\nexport const SPEC: Record<string, SymSpec> = {\n rb_ffi_abi_version: { args: [], ret: \"u32\" },\n rb_string_free: { args: [\"ptr\"], ret: \"void\" },\n rb_buffer_free: { args: [\"ptr\", \"usize\"], ret: \"void\" },\n\n rb_dsp_new: { args: [\"u32\"], ret: \"ptr\" },\n rb_dsp_free: { args: [\"ptr\"], ret: \"void\" },\n rb_dsp_set_input_frequency: { args: [\"ptr\", \"u32\"], ret: \"void\" },\n rb_dsp_flush: { args: [\"ptr\"], ret: \"void\" },\n rb_dsp_eq_enable: { args: [\"ptr\", \"bool\"], ret: \"void\" },\n rb_dsp_set_tone: { args: [\"ptr\", \"i32\", \"i32\"], ret: \"void\" },\n rb_dsp_set_tone_cutoffs: { args: [\"ptr\", \"i32\", \"i32\"], ret: \"void\" },\n rb_dsp_set_surround: { args: [\"ptr\", \"i32\", \"i32\", \"i32\", \"i32\"], ret: \"void\" },\n rb_dsp_set_channel_config: { args: [\"ptr\", \"i32\"], ret: \"void\" },\n rb_dsp_set_stereo_width: { args: [\"ptr\", \"i32\"], ret: \"void\" },\n rb_dsp_set_compressor: {\n args: [\"ptr\", \"i32\", \"i32\", \"i32\", \"i32\", \"i32\", \"i32\"],\n ret: \"void\",\n },\n rb_dsp_set_replaygain: { args: [\"ptr\", \"i32\", \"bool\", \"f32\"], ret: \"void\" },\n rb_dsp_set_replaygain_gains: {\n args: [\"ptr\", \"f32\", \"f32\", \"f32\", \"f32\"],\n ret: \"void\",\n },\n rb_dsp_set_replaygain_gains_raw: {\n args: [\"ptr\", \"u64\", \"u64\", \"u64\", \"u64\"],\n ret: \"void\",\n },\n rb_dsp_set_eq_band: { args: [\"ptr\", \"usize\", \"i32\", \"f32\", \"f32\"], ret: \"void\" },\n rb_dsp_set_eq_precut: { args: [\"ptr\", \"f32\"], ret: \"void\" },\n rb_dsp_process: { args: [\"ptr\", \"i16in\", \"usize\", \"outsize\"], ret: \"ptr\" },\n\n rb_meta_read_json: { args: [\"strbuf\"], ret: \"ptr\" },\n rb_meta_probe: { args: [\"strbuf\"], ret: \"ptr\" },\n\n rb_player_new: { args: [], ret: \"ptr\" },\n rb_player_new_with_config: {\n args: [\n \"u32\", \"f32\", \"f32\", \"i32\", \"f32\", \"bool\", \"i32\", \"u32\", \"u32\", \"u32\",\n \"u32\", \"i32\",\n ],\n ret: \"ptr\",\n },\n rb_player_free: { args: [\"ptr\"], ret: \"void\" },\n rb_player_set_queue_json: { args: [\"ptr\", \"strbuf\"], ret: \"void\" },\n rb_player_enqueue: { args: [\"ptr\", \"strbuf\"], ret: \"void\" },\n rb_player_play: { args: [\"ptr\"], ret: \"void\" },\n rb_player_pause: { args: [\"ptr\"], ret: \"void\" },\n rb_player_toggle: { args: [\"ptr\"], ret: \"void\" },\n rb_player_stop: { args: [\"ptr\"], ret: \"void\" },\n rb_player_next: { args: [\"ptr\"], ret: \"void\" },\n rb_player_previous: { args: [\"ptr\"], ret: \"void\" },\n rb_player_skip_to: { args: [\"ptr\", \"usize\"], ret: \"void\" },\n rb_player_seek_ms: { args: [\"ptr\", \"u64\"], ret: \"void\" },\n rb_player_set_volume: { args: [\"ptr\", \"f32\"], ret: \"void\" },\n rb_player_set_crossfade: {\n args: [\"ptr\", \"i32\", \"u32\", \"u32\", \"u32\", \"u32\", \"i32\"],\n ret: \"void\",\n },\n rb_player_set_replaygain: { args: [\"ptr\", \"i32\", \"f32\", \"bool\"], ret: \"void\" },\n rb_player_volume: { args: [\"ptr\"], ret: \"f32\" },\n rb_player_sample_rate: { args: [\"ptr\"], ret: \"u32\" },\n rb_player_status_json: { args: [\"ptr\"], ret: \"ptr\" },\n};\n\n/**\n * The low-level backend a runtime loader must provide. High-level classes in\n * api.ts are written once against this interface.\n */\nexport interface Raw {\n /** The dlopen'd symbol table; call e.g. `sym.rb_dsp_new(44100)`. */\n sym: Record<string, (...a: any[]) => any>;\n /** Marshal a JS string as a `const char *` argument. */\n cstr(s: string): unknown;\n /** Marshal an Int16Array as a `const int16_t *` argument. */\n i16in(a: Int16Array): unknown;\n /** Allocate a `size_t` out-parameter; `value()` reads it after the call. */\n sizeOut(): { arg: unknown; value(): number };\n /** Read a returned `char *` into a string and free it (null => null). */\n takeString(p: unknown): string | null;\n /** Read a returned `int16_t *` of `len` samples into a copy and free it. */\n takeI16(p: unknown, len: number): Int16Array;\n /** True if a returned handle/pointer is NULL. */\n isNull(p: unknown): boolean;\n}\n\nconst LIB_NAMES = [\"librockbox_ffi.dylib\", \"librockbox_ffi.so\", \"rockbox_ffi.dll\"];\n\n/**\n * Locate the shared library: `ROCKBOX_FFI_LIB` env var first, then by walking\n * up from `startDir` to a `target/release` directory. `exists` and the path\n * ops are injected so this works under both Bun and Deno.\n */\nexport function resolveLibPath(\n startDir: string,\n env: (k: string) => string | undefined,\n exists: (p: string) => boolean,\n join: (...p: string[]) => string,\n dirname: (p: string) => string,\n): string {\n const override = env(\"ROCKBOX_FFI_LIB\");\n if (override) return override;\n\n let dir = startDir;\n const tried: string[] = [];\n for (let i = 0; i < 40; i++) {\n const rel = join(dir, \"target\", \"release\");\n for (const name of LIB_NAMES) {\n const p = join(rel, name);\n tried.push(p);\n if (exists(p)) return p;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n throw new Error(\n \"could not locate librockbox_ffi. Set ROCKBOX_FFI_LIB or run \" +\n \"`cargo build --release -p rockbox-ffi`. Tried:\\n \" + tried.join(\"\\n \"),\n );\n}\n"
5
+ "// Runtime-agnostic FFI description: one symbol table with abstract type\n// tokens that each backend (bun.ts / deno.ts) maps to its own FFI types.\n\n/** Abstract argument/return type tokens used by the symbol spec. */\nexport type Tok =\n | \"void\"\n | \"ptr\" // opaque handle / returned pointer\n | \"u32\"\n | \"i32\"\n | \"u64\"\n | \"usize\"\n | \"f32\"\n | \"bool\"\n | \"strbuf\" // input: a JS string marshalled as `const char *`\n | \"i16in\" // input: an Int16Array marshalled as `const int16_t *`\n | \"outsize\"; // input: a `size_t *` out-parameter\n\nexport interface SymSpec {\n args: Tok[];\n ret: Tok;\n}\n\n// Mirrors include/rockbox_ffi.h. Keep in sync with the C ABI.\nexport const SPEC: Record<string, SymSpec> = {\n rb_ffi_abi_version: { args: [], ret: \"u32\" },\n rb_string_free: { args: [\"ptr\"], ret: \"void\" },\n rb_buffer_free: { args: [\"ptr\", \"usize\"], ret: \"void\" },\n\n rb_dsp_new: { args: [\"u32\"], ret: \"ptr\" },\n rb_dsp_free: { args: [\"ptr\"], ret: \"void\" },\n rb_dsp_set_input_frequency: { args: [\"ptr\", \"u32\"], ret: \"void\" },\n rb_dsp_flush: { args: [\"ptr\"], ret: \"void\" },\n rb_dsp_eq_enable: { args: [\"ptr\", \"bool\"], ret: \"void\" },\n rb_dsp_set_tone: { args: [\"ptr\", \"i32\", \"i32\"], ret: \"void\" },\n rb_dsp_set_tone_cutoffs: { args: [\"ptr\", \"i32\", \"i32\"], ret: \"void\" },\n rb_dsp_set_surround: { args: [\"ptr\", \"i32\", \"i32\", \"i32\", \"i32\"], ret: \"void\" },\n rb_dsp_set_channel_config: { args: [\"ptr\", \"i32\"], ret: \"void\" },\n rb_dsp_set_stereo_width: { args: [\"ptr\", \"i32\"], ret: \"void\" },\n rb_dsp_set_compressor: {\n args: [\"ptr\", \"i32\", \"i32\", \"i32\", \"i32\", \"i32\", \"i32\"],\n ret: \"void\",\n },\n rb_dsp_set_replaygain: { args: [\"ptr\", \"i32\", \"bool\", \"f32\"], ret: \"void\" },\n rb_dsp_set_replaygain_gains: {\n args: [\"ptr\", \"f32\", \"f32\", \"f32\", \"f32\"],\n ret: \"void\",\n },\n rb_dsp_set_replaygain_gains_raw: {\n args: [\"ptr\", \"u64\", \"u64\", \"u64\", \"u64\"],\n ret: \"void\",\n },\n rb_dsp_set_eq_band: { args: [\"ptr\", \"usize\", \"i32\", \"f32\", \"f32\"], ret: \"void\" },\n rb_dsp_set_eq_precut: { args: [\"ptr\", \"f32\"], ret: \"void\" },\n rb_dsp_process: { args: [\"ptr\", \"i16in\", \"usize\", \"outsize\"], ret: \"ptr\" },\n\n rb_meta_read_json: { args: [\"strbuf\"], ret: \"ptr\" },\n rb_meta_probe: { args: [\"strbuf\"], ret: \"ptr\" },\n\n rb_player_new: { args: [], ret: \"ptr\" },\n rb_player_new_with_config: {\n args: [\n \"u32\", \"f32\", \"f32\", \"i32\", \"f32\", \"bool\", \"i32\", \"u32\", \"u32\", \"u32\",\n \"u32\", \"i32\",\n ],\n ret: \"ptr\",\n },\n rb_player_free: { args: [\"ptr\"], ret: \"void\" },\n rb_player_set_queue_json: { args: [\"ptr\", \"strbuf\"], ret: \"void\" },\n rb_player_enqueue: { args: [\"ptr\", \"strbuf\"], ret: \"void\" },\n rb_player_play: { args: [\"ptr\"], ret: \"void\" },\n rb_player_pause: { args: [\"ptr\"], ret: \"void\" },\n rb_player_toggle: { args: [\"ptr\"], ret: \"void\" },\n rb_player_stop: { args: [\"ptr\"], ret: \"void\" },\n rb_player_next: { args: [\"ptr\"], ret: \"void\" },\n rb_player_previous: { args: [\"ptr\"], ret: \"void\" },\n rb_player_skip_to: { args: [\"ptr\", \"usize\"], ret: \"void\" },\n rb_player_seek_ms: { args: [\"ptr\", \"u64\"], ret: \"void\" },\n rb_player_set_volume: { args: [\"ptr\", \"f32\"], ret: \"void\" },\n rb_player_set_crossfade: {\n args: [\"ptr\", \"i32\", \"u32\", \"u32\", \"u32\", \"u32\", \"i32\"],\n ret: \"void\",\n },\n rb_player_set_replaygain: { args: [\"ptr\", \"i32\", \"f32\", \"bool\"], ret: \"void\" },\n rb_player_volume: { args: [\"ptr\"], ret: \"f32\" },\n rb_player_sample_rate: { args: [\"ptr\"], ret: \"u32\" },\n rb_player_status_json: { args: [\"ptr\"], ret: \"ptr\" },\n};\n\n/**\n * The low-level backend a runtime loader must provide. High-level classes in\n * api.ts are written once against this interface.\n */\nexport interface Raw {\n /** The dlopen'd symbol table; call e.g. `sym.rb_dsp_new(44100)`. */\n sym: Record<string, (...a: any[]) => any>;\n /** Marshal a JS string as a `const char *` argument. */\n cstr(s: string): unknown;\n /** Marshal an Int16Array as a `const int16_t *` argument. */\n i16in(a: Int16Array): unknown;\n /** Allocate a `size_t` out-parameter; `value()` reads it after the call. */\n sizeOut(): { arg: unknown; value(): number };\n /** Read a returned `char *` into a string and free it (null => null). */\n takeString(p: unknown): string | null;\n /** Read a returned `int16_t *` of `len` samples into a copy and free it. */\n takeI16(p: unknown, len: number): Int16Array;\n /** True if a returned handle/pointer is NULL. */\n isNull(p: unknown): boolean;\n}\n\nconst LIB_NAMES = [\"librockbox_ffi.dylib\", \"librockbox_ffi.so\", \"rockbox_ffi.dll\"];\n\n/** The shared-library filename for a Node-style `process.platform`. */\nexport function platformLibName(platform: string): string {\n if (platform === \"darwin\") return \"librockbox_ffi.dylib\";\n if (platform === \"win32\") return \"rockbox_ffi.dll\";\n return \"librockbox_ffi.so\";\n}\n\n/**\n * The per-platform npm package that ships the prebuilt binary, e.g.\n * `@rockbox-ffi/darwin-arm64`. Published as `optionalDependencies` of the main\n * package so npm installs only the one matching the host.\n */\nexport function platformPackageName(platform: string, arch: string): string {\n const cpu = arch === \"x64\" ? \"x64\" : arch === \"arm64\" ? \"arm64\" : arch;\n return `@rockbox-ffi/${platform}-${cpu}`;\n}\n\n/**\n * Locate the shared library. Precedence:\n * 1. `ROCKBOX_FFI_LIB` env var (explicit override)\n * 2. `bundled` — a path resolved from the per-platform npm package, if any\n * 3. walking up from `startDir` to a `target/release` dir (repo checkout)\n * `exists` and the path ops are injected so this works under Bun and Deno.\n */\nexport function resolveLibPath(\n startDir: string,\n env: (k: string) => string | undefined,\n exists: (p: string) => boolean,\n join: (...p: string[]) => string,\n dirname: (p: string) => string,\n bundled?: string,\n): string {\n const override = env(\"ROCKBOX_FFI_LIB\");\n if (override) return override;\n\n if (bundled && exists(bundled)) return bundled;\n\n let dir = startDir;\n const tried: string[] = [];\n for (let i = 0; i < 40; i++) {\n const rel = join(dir, \"target\", \"release\");\n for (const name of LIB_NAMES) {\n const p = join(rel, name);\n tried.push(p);\n if (exists(p)) return p;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n throw new Error(\n \"could not locate librockbox_ffi. Set ROCKBOX_FFI_LIB or run \" +\n \"`cargo build --release -p rockbox-ffi`. Tried:\\n \" + tried.join(\"\\n \"),\n );\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "EBB87DA589FF378164756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "7F5CAD4AD56C7E0E64756E2164756E21",
9
9
  "names": []
10
10
  }
package/dist/deno.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SPEC,
3
3
  resolveLibPath
4
- } from "./chunk-ewj9g332.js";
4
+ } from "./chunk-sycd0w1e.js";
5
5
  import {
6
6
  ChannelConfig,
7
7
  CrossfadeMode,
package/dist/ffi.d.ts CHANGED
@@ -28,10 +28,20 @@ export interface Raw {
28
28
  /** True if a returned handle/pointer is NULL. */
29
29
  isNull(p: unknown): boolean;
30
30
  }
31
+ /** The shared-library filename for a Node-style `process.platform`. */
32
+ export declare function platformLibName(platform: string): string;
31
33
  /**
32
- * Locate the shared library: `ROCKBOX_FFI_LIB` env var first, then by walking
33
- * up from `startDir` to a `target/release` directory. `exists` and the path
34
- * ops are injected so this works under both Bun and Deno.
34
+ * The per-platform npm package that ships the prebuilt binary, e.g.
35
+ * `@rockbox-ffi/darwin-arm64`. Published as `optionalDependencies` of the main
36
+ * package so npm installs only the one matching the host.
35
37
  */
36
- export declare function resolveLibPath(startDir: string, env: (k: string) => string | undefined, exists: (p: string) => boolean, join: (...p: string[]) => string, dirname: (p: string) => string): string;
38
+ export declare function platformPackageName(platform: string, arch: string): string;
39
+ /**
40
+ * Locate the shared library. Precedence:
41
+ * 1. `ROCKBOX_FFI_LIB` env var (explicit override)
42
+ * 2. `bundled` — a path resolved from the per-platform npm package, if any
43
+ * 3. walking up from `startDir` to a `target/release` dir (repo checkout)
44
+ * `exists` and the path ops are injected so this works under Bun and Deno.
45
+ */
46
+ export declare function resolveLibPath(startDir: string, env: (k: string) => string | undefined, exists: (p: string) => boolean, join: (...p: string[]) => string, dirname: (p: string) => string, bundled?: string): string;
37
47
  //# sourceMappingURL=ffi.d.ts.map
package/dist/ffi.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ffi.d.ts","sourceRoot":"","sources":["../src/ffi.ts"],"names":[],"mappings":"AAGA,oEAAoE;AACpE,MAAM,MAAM,GAAG,GACX,MAAM,GACN,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,GACL,OAAO,GACP,KAAK,GACL,MAAM,GACN,QAAQ,GACR,OAAO,GACP,SAAS,CAAC;AAEd,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,GAAG,EAAE,GAAG,CAAC;CACV;AAGD,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CA+DxC,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,GAAG;IAClB,oEAAoE;IACpE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC;IAC1C,wDAAwD;IACxD,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,6DAA6D;IAC7D,KAAK,CAAC,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;IAC9B,4EAA4E;IAC5E,OAAO,IAAI;QAAE,GAAG,EAAE,OAAO,CAAC;QAAC,KAAK,IAAI,MAAM,CAAA;KAAE,CAAC;IAC7C,yEAAyE;IACzE,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;IACtC,4EAA4E;IAC5E,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7C,iDAAiD;IACjD,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;CAC7B;AAID;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,EACtC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,EAC9B,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,MAAM,EAChC,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAC7B,MAAM,CAqBR"}
1
+ {"version":3,"file":"ffi.d.ts","sourceRoot":"","sources":["../src/ffi.ts"],"names":[],"mappings":"AAGA,oEAAoE;AACpE,MAAM,MAAM,GAAG,GACX,MAAM,GACN,KAAK,GACL,KAAK,GACL,KAAK,GACL,KAAK,GACL,OAAO,GACP,KAAK,GACL,MAAM,GACN,QAAQ,GACR,OAAO,GACP,SAAS,CAAC;AAEd,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,GAAG,EAAE,GAAG,CAAC;CACV;AAGD,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CA+DxC,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,GAAG;IAClB,oEAAoE;IACpE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC;IAC1C,wDAAwD;IACxD,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB,6DAA6D;IAC7D,KAAK,CAAC,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;IAC9B,4EAA4E;IAC5E,OAAO,IAAI;QAAE,GAAG,EAAE,OAAO,CAAC;QAAC,KAAK,IAAI,MAAM,CAAA;KAAE,CAAC;IAC7C,yEAAyE;IACzE,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;IACtC,4EAA4E;IAC5E,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7C,iDAAiD;IACjD,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;CAC7B;AAID,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIxD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG1E;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,EACtC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,EAC9B,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,MAAM,EAChC,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAC9B,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,CAuBR"}
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAWA,OAAO,EAAW,UAAU,EAAE,MAAM,UAAU,CAAC;AAwE/C,eAAO,MAAQ,UAAU,gBAAE,QAAQ;;;GAAE,GAAG;;;;;;;;;;;;;;;;;;;;;GAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;CAAQ,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,cAAc,YAAY,CAAC;AAC3B,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAWA,OAAO,EAAW,UAAU,EAAE,MAAM,UAAU,CAAC;AA0F/C,eAAO,MAAQ,UAAU,gBAAE,QAAQ;;;GAAE,GAAG;;;;;;;;;;;;;;;;;;;;;GAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;CAAQ,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,cAAc,YAAY,CAAC;AAC3B,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
package/dist/node.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import {
2
2
  SPEC,
3
+ platformLibName,
4
+ platformPackageName,
3
5
  resolveLibPath
4
- } from "./chunk-ewj9g332.js";
6
+ } from "./chunk-sycd0w1e.js";
5
7
  import {
6
8
  ChannelConfig,
7
9
  CrossfadeMode,
@@ -19,6 +21,14 @@ import { fileURLToPath } from "node:url";
19
21
  import { createRequire } from "node:module";
20
22
  var require2 = createRequire(import.meta.url);
21
23
  var koffi = require2("koffi");
24
+ function bundledLibPath() {
25
+ try {
26
+ const pkg = platformPackageName(process.platform, process.arch);
27
+ return require2.resolve(`${pkg}/${platformLibName(process.platform)}`);
28
+ } catch {
29
+ return;
30
+ }
31
+ }
22
32
  var TOK = {
23
33
  void: "void",
24
34
  ptr: "void *",
@@ -34,7 +44,7 @@ var TOK = {
34
44
  };
35
45
  function makeRaw() {
36
46
  const here = dirname(fileURLToPath(import.meta.url));
37
- const libPath = resolveLibPath(here, (k) => process.env[k], existsSync, join, dirname);
47
+ const libPath = resolveLibPath(here, (k) => process.env[k], existsSync, join, dirname, bundledLibPath());
38
48
  const lib = koffi.load(libPath);
39
49
  const sym = {};
40
50
  for (const [name, spec] of Object.entries(SPEC)) {
@@ -85,4 +95,4 @@ export {
85
95
  ChannelConfig
86
96
  };
87
97
 
88
- //# debugId=C34E81133977033C64756E2164756E21
98
+ //# debugId=4AF72A4D1A46AC1F64756E2164756E21
package/dist/node.js.map CHANGED
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/node.ts"],
4
4
  "sourcesContent": [
5
- "// Node.js backend + full API, using koffi for FFI. Import under Node:\n// import { Dsp, Player, metadata } from \"rockbox-ffi/node\";\n//\n// Node has no built-in FFI, so this backend depends on `koffi`\n// (https://koffi.dev) — install it with `npm install koffi`.\n\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createRequire } from \"node:module\";\n\nimport { makeApi, sineStereo } from \"./api.ts\";\nimport { resolveLibPath, SPEC, type Raw, type Tok } from \"./ffi.ts\";\n\n// koffi ships as CommonJS; load it via require to keep ESM interop simple.\nconst require = createRequire(import.meta.url);\n// deno-lint-ignore no-explicit-any\nconst koffi: any = require(\"koffi\");\n\nconst TOK: Record<Tok, string> = {\n void: \"void\",\n ptr: \"void *\",\n u32: \"uint32\",\n i32: \"int32\",\n u64: \"uint64\",\n usize: \"size_t\",\n f32: \"float\",\n bool: \"bool\",\n strbuf: \"str\", // koffi marshals a JS string to a NUL-terminated char*\n i16in: \"void *\", // we pass a Buffer view of the Int16Array\n outsize: \"void *\", // we pass a Buffer.alloc(8) and read it back\n};\n\nfunction makeRaw(): Raw {\n const here = dirname(fileURLToPath(import.meta.url));\n const libPath = resolveLibPath(\n here,\n (k) => process.env[k],\n existsSync,\n join,\n dirname,\n );\n\n const lib = koffi.load(libPath);\n const sym: Record<string, (...a: any[]) => any> = {};\n for (const [name, spec] of Object.entries(SPEC)) {\n sym[name] = lib.func(name, TOK[spec.ret], spec.args.map((t) => TOK[t]));\n }\n\n return {\n sym,\n cstr(s: string) {\n // The `strbuf` params are typed \"str\"; koffi marshals the JS string.\n return s;\n },\n i16in(a: Int16Array) {\n return Buffer.from(a.buffer, a.byteOffset, a.byteLength);\n },\n sizeOut() {\n const buf = Buffer.alloc(8);\n return { arg: buf, value: () => Number(buf.readBigUInt64LE(0)) };\n },\n takeString(p: unknown) {\n if (p === null || p === undefined) return null;\n // Read a NUL-terminated char* of unknown length. (koffi's \"str\" type\n // is unreliable for longer strings in 2.16 — this form is robust.)\n const str = koffi.decode(p, \"char\", -1) as string;\n sym.rb_string_free(p);\n return str;\n },\n takeI16(p: unknown, len: number) {\n const arr = koffi.decode(p, koffi.array(\"int16_t\", len)) as number[];\n const copy = Int16Array.from(arr);\n sym.rb_buffer_free(p, len);\n return copy;\n },\n isNull(p: unknown) {\n return p === null || p === undefined;\n },\n };\n}\n\nconst api = makeApi(makeRaw());\nexport const { abiVersion, metadata, Dsp, Player } = api;\nexport { sineStereo };\nexport * from \"./enums.ts\";\nexport type { Metadata, PlayerConfig, PlayerStatus } from \"./types.ts\";\n"
5
+ "// Node.js backend + full API, using koffi for FFI. Import under Node:\n// import { Dsp, Player, metadata } from \"rockbox-ffi/node\";\n//\n// Node has no built-in FFI, so this backend depends on `koffi`\n// (https://koffi.dev) — install it with `npm install koffi`.\n\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createRequire } from \"node:module\";\n\nimport { makeApi, sineStereo } from \"./api.ts\";\nimport {\n platformLibName,\n platformPackageName,\n resolveLibPath,\n SPEC,\n type Raw,\n type Tok,\n} from \"./ffi.ts\";\n\n// koffi ships as CommonJS; load it via require to keep ESM interop simple.\nconst require = createRequire(import.meta.url);\n// deno-lint-ignore no-explicit-any\nconst koffi: any = require(\"koffi\");\n\n/** Resolve the prebuilt binary from the per-platform npm package, if present. */\nfunction bundledLibPath(): string | undefined {\n try {\n const pkg = platformPackageName(process.platform, process.arch);\n return require.resolve(`${pkg}/${platformLibName(process.platform)}`);\n } catch {\n return undefined;\n }\n}\n\nconst TOK: Record<Tok, string> = {\n void: \"void\",\n ptr: \"void *\",\n u32: \"uint32\",\n i32: \"int32\",\n u64: \"uint64\",\n usize: \"size_t\",\n f32: \"float\",\n bool: \"bool\",\n strbuf: \"str\", // koffi marshals a JS string to a NUL-terminated char*\n i16in: \"void *\", // we pass a Buffer view of the Int16Array\n outsize: \"void *\", // we pass a Buffer.alloc(8) and read it back\n};\n\nfunction makeRaw(): Raw {\n const here = dirname(fileURLToPath(import.meta.url));\n const libPath = resolveLibPath(\n here,\n (k) => process.env[k],\n existsSync,\n join,\n dirname,\n bundledLibPath(),\n );\n\n const lib = koffi.load(libPath);\n const sym: Record<string, (...a: any[]) => any> = {};\n for (const [name, spec] of Object.entries(SPEC)) {\n sym[name] = lib.func(name, TOK[spec.ret], spec.args.map((t) => TOK[t]));\n }\n\n return {\n sym,\n cstr(s: string) {\n // The `strbuf` params are typed \"str\"; koffi marshals the JS string.\n return s;\n },\n i16in(a: Int16Array) {\n return Buffer.from(a.buffer, a.byteOffset, a.byteLength);\n },\n sizeOut() {\n const buf = Buffer.alloc(8);\n return { arg: buf, value: () => Number(buf.readBigUInt64LE(0)) };\n },\n takeString(p: unknown) {\n if (p === null || p === undefined) return null;\n // Read a NUL-terminated char* of unknown length. (koffi's \"str\" type\n // is unreliable for longer strings in 2.16 — this form is robust.)\n const str = koffi.decode(p, \"char\", -1) as string;\n sym.rb_string_free(p);\n return str;\n },\n takeI16(p: unknown, len: number) {\n const arr = koffi.decode(p, koffi.array(\"int16_t\", len)) as number[];\n const copy = Int16Array.from(arr);\n sym.rb_buffer_free(p, len);\n return copy;\n },\n isNull(p: unknown) {\n return p === null || p === undefined;\n },\n };\n}\n\nconst api = makeApi(makeRaw());\nexport const { abiVersion, metadata, Dsp, Player } = api;\nexport { sineStereo };\nexport * from \"./enums.ts\";\nexport type { Metadata, PlayerConfig, PlayerStatus } from \"./types.ts\";\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;AAMA;AACA;AACA;AACA;AAMA,IAAM,WAAU,cAAc,YAAY,GAAG;AAE7C,IAAM,QAAa,SAAQ,OAAO;AAElC,IAAM,MAA2B;AAAA,EAC/B,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,OAAO;AAAA,EACP,KAAK;AAAA,EACL,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS;AACX;AAEA,SAAS,OAAO,GAAQ;AAAA,EACtB,MAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;AAAA,EACnD,MAAM,UAAU,eACd,MACA,CAAC,MAAM,QAAQ,IAAI,IACnB,YACA,MACA,OACF;AAAA,EAEA,MAAM,MAAM,MAAM,KAAK,OAAO;AAAA,EAC9B,MAAM,MAA4C,CAAC;AAAA,EACnD,YAAY,MAAM,SAAS,OAAO,QAAQ,IAAI,GAAG;AAAA,IAC/C,IAAI,QAAQ,IAAI,KAAK,MAAM,IAAI,KAAK,MAAM,KAAK,KAAK,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;AAAA,EACxE;AAAA,EAEA,OAAO;AAAA,IACL;AAAA,IACA,IAAI,CAAC,GAAW;AAAA,MAEd,OAAO;AAAA;AAAA,IAET,KAAK,CAAC,GAAe;AAAA,MACnB,OAAO,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU;AAAA;AAAA,IAEzD,OAAO,GAAG;AAAA,MACR,MAAM,MAAM,OAAO,MAAM,CAAC;AAAA,MAC1B,OAAO,EAAE,KAAK,KAAK,OAAO,MAAM,OAAO,IAAI,gBAAgB,CAAC,CAAC,EAAE;AAAA;AAAA,IAEjE,UAAU,CAAC,GAAY;AAAA,MACrB,IAAI,MAAM,QAAQ,MAAM;AAAA,QAAW,OAAO;AAAA,MAG1C,MAAM,MAAM,MAAM,OAAO,GAAG,QAAQ,EAAE;AAAA,MACtC,IAAI,eAAe,CAAC;AAAA,MACpB,OAAO;AAAA;AAAA,IAET,OAAO,CAAC,GAAY,KAAa;AAAA,MAC/B,MAAM,MAAM,MAAM,OAAO,GAAG,MAAM,MAAM,WAAW,GAAG,CAAC;AAAA,MACvD,MAAM,OAAO,WAAW,KAAK,GAAG;AAAA,MAChC,IAAI,eAAe,GAAG,GAAG;AAAA,MACzB,OAAO;AAAA;AAAA,IAET,MAAM,CAAC,GAAY;AAAA,MACjB,OAAO,MAAM,QAAQ,MAAM;AAAA;AAAA,EAE/B;AAAA;AAGF,IAAM,MAAM,QAAQ,QAAQ,CAAC;AACtB,MAAQ,YAAY,UAAU,KAAK,WAAW;",
8
- "debugId": "C34E81133977033C64756E2164756E21",
7
+ "mappings": ";;;;;;;;;;;;;;;;;AAMA;AACA;AACA;AACA;AAaA,IAAM,WAAU,cAAc,YAAY,GAAG;AAE7C,IAAM,QAAa,SAAQ,OAAO;AAGlC,SAAS,cAAc,GAAuB;AAAA,EAC5C,IAAI;AAAA,IACF,MAAM,MAAM,oBAAoB,QAAQ,UAAU,QAAQ,IAAI;AAAA,IAC9D,OAAO,SAAQ,QAAQ,GAAG,OAAO,gBAAgB,QAAQ,QAAQ,GAAG;AAAA,IACpE,MAAM;AAAA,IACN;AAAA;AAAA;AAIJ,IAAM,MAA2B;AAAA,EAC/B,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,OAAO;AAAA,EACP,KAAK;AAAA,EACL,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS;AACX;AAEA,SAAS,OAAO,GAAQ;AAAA,EACtB,MAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;AAAA,EACnD,MAAM,UAAU,eACd,MACA,CAAC,MAAM,QAAQ,IAAI,IACnB,YACA,MACA,SACA,eAAe,CACjB;AAAA,EAEA,MAAM,MAAM,MAAM,KAAK,OAAO;AAAA,EAC9B,MAAM,MAA4C,CAAC;AAAA,EACnD,YAAY,MAAM,SAAS,OAAO,QAAQ,IAAI,GAAG;AAAA,IAC/C,IAAI,QAAQ,IAAI,KAAK,MAAM,IAAI,KAAK,MAAM,KAAK,KAAK,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;AAAA,EACxE;AAAA,EAEA,OAAO;AAAA,IACL;AAAA,IACA,IAAI,CAAC,GAAW;AAAA,MAEd,OAAO;AAAA;AAAA,IAET,KAAK,CAAC,GAAe;AAAA,MACnB,OAAO,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU;AAAA;AAAA,IAEzD,OAAO,GAAG;AAAA,MACR,MAAM,MAAM,OAAO,MAAM,CAAC;AAAA,MAC1B,OAAO,EAAE,KAAK,KAAK,OAAO,MAAM,OAAO,IAAI,gBAAgB,CAAC,CAAC,EAAE;AAAA;AAAA,IAEjE,UAAU,CAAC,GAAY;AAAA,MACrB,IAAI,MAAM,QAAQ,MAAM;AAAA,QAAW,OAAO;AAAA,MAG1C,MAAM,MAAM,MAAM,OAAO,GAAG,QAAQ,EAAE;AAAA,MACtC,IAAI,eAAe,CAAC;AAAA,MACpB,OAAO;AAAA;AAAA,IAET,OAAO,CAAC,GAAY,KAAa;AAAA,MAC/B,MAAM,MAAM,MAAM,OAAO,GAAG,MAAM,MAAM,WAAW,GAAG,CAAC;AAAA,MACvD,MAAM,OAAO,WAAW,KAAK,GAAG;AAAA,MAChC,IAAI,eAAe,GAAG,GAAG;AAAA,MACzB,OAAO;AAAA;AAAA,IAET,MAAM,CAAC,GAAY;AAAA,MACjB,OAAO,MAAM,QAAQ,MAAM;AAAA;AAAA,EAE/B;AAAA;AAGF,IAAM,MAAM,QAAQ,QAAQ,CAAC;AACtB,MAAQ,YAAY,UAAU,KAAK,WAAW;",
8
+ "debugId": "4AF72A4D1A46AC1F64756E2164756E21",
9
9
  "names": []
10
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rockbox-ffi",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "TypeScript bindings (Bun + Deno + Node.js FFI) for the Rockbox DSP / metadata / playback engine",
5
5
  "license": "GPL-2.0-or-later",
6
6
  "type": "module",
@@ -41,6 +41,14 @@
41
41
  "dependencies": {
42
42
  "koffi": "^2.9.0"
43
43
  },
44
+ "optionalDependencies": {
45
+ "@rockbox-ffi/darwin-arm64": "0.1.2",
46
+ "@rockbox-ffi/darwin-x64": "0.1.2",
47
+ "@rockbox-ffi/linux-x64": "0.1.2",
48
+ "@rockbox-ffi/linux-arm64": "0.1.2",
49
+ "@rockbox-ffi/freebsd-x64": "0.1.2",
50
+ "@rockbox-ffi/netbsd-x64": "0.1.2"
51
+ },
44
52
  "devDependencies": {
45
53
  "@types/bun": "latest",
46
54
  "@types/node": "^22.0.0",
package/src/bun.ts CHANGED
@@ -10,11 +10,30 @@ import {
10
10
  toArrayBuffer,
11
11
  } from "bun:ffi";
12
12
  import { existsSync } from "node:fs";
13
+ import { createRequire } from "node:module";
13
14
  import { dirname, join } from "node:path";
14
15
  import { fileURLToPath } from "node:url";
15
16
 
16
17
  import { makeApi, sineStereo } from "./api.ts";
17
- import { resolveLibPath, SPEC, type Raw, type Tok } from "./ffi.ts";
18
+ import {
19
+ platformLibName,
20
+ platformPackageName,
21
+ resolveLibPath,
22
+ SPEC,
23
+ type Raw,
24
+ type Tok,
25
+ } from "./ffi.ts";
26
+
27
+ /** Resolve the prebuilt binary from the per-platform npm package, if present. */
28
+ function bundledLibPath(): string | undefined {
29
+ try {
30
+ const require = createRequire(import.meta.url);
31
+ const pkg = platformPackageName(process.platform, process.arch);
32
+ return require.resolve(`${pkg}/${platformLibName(process.platform)}`);
33
+ } catch {
34
+ return undefined;
35
+ }
36
+ }
18
37
 
19
38
  const TOK: Record<Tok, FFIType> = {
20
39
  void: FFIType.void,
@@ -38,6 +57,7 @@ function makeRaw(): Raw {
38
57
  existsSync,
39
58
  join,
40
59
  dirname,
60
+ bundledLibPath(),
41
61
  );
42
62
 
43
63
  const symbols: Record<string, { args: FFIType[]; returns: FFIType }> = {};
package/src/ffi.ts CHANGED
@@ -109,10 +109,29 @@ export interface Raw {
109
109
 
110
110
  const LIB_NAMES = ["librockbox_ffi.dylib", "librockbox_ffi.so", "rockbox_ffi.dll"];
111
111
 
112
+ /** The shared-library filename for a Node-style `process.platform`. */
113
+ export function platformLibName(platform: string): string {
114
+ if (platform === "darwin") return "librockbox_ffi.dylib";
115
+ if (platform === "win32") return "rockbox_ffi.dll";
116
+ return "librockbox_ffi.so";
117
+ }
118
+
112
119
  /**
113
- * Locate the shared library: `ROCKBOX_FFI_LIB` env var first, then by walking
114
- * up from `startDir` to a `target/release` directory. `exists` and the path
115
- * ops are injected so this works under both Bun and Deno.
120
+ * The per-platform npm package that ships the prebuilt binary, e.g.
121
+ * `@rockbox-ffi/darwin-arm64`. Published as `optionalDependencies` of the main
122
+ * package so npm installs only the one matching the host.
123
+ */
124
+ export function platformPackageName(platform: string, arch: string): string {
125
+ const cpu = arch === "x64" ? "x64" : arch === "arm64" ? "arm64" : arch;
126
+ return `@rockbox-ffi/${platform}-${cpu}`;
127
+ }
128
+
129
+ /**
130
+ * Locate the shared library. Precedence:
131
+ * 1. `ROCKBOX_FFI_LIB` env var (explicit override)
132
+ * 2. `bundled` — a path resolved from the per-platform npm package, if any
133
+ * 3. walking up from `startDir` to a `target/release` dir (repo checkout)
134
+ * `exists` and the path ops are injected so this works under Bun and Deno.
116
135
  */
117
136
  export function resolveLibPath(
118
137
  startDir: string,
@@ -120,10 +139,13 @@ export function resolveLibPath(
120
139
  exists: (p: string) => boolean,
121
140
  join: (...p: string[]) => string,
122
141
  dirname: (p: string) => string,
142
+ bundled?: string,
123
143
  ): string {
124
144
  const override = env("ROCKBOX_FFI_LIB");
125
145
  if (override) return override;
126
146
 
147
+ if (bundled && exists(bundled)) return bundled;
148
+
127
149
  let dir = startDir;
128
150
  const tried: string[] = [];
129
151
  for (let i = 0; i < 40; i++) {
package/src/node.ts CHANGED
@@ -10,13 +10,30 @@ import { fileURLToPath } from "node:url";
10
10
  import { createRequire } from "node:module";
11
11
 
12
12
  import { makeApi, sineStereo } from "./api.ts";
13
- import { resolveLibPath, SPEC, type Raw, type Tok } from "./ffi.ts";
13
+ import {
14
+ platformLibName,
15
+ platformPackageName,
16
+ resolveLibPath,
17
+ SPEC,
18
+ type Raw,
19
+ type Tok,
20
+ } from "./ffi.ts";
14
21
 
15
22
  // koffi ships as CommonJS; load it via require to keep ESM interop simple.
16
23
  const require = createRequire(import.meta.url);
17
24
  // deno-lint-ignore no-explicit-any
18
25
  const koffi: any = require("koffi");
19
26
 
27
+ /** Resolve the prebuilt binary from the per-platform npm package, if present. */
28
+ function bundledLibPath(): string | undefined {
29
+ try {
30
+ const pkg = platformPackageName(process.platform, process.arch);
31
+ return require.resolve(`${pkg}/${platformLibName(process.platform)}`);
32
+ } catch {
33
+ return undefined;
34
+ }
35
+ }
36
+
20
37
  const TOK: Record<Tok, string> = {
21
38
  void: "void",
22
39
  ptr: "void *",
@@ -39,6 +56,7 @@ function makeRaw(): Raw {
39
56
  existsSync,
40
57
  join,
41
58
  dirname,
59
+ bundledLibPath(),
42
60
  );
43
61
 
44
62
  const lib = koffi.load(libPath);