{"_id":"@csnight/audio-recorder","_rev":"13-c638ae932bb25acdb96c0e81c50f4b50","name":"@csnight/audio-recorder","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.3":{"name":"@csnight/audio-recorder","version":"1.0.3","keywords":["audio-recorder","browser-audio-recorder","voice-recording","microphone-recorder","mediarecorder","webaudio","mediastream","pcm","wav","mp3","opus","flac","aac","amr","typescript","wasm"],"license":"SEE LICENSE IN LICENSE","_id":"@csnight/audio-recorder@1.0.3","maintainers":[{"name":"csnight","email":"chensi__chs@163.com"}],"homepage":"https://github.com/CSNight/audio-recorder#readme","bugs":{"url":"https://github.com/CSNight/audio-recorder/issues"},"dist":{"shasum":"719c870d8f0be74cc1cba616ea5406525399503d","tarball":"https://registry.npmjs.org/@csnight/audio-recorder/-/audio-recorder-1.0.3.tgz","fileCount":120,"integrity":"sha512-CYqK7d/Nuj5YsxZ/A7FNh2tB0VCwusHeIYlVZALPDGloKiHXLtU5xw+xb0yu7iPwRjwTDPvmJ7jG4Q80spZ0cQ==","signatures":[{"sig":"MEQCIDHbthdFhQQGbum3BliEJ7HnRMSTbVEhdMo+ilqmuAdHAiBhxCzfHKy2Mb6rvp4TV0vX6LHH6LRrqAsxgEafMNS+IA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2427100},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./codecs/aac":{"types":"./dist/codecs/aac/index.d.ts","import":"./dist/codecs/aac/index.js"},"./codecs/amr":{"types":"./dist/codecs/amr/index.d.ts","import":"./dist/codecs/amr/index.js"},"./codecs/mp3":{"types":"./dist/codecs/mp3/index.d.ts","import":"./dist/codecs/mp3/index.js"},"./codecs/base":{"types":"./dist/codecs/base/index.d.ts","import":"./dist/codecs/base/index.js"},"./codecs/flac":{"types":"./dist/codecs/flac/index.d.ts","import":"./dist/codecs/flac/index.js"},"./codecs/g711":{"types":"./dist/codecs/g711/index.d.ts","import":"./dist/codecs/g711/index.js"},"./codecs/opus":{"types":"./dist/codecs/opus/index.d.ts","import":"./dist/codecs/opus/index.js"},"./storage/opfs":{"types":"./dist/storage/opfs/index.d.ts","import":"./dist/storage/opfs/index.js"},"./storage/indexeddb":{"types":"./dist/storage/indexeddb/index.d.ts","import":"./dist/storage/indexeddb/index.js"},"./plugins/asr-export":{"types":"./dist/plugins/asr-export/index.d.ts","import":"./dist/plugins/asr-export/index.js"},"./plugins/level-meter":{"types":"./dist/plugins/level-meter/index.d.ts","import":"./dist/plugins/level-meter/index.js"},"./plugins/streaming-export":{"types":"./dist/plugins/streaming-export/index.d.ts","import":"./dist/plugins/streaming-export/index.js"},"./plugins/streaming-player":{"types":"./dist/plugins/streaming-player/index.d.ts","import":"./dist/plugins/streaming-player/index.js"}},"gitHead":"4ac75a1d45513a756f1d576b97bd5707a09b1bff","private":false,"scripts":{"dev":"vite","lint":"eslint . --fix","test":"npm run test:unit && npm run test:functional","build":"vite build && tsc -p tsconfig.build.json && npm run fix:dts","check":"npm run lint && npm run typecheck && npm run test && npm run build && npm run verify:exports","format":"prettier --write .","fix:dts":"node ./scripts/fix-dts-imports.mjs","prepare":"husky","preview":"vite preview","publish":"npm publish --access public --registry=https://registry.npmjs.org/","test:unit":"vitest run --coverage","typecheck":"tsc -p tsconfig.json --noEmit","build:wasm":"node ./scripts/wasm/build-docker.mjs --codec=all","commitlint":"commitlint --edit","dev:playground":"npm run build && vite --open /playground/","verify:exports":"node ./scripts/verify-exports.mjs","test:functional":"playwright test","benchmark:codecs":"node ./scripts/benchmark-codecs.mjs","build:wasm:select":"node ./scripts/wasm/build-docker.mjs","test:functional:headed":"playwright test --headed"},"_npmUser":{"name":"csnight","email":"chensi__chs@163.com"},"prettier":{"semi":false,"endOfLine":"lf","singleQuote":false,"cursorOffset":2,"trailingComma":"es5","bracketSpacing":true,"bracketSameLine":false},"repository":{"url":"git+https://github.com/CSNight/audio-recorder.git","type":"git"},"_npmVersion":"11.14.1","description":"TypeScript browser audio recorder for microphone and MediaStream input with PCM pipelines, plugins, streaming export, persistence, and WAV/MP3/Opus/FLAC/AAC/AMR output.","directories":{},"lint-staged":{"src/**/*.{ts,tsx,json,css,scss,md}":["npm lint"]},"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.12","husky":"^9.1.7","eslint":"^10.4.1","vitest":"^4.1.8","globals":"^17.6.0","prettier":"^3.8.3","@eslint/js":"^10.0.1","playwright":"^1.60.0","typescript":"~6.0.2","@commitlint/cli":"^21.0.2","@playwright/test":"^1.60.0","typescript-eslint":"^8.60.1","@vitest/coverage-v8":"^4.1.8","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@commitlint/config-conventional":"^21.0.2"},"_npmOperationalInternal":{"tmp":"tmp/audio-recorder_1.0.3_1782783923605_0.5420849605213525","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"1.0.4":{"name":"@csnight/audio-recorder","version":"1.0.4","keywords":["audio-recorder","browser-audio-recorder","voice-recording","microphone-recorder","mediarecorder","webaudio","mediastream","pcm","wav","mp3","opus","flac","aac","amr","typescript","wasm"],"license":"SEE LICENSE IN LICENSE","_id":"@csnight/audio-recorder@1.0.4","maintainers":[{"name":"csnight","email":"chensi__chs@163.com"}],"homepage":"https://github.com/CSNight/audio-recorder#readme","bugs":{"url":"https://github.com/CSNight/audio-recorder/issues"},"dist":{"shasum":"23086d3dbef41bc4f781891a6df1cdd876aaf43d","tarball":"https://registry.npmjs.org/@csnight/audio-recorder/-/audio-recorder-1.0.4.tgz","fileCount":122,"integrity":"sha512-PJmsWtzyW8palZ/zpsxpmmH9/0Tm6eMKq3/8Sbuf6/oUEP6EMRhsuB9puA/ZUELmtXLlSk/ZxG2inxCIRZYxWg==","signatures":[{"sig":"MEQCICkt5fR0b0blcvq0D3rzw9T6vqjI4Hhdbm1lR0iQz/uPAiAjQFohdM1GFnnBQkl7t2q91qnzMTACQ4yO+IskBK+QhQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2481367},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./codecs/aac":{"types":"./dist/codecs/aac/index.d.ts","import":"./dist/codecs/aac/index.js"},"./codecs/amr":{"types":"./dist/codecs/amr/index.d.ts","import":"./dist/codecs/amr/index.js"},"./codecs/mp3":{"types":"./dist/codecs/mp3/index.d.ts","import":"./dist/codecs/mp3/index.js"},"./codecs/base":{"types":"./dist/codecs/base/index.d.ts","import":"./dist/codecs/base/index.js"},"./codecs/flac":{"types":"./dist/codecs/flac/index.d.ts","import":"./dist/codecs/flac/index.js"},"./codecs/g711":{"types":"./dist/codecs/g711/index.d.ts","import":"./dist/codecs/g711/index.js"},"./codecs/opus":{"types":"./dist/codecs/opus/index.d.ts","import":"./dist/codecs/opus/index.js"},"./storage/opfs":{"types":"./dist/storage/opfs/index.d.ts","import":"./dist/storage/opfs/index.js"},"./storage/indexeddb":{"types":"./dist/storage/indexeddb/index.d.ts","import":"./dist/storage/indexeddb/index.js"},"./plugins/asr-export":{"types":"./dist/plugins/asr-export/index.d.ts","import":"./dist/plugins/asr-export/index.js"},"./plugins/level-meter":{"types":"./dist/plugins/level-meter/index.d.ts","import":"./dist/plugins/level-meter/index.js"},"./plugins/streaming-export":{"types":"./dist/plugins/streaming-export/index.d.ts","import":"./dist/plugins/streaming-export/index.js"},"./plugins/streaming-player":{"types":"./dist/plugins/streaming-player/index.d.ts","import":"./dist/plugins/streaming-player/index.js"}},"gitHead":"0056296351466ed69e54ba1eb7b1a3d9a9c815bc","private":false,"scripts":{"lint":"eslint . --fix","test":"npm run test:unit && npm run test:functional","build":"vite build && tsc -p tsconfig.build.json","check":"npm run lint && npm run typecheck && npm run test && npm run build && npm run verify:exports","format":"prettier --write .","prepare":"husky","publish":"npm publish --access public --registry=https://registry.npmjs.org/","test:unit":"vitest run --coverage","typecheck":"tsc -p tsconfig.json --noEmit","build:wasm":"node ./scripts/wasm/build-docker.mjs --codec=all","commitlint":"commitlint --edit","verify:exports":"node ./scripts/verify-exports.mjs","test:functional":"playwright test","benchmark:codecs":"node ./scripts/benchmark-codecs.mjs","build:wasm:select":"node ./scripts/wasm/build-docker.mjs","test:functional:headed":"playwright test --headed"},"_npmUser":{"name":"csnight","email":"chensi__chs@163.com"},"prettier":{"semi":false,"endOfLine":"lf","singleQuote":false,"cursorOffset":2,"trailingComma":"es5","bracketSpacing":true,"bracketSameLine":false},"repository":{"url":"git+https://github.com/CSNight/audio-recorder.git","type":"git"},"_npmVersion":"11.14.1","description":"TypeScript browser audio recorder for microphone and MediaStream input with PCM pipelines, plugins, streaming export, persistence, and WAV/MP3/Opus/FLAC/AAC/AMR output.","directories":{},"lint-staged":{"src/**/*.{ts,tsx,json,css,scss,md}":["npm lint"]},"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.12","husky":"^9.1.7","eslint":"^10.4.1","vitest":"^4.1.8","globals":"^17.6.0","prettier":"^3.8.3","@eslint/js":"^10.0.1","playwright":"^1.60.0","typescript":"~6.0.2","@commitlint/cli":"^21.0.2","@playwright/test":"^1.60.0","typescript-eslint":"^8.60.1","@vitest/coverage-v8":"^4.1.8","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@commitlint/config-conventional":"^21.0.2"},"_npmOperationalInternal":{"tmp":"tmp/audio-recorder_1.0.4_1783046531928_0.5944953535795476","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"1.0.5":{"name":"@csnight/audio-recorder","version":"1.0.5","keywords":["audio-recorder","browser-audio-recorder","voice-recording","microphone-recorder","mediarecorder","webaudio","mediastream","pcm","wav","mp3","opus","flac","aac","amr","ac3","typescript","wasm"],"license":"SEE LICENSE IN LICENSE","_id":"@csnight/audio-recorder@1.0.5","maintainers":[{"name":"csnight","email":"chensi__chs@163.com"}],"homepage":"https://github.com/CSNight/audio-recorder#readme","bugs":{"url":"https://github.com/CSNight/audio-recorder/issues"},"dist":{"shasum":"2c4dfe93b3a17324418b32447c420807d7fb75b3","tarball":"https://registry.npmjs.org/@csnight/audio-recorder/-/audio-recorder-1.0.5.tgz","fileCount":172,"integrity":"sha512-7gszZ8RRdoy2R51PcdUPmR8ruprmOIKb3xh1Fm2NJ9Z09ofMh2tNaArLsaXl5TmuFbWVdjlUQvFORUid33i3mA==","signatures":[{"sig":"MEUCIEVLeo6JSbpfOxDC0oQjVsYIOc/XbS0BEvucje4WcZV3AiEAv34K4EjQjDc5aI69VrblW8PwZ5KnJe1moC8JVcaGpcM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3256174},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./codecs/aac":{"types":"./dist/codecs/aac/index.d.ts","import":"./dist/codecs/aac/index.js"},"./codecs/ac3":{"types":"./dist/codecs/ac3/index.d.ts","import":"./dist/codecs/ac3/index.js"},"./codecs/amr":{"types":"./dist/codecs/amr/index.d.ts","import":"./dist/codecs/amr/index.js"},"./codecs/mp3":{"types":"./dist/codecs/mp3/index.d.ts","import":"./dist/codecs/mp3/index.js"},"./codecs/base":{"types":"./dist/codecs/base/index.d.ts","import":"./dist/codecs/base/index.js"},"./codecs/flac":{"types":"./dist/codecs/flac/index.d.ts","import":"./dist/codecs/flac/index.js"},"./codecs/g711":{"types":"./dist/codecs/g711/index.d.ts","import":"./dist/codecs/g711/index.js"},"./codecs/opus":{"types":"./dist/codecs/opus/index.d.ts","import":"./dist/codecs/opus/index.js"},"./plugins/dsp":{"types":"./dist/plugins/dsp/index.d.ts","import":"./dist/plugins/dsp/index.js"},"./plugins/dtmf":{"types":"./dist/plugins/dtmf/index.d.ts","import":"./dist/plugins/dtmf/index.js"},"./storage/opfs":{"types":"./dist/storage/opfs/index.d.ts","import":"./dist/storage/opfs/index.js"},"./plugins/nmn2pcm":{"types":"./dist/plugins/nmn2pcm/index.d.ts","import":"./dist/plugins/nmn2pcm/index.js"},"./storage/indexeddb":{"types":"./dist/storage/indexeddb/index.d.ts","import":"./dist/storage/indexeddb/index.js"},"./plugins/asr-export":{"types":"./dist/plugins/asr-export/index.d.ts","import":"./dist/plugins/asr-export/index.js"},"./plugins/level-meter":{"types":"./dist/plugins/level-meter/index.d.ts","import":"./dist/plugins/level-meter/index.js"},"./plugins/sonic-export":{"types":"./dist/plugins/sonic-export/index.d.ts","import":"./dist/plugins/sonic-export/index.js"},"./plugins/streaming-export":{"types":"./dist/plugins/streaming-export/index.d.ts","import":"./dist/plugins/streaming-export/index.js"},"./plugins/streaming-player":{"types":"./dist/plugins/streaming-player/index.d.ts","import":"./dist/plugins/streaming-player/index.js"},"./plugins/frequency-histogram":{"types":"./dist/plugins/frequency-histogram/index.d.ts","import":"./dist/plugins/frequency-histogram/index.js"}},"gitHead":"feaf6ee0a6807e97e3378ef157493ab9f5007d8d","private":false,"scripts":{"lint":"eslint . --fix","test":"npm run test:unit && npm run test:functional","build":"vite build && tsc -p tsconfig.build.json","check":"npm run lint && npm run typecheck && npm run test && npm run build && npm run verify:exports","format":"prettier --write .","prepare":"husky","publish":"npm publish --access public --registry=https://registry.npmjs.org/","test:unit":"vitest run --coverage","typecheck":"tsc -p tsconfig.json --noEmit","build:wasm":"node ./scripts/wasm/build-docker.mjs --codec=all","commitlint":"commitlint --edit","verify:exports":"node ./scripts/verify-exports.mjs","test:functional":"playwright test","benchmark:codecs":"node ./scripts/benchmark-codecs.mjs","build:wasm:select":"node ./scripts/wasm/build-docker.mjs","test:functional:headed":"playwright test --headed"},"_npmUser":{"name":"csnight","email":"chensi__chs@163.com"},"prettier":{"semi":false,"endOfLine":"lf","singleQuote":false,"cursorOffset":2,"trailingComma":"es5","bracketSpacing":true,"bracketSameLine":false},"repository":{"url":"git+https://github.com/CSNight/audio-recorder.git","type":"git"},"_npmVersion":"11.12.1","description":"TypeScript browser audio recorder for microphone and MediaStream input with PCM pipelines, plugins, streaming export, persistence, and WAV/MP3/Opus/FLAC/AAC/AMR/AC3 output.","directories":{},"lint-staged":{"src/**/*.{ts,tsx,json,css,scss,md}":["npm lint"]},"_nodeVersion":"25.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.12","husky":"^9.1.7","eslint":"^10.4.1","vitest":"^4.1.8","globals":"^17.6.0","prettier":"^3.8.3","@eslint/js":"^10.0.1","playwright":"^1.60.0","typescript":"~6.0.2","@commitlint/cli":"^21.0.2","@playwright/test":"^1.60.0","typescript-eslint":"^8.60.1","@vitest/coverage-v8":"^4.1.8","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@commitlint/config-conventional":"^21.0.2"},"_npmOperationalInternal":{"tmp":"tmp/audio-recorder_1.0.5_1783252946321_0.5650527123626203","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"1.0.6":{"name":"@csnight/audio-recorder","version":"1.0.6","keywords":["audio-recorder","browser-audio-recorder","voice-recording","microphone-recorder","mediarecorder","webaudio","mediastream","pcm","wav","mp3","opus","flac","aac","amr","ac3","typescript","wasm"],"license":"SEE LICENSE IN LICENSE","_id":"@csnight/audio-recorder@1.0.6","maintainers":[{"name":"csnight","email":"chensi__chs@163.com"}],"homepage":"https://github.com/CSNight/audio-recorder#readme","bugs":{"url":"https://github.com/CSNight/audio-recorder/issues"},"dist":{"shasum":"7fbce4049249e2f44bade658b3b90a0e5620ef6c","tarball":"https://registry.npmjs.org/@csnight/audio-recorder/-/audio-recorder-1.0.6.tgz","fileCount":172,"integrity":"sha512-YqEM2P5xbQzr1MhQ6TBKlC5awZ4GFEU0ubdp8XBZL+A2pTjq2gttO6VWb12l+JsgU2tr9gLYvaR5jPqDiARsjQ==","signatures":[{"sig":"MEUCICa2c7lyd2IXFqjKsEr+CxuS4UlhkCCPlQKsnnrmqCPOAiEAv7bULE6y+nZoG9un+NLIDv8WPN9nqqS06kthrXbXsBg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3257840},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./codecs/aac":{"types":"./dist/codecs/aac/index.d.ts","import":"./dist/codecs/aac/index.js"},"./codecs/ac3":{"types":"./dist/codecs/ac3/index.d.ts","import":"./dist/codecs/ac3/index.js"},"./codecs/amr":{"types":"./dist/codecs/amr/index.d.ts","import":"./dist/codecs/amr/index.js"},"./codecs/mp3":{"types":"./dist/codecs/mp3/index.d.ts","import":"./dist/codecs/mp3/index.js"},"./codecs/base":{"types":"./dist/codecs/base/index.d.ts","import":"./dist/codecs/base/index.js"},"./codecs/flac":{"types":"./dist/codecs/flac/index.d.ts","import":"./dist/codecs/flac/index.js"},"./codecs/g711":{"types":"./dist/codecs/g711/index.d.ts","import":"./dist/codecs/g711/index.js"},"./codecs/opus":{"types":"./dist/codecs/opus/index.d.ts","import":"./dist/codecs/opus/index.js"},"./plugins/dsp":{"types":"./dist/plugins/dsp/index.d.ts","import":"./dist/plugins/dsp/index.js"},"./plugins/dtmf":{"types":"./dist/plugins/dtmf/index.d.ts","import":"./dist/plugins/dtmf/index.js"},"./storage/opfs":{"types":"./dist/storage/opfs/index.d.ts","import":"./dist/storage/opfs/index.js"},"./plugins/nmn2pcm":{"types":"./dist/plugins/nmn2pcm/index.d.ts","import":"./dist/plugins/nmn2pcm/index.js"},"./storage/indexeddb":{"types":"./dist/storage/indexeddb/index.d.ts","import":"./dist/storage/indexeddb/index.js"},"./plugins/asr-export":{"types":"./dist/plugins/asr-export/index.d.ts","import":"./dist/plugins/asr-export/index.js"},"./plugins/level-meter":{"types":"./dist/plugins/level-meter/index.d.ts","import":"./dist/plugins/level-meter/index.js"},"./plugins/sonic-export":{"types":"./dist/plugins/sonic-export/index.d.ts","import":"./dist/plugins/sonic-export/index.js"},"./plugins/streaming-export":{"types":"./dist/plugins/streaming-export/index.d.ts","import":"./dist/plugins/streaming-export/index.js"},"./plugins/streaming-player":{"types":"./dist/plugins/streaming-player/index.d.ts","import":"./dist/plugins/streaming-player/index.js"},"./plugins/frequency-histogram":{"types":"./dist/plugins/frequency-histogram/index.d.ts","import":"./dist/plugins/frequency-histogram/index.js"}},"gitHead":"b4973216732e1f2e8f5942aab6b67b47611c5f65","private":false,"scripts":{"lint":"eslint . --fix","test":"npm run test:unit && npm run test:functional","build":"vite build && tsc -p tsconfig.build.json","check":"npm run lint && npm run typecheck && npm run test && npm run build && npm run verify:exports","format":"prettier --write .","prepare":"husky","publish":"npm publish --access public --registry=https://registry.npmjs.org/","test:unit":"vitest run --coverage","typecheck":"tsc -p tsconfig.json --noEmit","build:wasm":"node ./scripts/wasm/build-docker.mjs --codec=all","commitlint":"commitlint --edit","verify:exports":"node ./scripts/verify-exports.mjs","test:functional":"playwright test","benchmark:codecs":"node ./scripts/benchmark-codecs.mjs","build:wasm:select":"node ./scripts/wasm/build-docker.mjs","test:functional:headed":"playwright test --headed"},"_npmUser":{"name":"csnight","email":"chensi__chs@163.com"},"prettier":{"semi":false,"endOfLine":"lf","singleQuote":false,"cursorOffset":2,"trailingComma":"es5","bracketSpacing":true,"bracketSameLine":false},"repository":{"url":"git+https://github.com/CSNight/audio-recorder.git","type":"git"},"_npmVersion":"11.12.1","description":"TypeScript browser audio recorder for microphone and MediaStream input with PCM pipelines, plugins, streaming export, persistence, and WAV/MP3/Opus/FLAC/AAC/AMR/AC3 output.","directories":{},"lint-staged":{"src/**/*.{ts,tsx,json,css,scss,md}":["npm lint"]},"_nodeVersion":"25.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.12","husky":"^9.1.7","eslint":"^10.4.1","vitest":"^4.1.8","globals":"^17.6.0","prettier":"^3.8.3","@eslint/js":"^10.0.1","playwright":"^1.60.0","typescript":"~6.0.2","@commitlint/cli":"^21.0.2","@playwright/test":"^1.60.0","typescript-eslint":"^8.60.1","@vitest/coverage-v8":"^4.1.8","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@commitlint/config-conventional":"^21.0.2"},"_npmOperationalInternal":{"tmp":"tmp/audio-recorder_1.0.6_1783348523209_0.813540473977415","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"1.0.7":{"name":"@csnight/audio-recorder","version":"1.0.7","keywords":["audio-recorder","browser-audio-recorder","voice-recording","microphone-recorder","mediarecorder","webaudio","mediastream","pcm","wav","mp3","opus","flac","aac","amr","ac3","typescript","wasm"],"license":"SEE LICENSE IN LICENSE","_id":"@csnight/audio-recorder@1.0.7","maintainers":[{"name":"csnight","email":"chensi__chs@163.com"}],"homepage":"https://github.com/CSNight/audio-recorder#readme","bugs":{"url":"https://github.com/CSNight/audio-recorder/issues"},"dist":{"shasum":"7b2a03e8371ba5d346d8ddfce537ef4f3e985092","tarball":"https://registry.npmjs.org/@csnight/audio-recorder/-/audio-recorder-1.0.7.tgz","fileCount":172,"integrity":"sha512-tbNLRLLNc79/buY61amiqi8AC1EetjwsdfLJyFMDF35X5O6fKi1a4YbzAAa+8CyC5IQRXKpPKK6BMyAlyTdU1Q==","signatures":[{"sig":"MEQCIAahMTqz1M5JeUixZ7BCRjwPubP1Nabk3dM576aiQVXDAiBx2S+5NvorcARviH4mQ4oxFyB2fG28uwRFP5vtX1BXfA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3258731},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./codecs/aac":{"types":"./dist/codecs/aac/index.d.ts","import":"./dist/codecs/aac/index.js"},"./codecs/ac3":{"types":"./dist/codecs/ac3/index.d.ts","import":"./dist/codecs/ac3/index.js"},"./codecs/amr":{"types":"./dist/codecs/amr/index.d.ts","import":"./dist/codecs/amr/index.js"},"./codecs/mp3":{"types":"./dist/codecs/mp3/index.d.ts","import":"./dist/codecs/mp3/index.js"},"./codecs/base":{"types":"./dist/codecs/base/index.d.ts","import":"./dist/codecs/base/index.js"},"./codecs/flac":{"types":"./dist/codecs/flac/index.d.ts","import":"./dist/codecs/flac/index.js"},"./codecs/g711":{"types":"./dist/codecs/g711/index.d.ts","import":"./dist/codecs/g711/index.js"},"./codecs/opus":{"types":"./dist/codecs/opus/index.d.ts","import":"./dist/codecs/opus/index.js"},"./plugins/dsp":{"types":"./dist/plugins/dsp/index.d.ts","import":"./dist/plugins/dsp/index.js"},"./plugins/dtmf":{"types":"./dist/plugins/dtmf/index.d.ts","import":"./dist/plugins/dtmf/index.js"},"./storage/opfs":{"types":"./dist/storage/opfs/index.d.ts","import":"./dist/storage/opfs/index.js"},"./plugins/nmn2pcm":{"types":"./dist/plugins/nmn2pcm/index.d.ts","import":"./dist/plugins/nmn2pcm/index.js"},"./storage/indexeddb":{"types":"./dist/storage/indexeddb/index.d.ts","import":"./dist/storage/indexeddb/index.js"},"./plugins/asr-export":{"types":"./dist/plugins/asr-export/index.d.ts","import":"./dist/plugins/asr-export/index.js"},"./plugins/level-meter":{"types":"./dist/plugins/level-meter/index.d.ts","import":"./dist/plugins/level-meter/index.js"},"./plugins/sonic-export":{"types":"./dist/plugins/sonic-export/index.d.ts","import":"./dist/plugins/sonic-export/index.js"},"./plugins/streaming-export":{"types":"./dist/plugins/streaming-export/index.d.ts","import":"./dist/plugins/streaming-export/index.js"},"./plugins/streaming-player":{"types":"./dist/plugins/streaming-player/index.d.ts","import":"./dist/plugins/streaming-player/index.js"},"./plugins/frequency-histogram":{"types":"./dist/plugins/frequency-histogram/index.d.ts","import":"./dist/plugins/frequency-histogram/index.js"}},"gitHead":"40fa8c3ac23e34c83c34e5a4cf777048455bd77f","private":false,"scripts":{"lint":"eslint . --fix","test":"npm run test:unit && npm run test:functional","build":"vite build && tsc -p tsconfig.build.json","check":"npm run lint && npm run typecheck && npm run test && npm run build && npm run verify:exports","format":"prettier --write .","prepare":"husky","publish":"npm run build && npm publish --access public --registry=https://registry.npmjs.org/","test:unit":"vitest run --coverage","typecheck":"tsc -p tsconfig.json --noEmit","build:wasm":"node ./scripts/wasm/build-docker.mjs --codec=all","commitlint":"commitlint --edit","verify:exports":"node ./scripts/verify-exports.mjs","test:functional":"playwright test","benchmark:codecs":"node ./scripts/benchmark-codecs.mjs","build:wasm:select":"node ./scripts/wasm/build-docker.mjs","test:functional:headed":"playwright test --headed"},"_npmUser":{"name":"csnight","email":"chensi__chs@163.com"},"prettier":{"semi":false,"endOfLine":"lf","singleQuote":false,"cursorOffset":2,"trailingComma":"es5","bracketSpacing":true,"bracketSameLine":false},"repository":{"url":"git+https://github.com/CSNight/audio-recorder.git","type":"git"},"_npmVersion":"11.12.1","description":"TypeScript browser audio recorder for microphone and MediaStream input with PCM pipelines, plugins, streaming export, persistence, and WAV/MP3/Opus/FLAC/AAC/AMR/AC3 output.","directories":{},"lint-staged":{"src/**/*.{ts,tsx,json,css,scss,md}":["npm lint"]},"_nodeVersion":"25.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.12","husky":"^9.1.7","eslint":"^10.4.1","vitest":"^4.1.8","globals":"^17.6.0","prettier":"^3.8.3","@eslint/js":"^10.0.1","playwright":"^1.60.0","typescript":"~6.0.2","@commitlint/cli":"^21.0.2","@playwright/test":"^1.60.0","typescript-eslint":"^8.60.1","@vitest/coverage-v8":"^4.1.8","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@commitlint/config-conventional":"^21.0.2"},"_npmOperationalInternal":{"tmp":"tmp/audio-recorder_1.0.7_1783348674135_0.12432233722344121","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"1.0.8":{"name":"@csnight/audio-recorder","version":"1.0.8","keywords":["audio-recorder","browser-audio-recorder","voice-recording","microphone-recorder","mediarecorder","webaudio","mediastream","pcm","wav","mp3","opus","flac","aac","amr","ac3","typescript","wasm"],"license":"SEE LICENSE IN LICENSE","_id":"@csnight/audio-recorder@1.0.8","maintainers":[{"name":"csnight","email":"chensi__chs@163.com"}],"homepage":"https://github.com/CSNight/audio-recorder#readme","bugs":{"url":"https://github.com/CSNight/audio-recorder/issues"},"dist":{"shasum":"87dc21c62c160693f608f9179778606d2458b411","tarball":"https://registry.npmjs.org/@csnight/audio-recorder/-/audio-recorder-1.0.8.tgz","fileCount":172,"integrity":"sha512-st37++GpJ08J7nqlfT46DculsfhnrnC1XhePLOqc5fNm0/3JMzs7MXkpo5ZrUx5NmurvAB/qFpXqU4OfbbKUxQ==","signatures":[{"sig":"MEUCIGHKpUDTQnSQauLeFB2jRNiLYyFauvJ993occmnFhptMAiEAoJk+sWbx4e0p8QlNzlYNX7rEhp3kJH29MIZ9soicwbI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3258982},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./codecs/aac":{"types":"./dist/codecs/aac/index.d.ts","import":"./dist/codecs/aac/index.js"},"./codecs/ac3":{"types":"./dist/codecs/ac3/index.d.ts","import":"./dist/codecs/ac3/index.js"},"./codecs/amr":{"types":"./dist/codecs/amr/index.d.ts","import":"./dist/codecs/amr/index.js"},"./codecs/mp3":{"types":"./dist/codecs/mp3/index.d.ts","import":"./dist/codecs/mp3/index.js"},"./codecs/base":{"types":"./dist/codecs/base/index.d.ts","import":"./dist/codecs/base/index.js"},"./codecs/flac":{"types":"./dist/codecs/flac/index.d.ts","import":"./dist/codecs/flac/index.js"},"./codecs/g711":{"types":"./dist/codecs/g711/index.d.ts","import":"./dist/codecs/g711/index.js"},"./codecs/opus":{"types":"./dist/codecs/opus/index.d.ts","import":"./dist/codecs/opus/index.js"},"./plugins/dsp":{"types":"./dist/plugins/dsp/index.d.ts","import":"./dist/plugins/dsp/index.js"},"./plugins/dtmf":{"types":"./dist/plugins/dtmf/index.d.ts","import":"./dist/plugins/dtmf/index.js"},"./storage/opfs":{"types":"./dist/storage/opfs/index.d.ts","import":"./dist/storage/opfs/index.js"},"./plugins/nmn2pcm":{"types":"./dist/plugins/nmn2pcm/index.d.ts","import":"./dist/plugins/nmn2pcm/index.js"},"./storage/indexeddb":{"types":"./dist/storage/indexeddb/index.d.ts","import":"./dist/storage/indexeddb/index.js"},"./plugins/asr-export":{"types":"./dist/plugins/asr-export/index.d.ts","import":"./dist/plugins/asr-export/index.js"},"./plugins/level-meter":{"types":"./dist/plugins/level-meter/index.d.ts","import":"./dist/plugins/level-meter/index.js"},"./plugins/sonic-export":{"types":"./dist/plugins/sonic-export/index.d.ts","import":"./dist/plugins/sonic-export/index.js"},"./plugins/streaming-export":{"types":"./dist/plugins/streaming-export/index.d.ts","import":"./dist/plugins/streaming-export/index.js"},"./plugins/streaming-player":{"types":"./dist/plugins/streaming-player/index.d.ts","import":"./dist/plugins/streaming-player/index.js"},"./plugins/frequency-histogram":{"types":"./dist/plugins/frequency-histogram/index.d.ts","import":"./dist/plugins/frequency-histogram/index.js"}},"gitHead":"0ebb32ec7149202acac7465be7e39d0ab0ab9bc0","private":false,"scripts":{"lint":"eslint . --fix","test":"npm run test:unit && npm run test:functional","build":"vite build && tsc -p tsconfig.build.json","check":"npm run lint && npm run typecheck && npm run test && npm run build && npm run verify:exports","format":"prettier --write .","prepare":"husky","publish":"npm run build && npm publish --access public --registry=https://registry.npmjs.org/","test:unit":"vitest run --coverage","typecheck":"tsc -p tsconfig.json --noEmit","build:wasm":"node ./scripts/wasm/build-docker.mjs --codec=all","commitlint":"commitlint --edit","verify:exports":"node ./scripts/verify-exports.mjs","test:functional":"playwright test","benchmark:codecs":"node ./scripts/benchmark-codecs.mjs","build:wasm:select":"node ./scripts/wasm/build-docker.mjs","test:functional:headed":"playwright test --headed"},"_npmUser":{"name":"csnight","email":"chensi__chs@163.com"},"prettier":{"semi":false,"endOfLine":"lf","singleQuote":false,"cursorOffset":2,"trailingComma":"es5","bracketSpacing":true,"bracketSameLine":false},"repository":{"url":"git+https://github.com/CSNight/audio-recorder.git","type":"git"},"_npmVersion":"11.12.1","description":"TypeScript browser audio recorder for microphone and MediaStream input with PCM pipelines, plugins, streaming export, persistence, and WAV/MP3/Opus/FLAC/AAC/AMR/AC3 output.","directories":{},"lint-staged":{"src/**/*.{ts,tsx,json,css,scss,md}":["npm lint"]},"_nodeVersion":"25.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.12","husky":"^9.1.7","eslint":"^10.4.1","vitest":"^4.1.8","globals":"^17.6.0","prettier":"^3.8.3","@eslint/js":"^10.0.1","playwright":"^1.60.0","typescript":"~6.0.2","@commitlint/cli":"^21.0.2","@playwright/test":"^1.60.0","typescript-eslint":"^8.60.1","@vitest/coverage-v8":"^4.1.8","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@commitlint/config-conventional":"^21.0.2"},"_npmOperationalInternal":{"tmp":"tmp/audio-recorder_1.0.8_1783349308478_0.9398498571318683","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"1.0.9":{"name":"@csnight/audio-recorder","version":"1.0.9","keywords":["audio-recorder","browser-audio-recorder","voice-recording","microphone-recorder","mediarecorder","webaudio","mediastream","pcm","wav","mp3","opus","flac","aac","amr","ac3","typescript","wasm"],"license":"SEE LICENSE IN LICENSE","_id":"@csnight/audio-recorder@1.0.9","maintainers":[{"name":"csnight","email":"chensi__chs@163.com"}],"homepage":"https://github.com/CSNight/audio-recorder#readme","bugs":{"url":"https://github.com/CSNight/audio-recorder/issues"},"dist":{"shasum":"acfda3f3ddac86a4ef417c824f5d6fa0e0b138cc","tarball":"https://registry.npmjs.org/@csnight/audio-recorder/-/audio-recorder-1.0.9.tgz","fileCount":172,"integrity":"sha512-beWLwcC7vbEMo4qW2GuxIe1iRg+Nx3ifFwoLgkK72X6foPcEXuYADQ+1fuz643haAN/jlEafyl19vBnO8JO/VA==","signatures":[{"sig":"MEUCIQCCFN/llHu3tcL3EvrJPg45aIwOC6ZM+c3+zFtW5J/UDwIgUZO16usbSwFJlw0BuaMvNJSgL38ePcp9+LGCFtel1Yg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3265721},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./codecs/aac":{"types":"./dist/codecs/aac/index.d.ts","import":"./dist/codecs/aac/index.js"},"./codecs/ac3":{"types":"./dist/codecs/ac3/index.d.ts","import":"./dist/codecs/ac3/index.js"},"./codecs/amr":{"types":"./dist/codecs/amr/index.d.ts","import":"./dist/codecs/amr/index.js"},"./codecs/mp3":{"types":"./dist/codecs/mp3/index.d.ts","import":"./dist/codecs/mp3/index.js"},"./codecs/base":{"types":"./dist/codecs/base/index.d.ts","import":"./dist/codecs/base/index.js"},"./codecs/flac":{"types":"./dist/codecs/flac/index.d.ts","import":"./dist/codecs/flac/index.js"},"./codecs/g711":{"types":"./dist/codecs/g711/index.d.ts","import":"./dist/codecs/g711/index.js"},"./codecs/opus":{"types":"./dist/codecs/opus/index.d.ts","import":"./dist/codecs/opus/index.js"},"./plugins/dsp":{"types":"./dist/plugins/dsp/index.d.ts","import":"./dist/plugins/dsp/index.js"},"./plugins/dtmf":{"types":"./dist/plugins/dtmf/index.d.ts","import":"./dist/plugins/dtmf/index.js"},"./storage/opfs":{"types":"./dist/storage/opfs/index.d.ts","import":"./dist/storage/opfs/index.js"},"./plugins/nmn2pcm":{"types":"./dist/plugins/nmn2pcm/index.d.ts","import":"./dist/plugins/nmn2pcm/index.js"},"./storage/indexeddb":{"types":"./dist/storage/indexeddb/index.d.ts","import":"./dist/storage/indexeddb/index.js"},"./plugins/asr-export":{"types":"./dist/plugins/asr-export/index.d.ts","import":"./dist/plugins/asr-export/index.js"},"./plugins/level-meter":{"types":"./dist/plugins/level-meter/index.d.ts","import":"./dist/plugins/level-meter/index.js"},"./plugins/sonic-export":{"types":"./dist/plugins/sonic-export/index.d.ts","import":"./dist/plugins/sonic-export/index.js"},"./plugins/streaming-export":{"types":"./dist/plugins/streaming-export/index.d.ts","import":"./dist/plugins/streaming-export/index.js"},"./plugins/streaming-player":{"types":"./dist/plugins/streaming-player/index.d.ts","import":"./dist/plugins/streaming-player/index.js"},"./plugins/frequency-histogram":{"types":"./dist/plugins/frequency-histogram/index.d.ts","import":"./dist/plugins/frequency-histogram/index.js"}},"gitHead":"389c854e7dbb38d88b6ad2dc6ff3e9457396ecd7","private":false,"scripts":{"lint":"eslint . --fix","test":"npm run test:unit && npm run test:functional","build":"vite build && tsc -p tsconfig.build.json","check":"npm run lint && npm run typecheck && npm run test && npm run build && npm run verify:exports","format":"prettier --write .","prepare":"husky","publish":"npm run build && npm publish --access public --registry=https://registry.npmjs.org/","test:unit":"vitest run --coverage","typecheck":"tsc -p tsconfig.json --noEmit","build:wasm":"node ./scripts/wasm/build-docker.mjs --codec=all","commitlint":"commitlint --edit","verify:exports":"node ./scripts/verify-exports.mjs","test:functional":"playwright test","benchmark:codecs":"node ./scripts/benchmark-codecs.mjs","build:wasm:select":"node ./scripts/wasm/build-docker.mjs","test:functional:headed":"playwright test --headed"},"_npmUser":{"name":"csnight","email":"chensi__chs@163.com"},"prettier":{"semi":false,"endOfLine":"lf","singleQuote":false,"cursorOffset":2,"trailingComma":"es5","bracketSpacing":true,"bracketSameLine":false},"repository":{"url":"git+https://github.com/CSNight/audio-recorder.git","type":"git"},"_npmVersion":"11.14.1","description":"TypeScript browser audio recorder for microphone and MediaStream input with PCM pipelines, plugins, streaming export, persistence, and WAV/MP3/Opus/FLAC/AAC/AMR/AC3 output.","directories":{},"lint-staged":{"src/**/*.{ts,tsx,json,css,scss,md}":["npm lint"]},"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.12","husky":"^9.1.7","eslint":"^10.4.1","vitest":"^4.1.8","globals":"^17.6.0","prettier":"^3.8.3","@eslint/js":"^10.0.1","playwright":"^1.60.0","typescript":"~6.0.2","@commitlint/cli":"^21.0.2","@playwright/test":"^1.60.0","typescript-eslint":"^8.60.1","@vitest/coverage-v8":"^4.1.8","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@commitlint/config-conventional":"^21.0.2"},"_npmOperationalInternal":{"tmp":"tmp/audio-recorder_1.0.9_1783410590545_0.9359271938931197","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"1.1.0":{"name":"@csnight/audio-recorder","version":"1.1.0","keywords":["audio-recorder","browser-audio-recorder","voice-recording","microphone-recorder","mediarecorder","webaudio","mediastream","pcm","wav","mp3","opus","flac","aac","amr","ac3","typescript","wasm"],"license":"SEE LICENSE IN LICENSE","_id":"@csnight/audio-recorder@1.1.0","maintainers":[{"name":"csnight","email":"chensi__chs@163.com"}],"homepage":"https://github.com/CSNight/audio-recorder#readme","bugs":{"url":"https://github.com/CSNight/audio-recorder/issues"},"dist":{"shasum":"fd1da6648f89a6995604e9256092cbc7dc4437da","tarball":"https://registry.npmjs.org/@csnight/audio-recorder/-/audio-recorder-1.1.0.tgz","fileCount":172,"integrity":"sha512-m72JT9YDfvejnmSbEvuZjIR0My6c2Us5JMNZNyM7i0VaaGoDok2B6HtbpOyYyeuj1STBBffRdnO+lf6aKXDCRA==","signatures":[{"sig":"MEQCIAs1alxZ1u9lOEL/AGjx7wU7kJLUBCofDzH+xN2pgxbkAiBDSocQe0Bv35aqke7emg+ZcMciWCIS0eV+EuIDGK/biw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3265721},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./codecs/aac":{"types":"./dist/codecs/aac/index.d.ts","import":"./dist/codecs/aac/index.js"},"./codecs/ac3":{"types":"./dist/codecs/ac3/index.d.ts","import":"./dist/codecs/ac3/index.js"},"./codecs/amr":{"types":"./dist/codecs/amr/index.d.ts","import":"./dist/codecs/amr/index.js"},"./codecs/mp3":{"types":"./dist/codecs/mp3/index.d.ts","import":"./dist/codecs/mp3/index.js"},"./codecs/base":{"types":"./dist/codecs/base/index.d.ts","import":"./dist/codecs/base/index.js"},"./codecs/flac":{"types":"./dist/codecs/flac/index.d.ts","import":"./dist/codecs/flac/index.js"},"./codecs/g711":{"types":"./dist/codecs/g711/index.d.ts","import":"./dist/codecs/g711/index.js"},"./codecs/opus":{"types":"./dist/codecs/opus/index.d.ts","import":"./dist/codecs/opus/index.js"},"./plugins/dsp":{"types":"./dist/plugins/dsp/index.d.ts","import":"./dist/plugins/dsp/index.js"},"./plugins/dtmf":{"types":"./dist/plugins/dtmf/index.d.ts","import":"./dist/plugins/dtmf/index.js"},"./storage/opfs":{"types":"./dist/storage/opfs/index.d.ts","import":"./dist/storage/opfs/index.js"},"./plugins/nmn2pcm":{"types":"./dist/plugins/nmn2pcm/index.d.ts","import":"./dist/plugins/nmn2pcm/index.js"},"./storage/indexeddb":{"types":"./dist/storage/indexeddb/index.d.ts","import":"./dist/storage/indexeddb/index.js"},"./plugins/asr-export":{"types":"./dist/plugins/asr-export/index.d.ts","import":"./dist/plugins/asr-export/index.js"},"./plugins/level-meter":{"types":"./dist/plugins/level-meter/index.d.ts","import":"./dist/plugins/level-meter/index.js"},"./plugins/sonic-export":{"types":"./dist/plugins/sonic-export/index.d.ts","import":"./dist/plugins/sonic-export/index.js"},"./plugins/streaming-export":{"types":"./dist/plugins/streaming-export/index.d.ts","import":"./dist/plugins/streaming-export/index.js"},"./plugins/streaming-player":{"types":"./dist/plugins/streaming-player/index.d.ts","import":"./dist/plugins/streaming-player/index.js"},"./plugins/frequency-histogram":{"types":"./dist/plugins/frequency-histogram/index.d.ts","import":"./dist/plugins/frequency-histogram/index.js"}},"gitHead":"c1a6bc56ea2b192fa9c47bd976412bad05e2a9a2","private":false,"scripts":{"lint":"eslint . --fix","test":"npm run test:unit && npm run test:functional","build":"vite build && tsc -p tsconfig.build.json","check":"npm run lint && npm run typecheck && npm run test && npm run build && npm run verify:exports","format":"prettier --write .","prepare":"husky","publish":"npm run build && npm publish --access public --registry=https://registry.npmjs.org/","test:unit":"vitest run --coverage","typecheck":"tsc -p tsconfig.json --noEmit","build:wasm":"node ./scripts/wasm/build-docker.mjs --codec=all","commitlint":"commitlint --edit","verify:exports":"node ./scripts/verify-exports.mjs","test:functional":"playwright test","benchmark:codecs":"node ./scripts/benchmark-codecs.mjs","build:wasm:select":"node ./scripts/wasm/build-docker.mjs","test:functional:headed":"playwright test --headed"},"_npmUser":{"name":"csnight","email":"chensi__chs@163.com"},"prettier":{"semi":false,"endOfLine":"lf","singleQuote":false,"cursorOffset":2,"trailingComma":"es5","bracketSpacing":true,"bracketSameLine":false},"repository":{"url":"git+https://github.com/CSNight/audio-recorder.git","type":"git"},"_npmVersion":"11.14.1","description":"TypeScript browser audio recorder for microphone and MediaStream input with PCM pipelines, plugins, streaming export, persistence, and WAV/MP3/Opus/FLAC/AAC/AMR/AC3 output.","directories":{},"lint-staged":{"src/**/*.{ts,tsx,json,css,scss,md}":["npm lint"]},"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^8.0.12","husky":"^9.1.7","eslint":"^10.4.1","vitest":"^4.1.8","globals":"^17.6.0","prettier":"^3.8.3","@eslint/js":"^10.0.1","playwright":"^1.60.0","typescript":"~6.0.2","@commitlint/cli":"^21.0.2","@playwright/test":"^1.60.0","typescript-eslint":"^8.60.1","@vitest/coverage-v8":"^4.1.8","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@commitlint/config-conventional":"^21.0.2"},"_npmOperationalInternal":{"tmp":"tmp/audio-recorder_1.1.0_1783486337445_0.04559085258679629","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."}},"time":{"created":"2026-06-30T01:45:23.448Z","modified":"2026-07-08T05:12:55.236Z","1.0.0":"2026-06-29T14:50:03.704Z","1.0.1":"2026-06-29T15:04:39.144Z","1.0.3":"2026-06-30T01:45:23.788Z","1.0.4":"2026-07-03T02:42:12.131Z","1.0.5":"2026-07-05T12:02:26.551Z","1.0.6":"2026-07-06T14:35:23.443Z","1.0.7":"2026-07-06T14:37:54.449Z","1.0.8":"2026-07-06T14:48:28.716Z","1.0.9":"2026-07-07T07:49:50.738Z","1.1.0":"2026-07-08T04:52:17.593Z"},"bugs":{"url":"https://github.com/CSNight/audio-recorder/issues"},"license":"SEE LICENSE IN LICENSE","homepage":"https://github.com/CSNight/audio-recorder#readme","keywords":["audio-recorder","browser-audio-recorder","voice-recording","microphone-recorder","mediarecorder","webaudio","mediastream","pcm","wav","mp3","opus","flac","aac","amr","ac3","typescript","wasm"],"repository":{"url":"git+https://github.com/CSNight/audio-recorder.git","type":"git"},"description":"TypeScript browser audio recorder for microphone and MediaStream input with PCM pipelines, plugins, streaming export, persistence, and WAV/MP3/Opus/FLAC/AAC/AMR/AC3 output.","maintainers":[{"name":"csnight","email":"chensi__chs@163.com"}],"readme":"# @csnight/audio-recorder\n\n[English](./README.md) | [中文](./README.zh-CN.md)\n\n面向浏览器端麦克风和 `MediaStream` 输入的 TypeScript 录音库。适合在现代 Web 应用中构建音频录制、PCM 处理、流式导出、插件扩展、持久化存储，以及 WAV、MP3、Opus、FLAC、AAC、AMR、AC3/E-AC3、G.711 等格式输出能力。\n\n## 目录\n\n- [概览](#概览)\n- [快速开始](#快速开始)\n- [功能](#功能)\n- [API](#api)\n- [插件](#插件)\n- [`level-meter`](#level-meter)\n- [`streaming-export`](#streaming-export)\n- [`sonic-export`](#sonic-export)\n- [`dsp`](#dsp)\n- [`asr-export`](#asr-export)\n- [`frequency-histogram`](#frequency-histogram)\n- [`dtmf`](#dtmf)\n- [`nmn2pcm`](#nmn2pcm)\n- [`streaming-player`](#streaming-player)\n- [存储](#存储)\n- [`storage/opfs`](#storageopfs)\n- [`storage/indexeddb`](#storageindexeddb)\n- [编码器](#编码器)\n- [开发](#开发)\n- [浏览器支持](#浏览器支持)\n- [基准测试](#基准测试)\n- [架构](#架构)\n- [引用](#引用)\n\n## 概览\n\n`@csnight/audio-recorder` 提供一套面向 Web 应用的浏览器录音能力，适合以下场景：\n\n- 麦克风录音或外部 `MediaStream` 输入\n- 浏览器输入后端自动降级与回退\n- PCM 帧事件与内存内音频处理\n- 录音快照导出与流式音频分片导出\n- 面向电平检测、频谱分析、播放、ASR 与简谱转 PCM 的插件扩展\n- 使用 OPFS 和 IndexedDB 的长时录音持久化能力\n\n构建目标：`es2022`。\n\n## 安装\n\n```bash\nnpm install @csnight/audio-recorder\n```\n\n也可以使用：\n\n```bash\npnpm add @csnight/audio-recorder\nyarn add @csnight/audio-recorder\n```\n\n## 快速开始\n\n```ts\nimport { createRecorder } from \"@csnight/audio-recorder\"\nimport {\n  pcmExportEncoder,\n  wavExportEncoder,\n} from \"@csnight/audio-recorder/codecs/base\"\nimport { createLevelMeterPlugin } from \"@csnight/audio-recorder/plugins/level-meter\"\n\nconst recorder = createRecorder({\n  channelCount: 1,\n  inputStrategy: \"auto\",\n  encoders: [pcmExportEncoder, wavExportEncoder],\n})\n\nawait recorder.use(createLevelMeterPlugin())\nawait recorder.open()\nawait recorder.start()\n\nconst summary = await recorder.stop()\nconst wav = await recorder.exportEncoded(\"wav\", { bitRate: 16 })\n\nconsole.log(summary.durationMs, wav.arrayBuffer.byteLength)\n```\n\n## 功能\n\n- 录音生命周期：`open / start / pause / resume / stop / close / destroy`\n- 输入策略：`media-recorder`、`audio-worklet`、`script-processor`\n- 设备枚举：`listMicrophoneDevices()`\n- 能力检测：`checkRecorderCapability()`\n- 录音事件：`statechange`、`frame:async`、`issue`\n- 快照导出：`pcm`、`wav`、`mp3`、`flac`、`ogg`、`webm`、`g711`、`aac`、`amr`、`ac3`、`eac3`\n- 持久化后端：`storage/opfs`、`storage/indexeddb`\n- 内置插件子路径：`level-meter`、`streaming-export`、`sonic-export`、`dsp`、`asr-export`、`frequency-histogram`、`dtmf`、`nmn2pcm`、`streaming-player`\n\n## API\n\n### 主入口\n\n```ts\nimport {\n  createRecorder,\n  listMicrophoneDevices,\n  checkRecorderCapability,\n  RecorderController,\n} from \"@csnight/audio-recorder\"\n```\n\n导出项：\n\n| Export                                                | Description        |\n| ----------------------------------------------------- | ------------------ |\n| `createRecorder(options?)`                            | 创建录音控制器     |\n| `listMicrophoneDevices()`                             | 枚举麦克风设备     |\n| `checkRecorderCapability()`                           | 返回浏览器能力报告 |\n| `RecorderController`                                  | 录音控制器类       |\n| `resample()`                                          | PCM 重采样工具     |\n| `serializePcmSnapshot()` / `deserializePcmSnapshot()` | PCM 快照编解码     |\n| `RecorderState`                                       | 录音状态枚举       |\n| `RecorderWarningCode`                                 | 告警码枚举         |\n| `RecorderInputSource`                                 | 输入来源枚举       |\n\n### `createRecorder(options?)`\n\n| Option                         | Type                                                                  | Default  | Notes          |\n| ------------------------------ | --------------------------------------------------------------------- | -------- | -------------- |\n| `sampleRate`                   | `number`                                                              | `-`      | 期望输入采样率 |\n| `channelCount`                 | `number`                                                              | `-`      | 期望声道数     |\n| `echoCancellation`             | `boolean`                                                             | `true`   | 输入约束       |\n| `noiseSuppression`             | `boolean`                                                             | `true`   | 输入约束       |\n| `autoGainControl`              | `boolean`                                                             | `true`   | 输入约束       |\n| `deviceId`                     | `string`                                                              | `-`      | 目标麦克风设备 |\n| `disableFrameLossCompensation` | `boolean`                                                             | `false`  | 跳过静音补帧   |\n| `inputStrategy`                | `\"auto\" \\| \"media-recorder\" \\| \"audio-worklet\" \\| \"script-processor\"` | `\"auto\"` | 输入后端选择   |\n| `storage`                      | `RecorderStorageOptions`                                              | `-`      | 缓冲持久化策略 |\n| `encoders`                     | `ExportEncoderDefinition[]`                                           | `[]`     | 快照编码器     |\n\n返回值：\n\n| Type                 | Description    |\n| -------------------- | -------------- |\n| `RecorderController` | 录音控制器实例 |\n\n### `storage`\n\n通过 `createRecorder({ storage })` 配置持久化。\n\n`RecorderStorageOptions`：\n\n| Field                   | Type                                 | Default | Description          |\n| ----------------------- | ------------------------------------ | ------- | -------------------- |\n| `mode`                  | `\"memory\" \\| \"persistent\" \\| \"auto\"` | `-`     | 缓冲模式             |\n| `memoryThresholdBytes`  | `number`                             | `-`     | `auto` 模式切换阈值  |\n| `persistenceChunkBytes` | `number`                             | `-`     | 持久化写入目标块大小 |\n| `persistencePlugin`     | `RecorderPersistencePlugin`          | `-`     | 持久化后端           |\n\n### `RecorderController`\n\n#### `on(event, listener)`\n\n订阅录音器或插件事件。\n\n参数：\n\n| Name       | Type                     | Description |\n| ---------- | ------------------------ | ----------- |\n| `event`    | `keyof RecorderEventMap` | 事件名      |\n| `listener` | `(payload) => void`      | 事件监听器  |\n\n返回值：\n\n| Type         | Description  |\n| ------------ | ------------ |\n| `() => void` | 取消订阅函数 |\n\n#### `off(event, listener)`\n\n移除事件监听器。\n\n参数：\n\n| Name       | Type                     | Description    |\n| ---------- | ------------------------ | -------------- |\n| `event`    | `keyof RecorderEventMap` | 事件名         |\n| `listener` | `(payload) => void`      | 要移除的监听器 |\n\n返回值：\n\n| Type   | Description |\n| ------ | ----------- |\n| `void` | 无返回值    |\n\n#### `getState()`\n\n返回值：\n\n| Type            | Description  |\n| --------------- | ------------ |\n| `RecorderState` | 当前录音状态 |\n\n#### `getRuntimeInfo()`\n\n返回值：\n\n| Type                  | Description                |\n| --------------------- | -------------------------- |\n| `RecorderRuntimeInfo` | 请求值与实际输入运行时信息 |\n\n#### `getLatestSummary()`\n\n返回值：\n\n| Type                     | Description  |\n| ------------------------ | ------------ |\n| `RecorderSessionSummary` | 当前会话摘要 |\n\n#### `use(plugin)`\n\n注册插件。\n\n参数：\n\n| Name     | Type             | Description |\n| -------- | ---------------- | ----------- |\n| `plugin` | `RecorderPlugin` | 插件实例    |\n\n返回值：\n\n| Type            | Description              |\n| --------------- | ------------------------ |\n| `Promise<void>` | 插件初始化完成后 resolve |\n\n#### `unuse(name)`\n\n在录音器处于 idle 时卸载插件或插件族前缀。\n\n参数：\n\n| Name   | Type     | Description                                                          |\n| ------ | -------- | -------------------------------------------------------------------- |\n| `name` | `string` | 插件名或插件族前缀。`streaming-export` / `sonic-export` 会卸载整个族 |\n\n返回值：\n\n| Type            | Description            |\n| --------------- | ---------------------- |\n| `Promise<void>` | 插件释放完成后 resolve |\n\n#### `registerEncoder(definition)`\n\n注册快照编码器。\n\n参数：\n\n| Name         | Type                      | Description                           |\n| ------------ | ------------------------- | ------------------------------------- |\n| `definition` | `ExportEncoderDefinition` | 供 `exportEncoded()` 使用的编码器定义 |\n\n返回值：\n\n| Type   | Description |\n| ------ | ----------- |\n| `void` | 无返回值    |\n\n#### `exportEncoded(type, options?)`\n\n使用已注册编码器导出当前 PCM 快照。\n\n参数：\n\n| Name      | Type                         | Description          |\n| --------- | ---------------------------- | -------------------- |\n| `type`    | `keyof EncoderMap \\| string` | 编码器类型           |\n| `options` | encoder-specific             | 对应编码器的导出参数 |\n\n返回值：\n\n| Type               | Description              |\n| ------------------ | ------------------------ |\n| `Promise<TResult>` | 所选编码器返回的编码结果 |\n\n常见内置结果类型：\n\n| Type           | Result             |\n| -------------- | ------------------ |\n| `pcm`          | `PcmExportResult`  |\n| `wav`          | `WavExportResult`  |\n| `mp3`          | `Mp3ExportResult`  |\n| `flac`         | `FlacExportResult` |\n| `ogg` / `webm` | `OpusExportResult` |\n| `g711`         | `G711ExportResult` |\n| `aac`          | `AacExportResult`  |\n| `amr`          | `AmrExportResult`  |\n| `ac3` / `eac3` | `Ac3ExportResult`  |\n\n#### `open(options?)`\n\n打开录音会话。\n\n参数：\n\n| Name      | Type                   | Description          |\n| --------- | ---------------------- | -------------------- |\n| `options` | `RecorderInputOptions` | 每次会话的输入覆盖项 |\n\n`RecorderInputOptions` 字段：\n\n| Field                          | Type                                                                  | Default  | Description      |\n| ------------------------------ | --------------------------------------------------------------------- | -------- | ---------------- |\n| `sampleRate`                   | `number`                                                              | `-`      | 期望采样率       |\n| `channelCount`                 | `number`                                                              | `-`      | 期望声道数       |\n| `echoCancellation`             | `boolean`                                                             | `true`   | 启用回声消除     |\n| `noiseSuppression`             | `boolean`                                                             | `true`   | 启用降噪         |\n| `autoGainControl`              | `boolean`                                                             | `true`   | 启用自动增益     |\n| `deviceId`                     | `string`                                                              | `-`      | 目标麦克风设备   |\n| `disableFrameLossCompensation` | `boolean`                                                             | `false`  | 禁用丢帧静音补偿 |\n| `inputStrategy`                | `\"auto\" \\| \"media-recorder\" \\| \"audio-worklet\" \\| \"script-processor\"` | `\"auto\"` | 首选输入后端     |\n\n返回值：\n\n| Type                           | Description                |\n| ------------------------------ | -------------------------- |\n| `Promise<RecorderRuntimeInfo>` | 会话打开后的实际运行时信息 |\n\n#### `start()`\n\n返回值：\n\n| Type                           | Description        |\n| ------------------------------ | ------------------ |\n| `Promise<RecorderRuntimeInfo>` | 更新后的运行时信息 |\n\n#### `pause()`\n\n返回值：\n\n| Type   | Description |\n| ------ | ----------- |\n| `void` | 无返回值    |\n\n#### `resume()`\n\n返回值：\n\n| Type                           | Description        |\n| ------------------------------ | ------------------ |\n| `Promise<RecorderRuntimeInfo>` | 更新后的运行时信息 |\n\n#### `stop()`\n\n返回值：\n\n| Type                              | Description  |\n| --------------------------------- | ------------ |\n| `Promise<RecorderSessionSummary>` | 最终会话摘要 |\n\n#### `close()`\n\n返回值：\n\n| Type            | Description            |\n| --------------- | ---------------------- |\n| `Promise<void>` | 会话资源关闭后 resolve |\n\n#### `destroy()`\n\n返回值：\n\n| Type            | Description        |\n| --------------- | ------------------ |\n| `Promise<void>` | 销毁完成后 resolve |\n\n### 子路径\n\n| Package path                                          | Exports                                                                        |\n| ----------------------------------------------------- | ------------------------------------------------------------------------------ |\n| `@csnight/audio-recorder/codecs/base`                 | PCM / WAV 编码器与解码器                                                       |\n| `@csnight/audio-recorder/codecs/mp3`                  | MP3 编码器                                                                     |\n| `@csnight/audio-recorder/codecs/flac`                 | FLAC 编码器                                                                    |\n| `@csnight/audio-recorder/codecs/opus`                 | Opus 编码器                                                                    |\n| `@csnight/audio-recorder/codecs/aac`                  | AAC 编码器                                                                     |\n| `@csnight/audio-recorder/codecs/amr`                  | AMR 编码器                                                                     |\n| `@csnight/audio-recorder/codecs/ac3`                  | AC3 / E-AC3 编码器                                                             |\n| `@csnight/audio-recorder/codecs/g711`                 | G.711 编码器                                                                   |\n| `@csnight/audio-recorder/plugins/level-meter`         | `createLevelMeterPlugin()`                                                     |\n| `@csnight/audio-recorder/plugins/streaming-export`    | `createStreamingExportPlugin()`                                                |\n| `@csnight/audio-recorder/plugins/sonic-export`        | `createSonicExportPlugin()`                                                    |\n| `@csnight/audio-recorder/plugins/dsp`                 | `createHighpassPlugin()` / `createLowpassPlugin()` / `createNoiseGatePlugin()` |\n| `@csnight/audio-recorder/plugins/asr-export`          | `createAsrExportPlugin()`                                                      |\n| `@csnight/audio-recorder/plugins/frequency-histogram` | `createFrequencyHistogramPlugin()`                                             |\n| `@csnight/audio-recorder/plugins/dtmf`                | `encodeDtmf()` / `lookupDtmfFrequencies()` / `createDtmfDecoderPlugin()`       |\n| `@csnight/audio-recorder/plugins/nmn2pcm`             | `nmn2pcm()` / `DEFAULT_NMN_OPTIONS` / `DYNAMIC_VELOCITY` / `NMN_KEY_OFFSETS`   |\n| `@csnight/audio-recorder/plugins/streaming-player`    | `createStreamingPlayer()`                                                      |\n| `@csnight/audio-recorder/storage/opfs`                | `createOpfsPersistencePlugin()` / `isSupport()`                                |\n| `@csnight/audio-recorder/storage/indexeddb`           | `createIndexedDbPersistencePlugin()` / `isSupport()`                           |\n\n### 事件\n\n#### `statechange`\n\n录音状态变化时触发。\n\n| Field           | Type                     | Description    |\n| --------------- | ------------------------ | -------------- |\n| `controller`    | `RecorderController`     | 录音器实例     |\n| `sessionId`     | `string`                 | 当前会话 ID    |\n| `emittedAt`     | `number`                 | 毫秒时间戳     |\n| `previousState` | `RecorderState`          | 之前状态       |\n| `state`         | `RecorderState`          | 新状态         |\n| `runtimeInfo`   | `RecorderRuntimeInfo`    | 运行时信息快照 |\n| `summary`       | `RecorderSessionSummary` | 会话摘要快照   |\n\n#### `frame:async`\n\n异步 PCM 帧事件。\n\n| Field         | Type                     | Description    |\n| ------------- | ------------------------ | -------------- |\n| `controller`  | `RecorderController`     | 录音器实例     |\n| `sessionId`   | `string`                 | 当前会话 ID    |\n| `emittedAt`   | `number`                 | 毫秒时间戳     |\n| `frame`       | `AudioFrame`             | PCM 帧         |\n| `runtimeInfo` | `RecorderRuntimeInfo`    | 运行时信息快照 |\n| `summary`     | `RecorderSessionSummary` | 会话摘要快照   |\n\n`frame` 字段：\n\n| Field        | Type           | Description           |\n| ------------ | -------------- | --------------------- |\n| `channels`   | `number`       | 声道数                |\n| `sampleRate` | `number`       | 帧采样率              |\n| `timestamp`  | `number`       | 帧时间戳（毫秒）      |\n| `durationMs` | `number`       | 帧时长（毫秒）        |\n| `planar`     | `Int16Array[]` | 按声道拆分的 PCM 样本 |\n\n#### `issue`\n\n告警或错误事件。\n\n| Field         | Type                     | Description    |\n| ------------- | ------------------------ | -------------- |\n| `controller`  | `RecorderController`     | 录音器实例     |\n| `sessionId`   | `string`                 | 当前会话 ID    |\n| `emittedAt`   | `number`                 | 毫秒时间戳     |\n| `issue`       | `RecorderIssue`          | 告警或错误负载 |\n| `runtimeInfo` | `RecorderRuntimeInfo`    | 运行时信息快照 |\n| `summary`     | `RecorderSessionSummary` | 会话摘要快照   |\n\n`issue` 变体：\n\n| Variant   | Fields                                            |\n| --------- | ------------------------------------------------- |\n| `warning` | `{ kind: \"warning\", warning: { code, message } }` |\n| `error`   | `{ kind: \"error\", error: Error }`                 |\n\n## 插件\n\n### `level-meter`\n\n#### Introduction\n\n实时电平插件。消费录音帧，输出整体与分声道 `peak / rms`。\n\n事件：\n\n- `plugin:level`\n\n#### Quick Start\n\n```ts\nimport { createRecorder } from \"@csnight/audio-recorder\"\nimport { createLevelMeterPlugin } from \"@csnight/audio-recorder/plugins/level-meter\"\n\nconst recorder = createRecorder()\n\nawait recorder.use(createLevelMeterPlugin())\n\nrecorder.on(\"plugin:level\", ({ payload }) => {\n  console.log(payload.level.peak, payload.level.rms)\n})\n```\n\n#### API\n\n以下运行时与事件类型从 `@csnight/audio-recorder/plugins/level-meter` 子路径导出。\n\n| Export                     | Description                 |\n| -------------------------- | --------------------------- |\n| `createLevelMeterPlugin()` | 创建电平插件                |\n| `RecorderLevel`            | 电平负载主体类型            |\n| `RecorderLevelChannel`     | 分声道电平类型              |\n| `RecorderLevelEvent`       | `plugin:level` 事件负载类型 |\n\nOptions:\n\nNone.\n\nEvent payload: `plugin:level`\n\n| Field         | Type                     | Description    |\n| ------------- | ------------------------ | -------------- |\n| `controller`  | `RecorderController`     | 录音器实例     |\n| `sessionId`   | `string`                 | 当前会话 ID    |\n| `emittedAt`   | `number`                 | 毫秒时间戳     |\n| `pluginName`  | `string`                 | 插件名         |\n| `runtimeInfo` | `RecorderRuntimeInfo`    | 运行时信息快照 |\n| `summary`     | `RecorderSessionSummary` | 会话摘要快照   |\n| `payload`     | `RecorderLevelEvent`     | 电平负载       |\n\n`payload.level` 字段：\n\n| Field      | Type                     | Description     |\n| ---------- | ------------------------ | --------------- |\n| `peak`     | `number`                 | `0..1` 范围峰值 |\n| `rms`      | `number`                 | `0..1` 范围 RMS |\n| `channels` | `RecorderLevelChannel[]` | 分声道电平数组  |\n\n### `streaming-export`\n\n#### Introduction\n\n实时分片导出插件。录音过程中将 PCM 帧经 `ChunkedEncoderBridge` 送入 `StreamEncoderDefinition`，持续产出标准化流式 packet。内置基础编解码器提供 `pcm` 和 `wav`，调用方通过 `encoders` 传入匹配编码器。插件会在多个录音会话之间复用同一个 bridge，在 `start()` 时重置，优先使用 Worker 编码，必要时可降级到主线程；`stop()` 时只有在编码器仍有缓冲输出时才会额外 `flush()` 一个最终 packet。\n\n事件：\n\n- `plugin:stream`\n\n#### Quick Start\n\n```ts\nimport { createRecorder } from \"@csnight/audio-recorder\"\nimport { pcmStreamEncoder } from \"@csnight/audio-recorder/codecs/base\"\nimport { createStreamingExportPlugin } from \"@csnight/audio-recorder/plugins/streaming-export\"\n\nconst recorder = createRecorder()\n\nawait recorder.use(\n  createStreamingExportPlugin({\n    format: \"pcm\",\n    encoders: [pcmStreamEncoder],\n  })\n)\n\nrecorder.on(\"plugin:stream\", ({ payload }) => {\n  console.log(payload.format, payload.chunk.byteLength, payload.isFinal)\n})\n```\n\n#### API\n\n| Export                                 | Description          |\n| -------------------------------------- | -------------------- |\n| `createStreamingExportPlugin(options)` | 创建实时分片导出插件 |\n| `StreamingExportPluginOptions`         | 插件选项             |\n\nOptions: `StreamingExportPluginOptions`\n\n| Field                     | Type                        | Default | Description                                                                     |\n| ------------------------- | --------------------------- | ------- | ------------------------------------------------------------------------------- |\n| `format`                  | `string`                    | `-`     | 输出 chunk 格式                                                                 |\n| `encoderOptions`          | `unknown`                   | `-`     | 传给 `definition.create(options)` 和 `bridge.reset(options)` 的编码参数         |\n| `encoders`                | `StreamEncoderDefinition[]` | `-`     | 可用流式编码器，必须包含选中的 `format`                                         |\n| `allowMainThreadFallback` | `boolean`                   | `true`  | Worker 不可用时退回主线程编码                                                   |\n| `streamId`                | `string`                    | 自动    | 固定逻辑流 ID；跨会话保持稳定。未传时从 `createStreamId()` 求值一次，或自动生成 |\n| `createStreamId`          | `() => string`              | `-`     | 懒生成流 ID 的工厂函数，在插件创建时调用一次；设置了 `streamId` 时忽略          |\n| `createSessionId`         | `() => string`              | 自动    | 每次 `start()` 时调用的会话 ID 工厂；默认基于 `crypto.randomUUID()` 生成        |\n| `metadata`                | `Record<string, unknown>`   | `-`     | 附加到每个 packet 的静态元数据                                                  |\n\n`StreamEncoderDefinition` 字段：\n\n| Field           | Type                          | Description                                   |\n| --------------- | ----------------------------- | --------------------------------------------- |\n| `format`        | `string`                      | 编码格式键                                    |\n| `workerFactory` | `() => Worker`                | `ChunkedEncoderBridge` 使用的可选 Worker 工厂 |\n| `preload`       | `() => Promise<void>`         | 在插件 `setup()` 中调用的可选预加载钩子       |\n| `create`        | `(options?) => StreamEncoder` | 创建编码器实例                                |\n\nEvent payload: `plugin:stream`\n\n| Field         | Type                     | Description          |\n| ------------- | ------------------------ | -------------------- |\n| `controller`  | `RecorderController`     | 录音器实例           |\n| `sessionId`   | `string`                 | 当前会话 ID          |\n| `emittedAt`   | `number`                 | 毫秒时间戳           |\n| `pluginName`  | `string`                 | 插件名               |\n| `runtimeInfo` | `RecorderRuntimeInfo`    | 运行时信息快照       |\n| `summary`     | `RecorderSessionSummary` | 会话摘要快照         |\n| `payload`     | `StreamingPacketPayload` | 编码流式 packet 负载 |\n\n`StreamingPacketPayload` 字段：\n\n| Field           | Type                                   | Description                                       |\n| --------------- | -------------------------------------- | ------------------------------------------------- |\n| `streamId`      | `string`                               | 逻辑流 ID；跨会话保持稳定                         |\n| `sessionId`     | `string`                               | 每次 `start()` 生成的流式会话 ID                  |\n| `seq`           | `number`                               | 会话内单调递增的 packet 序号                      |\n| `timestampMs`   | `number`                               | 来源帧时间戳；最终 packet 为 `flush()` 时刻时间戳 |\n| `durationMs`    | `number`                               | 当前 packet 覆盖的累计源帧时长                    |\n| `sampleRate`    | `number`                               | packet 采样率                                     |\n| `channels`      | `number`                               | packet 声道数                                     |\n| `format`        | `string`                               | packet 格式                                       |\n| `chunk`         | `Uint8Array`                           | 编码后的字节                                      |\n| `isFinal`       | `boolean`                              | 是否为由 `flush()` 产出的最终 packet              |\n| `discontinuity` | `boolean \\| undefined`                 | 供传输层或播放层识别 gap 的可选标记               |\n| `metadata`      | `Record<string, unknown> \\| undefined` | 预留扩展字段                                      |\n\n### `sonic-export`\n\n#### Introduction\n\n实时 Sonic 变速变调插件。在旁路中累积 PCM 帧，执行 Sonic 的速度 / 音调 / rate / 音量处理，再通过匹配的 `StreamEncoderDefinition` 输出标准化流式 packet。实时输出只支持 `pcm` 和 `wav`，保留原始声道布局，并在累计源音频达到 `blockMs` 后于主线程执行一次 Sonic 处理，再把结果送入 `ChunkedEncoderBridge`。插件只通过 `plugin:stream` 输出处理后的实时流，不会改写录音核心 buffer；快照和 `exportEncoded()` 仍然基于原始 PCM。同一个插件实例也提供离线转换方法，可直接处理 snapshot 或任意 PCM，并且与 `streaming-export` 互斥；切换插件族只允许在 idle 状态下通过 `recorder.unuse(\"streaming-export\")` 或 `recorder.unuse(\"sonic-export\")` 完成。\n\n事件：\n\n- `plugin:stream`\n\n#### Quick Start\n\n```ts\nimport { createRecorder } from \"@csnight/audio-recorder\"\nimport { wavStreamEncoder } from \"@csnight/audio-recorder/codecs/base\"\nimport { createSonicExportPlugin } from \"@csnight/audio-recorder/plugins/sonic-export\"\n\nconst recorder = createRecorder()\nconst sonic = createSonicExportPlugin({\n  format: \"wav\",\n  encoders: [wavStreamEncoder],\n  speed: 1.25,\n  blockMs: 200,\n})\n\nawait recorder.use(sonic)\n\nrecorder.on(\"plugin:stream\", ({ payload }) => {\n  console.log(\n    payload.format,\n    payload.channels,\n    payload.chunk.byteLength,\n    payload.isFinal\n  )\n})\n```\n\n离线转换示例：\n\n```ts\nimport { deserializePcmSnapshot } from \"@csnight/audio-recorder\"\n\nconst snapshot = deserializePcmSnapshot(savedSnapshotBuffer)\nconst processed = await sonic.transformSnapshot(snapshot, { speed: 0.85 })\n\nconsole.log(processed instanceof Int16Array, processed.length)\n```\n\n#### API\n\n| Export                             | Description                   |\n| ---------------------------------- | ----------------------------- |\n| `createSonicExportPlugin(options)` | 创建 Sonic 导出插件           |\n| `SonicExportFormat`                | 实时输出格式联合类型          |\n| `SonicExportOptions`               | 插件选项                      |\n| `SonicTransformOptions`            | Sonic 处理参数                |\n| `SonicExportPlugin`                | 带离线转换 API 的插件实例类型 |\n\nOptions: `SonicExportOptions`\n\n| Field                     | Type                        | Default | Description                                                                     |\n| ------------------------- | --------------------------- | ------- | ------------------------------------------------------------------------------- |\n| `format`                  | `\"pcm\" \\| \"wav\"`            | `-`     | 实时输出格式                                                                    |\n| `speed`                   | `number`                    | `1`     | 变速不变调                                                                      |\n| `pitch`                   | `number`                    | `1`     | 变调不变速                                                                      |\n| `rate`                    | `number`                    | `1`     | 同时影响速度和音调                                                              |\n| `volume`                  | `number`                    | `1`     | 输出音量倍率                                                                    |\n| `blockMs`                 | `number`                    | `200`   | 触发一次 Sonic 处理前累计的源音频时长                                           |\n| `encoders`                | `StreamEncoderDefinition[]` | `-`     | 可用流式编码器，必须包含选中的 `format`                                         |\n| `encoderOptions`          | `unknown`                   | `-`     | 传给 `ChunkedEncoderBridge` 的编码器参数                                        |\n| `allowMainThreadFallback` | `boolean`                   | `true`  | Worker 不可用时回退到主线程执行 chunk 编码                                      |\n| `streamId`                | `string`                    | 自动    | 固定逻辑流 ID；跨会话保持稳定。未传时从 `createStreamId()` 求值一次，或自动生成 |\n| `createStreamId`          | `() => string`              | `-`     | 懒生成流 ID 的工厂函数，在插件创建时调用一次；设置了 `streamId` 时忽略          |\n| `createSessionId`         | `() => string`              | 自动    | 每次 `start()` 时调用的会话 ID 工厂；默认基于 `crypto.randomUUID()` 生成        |\n| `metadata`                | `Record<string, unknown>`   | `-`     | 附加到每个 packet 的静态元数据                                                  |\n\n实例方法：\n\n| Member                                                     | Type                  | Description                                                         |\n| ---------------------------------------------------------- | --------------------- | ------------------------------------------------------------------- |\n| `transformSnapshot(snapshot, options?)`                    | `Promise<Int16Array>` | 处理 `PcmBufferSnapshot`，返回保持原声道布局的交织 `Int16Array` PCM |\n| `transform(pcm, sampleRate, channelsOrOptions?, options?)` | `Promise<Int16Array>` | 处理任意交织 PCM。默认按单声道解释；多声道输入需要显式传 `channels` |\n\nEvent payload: `plugin:stream`\n\n| Field         | Type                     | Description          |\n| ------------- | ------------------------ | -------------------- |\n| `controller`  | `RecorderController`     | 录音器实例           |\n| `sessionId`   | `string`                 | 当前会话 ID          |\n| `emittedAt`   | `number`                 | 毫秒时间戳           |\n| `pluginName`  | `string`                 | 插件名               |\n| `runtimeInfo` | `RecorderRuntimeInfo`    | 运行时信息快照       |\n| `summary`     | `RecorderSessionSummary` | 会话摘要快照         |\n| `payload`     | `StreamingPacketPayload` | 编码流式 packet 负载 |\n\n`StreamingPacketPayload` 字段与上面的 `streaming-export` 一致。\n\n### `dsp`\n\n#### Introduction\n\n主链路 DSP 插件族。这组插件会在 `onBeforeFrame()` 中同步运行，位置在 PCM 帧进入录音 buffer、会话摘要、`frame:async`、快照导出以及后续插件 `onFrame()` 之前。当前内置三种实现：`highpass`、`lowpass`、`noise-gate`。\n\n其中 `highpass` 和 `lowpass` 还实现了 `onFlush()`，因此在 `stop()` 阶段产生的尾帧会重新写回同一条录音主链。当前这一版只支持“长度不变”的逐帧处理；混响/回声尾音、lookahead 压缩、变速类变长输出、FFT 重建类效果器都不在当前支持范围内。\n\n事件：\n\n- 无\n\n#### Quick Start\n\n```ts\nimport { createRecorder } from \"@csnight/audio-recorder\"\nimport { wavExportEncoder } from \"@csnight/audio-recorder/codecs/base\"\nimport {\n  createHighpassPlugin,\n  createNoiseGatePlugin,\n} from \"@csnight/audio-recorder/plugins/dsp\"\n\nconst recorder = createRecorder({\n  encoders: [wavExportEncoder],\n})\n\nawait recorder.use(createHighpassPlugin({ cutoffHz: 120 }))\nawait recorder.use(createNoiseGatePlugin({ thresholdDb: -42 }))\n\nawait recorder.open()\nawait recorder.start()\n\nconst summary = await recorder.stop()\nconst wav = await recorder.exportEncoded(\"wav\", { bitRate: 16 })\n\nconsole.log(summary.durationMs, wav.arrayBuffer.byteLength)\n```\n\n#### API\n\n以下 API 从 `@csnight/audio-recorder/plugins/dsp` 子路径导出。\n\n| Export                    | Description         |\n| ------------------------- | ------------------- |\n| `createHighpassPlugin()`  | 创建高通滤波插件    |\n| `createLowpassPlugin()`   | 创建低通滤波插件    |\n| `createNoiseGatePlugin()` | 创建噪声门插件      |\n| `DspFilterOptions`        | cutoff 参数共享类型 |\n| `NoiseGatePluginOptions`  | 噪声门参数类型      |\n\n`createHighpassPlugin(options?)`\n\n| Field      | Type     | Default | Description        |\n| ---------- | -------- | ------- | ------------------ |\n| `cutoffHz` | `number` | `120`   | 高通截止频率（Hz） |\n\n`createLowpassPlugin(options?)`\n\n| Field      | Type     | Default | Description        |\n| ---------- | -------- | ------- | ------------------ |\n| `cutoffHz` | `number` | `3400`  | 低通截止频率（Hz） |\n\n`createNoiseGatePlugin(options?)`\n\n| Field         | Type     | Default | Description                 |\n| ------------- | -------- | ------- | --------------------------- |\n| `thresholdDb` | `number` | `-45`   | 低于该 RMS 阈值时衰减当前帧 |\n| `attackMs`    | `number` | `10`    | 增益打开平滑时间            |\n| `releaseMs`   | `number` | `80`    | 增益关闭平滑时间            |\n\n介绍：\n\n- 多个 DSP 插件按 `recorder.use()` 注册顺序串联执行。\n- `onBeforeFrame()` 必须保持帧时间轴和格式稳定。宿主会强制保留 `timestamp`、`durationMs`、`sampleRate`、`channels` 以及每声道长度，只接收变换后的 PCM 样本数据。\n- DSP 插件在 `onBeforeFrame()` 中抛错时，宿主会发出 `issue` 错误，并回退到该插件处理前的帧。\n- `onFlush()` 产出的尾帧会先校验当前录音会话的采样率与声道数，再继续经过下游 `onBeforeFrame()` 插件，最后才写入主链路。\n- `highpass` 和 `lowpass` 只会输出有界尾帧；`noise-gate` 不会产出 flush 帧。\n\n### `asr-export`\n\n#### Introduction\n\n面向 ASR 的分片导出插件。会先下混到单声道，再按固定时长切片并编码。\n\n事件：\n\n- `plugin:asr:chunk`\n\n#### Quick Start\n\n```ts\nimport { createRecorder } from \"@csnight/audio-recorder\"\nimport { pcmExportEncoder } from \"@csnight/audio-recorder/codecs/base\"\nimport { createAsrExportPlugin } from \"@csnight/audio-recorder/plugins/asr-export\"\n\nconst recorder = createRecorder()\n\nawait recorder.use(\n  createAsrExportPlugin({\n    format: \"pcm\",\n    encoders: [pcmExportEncoder],\n    sampleRate: 16000,\n    chunkDurationMs: 40,\n  })\n)\n\nrecorder.on(\"plugin:asr:chunk\", ({ payload }) => {\n  console.log(payload.seq, payload.chunk.byteLength, payload.isFinal)\n})\n```\n\n#### API\n\n以下类型从 `@csnight/audio-recorder/plugins/asr-export` 子路径导出。\n\n| Export                           | Description           |\n| -------------------------------- | --------------------- |\n| `createAsrExportPlugin(options)` | 创建 ASR 分片导出插件 |\n| `AsrChunkPayload`                | ASR chunk 负载        |\n| `AsrExportPluginOptions`         | 插件选项              |\n\nOptions: `AsrExportPluginOptions`\n\n| Field             | Type                                       | Default | Description            |\n| ----------------- | ------------------------------------------ | ------- | ---------------------- |\n| `format`          | `\"pcm\" \\| \"wav\"`                           | `\"pcm\"` | chunk 输出格式         |\n| `encoders`        | `ExportEncoderDefinition[]`                | `-`     | 可用快照编码器         |\n| `sampleRate`      | `8000 \\| 16000 \\| 24000 \\| 32000 \\| 48000` | `16000` | 输出采样率             |\n| `channels`        | `1`                                        | `-`     | 仅支持单声道           |\n| `chunkDurationMs` | `number`                                   | `40`    | chunk 时长（毫秒）     |\n| `bitsPerSample`   | `16`                                       | `16`    | 当前固定为 16-bit 输出 |\n\nEvent payload: `plugin:asr:chunk`\n\n| Field         | Type                     | Description    |\n| ------------- | ------------------------ | -------------- |\n| `controller`  | `RecorderController`     | 录音器实例     |\n| `sessionId`   | `string`                 | 当前会话 ID    |\n| `emittedAt`   | `number`                 | 毫秒时间戳     |\n| `pluginName`  | `string`                 | 插件名         |\n| `runtimeInfo` | `RecorderRuntimeInfo`    | 运行时信息快照 |\n| `summary`     | `RecorderSessionSummary` | 会话摘要快照   |\n| `payload`     | `AsrChunkPayload`        | ASR chunk 负载 |\n\n`AsrChunkPayload` 字段：\n\n| Field         | Type             | Description          |\n| ------------- | ---------------- | -------------------- |\n| `format`      | `\"pcm\" \\| \"wav\"` | 输出格式             |\n| `chunk`       | `Uint8Array`     | 编码后的字节         |\n| `seq`         | `number`         | 单调递增序号         |\n| `timestampMs` | `number`         | chunk 时间戳（毫秒） |\n| `durationMs`  | `number`         | chunk 时长           |\n| `sampleRate`  | `number`         | 输出采样率           |\n| `channels`    | `1`              | 固定单声道           |\n| `isFinal`     | `boolean`        | 是否为最终 chunk     |\n\n### `frequency-histogram`\n\n#### Introduction\n\n实时 FFT 分析插件。会从 `frame.planar[0]` 累积 PCM 样本，用纯 TypeScript 的 radix-2 FFT 做定长窗口分析，并向 UI 或其他消费方发出归一化频谱柱数据。\n\n事件：\n\n- `plugin:fft`\n\n#### Quick Start\n\n```ts\nimport { createRecorder } from \"@csnight/audio-recorder\"\nimport { createFrequencyHistogramPlugin } from \"@csnight/audio-recorder/plugins/frequency-histogram\"\n\nconst recorder = createRecorder()\n\nawait recorder.use(\n  createFrequencyHistogramPlugin({\n    fftSize: 2048,\n    barCount: 48,\n    frameInterval: 1,\n  })\n)\n\nrecorder.on(\"plugin:fft\", ({ payload }) => {\n  console.log(payload.bars.length, payload.timestampMs, payload.sampleRate)\n})\n```\n\n#### API\n\n`@csnight/audio-recorder/plugins/frequency-histogram` 导出：\n\n| Export                                    | Description               |\n| ----------------------------------------- | ------------------------- |\n| `createFrequencyHistogramPlugin(options)` | 创建 FFT 分析插件         |\n| `FrequencyHistogramOptions`               | 插件选项                  |\n| `FrequencyFftEvent`                       | `plugin:fft` 事件负载类型 |\n\n选项：`FrequencyHistogramOptions`\n\n| Field           | Type                          | Default | Description                    |\n| --------------- | ----------------------------- | ------- | ------------------------------ |\n| `fftSize`       | `512 \\| 1024 \\| 2048 \\| 4096` | `2048`  | FFT 窗口大小，必须为 2 的幂    |\n| `barCount`      | `number`                      | `64`    | 输出频谱柱数量                 |\n| `frameInterval` | `number`                      | `1`     | 每隔 N 个已接收 PCM 帧分析一次 |\n\nEvent payload: `plugin:fft`\n\n| Field         | Type                     | Description    |\n| ------------- | ------------------------ | -------------- |\n| `controller`  | `RecorderController`     | 录音器实例     |\n| `sessionId`   | `string`                 | 当前会话 ID    |\n| `emittedAt`   | `number`                 | 毫秒时间戳     |\n| `pluginName`  | `string`                 | 插件名         |\n| `runtimeInfo` | `RecorderRuntimeInfo`    | 运行时信息快照 |\n| `summary`     | `RecorderSessionSummary` | 会话摘要快照   |\n| `payload`     | `FrequencyFftEvent`      | FFT 负载       |\n\n`FrequencyFftEvent` 字段：\n\n| Field         | Type           | Description               |\n| ------------- | -------------- | ------------------------- |\n| `bars`        | `Float32Array` | `[0, 1]` 归一化频谱柱数据 |\n| `timestampMs` | `number`       | 当前 FFT 窗口结束时刻     |\n| `fftSize`     | `number`       | 实际使用的 FFT 窗口大小   |\n| `sampleRate`  | `number`       | 被分析 PCM 的采样率       |\n\n### `dtmf`\n\n#### Introduction\n\nDTMF 辅助子路径，包含两类能力：通过 `encodeDtmf()` 离线合成电话按键音，以及通过 `createDtmfDecoderPlugin()` 在录音过程中实时识别按键音。解码插件会先下混到单声道，再使用 Goertzel 检测器识别稳定音调，并通过插件事件总线输出结果。\n\n事件：\n\n- `plugin:dtmf:detect`\n\n#### Quick Start\n\n```ts\nimport { createRecorder } from \"@csnight/audio-recorder\"\nimport {\n  createDtmfDecoderPlugin,\n  encodeDtmf,\n} from \"@csnight/audio-recorder/plugins/dtmf\"\n\nconst recorder = createRecorder()\nconst tone = encodeDtmf([\"1\", \"2\", \"3\"], { sampleRate: 8000 })\n\nawait recorder.use(\n  createDtmfDecoderPlugin({\n    frameWindowMs: 40,\n    minToneMs: 60,\n    minGapMs: 30,\n  })\n)\n\nrecorder.on(\"plugin:dtmf:detect\", ({ payload }) => {\n  console.log(payload.key, payload.durationMs, tone.length)\n})\n```\n\n#### API\n\n`@csnight/audio-recorder/plugins/dtmf` 导出：\n\n| Export                             | Description                   |\n| ---------------------------------- | ----------------------------- |\n| `encodeDtmf(keys, options)`        | 生成 DTMF PCM 按键音          |\n| `lookupDtmfFrequencies(key)`       | 返回某个按键对应的行/列频率   |\n| `createDtmfDecoderPlugin(options)` | 创建实时 DTMF 检测插件        |\n| `DtmfKey`                          | 支持的按键联合类型            |\n| `DtmfEncodeOptions`                | 合成选项                      |\n| `DtmfDecodeOptions`                | 检测选项                      |\n| `DtmfDetectEvent`                  | `plugin:dtmf:detect` 负载类型 |\n\n选项：`DtmfEncodeOptions`\n\n| Field        | Type     | Default | Description               |\n| ------------ | -------- | ------- | ------------------------- |\n| `sampleRate` | `number` | `8000`  | 输出 PCM 采样率           |\n| `toneMs`     | `number` | `100`   | 单个按键音时长（毫秒）    |\n| `gapMs`      | `number` | `50`    | 相邻按键之间的静音间隔    |\n| `amplitude`  | `number` | `0.7`   | `[0, 1]` 范围内的合成振幅 |\n\n选项：`DtmfDecodeOptions`\n\n| Field             | Type     | Default | Description                  |\n| ----------------- | -------- | ------- | ---------------------------- |\n| `frameWindowMs`   | `number` | `40`    | Goertzel 分析窗口时长        |\n| `minToneMs`       | `number` | `60`    | 触发事件前所需的最短稳定时长 |\n| `minGapMs`        | `number` | `30`    | 清空当前候选音调前的最短静音 |\n| `energyThreshold` | `number` | `0.03`  | 执行检测前的 RMS 能量门限    |\n\nEvent payload: `plugin:dtmf:detect`\n\n| Field         | Type                     | Description    |\n| ------------- | ------------------------ | -------------- |\n| `controller`  | `RecorderController`     | 录音器实例     |\n| `sessionId`   | `string`                 | 当前会话 ID    |\n| `emittedAt`   | `number`                 | 毫秒时间戳     |\n| `pluginName`  | `string`                 | 插件名         |\n| `runtimeInfo` | `RecorderRuntimeInfo`    | 运行时信息快照 |\n| `summary`     | `RecorderSessionSummary` | 会话摘要快照   |\n| `payload`     | `DtmfDetectEvent`        | 检测负载       |\n\n`DtmfDetectEvent` 字段：\n\n| Field         | Type      | Description      |\n| ------------- | --------- | ---------------- |\n| `key`         | `DtmfKey` | 识别到的按键     |\n| `startedAtMs` | `number`  | 稳定音调起始时间 |\n| `endedAtMs`   | `number`  | 稳定音调结束时间 |\n| `durationMs`  | `number`  | 稳定识别时长     |\n| `rowHz`       | `number`  | 匹配到的行频     |\n| `colHz`       | `number`  | 匹配到的列频     |\n\n### `nmn2pcm`\n\n#### Introduction\n\n独立的简谱转 PCM 工具。它会解析数字简谱字符串，结合调号和移调信息编译音符事件，再用纯 TypeScript 合成单声道 PCM，不依赖录音器生命周期。\n\n#### Quick Start\n\n```ts\nimport { nmn2pcm } from \"@csnight/audio-recorder/plugins/nmn2pcm\"\n\nconst result = nmn2pcm(\"!mf! [1 3 5]- 1 ~ 1 0 6.\", {\n  sampleRate: 16000,\n  bpm: 96,\n  volume: 0.6,\n  key: \"C\",\n  transpose: 0,\n})\n\nconsole.log(result.sampleRate, result.durationMs, result.channels)\n```\n\n#### API\n\n`@csnight/audio-recorder/plugins/nmn2pcm` 导出：\n\n| Export                    | Description                |\n| ------------------------- | -------------------------- |\n| `nmn2pcm(score, options)` | 将简谱转换为单声道 PCM     |\n| `DEFAULT_NMN_OPTIONS`     | 默认简谱转换选项           |\n| `DYNAMIC_VELOCITY`        | 内置力度到速度映射表       |\n| `NMN_KEY_OFFSETS`         | 支持的调名与半音偏移映射表 |\n| `NmnConvertOptions`       | 转换选项                   |\n| `NmnConvertResult`        | 转换结果                   |\n\n选项：`NmnConvertOptions`\n\n| Field        | Type     | Default | Description                    |\n| ------------ | -------- | ------- | ------------------------------ |\n| `sampleRate` | `number` | `16000` | 输出 PCM 采样率                |\n| `bpm`        | `number` | `60`    | 每分钟拍数                     |\n| `volume`     | `number` | `0.5`   | `[0, 1]` 范围内的主音量        |\n| `key`        | `string` | `\"C\"`   | 调号，如 `C`、`F#`、`Am`       |\n| `transpose`  | `number` | `0`     | 在调号解析后再叠加的半音移调量 |\n\n返回值：`NmnConvertResult`\n\n| Field        | Type         | Description        |\n| ------------ | ------------ | ------------------ |\n| `data`       | `Int16Array` | 合成后的单声道 PCM |\n| `sampleRate` | `number`     | 实际输出采样率     |\n| `durationMs` | `number`     | 总时长             |\n| `channels`   | `1`          | 固定单声道         |\n\n### `streaming-player`\n\n#### Introduction\n\n独立流式音频播放引擎。接收来自任意来源（WebSocket、录音插件等）的 `StreamingPacketPayload` 数据包，经重排与抖动缓冲管线处理后，通过调用方提供的解码器解码，并在 `AudioContext` 上调度连续播放。每次 `push()` 都会写入持久化存储，`start()` 和 `resume()` 会重置 live 播放管线并回到 live edge，`replay()` 仅在暂停状态下可用，`persistMode` 支持 `\"memory\"`、`\"indexeddb\"` 和 `\"custom\"`，而当 `ReorderBuffer + JitterBuffer` 的 live 积压超过 `maxBufferMs` 时会触发 `onPacketDrop`。播放器通过 `StreamingPacketPayload.sessionId` 自动检测录音会话切换：当推入的数据包携带与当前不同的 `sessionId` 时，播放器会自动重置管线并从新会话的 live edge 重新起播，无需调用方手动干预，适用于录音器 stop→start 多轮录音场景。\n\n#### Quick Start\n\n```ts\nimport { createStreamingPlayer } from \"@csnight/audio-recorder/plugins/streaming-player\"\nimport { pcmDecoderDefinition } from \"@csnight/audio-recorder/codecs/base\"\n\nconst player = await createStreamingPlayer({\n  decoders: [pcmDecoderDefinition],\n  targetLatencyMs: 300,\n  onStateChange: (s) => console.log(\"state →\", s),\n})\n\nawait player.start()\n\n// 从任意来源推入数据包\nwebsocket.onmessage = ({ data }) => player.push(JSON.parse(data))\n\n// 控制\nplayer.pause()\nplayer.resume()\nplayer.replay(5) // 重播最近 5 秒（仅暂停状态下可用）\nplayer.setVolume(0.8)\nplayer.destroy()\n```\n\n#### API\n\n`@csnight/audio-recorder/plugins/streaming-player` 导出：\n\n| 导出                             | 说明                                                      |\n| -------------------------------- | --------------------------------------------------------- |\n| `createStreamingPlayer(options)` | 创建并初始化流式播放器                                    |\n| `PersistStore`                   | 自定义持久化存储需要实现的公开接口                        |\n| `StreamingPlayerOptions`         | 播放器创建选项                                            |\n| `StreamingPlayerHandle`          | 播放器控制句柄                                            |\n| `StreamingPlayerState`           | 播放器状态联合类型                                        |\n| `PersistMode`                    | 持久化模式联合类型：`\"memory\" \\| \"indexeddb\" \\| \"custom\"` |\n\n选项：`StreamingPlayerOptions`\n\n| 字段              | 类型                                                  | 默认值     | 说明                                                                                    |\n| ----------------- | ----------------------------------------------------- | ---------- | --------------------------------------------------------------------------------------- |\n| `decoders`        | `AudioDecoderDefinition[]`                            | **必填**   | 解码器定义列表，每项将 `format` 字符串映射到解码函数                                    |\n| `targetLatencyMs` | `number`                                              | `300`      | 起播垫片和抖动缓冲的目标深度（毫秒）                                                    |\n| `maxBufferMs`     | `number`                                              | `3000`     | `ReorderBuffer + JitterBuffer` 的最大 live 积压时长，超出后触发丢弃旧数据               |\n| `volume`          | `number`                                              | `1.0`      | 初始增益 `[0, 1]`                                                                       |\n| `persistMode`     | `\"memory\" \\| \"indexeddb\" \\| \"custom\"`                 | `\"memory\"` | 选择内置持久化存储后端，或在 `\"custom\"` 模式下要求通过 `player.use(store)` 注入外部实现 |\n| `persistBufferMs` | `number`                                              | `10000`    | 使用内置存储时的最大历史时长（毫秒）；`\"custom\"` 模式下忽略                             |\n| `audioContext`    | `AudioContext`                                        | 自动       | 外部 `AudioContext`；未传则内部创建                                                     |\n| `onUnderrun`      | `(detail: { bufferedMs: number }) => void`            | `-`        | 整条播放管线在播放中见底时触发                                                          |\n| `onPacketDrop`    | `(detail: { count: number; reason: string }) => void` | `-`        | 数据包因积压被丢弃时触发                                                                |\n| `onStateChange`   | `(state: StreamingPlayerState) => void`               | `-`        | 每次状态变化时触发                                                                      |\n\n`persistMode: \"indexeddb\"` 说明：\n\n- 数据包会旁路镜像写入 IndexedDB。\n- `replay()` 仍只读取当前实例的内存历史。\n- 重建播放器实例后，不会从 IndexedDB 恢复重播历史。\n\n`persistMode: \"custom\"` 说明：\n\n- 必须在首次 `push()` / `start()` 之前调用一次 `player.use(store)`。\n- 自定义 store 需要实现公开导出的 `PersistStore` 接口。\n- 保留时长、淘汰策略、容量上限完全由用户代码自己控制。\n- `destroy()` 不会对自定义 store 自动调用 `clear()`。\n\n句柄：`StreamingPlayerHandle`\n\n| 成员              | 类型                        | 说明                                                                                                                                                                                                                         |\n| ----------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `state`           | `StreamingPlayerState`      | 当前状态：`idle \\| buffering \\| playing \\| paused \\| stopped`                                                                                                                                                                |\n| `bufferedMs`      | `number`                    | 当前整条播放管线的总余量（毫秒），包含 `ReorderBuffer + JitterBuffer + 待解码时长 + 已调度未播完音频`                                                                                                                        |\n| `droppedPackets`  | `number`                    | 累计丢弃数据包数量                                                                                                                                                                                                           |\n| `storedMs`        | `number`                    | 持久化存储中的音频时长（毫秒），可用于展示可重播时长                                                                                                                                                                         |\n| `use(store)`      | `void`                      | 注册自定义 `PersistStore`；仅 `persistMode === \"custom\"` 且首次 `push()` / `start()` 之前可调用                                                                                                                              |\n| `push(packet)`    | `void`                      | 推入一个 `StreamingPacketPayload`；始终写入持久化存储，只有 `buffering / playing` 时才进入播放管线；若检测到 `sessionId` 变化（录音停止后重新开始），自动重置整条播放管线并丢弃旧 session 的积压数据，无需重新调用 `start()` |\n| `start()`         | `Promise<void>`             | 从 `idle` 切换到 `buffering`；从 live edge 起播，并用最近一小段历史作为启动垫片                                                                                                                                              |\n| `pause()`         | `void`                      | 暂停 `AudioContext` 并停止管线；新数据包仍写入持久化存储                                                                                                                                                                     |\n| `resume()`        | `void`                      | 重置管线积压并从新的 live 数据包恢复播放                                                                                                                                                                                     |\n| `setVolume(v)`    | `void`                      | 随时调整增益 `[0, 1]`                                                                                                                                                                                                        |\n| `replay(seconds)` | `void`                      | 从持久化存储播放最近 N 秒；仅暂停状态下有效                                                                                                                                                                                  |\n| `destroy()`       | `void`                      | 释放所有资源                                                                                                                                                                                                                 |\n| `onStateChange`   | `((state) => void) \\| null` | 创建后可直接赋值；`null` 表示取消监听                                                                                                                                                                                        |\n\n## 存储\n\n### `storage/opfs`\n\n#### Introduction\n\nOPFS 持久化后端。按 chunk 文件写入快照，适合长录音和更大的本地缓存。\n\n#### Quick Start\n\n```ts\nimport { createRecorder } from \"@csnight/audio-recorder\"\nimport {\n  createOpfsPersistencePlugin,\n  isSupport,\n} from \"@csnight/audio-recorder/storage/opfs\"\n\nif (!isSupport()) {\n  throw new Error(\"当前浏览器不支持 OPFS。\")\n}\n\nconst recorder = createRecorder({\n  storage: {\n    mode: \"auto\",\n    memoryThresholdBytes: 256 * 1024,\n    persistencePlugin: createOpfsPersistencePlugin(),\n  },\n})\n```\n\n#### API\n\n| Export                          | Description          |\n| ------------------------------- | -------------------- |\n| `createOpfsPersistencePlugin()` | 创建 OPFS 持久化插件 |\n| `isSupport()`                   | 检查 OPFS 是否可用   |\n\n通过主录音器 `storage` 选项使用：\n\n```ts\nimport { createRecorder } from \"@csnight/audio-recorder\"\nimport {\n  createOpfsPersistencePlugin,\n  isSupport,\n} from \"@csnight/audio-recorder/storage/opfs\"\n\nif (!isSupport()) {\n  throw new Error(\"当前浏览器不支持 OPFS。\")\n}\n\ncreateRecorder({\n  storage: {\n    mode: \"auto\",\n    persistencePlugin: createOpfsPersistencePlugin(),\n  },\n})\n```\n\n### `storage/indexeddb`\n\n#### Introduction\n\nIndexedDB 持久化后端。按 chunk 写入 object store，适合更通用的兼容路径。\n\n#### Quick Start\n\n```ts\nimport { createRecorder } from \"@csnight/audio-recorder\"\nimport {\n  createIndexedDbPersistencePlugin,\n  isSupport,\n} from \"@csnight/audio-recorder/storage/indexeddb\"\n\nif (!isSupport()) {\n  throw new Error(\"当前浏览器不支持 IndexedDB。\")\n}\n\nconst recorder = createRecorder({\n  storage: {\n    mode: \"auto\",\n    memoryThresholdBytes: 256 * 1024,\n    persistencePlugin: createIndexedDbPersistencePlugin(),\n  },\n})\n```\n\n#### API\n\n| Export                               | Description               |\n| ------------------------------------ | ------------------------- |\n| `createIndexedDbPersistencePlugin()` | 创建 IndexedDB 持久化插件 |\n| `isSupport()`                        | 检查 IndexedDB 是否可用   |\n\n通过主录音器 `storage` 选项使用：\n\n```ts\nimport { createRecorder } from \"@csnight/audio-recorder\"\nimport {\n  createIndexedDbPersistencePlugin,\n  isSupport,\n} from \"@csnight/audio-recorder/storage/indexeddb\"\n\nif (!isSupport()) {\n  throw new Error(\"当前浏览器不支持 IndexedDB。\")\n}\n\ncreateRecorder({\n  storage: {\n    mode: \"auto\",\n    persistencePlugin: createIndexedDbPersistencePlugin(),\n  },\n})\n```\n\n## 编码器\n\n所有导出编码器在 `sampleRate` 上都遵循同一套解析规则：\n\n- 如果显式传入了 `options.sampleRate`，且当前编码器支持该采样率，则直接使用\n- 如果显式传入了 `options.sampleRate`，但当前编码器不支持，则自动选择最近的受支持采样率\n- 如果没有传入 `options.sampleRate`，且输入快照的实际采样率受支持，则沿用实际采样率\n- 如果没有传入 `options.sampleRate`，且输入快照的实际采样率不受支持，则自动选择最近的受支持采样率\n\n对于“受支持采样率集合会被其他选项约束”的编码器，最近值的选择会在该受限集合内进行，例如 `amr` 的 `bandMode: \"nb\" | \"wb\"`，以及 `ac3` 和 `eac3` 两种模式。\n\n### `codecs/base`\n\n核心 PCM / WAV 支持。\n\n- `pcmExportEncoder`：导出原始 PCM 快照\n- `wavExportEncoder`：导出 WAV 文件\n- `pcmStreamEncoder`：实时输出 PCM chunk\n- `wavStreamEncoder`：实时输出 WAV chunk\n\n### `codecs/mp3`\n\n基于 WASM 编码器的 MP3 导出。\n\n- 适合更广泛的播放兼容性\n- 独立子路径暴露，避免增大根包体积\n\n### `codecs/flac`\n\n基于 WASM 编码器的无损 FLAC 导出。\n\n- 适合归档或后处理流程\n- 保持无损，但输出通常大于有损编码\n\n### `codecs/opus`\n\n基于 WASM 编码器的 Opus 导出。\n\n- 支持 `ogg` 和 `webm` 容器\n- 适合高压缩率的语音和通用音频场景\n\n### `codecs/aac`\n\n基于 WASM 编码器的 AAC 导出。\n\n- 适合需要 AAC elementary stream 的流程\n- 通过独立子路径暴露\n\n### `codecs/amr`\n\n基于 WASM 编码器的 AMR 导出。\n\n- 支持 `nb` 和 `wb`\n- 采样率解析受 `bandMode` 约束：`nb` 只会解析到 8 kHz，`wb` 只会解析到 16 kHz\n- 面向电话语音和 speech pipeline\n\n### `codecs/ac3`\n\n基于 WASM 编码器的 AC3 / E-AC3 导出。\n\n- 同一子路径同时暴露 `ac3` 和 `eac3` 两种快照编码器\n- 采样率解析受所选 codec 约束，因此 `ac3` 和 `eac3` 可能会解析到不同的最近受支持采样率\n- 适合 Dolby Digital 兼容分发或转码流程\n\n### `codecs/g711`\n\n纯 TypeScript 实现的 G.711 导出。\n\n- 支持 `alaw` 和 `ulaw`\n- 适合电话互通场景\n\n## 开发\n\n### 安装\n\n```bash\nnpm install\n```\n\n### 常用命令\n\n```bash\nnpm run dev\nnpm run build\nnpm run typecheck\nnpm run test\n```\n\n### 编译全部 WASM 编码器\n\n```bash\nnpm run build:wasm\n```\n\n该命令会通过 Docker 触发所有 WASM 编码器的构建流程。\n\n### 按需编译部分 WASM 编码器\n\n```bash\nnpm run build:wasm:select -- --codec=mp3\nnpm run build:wasm:select -- --codec=flac,opus\nnpm run build:wasm:select -- --codec=aac,amr,ac3\n```\n\n可选项由构建脚本驱动，当前对应 `scripts/wasm/` 下的各编码器构建入口。\n\n### 相关脚本\n\n| Command                                       | Description            |\n| --------------------------------------------- | ---------------------- |\n| `npm run build:wasm`                          | 编译全部 WASM 编码器   |\n| `npm run build:wasm:select -- --codec=<list>` | 只编译指定 WASM 编码器 |\n| `npm run benchmark:codecs`                    | 运行编码器基准测试     |\n| `npm run verify:exports`                      | 校验包导出入口         |\n\n### 脚本入口\n\n| Path                            | Description                    |\n| ------------------------------- | ------------------------------ |\n| `scripts/wasm/build-docker.mjs` | 基于 Docker 的 WASM 主构建入口 |\n| `scripts/wasm/build.mjs`        | 通用 WASM 构建编排             |\n| `scripts/wasm/build-ac3.mjs`    | AC3 / E-AC3 构建               |\n| `scripts/wasm/build-aac.mjs`    | AAC 构建                       |\n| `scripts/wasm/build-amr.mjs`    | AMR 构建                       |\n| `scripts/wasm/build-flac.mjs`   | FLAC 构建                      |\n| `scripts/wasm/build-mp3.mjs`    | MP3 构建                       |\n| `scripts/wasm/build-opus.mjs`   | Opus 构建                      |\n\n## 浏览器支持\n\n基于 `src/` 中的实际 API 使用和 `vite.config.ts` 的 `es2022` 目标。\n\n### 主库\n\n| Module                      | Chrome | Firefox | Safari | Notes                                                          |\n| --------------------------- | -----: | ------: | -----: | -------------------------------------------------------------- |\n| Core recorder               |     66 |      76 |   14.1 | `AudioWorkletNode` 是稳定基线                                  |\n| Auto input fallback         |     66 |      76 |   14.1 | PCM `MediaRecorder` 不可用时回退到 `audio-worklet`             |\n| `media-recorder` path       |    105 |       - |      - | 使用 `MediaRecorder.isTypeSupported(\"audio/webm; codecs=pcm\")` |\n| `script-processor` fallback |     35 |      25 |      6 | 仅保底                                                         |\n\n### 插件\n\n| Plugin                | Chrome | Firefox | Safari | Notes                       |\n| --------------------- | -----: | ------: | -----: | --------------------------- |\n| `level-meter`         |     66 |      76 |   14.1 | PCM 帧消费者                |\n| `streaming-export`    |     66 |      76 |   14.1 | Worker 分片导出             |\n| `sonic-export`        |     66 |      76 |   14.1 | 旁路 Sonic 处理 + 分片导出  |\n| `dsp`                 |     66 |      76 |   14.1 | 主链路逐帧 DSP + 有界尾帧   |\n| `asr-export`          |     66 |      76 |   14.1 | PCM 切块 + 编码器           |\n| `frequency-histogram` |     66 |      76 |   14.1 | 录音帧上的纯 TS FFT 分析    |\n| `dtmf`                |     66 |      76 |   14.1 | 实时 DTMF 检测 + 离线按键音 |\n| `nmn2pcm`             |     57 |      52 |     11 | 纯 TypeScript 简谱转 PCM    |\n\n### 编码器\n\n| Codec | Chrome | Firefox | Safari | Notes               |\n| ----- | -----: | ------: | -----: | ------------------- |\n| PCM   |     57 |      52 |     11 | 纯 typed array 处理 |\n| WAV   |     57 |      52 |     11 | 纯文件封装          |\n| G.711 |     57 |      52 |     11 | 纯算术              |\n| MP3   |     57 |      52 |     11 | WASM 编码器         |\n| FLAC  |     57 |      52 |     11 | WASM 编码器         |\n| Opus  |     57 |      52 |     11 | WASM 编码器         |\n| AAC   |     57 |      52 |     11 | WASM 编码器         |\n| AMR   |     57 |      52 |     11 | WASM 编码器         |\n| AC3   |     57 |      52 |     11 | WASM 编码器         |\n| E-AC3 |     57 |      52 |     11 | WASM 编码器         |\n\n### 存储\n\n| Module              | Chrome | Firefox | Safari | Notes                              |\n| ------------------- | -----: | ------: | -----: | ---------------------------------- |\n| `storage/indexeddb` |     24 |      16 |      8 | 标准 IndexedDB                     |\n| `storage/opfs`      |    102 |     111 |   15.2 | `navigator.storage.getDirectory()` |\n\n## 基准测试\n\n最近一次记录：2026-06-28。\n\n### 汇总\n\n| Codec | Variant | Scenario  | Avg ms |    RTF x |   Bytes |\n| ----- | ------- | --------- | -----: | -------: | ------: |\n| pcm   | default | snapshot  |   0.48 | 31493.03 | 1440000 |\n| pcm   | default | streaming |   3.82 |  3987.75 | 1440000 |\n| wav   | default | snapshot  |   1.06 | 14286.82 | 1440044 |\n| wav   | default | streaming |   2.12 |  8476.78 | 1440352 |\n| mp3   | default | snapshot  | 208.64 |    74.52 |  240384 |\n| mp3   | default | streaming | 202.66 |    77.35 |  240384 |\n| flac  | default | snapshot  |  11.04 |  1374.19 |  679568 |\n| flac  | default | streaming |  10.37 |  1447.88 |  679568 |\n| opus  | ogg     | snapshot  |  49.84 |   305.77 |  262774 |\n| opus  | ogg     | streaming |  49.66 |   307.30 |  263229 |\n| opus  | webm    | snapshot  |  48.38 |   314.75 |  246569 |\n| opus  | webm    | streaming |  48.40 |   315.27 |  246569 |\n| aac   | default | snapshot  |  94.14 |   159.52 |  245066 |\n| aac   | default | streaming |  96.38 |   155.70 |  245066 |\n| amr   | nb      | snapshot  |  29.05 |   516.49 |   24006 |\n| amr   | nb      | streaming |  29.09 |   515.67 |   24006 |\n| amr   | wb      | snapshot  |  59.24 |   253.26 |   45759 |\n| amr   | wb      | streaming |  59.31 |   252.93 |   45759 |\n\n### SIMD\n\n| Codec | Variant | Scenario  | off/on |\n| ----- | ------- | --------- | -----: |\n| flac  | default | snapshot  |  1.370 |\n| flac  | default | streaming |  1.305 |\n| opus  | ogg     | snapshot  |  1.130 |\n| opus  | ogg     | streaming |  1.118 |\n| opus  | webm    | snapshot  |  1.215 |\n| opus  | webm    | streaming |  1.264 |\n| aac   | default | snapshot  |  1.377 |\n| aac   | default | streaming |  1.361 |\n| amr   | nb      | snapshot  |  1.055 |\n| amr   | nb      | streaming |  1.097 |\n| amr   | wb      | snapshot  |  1.107 |\n| amr   | wb      | streaming |  1.126 |\n\n## 架构\n\n当前执行链：\n\n```text\ncreateRecorder\n  -> RecorderController\n  -> BrowserInputAdapter\n  -> BrowserInputSession\n  -> input backend\n  -> PluginHost.onBeforeFrame\n  -> PcmFramePipeline\n  -> PcmBufferStore\n  -> onFrame / encoders / persistence\n```\n\n说明：\n\n- 根入口不会自动注册编码器\n- 插件都是显式启用的独立子路径\n- `dsp` 插件会直接改写主链路 PCM，并可在 `stop()` 时补出有界尾帧\n- `streaming-export`、`sonic-export`、`asr-export`、`frequency-histogram`、`dtmf` 都是独立扩展\n- `nmn2pcm` 是位于插件子路径体系下的独立简谱转 PCM 工具\n- `opfs` 和 `indexeddb` 是可选持久化后端\n\n详细架构文档：\n\n- [docs/architecture/execution-chain.md](./docs/architecture/execution-chain.md)\n- [docs/README.md](./docs/README.md)\n\n## 引用\n\n鸣谢：\n\n- [Recorder](https://github.com/xiangyuecn/Recorder)：录音实现参考\n- [Mediabunny](https://github.com/Vanilagy/mediabunny)：编码器与打包参考\n- WASM 编译流程所用上游编码库：\n  - [libopus](https://github.com/xiph/opus)\n  - [LAME](https://sourceforge.net/projects/lame/)\n  - [libFLAC](https://github.com/xiph/flac)\n  - [FFmpeg](https://ffmpeg.org/)\n  - [opencore-amr](https://github.com/mstorsjo/opencore-amr)\n  - [vo-amrwbenc](https://sourceforge.net/projects/opencore-amr/)\n","readmeFilename":"README.zh-CN.md"}