dss-codec
v0.1.2
Published
Browser and Node.js WASM decoder for Olympus DSS and DS2 audio, including encrypted DS2 decryption
Maintainers
Readme
dss-codec
dss-codec is a WASM-based decoder for Olympus .dss and .ds2 dictation audio files. It works in browser and Node.js environments and can decode encrypted DS2 input when a password is provided.
For codec internals and deeper technical background, see ext/dss-codec/README.md and ext/dss-codec/dss-codec/CODEC_SPECIFICATION.md.
Capabilities
| Input | Machine-readable format | Native rate | Notes |
|-------|----------------------------|-------------|-------|
| .dss | dss_sp | 11025 Hz | DSS SP |
| .ds2 standard-play | ds2_sp | 12000 Hz | DS2 SP |
| .ds2 quality-play | ds2_qp | 16000 Hz | DS2 QP |
| Encrypted .ds2 | ds2_sp or ds2_qp after inspection/decode | 12000 or 16000 Hz | Password required for decode or decrypt |
inspect(...)reportsformat,nativeRate, andencryption.encryptionis a stable machine-readable identifier such asnone,ds2_aes_128, ords2_aes_256.- Decoded audio exposed to JavaScript is mono
Float32ArrayPCM normalized to[-1.0, 1.0]. decrypt(...)anddecryptWithPassword(...)return plain container bytes. For encrypted DS2 input, this normalizes back to plain.ds2bytes.
Install
npm install dss-codec- Node.js
>=18is required. - The package ships entrypoints for bundlers, browser apps, direct browser module loading, and Node.js consumers.
Demo
The repository includes a plain HTML reference integration in examples/simple-html. It inspects DSS and DS2 files in the browser, prompts for a password when needed, decodes to PCM, builds a WAV for playback, and draws a waveform locally in the browser.
- Local demo:
npm run demo - Hosted demo: https://gaspardpetit.github.io/dss-codec-wasm/
- Standalone demo build:
npm run build:standalone
Quick Start
import { readFile } from "node:fs/promises";
import { decode, decodeWithPassword, inspect } from "dss-codec";
const bytes = new Uint8Array(await readFile("recording.ds2"));
const inspection = inspect(bytes);
try {
const password =
inspection.encryption === "none"
? undefined
: new TextEncoder().encode("1234");
const result = password
? decodeWithPassword(bytes, password)
: decode(bytes);
try {
console.log(result.format); // "dss_sp" | "ds2_sp" | "ds2_qp"
console.log(result.nativeRate); // 11025 | 12000 | 16000
console.log(result.samples); // Float32Array mono PCM in [-1, 1]
} finally {
result.free();
}
} finally {
inspection.free();
}Use inspect(...) first when the caller may receive a mix of plain and encrypted inputs. If the source is encrypted, pass password bytes to decodeWithPassword(...).
Entrypoints
dss-codec: default package entry for Node.js and most bundler/browser usage.dss-codec/web: browser-oriented entry with explicit asyncinit(...)control for WASM loading.dss-codec/wasm: public WASM asset export for toolchains that need the.wasmfile URL explicitly.
Bundlers And Workers
dss-codec/web already falls back to runtime-relative WASM loading with new URL("dss_codec_wasm_bg.wasm", import.meta.url). Some worker and bundler setups handle dependency assets more reliably when the WASM file is imported explicitly:
import init, { decode } from "dss-codec/web";
import wasmUrl from "dss-codec/wasm?url";
await init(wasmUrl);
const result = decode(fileBytes);
try {
console.log(result.nativeRate);
} finally {
result.free();
}For direct browser module usage without a bundler, import from dss-codec/web and await init(...) before calling the decode APIs.
API Overview
Inspection
inspect(data: Uint8Array) -> InspectResultisEncryptedDs2(data: Uint8Array) -> boolean
InspectResult exposes:
format: stringnativeRate: numberencryption: string
Full Decode
decode(data: Uint8Array) -> DecodeResultdecodeWithPassword(data: Uint8Array, password: Uint8Array) -> DecodeResult
DecodeResult exposes:
format: stringnativeRate: numbersamples: Float32Array
Container Decryption
decrypt(data: Uint8Array) -> Uint8ArraydecryptWithPassword(data: Uint8Array, password: Uint8Array) -> Uint8Array
These APIs return plain container bytes. They are useful when the caller needs normalized .ds2 bytes instead of decoded PCM.
Streaming Decode
new StreamDecoder()StreamDecoder.withPassword(password: Uint8Array)streamer.push(chunk: Uint8Array) -> Float32Arraystreamer.finish() -> Float32Arraystreamer.format -> string | undefinedstreamer.nativeRate -> number | undefined
import { StreamDecoder, inspect } from "dss-codec";
const header = fileChunks[0];
const inspection = inspect(header);
let streamer;
try {
streamer =
inspection.encryption === "none"
? new StreamDecoder()
: StreamDecoder.withPassword(new TextEncoder().encode("1234"));
} finally {
inspection.free();
}
const pcmChunks = [];
try {
for (const chunk of fileChunks) {
const samples = streamer.push(chunk);
if (samples.length > 0) {
pcmChunks.push(samples);
}
console.log(streamer.format); // may be undefined until enough input is buffered
console.log(streamer.nativeRate); // may be undefined until format detection completes
}
const tail = streamer.finish();
if (tail.length > 0) {
pcmChunks.push(tail);
}
} finally {
streamer.free();
}Returned Data And Integration Notes
DecodeResultandInspectResultare WASM-backed objects. Read their properties, then callfree()when finished.- Password-taking APIs expect password bytes, not a JavaScript string. Use
new TextEncoder().encode(passwordString)when needed. nativeRateis the source format's native sample rate. The package does not resample during JavaScript decode.- Decoded samples are always mono PCM. Convert or resample them in application code if a downstream system requires another layout.
inspect(...)is useful when deciding whether to calldecode(...)ordecodeWithPassword(...).
Development
Prerequisites:
- Rust with
wasm32-unknown-unknown wasm-bindgen-cli- Node.js 18+
- The
ext/dss-codecgit submodule checked out locally
Build package artifacts:
git submodule update --init --recursive
rustup target add wasm32-unknown-unknown
cargo install wasm-bindgen-cli
npm run buildThis generates:
dist/browserfor bundlers and browser appsdist/nodefor Node.js consumersdist/webfor direct browser module usage with explicitinit(...)
Verify the packed npm artifact as installed by consumers:
npm run test:packPublishing is tag-driven. The npm publish workflow uses Git tags such as v1.0.0 as the release version source of truth.
