{"id":50917260,"url":"https://github.com/cwilliams5/foobar-dsp-host","last_synced_at":"2026-06-16T16:32:23.009Z","repository":{"id":363999950,"uuid":"1257649116","full_name":"cwilliams5/foobar-dsp-host","owner":"cwilliams5","description":"Host real foobar2000 DSP components (foo_dsp_*) outside foobar2000 — chains, native config dialogs, crash isolation, out-of-process. C++ worker + importable Rust client crate.","archived":false,"fork":false,"pushed_at":"2026-06-11T08:12:29.000Z","size":3492,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-11T09:14:36.952Z","etag":null,"topics":["audio","dsp","foobar2000","plugin-host","rust"],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cwilliams5.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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-02T22:03:38.000Z","updated_at":"2026-06-11T08:12:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cwilliams5/foobar-dsp-host","commit_stats":null,"previous_names":["cwilliams5/foobar-dsp-host"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/cwilliams5/foobar-dsp-host","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cwilliams5%2Ffoobar-dsp-host","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cwilliams5%2Ffoobar-dsp-host/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cwilliams5%2Ffoobar-dsp-host/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cwilliams5%2Ffoobar-dsp-host/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cwilliams5","download_url":"https://codeload.github.com/cwilliams5/foobar-dsp-host/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cwilliams5%2Ffoobar-dsp-host/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34415240,"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-16T02:00:06.860Z","response_time":126,"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":["audio","dsp","foobar2000","plugin-host","rust"],"created_at":"2026-06-16T16:32:21.501Z","updated_at":"2026-06-16T16:32:23.003Z","avatar_url":"https://github.com/cwilliams5.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# foobar-dsp-host\n\n**Host real foobar2000 DSP components (`foo_dsp_*`) outside of foobar2000.** Load them, **chain several\ntogether**, stream audio through the chain, drive each plugin's *own* config dialog, and get processed\naudio back. As far as I know this is the first open-source host that does this generically.\n\nfoobar2000's component model isn't a flat C ABI — it's a COM-like C++ **service** SDK, which is why\nhosting its DSPs outside the player has long been considered impractical. It turns out the surface a\n*DSP* actually needs is small. This repo implements it: a tiny C++ host that satisfies the foobar2000\nSDK handshake, runs the plugin in an **isolated worker process**, and exposes a dead-simple stdio\nprotocol that any program can drive. A Rust + egui \"lab\" is included as a reference client.\n\n\u003e **Status: canonical home of the host.** Built for (and consumed by) the\n\u003e *[Resonance](https://github.com/cwilliams5/Resonance)* media player — Resonance imports the\n\u003e [`foobar-dsp-host`](crates/client) crate, so fixes land here first and circulate to everyone.\n\u003e See [Maintenance](#maintenance).\n\n![The lab: a foobar2000 DSP (VLevel) running in an isolated worker — its own config dialog open, live IN/OUT meters showing the effect (−26.7 → −18.6 dBFS), and instant A/B bypass.](screenshot.png)\n\n## What works\n\n- Loads any foobar2000 **v2 (x64)** or **v1.x (x86)** DSP component.\n- Enumerates its `dsp_entry`, instantiates it, and streams **f32** audio through `dsp::run`.\n- **Chains DSPs** — load several into one worker and run them in series via the SDK's own `dsp_manager`\n  (one process, one pass, f32 throughout — not `.exe→.exe→.exe`). Reorder, remove, and configure each\n  stage individually. See `docs/HOW-IT-WORKS.md` → *Chaining*.\n- Opens the plugin's **own** Win32 config dialog and round-trips its preset blob.\n- **Isolation:** the plugin runs in a separate worker process — a crash drops the pipe and the host\n  survives (foobar's own VST adapter uses the same out-of-process trick).\n- **x64 and x86** workers (32-bit-only components — convolvers, crossfeed, Dolby Headphone — need the\n  x86 worker; a 64-bit process can't load a 32-bit DLL).\n- **Remembers settings:** a DSP's config (its `dsp_preset` blob) is saved per-component and restored on\n  the next load (the lab keeps these under `presets/`).\n\n## Quick start\n\n1. Install **Visual Studio 2022** with the Desktop C++ workload (**x64 + x86**), and **Rust** (`cargo`).\n2. Drop one or more `.fb2k-component` files into **`components/`**.\n3. Double-click **`run-lab.bat`**.\n4. In the window: pick a DSP → **➕ Add to chain** (repeat to stack more) → reorder with **↑/↓**, drop one\n   with **✕** → **▶ Apply chain** → **▶ Play** → toggle **A · Bypass / B · Processed**, and **⚙ Config** any\n   stage to open its own dialog. It plays the bundled CC0 clip; drop your own `test.mp3`/`.flac`/`.wav` in\n   the repo root to use that instead.\n\n**No foobar2000 installation is required** — `shared.dll` builds from the bundled BSD SDK source.\n\n## How it's put together\n\n```\n your program            ── stdio protocol ──▶  foo_dsp_host.exe --worker  ── fb2k SDK ──▶  foo_dsp_*.dll\n (the foobar-dsp-host crate:                     (implements foobar2000_api;                 (the real\n  the lab / your own host)                        hosts the chain; x64 or x86)                DSP plugins)\n```\n\n### Use it from Rust — the client crate\n\nThe **`foobar-dsp-host`** crate is the importable API: spawn the right-arch worker, build chains,\nstream f32 audio, open native config dialogs, persist `dsp_preset` blobs, survive crashes.\n\n```rust\nuse foobar_dsp_host::{StageSpec, Worker};\n\nlet mut w = Worker::spawn(Path::new(r\"host\\build\\x64\\Debug\\foo_dsp_host.exe\"))?;\nlet names = w.build_chain(44100, 2, \u0026[\n    StageSpec::new(r\"C:\\components\\foo_dsp_xgeq.dll\"),\n    StageSpec::new(r\"C:\\components\\foo_dsp_vlevel.dll\"),\n])?;\nlet processed = w.process(\u0026block)?.to_vec(); // one IPC round-trip runs the whole chain\nlet tail = w.drain()?.to_vec();              // end-of-stream: look-ahead tails\nlet blob = w.configure(1)?;                  // stage 1's OWN dialog (modal); persist the blob\n```\n\nA component crash surfaces as `Error::WorkerGone` — your process carries on. Arch routing\n(`dll_arch`) and companion-DLL staging (`stage_companion_dlls`) ship in the crate.\n\n| Path | Crate | What |\n|---|---|---|\n| `crates/client/` | **`foobar-dsp-host`** | **The importable API** (Rust): spawn + version handshake, chains, processing, dialogs, preset blobs, crash detection, arch routing. Built on the shared [`tagpipe`](https://github.com/cwilliams5/winamp-vst2-dsp-host) worker transport. |\n| `host/` | — | The C++ worker. Implements the ~11-method `foobar2000_api`, links the SDK, hosts the chain, and speaks the protocol on stdin/stdout. The same exe is a standalone CLI (`foo_dsp_host.exe \u003cdll\u003e`) and the IPC worker (`--worker`). |\n| `host/ref_dsp/` | — | `foo_dsp_ref.dll` — the repo's own known-good component (two deterministic ×0.5 gain entries), built from the bundled SDK; powers the bit-exact integration tests + CI. |\n| `lab/` | — | The Rust/egui reference client — spawns the worker via the client crate, decodes audio (symphonia), plays via cpal, with A/B, level meters, and config. |\n| `sdk/` | — | The vendored foobar2000 SDK (BSD-2) + one tiny ATL-removal patch so `shared.dll` builds without the VS ATL component. |\n| `docs/PROTOCOL.md` | | The worker IPC protocol — the language-agnostic \"API\" (handshake + vocabulary). |\n| `docs/HOW-IT-WORKS.md` | | How the foobar2000 SDK handshake is satisfied (the interesting part). |\n\nBuild manually instead of the `.bat`: `./build.ps1` (workers + `shared.dll` + `foo_dsp_ref`, both\narches), then `cargo run -p foobar-dsp-lab`.\n\n## Compatibility\n\nDSP compatibility is a spectrum. Effects that don't read track metadata — gain, EQ, resamplers,\nlevelers, exciters, stereo tools — work with a null track handle (most DSPs). A minimal `configStore`\nstub satisfies plugins that read a stored default (e.g. resamplers, which would otherwise bail). Plugins\nthat deeply inspect the *current track* via `metadb` aren't supported (that subsystem isn't stood up).\nSettings persistence covers DSPs that keep config in the preset (most, including the dialogs you'll\nuse); the few that stash global state in `configStore` won't persist it (the stub is pass-through).\nDetails in `docs/HOW-IT-WORKS.md`.\n\n## Maintenance\n\n**This repository is the canonical home of the host** — the C++ worker, the protocol, and the\n`foobar-dsp-host` client crate live and evolve here, and\n[Resonance](https://github.com/cwilliams5/Resonance) consumes them as dependencies (so its fixes land\nhere first, publicly). Maintained as used by Resonance: issues and PRs are welcome, reviewed on a\nbest-effort basis, no SLA. The crate will publish to crates.io once the API has survived its first\nfull downstream integration (and its `tagpipe` transport dependency is on crates.io).\n\n## Licensing\n\n- This repo's original code (host, lab, build scripts, docs): **MIT** — see `LICENSE`.\n- Bundled foobar2000 SDK under `sdk/`: **BSD-2-Clause**, © Peter Pawlowski — see `sdk/sdk-license.txt`.\n- Rust dependencies and full notices: **`THIRD-PARTY-NOTICES.md`**.\n- foobar2000 DSP components you load are third-party and remain under their own licenses — **none are\n  bundled here**.\n- \"foobar2000\" is a trademark of its owner. This project is unaffiliated and describes compatibility only.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcwilliams5%2Ffoobar-dsp-host","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcwilliams5%2Ffoobar-dsp-host","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcwilliams5%2Ffoobar-dsp-host/lists"}