{"id":50893973,"url":"https://github.com/pmarreck/speakrs_ffi","last_synced_at":"2026-06-15T23:01:21.564Z","repository":{"id":363903613,"uuid":"1265403566","full_name":"pmarreck/speakrs_ffi","owner":"pmarreck","description":"C FFI for speakrs speaker diarization — PCM samples in, JSON speaker turns out. Python/ctypes-ready, Nix-first packaging (zero build-time downloads).","archived":false,"fork":false,"pushed_at":"2026-06-10T20:42:35.000Z","size":736,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"yolo","last_synced_at":"2026-06-10T22:13:16.000Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pmarreck.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-10T18:38:10.000Z","updated_at":"2026-06-10T20:43:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pmarreck/speakrs_ffi","commit_stats":null,"previous_names":["pmarreck/speakrs_ffi"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/pmarreck/speakrs_ffi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmarreck%2Fspeakrs_ffi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmarreck%2Fspeakrs_ffi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmarreck%2Fspeakrs_ffi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmarreck%2Fspeakrs_ffi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pmarreck","download_url":"https://codeload.github.com/pmarreck/speakrs_ffi/tar.gz/refs/heads/yolo","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pmarreck%2Fspeakrs_ffi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34383468,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-15T23:01:20.635Z","updated_at":"2026-06-15T23:01:21.556Z","avatar_url":"https://github.com/pmarreck.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# speakrs_ffi\n\n[![Garnix](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgarnix.io%2Fapi%2Fbadges%2Fpmarreck%2Fspeakrs_ffi%3Fbranch%3Dyolo)](https://garnix.io/repo/pmarreck/speakrs_ffi)\n\nC FFI for [speakrs](https://github.com/avencera/speakrs) — speaker diarization\n(who spoke when) with pyannote-level accuracy at hundreds-of-× realtime, callable\nfrom anything that speaks C: Python (`ctypes`), C, Zig, LuaJIT, Swift, …\n\n```\nconsumer (Python / C CLI / …) ──► C FFI ──► speakrs (Rust: CoreML / ONNX Runtime)\n```\n\nMeasured on an Apple Silicon Mac (CoreML mode): a 21.5-minute video diarized in\n**5.8 seconds** (~220× realtime), 8 speakers, 207 turns.\n\n## Design\n\n**Pure in-memory transform: PCM samples in, JSON out.** The library does no\nfile I/O and no audio decoding — callers decode to mono 16 kHz f32 PCM first:\n\n```sh\nffmpeg -i input.mp3 -f f32le -ac 1 -ar 16000 output.pcm\n```\n\nEvery failure — including a Rust panic — returns as `{\"ok\":false,\"error\":\"…\"}`.\nPanics never unwind across the FFI boundary.\n\n## C API\n\n```c\n#include \u003cspeakrs_ffi.h\u003e\n\nconst char *speakrs_ffi_version(void);                     /* static; do not free */\nchar *speakrs_ffi_diarize(const float *samples, size_t n,  /* mono 16 kHz f32 PCM */\n                          const char *opts_json);          /* NULL = defaults     */\nvoid speakrs_ffi_free(char *s);\n```\n\nOptions JSON (all fields optional):\n\n```json\n{\n  \"mode\": \"coreml\",            // cpu | coreml | coreml-fast | cuda | cuda-fast | migraphx\n  \"models_dir\": \"/path\"        // omit → auto-download from HF on first use\n}\n```\n\nDefault mode is `coreml` on macOS, `cpu` elsewhere. Result:\n\n```json\n{\"ok\": true,\n \"segments\": [{\"start\": 0.14, \"end\": 0.99, \"speaker\": \"SPEAKER_05\"}, …],\n \"speakers\": [\"SPEAKER_00\", …]}\n```\n\n## CLI\n\n`speakrs-diarize` is a C program that consumes the FFI exactly like any\nexternal consumer (dogfooding the header and linkage):\n\n```sh\nffmpeg -i talk.mp3 -f f32le -ac 1 -ar 16000 - | speakrs-diarize -\nspeakrs-diarize --mode coreml-fast --models-dir ~/models talk.pcm\n```\n\nJSON to stdout, progress to stderr, exit 0/1.\n\n## Python (ctypes)\n\n```python\nimport ctypes, json, subprocess\n\nlib = ctypes.CDLL(\"libspeakrs_ffi.dylib\")\nlib.speakrs_ffi_diarize.restype = ctypes.c_void_p   # keep pointer for free()\nlib.speakrs_ffi_diarize.argtypes = [ctypes.POINTER(ctypes.c_float), ctypes.c_size_t, ctypes.c_char_p]\n\npcm = subprocess.run([\"ffmpeg\", \"-v\", \"error\", \"-i\", \"in.mp3\",\n                      \"-f\", \"f32le\", \"-ac\", \"1\", \"-ar\", \"16000\", \"-\"],\n                     capture_output=True, check=True).stdout\nbuf = (ctypes.c_float * (len(pcm) // 4)).from_buffer_copy(pcm)\n\nptr = lib.speakrs_ffi_diarize(buf, len(buf), None)\nresult = json.loads(ctypes.cast(ptr, ctypes.c_char_p).value)\nlib.speakrs_ffi_free(ctypes.c_void_p(ptr))\n```\n\n## Building (Nix)\n\n```sh\n./build      # nix build → result/{lib,include,bin}\n./test       # cargo tests + CLI tests (offline) + functional test (real models)\n```\n\n**Nothing downloads during the build** — that's the point of this packaging:\n\n| upstream default | what it does | what we use instead |\n|---|---|---|\n| `default-linalg` | fetches Intel MKL / static OpenBLAS at build time | `openblas-system` → nixpkgs openblas via pkg-config |\n| ort prebuilt binaries | downloads ONNX Runtime during the build | `load-dynamic` → dlopen at runtime via `ORT_DYLIB_PATH` |\n\nModels (`avencera/speakrs-models`, no HF token needed) download at **runtime**\non first use, or load offline from `models_dir`. In CoreML mode, ONNX Runtime\nis never loaded at all; for `cpu`/`cuda` modes set `ORT_DYLIB_PATH` to a\n`libonnxruntime` (the Nix-built CLI has a default wired in; the flake exposes\nit as `packages.\u003csystem\u003e.default.passthru.ortLib`).\n\nCI runs real diarization hermetically: `checks.functional-test` pins the\ncpu-mode model files as fixed-output derivations and diarizes a committed\ntwo-speaker fixture (A-B-A pattern -- see `tests/fixtures/README.md`) inside\nthe pure sandbox, asserting exactly two speakers and correct re-identification.\n\n## As a flake input\n\n```nix\ninputs.speakrs-ffi.url = \"github:pmarreck/speakrs_ffi\";\n# then: speakrs-ffi.packages.${system}.default → lib/, include/, bin/\n```\n\n## License\n\nApache-2.0, same as speakrs.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpmarreck%2Fspeakrs_ffi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpmarreck%2Fspeakrs_ffi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpmarreck%2Fspeakrs_ffi/lists"}