{"_id":"@audio/decode-dsd","name":"@audio/decode-dsd","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@audio/decode-dsd","version":"1.0.0","description":"Decode DSD (DSF, DFF) audio to PCM samples","type":"module","main":"decode-dsd.js","types":"decode-dsd.d.ts","exports":{".":"./decode-dsd.js","./meta":"./meta.js","./audio":{"types":"./audio.d.ts","default":"./audio.js"},"./package.json":"./package.json"},"scripts":{"test":"node test.js"},"keywords":["dsd","dsf","dff","dsdiff","sacd","audio","decode","decoder","pcm","sigma-delta"],"publishConfig":{"access":"public"},"license":"MIT","author":{"name":"audiojs"},"homepage":"https://github.com/audiojs/decode/tree/master/packages/decode-dsd#readme","repository":{"type":"git","url":"git+https://github.com/audiojs/decode.git","directory":"packages/decode-dsd"},"bugs":{"url":"https://github.com/audiojs/decode/issues"},"dependencies":{"@audio/decode-mp3":"^1.3.0"},"devDependencies":{"tst":"^9.2.2"},"engines":{"node":">=18"},"audio":"./audio.js","sideEffects":false,"funding":"https://github.com/sponsors/audiojs","gitHead":"c227363046dc3a0308c95ee112f22795c0ac04ca","_id":"@audio/decode-dsd@1.0.0","_nodeVersion":"25.9.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-CRO0jS1M7DAeePn8cSDxw41xHDRLe79bZkOGYdCvZkjpdkrMHXR2eVJ6YIge3NX21wM+RLrJuMCmoHVikQcgnA==","shasum":"563a2fee84014a84933fdf28ce05f4e207cc325a","tarball":"https://registry.npmjs.org/@audio/decode-dsd/-/decode-dsd-1.0.0.tgz","fileCount":8,"unpackedSize":30626,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFK4SUMGFoEnBz4hqFK2+pnUsQVM06X/75+aIvo/6gBqAiEA9bxhcsMJCmzlfnmdJaAOlCQkEipPrLMt6eq2aVLmUtE="}]},"_npmUser":{"name":"dy","email":"df.creative@gmail.com"},"directories":{},"maintainers":[{"name":"jamen","email":"jamenmarz@gmail.com"},{"name":"dfcreative","email":"df.creative@gmail.com"},{"name":"dy","email":"df.creative@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/decode-dsd_1.0.0_1787966879643_0.7548021756085068"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-29T01:27:59.451Z","1.0.0":"2026-08-29T01:27:59.788Z","modified":"2026-08-29T01:28:00.089Z"},"maintainers":[{"name":"jamen","email":"jamenmarz@gmail.com"},{"name":"dfcreative","email":"df.creative@gmail.com"},{"name":"dy","email":"df.creative@gmail.com"}],"description":"Decode DSD (DSF, DFF) audio to PCM samples","homepage":"https://github.com/audiojs/decode/tree/master/packages/decode-dsd#readme","keywords":["dsd","dsf","dff","dsdiff","sacd","audio","decode","decoder","pcm","sigma-delta"],"repository":{"type":"git","url":"git+https://github.com/audiojs/decode.git","directory":"packages/decode-dsd"},"author":{"name":"audiojs"},"bugs":{"url":"https://github.com/audiojs/decode/issues"},"license":"MIT","readme":"# @audio/decode-dsd [![npm](https://img.shields.io/npm/v/@audio/decode-dsd)](https://www.npmjs.com/package/@audio/decode-dsd) [![MIT](https://img.shields.io/badge/MIT-%E0%A5%90-white)](https://github.com/krishnized/license)\n\nDecode DSD (DSF, DFF) audio to PCM float samples.\n\n```\nnpm install @audio/decode-dsd\n```\n\n```js\nimport decode, { decoder } from '@audio/decode-dsd'\n```\n\nDSD is a 1-bit-per-sample sigma-delta stream running at 2.8224 MHz and up (\"DSD64\", \"DSD128\", …). Getting to PCM is a low-pass + decimate: a Kaiser-windowed-sinc FIR removes the noise-shaped quantization noise pushed above the audio band, then every Nth sample is kept. The first decimation stage (raw bits → 8× that rate) runs directly on the 1-bit stream using per-byte lookup tables — the classic trick from Sebastian Gesemann's [dsd2pcm](https://github.com/sdaau/dsd2pcm) (BSD), also used by ffmpeg's `dsd_lsbf_planar`/`dsd_msbf` decoders; this is an independent implementation of the same idea, not a port. A second, ordinary decimating FIR (on the now multi-bit float stream, much cheaper) reaches 44.1/88.2/176.4 kHz.\n\n```js\nlet { channelData, sampleRate } = decode(dsfOrDffBytes)\n\nlet dec = decoder({ sampleRate: 88200 })\nlet head = dec.decode(chunk1)   // synchronous; partial blocks carry over\nlet tail = dec.flush()\ndec.free()\n```\n\nBoth `decode()` and `decoder()` are synchronous — no WASM, no async setup.\n\n## Formats\n\n**DSF** (Sony DSD Stream File): `DSD `/`fmt `/`data` chunks, up to 5.1 channels, DSD64/128/256/512 sample rates, 1-bit LSB-first or MSB-first packing (per the `fmt` chunk's `bitsPerSample` field, exactly as the [spec](https://dsd-guide.com/sonys-dsf-file-format-spec) defines it), data interleaved in fixed 4096-byte-per-channel blocks. Trailing ID3v2 tag (pointed to by the `DSD ` chunk header) via `@audio/decode-dsd/meta`.\n\n**DFF** (Philips DSDIFF): `FRM8`/`FVER`/`PROP` (`FS`, `CHNL`, `CMPR`)/`DSD ` chunks, data interleaved byte-per-channel, always MSB-first. DST-compressed files (`CMPR` = `'DST '`) throw `Unsupported: DST` — DST is a separate lossless codec, not raw DSD. Edited-master chunks (`DIIN`/`DITI`/…) are ignored. A non-standard `ID3 ` chunk, if present, is also readable via `@audio/decode-dsd/meta`.\n\n## API\n\n### `decode(src, opts?): AudioData`\n\nDecode a complete `Uint8Array` or `ArrayBuffer`.\n\n### `decoder(opts?): DSDDecoder`\n\n- `dec.decode(data)`: decode a chunk. DSF needs a full `4096 × channels`-byte block group per channel before it can decode anything (that's the container's own interleave unit); DFF only needs one `channels`-byte frame.\n- `dec.flush()`: zero-pads a short final block to the container's own frame size (matching the DSF spec's own convention for a partial last block) and decodes it — it does not separately drain the FIR's group-delay tail.\n- `dec.free()`: release state. Idempotent.\n\n| Option | Default | |\n|---|---|---|\n| `sampleRate` | `dsdRate / 32` | Output rate. Must equal `dsdRate / 8`, `/16`, `/32`, or `/64` (DSD64 → 352.8k / 176.4k / 88.2k / 44.1k) — the byte-lookup-table stage only decimates in whole-byte (8-bit) steps. |\n| `cutoff` | `0.45` | Passband edge, as a fraction of the output rate, for whichever stage sets the final rate. |\n| `length` | computed | FIR taps for that stage. Longer = closer to the `stopband` target, slower. |\n| `stopband` | `100` | Target stopband attenuation (dB), used to derive the Kaiser β. |\n\n`AudioData` is `{ channelData: Float32Array[], sampleRate: number }`.\n\nDSD's 1-bit ±1 maps directly to PCM full scale — no gain change is applied. SACD material mastered at DSD's nominal \"0 dB\" can legitimately hit +6 dB relative to PCM full scale; this decoder does not clamp or renormalize, so such material will clip in the Float32 sense (values outside ±1) exactly as it would on any other bit-transparent DSD decoder.\n\n## Numbers\n\n- Bit-domain stage 1 (fixed): 192 taps (96-tap half + symmetry, 12-byte lookup tables), cutoff 0.45×(dsdRate/8), 100 dB target stopband.\n- Stage 2 (built per request): length computed from the Kaiser formula for a truly alias-free transition band (passband edge to the decimation's own mirror point) at the requested `stopband`.\n- Measured (2nd-order test tone through a properly-synthesized 5th-order 1-bit modulator, `test.js`): in-band SNR 114–117 dB at 44.1k/88.2k/176.4k (≥ 80 dB required, conservative under the modulator's own ~100+ dB theoretical ceiling — see `test.js` for the exact citation).\n- Cross-checked against `ffmpeg -i x.dsf -f f32le -ar 88200 -`: 1 kHz tone amplitude within 0.000 dB, phase within 1.6° after delay alignment, duration exact.\n- Speed: 60 s stereo DSD64 → 88.2 kHz decodes in ≈ 2.1 s (pure JS, single core).\n\n## License\n\n[ॐ](https://github.com/krishnized/license/) · [MIT](./LICENSE)\n\n**Use when:** you have raw DSD/SACD-rip material (`.dsf`/`.dff`) and need PCM — playback, format conversion, or feeding a conventional DSP chain. Not a DST decoder (DST-compressed DFF throws) and not a DSD *encoder*.\n\n---\n\nPart of [@audio/decode](https://github.com/audiojs/decode) — the decode family umbrella.\n\nMIT © [audiojs](https://github.com/audiojs)\n","readmeFilename":"README.md","_rev":"1-25c71d1ec43edc0f17c01a35502b774c"}