{"_id":"@abhayaku/rn-vad","_rev":"4-21cb3eed9f2e3126133a3cc57ddac2e1","name":"@abhayaku/rn-vad","dist-tags":{"latest":"0.2.5"},"versions":{"0.2.2":{"name":"@abhayaku/rn-vad","version":"0.2.2","keywords":["react-native","ios","android","vad","voice-activity-detection","speech-detection","speech-recognition","webrtc","microphone","audio","turbomodule","new-architecture"],"author":{"url":"https://github.com/Abhayaku","name":"abhayaku","email":"abhayaku21@gmail.com"},"license":"UNLICENSED","_id":"@abhayaku/rn-vad@0.2.2","maintainers":[{"name":"abhayaku","email":"abhayaku21@gmail.com"}],"homepage":"https://github.com/Abhayaku/rn-vad#readme","bugs":{"url":"https://github.com/Abhayaku/rn-vad/issues"},"dist":{"shasum":"fa2e2130accb0d88d8760912e480b294fcd6c7f0","tarball":"https://registry.npmjs.org/@abhayaku/rn-vad/-/rn-vad-0.2.2.tgz","fileCount":104,"integrity":"sha512-rgi2irg9NIy1nWYbeflvYcMPBU0WlP06NzMU7w+pIHWDJk2uBK+eLr+6f8nDhluMCnkPpgK3Hw3AG3y4SCfmQg==","signatures":[{"sig":"MEQCIFjJdGmbVau4pLKdsYab8jVFYIN8bcdC5bAKNOwYeY9eAiA9lfTleKVX8lcLitSXpvSLwPZj2x/Kj+Si/Tnxt4DlLw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":383723},"jest":{"preset":"@react-native/jest-preset","modulePathIgnorePatterns":["<rootDir>/example/node_modules","<rootDir>/lib/"]},"main":"./lib/module/index.js","types":"./lib/typescript/src/index.d.ts","exports":{".":{"types":"./lib/typescript/src/index.d.ts","source":"./src/index.tsx","default":"./lib/module/index.js"},"./package.json":"./package.json"},"gitHead":"429911823aa7295c9f0a94a48bc8df94d8b0dcd2","scripts":{"web":"vite","lint":"eslint \"**/*.{js,ts,tsx}\"","test":"jest","clean":"del-cli android/build example/android/build example/android/app/build example/ios/build lib","example":"yarn workspace rn-vad-example","prepare":"bob build","release":"release-it --only-version","build:web":"vite build","typecheck":"tsc"},"_npmUser":{"name":"abhayaku","email":"abhayaku21@gmail.com"},"prettier":{"useTabs":false,"tabWidth":2,"quoteProps":"consistent","singleQuote":true,"trailingComma":"es5"},"commitlint":{"extends":["@commitlint/config-conventional"]},"release-it":{"git":{"tagName":"v${version}","commitMessage":"chore: release ${version}"},"npm":{"publish":false},"github":{"release":true},"plugins":{"@release-it/conventional-changelog":{"preset":{"name":"angular"}}}},"repository":{"url":"git+https://github.com/Abhayaku/rn-vad.git","type":"git"},"workspaces":["example"],"_npmVersion":"11.6.2","description":"Real-time Voice Activity Detection (VAD) for React Native — speech, noise, and silence classification using WebRTC VAD with adaptive noise floor.","directories":{},"_nodeVersion":"24.13.0","codegenConfig":{"name":"RnVadSpec","type":"modules","android":{"javaPackageName":"com.rnvad"},"jsSrcsDir":"src"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"packageManager":"yarn@4.11.0","devDependencies":{"jest":"^29.0.0","react":"19.2.3","turbo":"^2.8.21","eslint":"^9.39.4","del-cli":"^7.0.0","lefthook":"^2.1.4","prettier":"^3.8.1","@eslint/js":"^10.0.1","commitlint":"^20.5.0","release-it":"^19.2.4","typescript":"^6.0.2","@types/react":"^19.2.0","react-native":"0.85.0","@jest/globals":"^29.0.0","@eslint/compat":"^2.0.3","@eslint/eslintrc":"^3.3.5","react-native-web":"~0.21.1","eslint-plugin-ft-flow":"^3.0.11","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","react-native-builder-bob":"^0.41.0","@react-native/jest-preset":"0.85.0","@react-native/babel-preset":"0.85.0","@react-native/eslint-config":"0.85.0","@commitlint/config-conventional":"^20.5.0","@release-it/conventional-changelog":"^10.0.6"},"peerDependencies":{"react":"*","react-native":"*"},"_npmOperationalInternal":{"tmp":"tmp/rn-vad_0.2.2_1780167264755_0.11606862817658281","host":"s3://npm-registry-packages-npm-production"},"react-native-builder-bob":{"output":"lib","source":"src","targets":[["module",{"esm":true}],["typescript",{"project":"tsconfig.build.json"}]]},"create-react-native-library":{"type":"turbo-module","tools":["eslint","jest","lefthook","release-it","vite"],"version":"0.62.0","languages":"kotlin-objc"}},"0.2.3":{"name":"@abhayaku/rn-vad","version":"0.2.3","keywords":["react-native","ios","android","vad","voice-activity-detection","speech-detection","speech-recognition","webrtc","microphone","audio","turbomodule","new-architecture"],"author":{"url":"https://github.com/Abhayaku","name":"abhayaku","email":"abhayaku21@gmail.com"},"license":"UNLICENSED","_id":"@abhayaku/rn-vad@0.2.3","maintainers":[{"name":"abhayaku","email":"abhayaku21@gmail.com"}],"homepage":"https://github.com/Abhayaku/rn-vad#readme","bugs":{"url":"https://github.com/Abhayaku/rn-vad/issues"},"dist":{"shasum":"1c4392c36b753714010895580c60ea4ab121b04a","tarball":"https://registry.npmjs.org/@abhayaku/rn-vad/-/rn-vad-0.2.3.tgz","fileCount":104,"integrity":"sha512-86oT5vm6TX9H9bZh/TVqAHxf98GdePgHBuUQ9JV48qyfWqr0d0ozZrEHRlNqpLAJjyivPT5Q8H01yzJZENqeUQ==","signatures":[{"sig":"MEUCIAGP81rCE2lE+KPvCoBKbKdeTOOipjqo63E3vv3dDdjTAiEAq6f6yDWm7nXg09N8VrUf3vaJf4tFOE9ivBbfqyKCRIE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":383723},"jest":{"preset":"@react-native/jest-preset","modulePathIgnorePatterns":["<rootDir>/example/node_modules","<rootDir>/lib/"]},"main":"./lib/module/index.js","types":"./lib/typescript/src/index.d.ts","exports":{".":{"types":"./lib/typescript/src/index.d.ts","source":"./src/index.tsx","default":"./lib/module/index.js"},"./package.json":"./package.json"},"gitHead":"22c1cfc136a019866787a42807e2e26b06ff44c1","scripts":{"web":"vite","lint":"eslint \"**/*.{js,ts,tsx}\"","test":"jest","clean":"del-cli android/build example/android/build example/android/app/build example/ios/build lib","example":"yarn workspace rn-vad-example","prepare":"bob build","release":"release-it --only-version","build:web":"vite build","typecheck":"tsc"},"_npmUser":{"name":"abhayaku","email":"abhayaku21@gmail.com"},"prettier":{"useTabs":false,"tabWidth":2,"quoteProps":"consistent","singleQuote":true,"trailingComma":"es5"},"commitlint":{"extends":["@commitlint/config-conventional"]},"release-it":{"git":{"tagName":"v${version}","commitMessage":"chore: release ${version}"},"npm":{"publish":false},"github":{"release":true},"plugins":{"@release-it/conventional-changelog":{"preset":{"name":"angular"}}}},"repository":{"url":"git+https://github.com/Abhayaku/rn-vad.git","type":"git"},"workspaces":["example"],"_npmVersion":"11.6.2","description":"Real-time Voice Activity Detection (VAD) for React Native — speech, noise, and silence classification using WebRTC VAD with adaptive noise floor.","directories":{},"_nodeVersion":"24.13.0","codegenConfig":{"name":"RnVadSpec","type":"modules","android":{"javaPackageName":"com.rnvad"},"jsSrcsDir":"src"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"packageManager":"yarn@4.11.0","devDependencies":{"jest":"^29.0.0","react":"19.2.3","turbo":"^2.8.21","eslint":"^9.39.4","del-cli":"^7.0.0","lefthook":"^2.1.4","prettier":"^3.8.1","@eslint/js":"^10.0.1","commitlint":"^20.5.0","release-it":"^19.2.4","typescript":"^6.0.2","@types/react":"^19.2.0","react-native":"0.85.0","@jest/globals":"^29.0.0","@eslint/compat":"^2.0.3","@eslint/eslintrc":"^3.3.5","react-native-web":"~0.21.1","eslint-plugin-ft-flow":"^3.0.11","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","react-native-builder-bob":"^0.41.0","@react-native/jest-preset":"0.85.0","@react-native/babel-preset":"0.85.0","@react-native/eslint-config":"0.85.0","@commitlint/config-conventional":"^20.5.0","@release-it/conventional-changelog":"^10.0.6"},"peerDependencies":{"react":"*","react-native":"*"},"_npmOperationalInternal":{"tmp":"tmp/rn-vad_0.2.3_1780197364228_0.3009259883989799","host":"s3://npm-registry-packages-npm-production"},"react-native-builder-bob":{"output":"lib","source":"src","targets":[["module",{"esm":true}],["typescript",{"project":"tsconfig.build.json"}]]},"create-react-native-library":{"type":"turbo-module","tools":["eslint","jest","lefthook","release-it","vite"],"version":"0.62.0","languages":"kotlin-objc"}},"0.2.4":{"name":"@abhayaku/rn-vad","version":"0.2.4","keywords":["react-native","ios","android","vad","voice-activity-detection","speech-detection","speech-recognition","webrtc","microphone","audio","turbomodule","new-architecture"],"author":{"url":"https://github.com/Abhayaku","name":"abhayaku","email":"abhayaku21@gmail.com"},"license":"UNLICENSED","_id":"@abhayaku/rn-vad@0.2.4","maintainers":[{"name":"abhayaku","email":"abhayaku21@gmail.com"}],"homepage":"https://github.com/Abhayaku/rn-vad#readme","bugs":{"url":"https://github.com/Abhayaku/rn-vad/issues"},"dist":{"shasum":"260f007def8bf4d42b55e9e6695e17bb4d01308c","tarball":"https://registry.npmjs.org/@abhayaku/rn-vad/-/rn-vad-0.2.4.tgz","fileCount":104,"integrity":"sha512-w9ZAKPEePkUYEEXQVI4jwfkTjvjZerLL/igFmwLypoXSe5Go50cDHvPi+R5exipC29PEO/HXygPcrScayM93Hw==","signatures":[{"sig":"MEUCIQD3h1fDzR7hL2QeqAOuul9GRC7Vq3GxZ7+fS0T05BrijgIgcbPHr9qIZ9bDvYI6GelUtycSHVaSrT4VIeqGcPGtxtU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":383767},"jest":{"preset":"@react-native/jest-preset","modulePathIgnorePatterns":["<rootDir>/example/node_modules","<rootDir>/lib/"]},"main":"./lib/module/index.js","types":"./lib/typescript/src/index.d.ts","exports":{".":{"types":"./lib/typescript/src/index.d.ts","source":"./src/index.tsx","default":"./lib/module/index.js"},"./package.json":"./package.json"},"gitHead":"46e3d17231d272139395a61c5b780562054c31ef","scripts":{"web":"vite","lint":"eslint \"**/*.{js,ts,tsx}\"","test":"jest","clean":"del-cli android/build example/android/build example/android/app/build example/ios/build lib","example":"yarn workspace rn-vad-example","prepare":"bob build","release":"release-it --only-version","build:web":"vite build","typecheck":"tsc"},"_npmUser":{"name":"abhayaku","email":"abhayaku21@gmail.com"},"prettier":{"useTabs":false,"tabWidth":2,"quoteProps":"consistent","singleQuote":true,"trailingComma":"es5"},"commitlint":{"extends":["@commitlint/config-conventional"]},"release-it":{"git":{"tagName":"v${version}","commitMessage":"chore: release ${version}"},"npm":{"publish":false},"github":{"release":true},"plugins":{"@release-it/conventional-changelog":{"preset":{"name":"angular"}}}},"repository":{"url":"git+https://github.com/Abhayaku/rn-vad.git","type":"git"},"workspaces":["example"],"_npmVersion":"11.6.2","description":"Real-time Voice Activity Detection (VAD) for React Native — speech, noise, and silence classification using WebRTC VAD with adaptive noise floor.","directories":{},"_nodeVersion":"24.13.0","codegenConfig":{"name":"RnVadSpec","type":"modules","android":{"javaPackageName":"com.rnvad"},"jsSrcsDir":"src"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"packageManager":"yarn@4.11.0","devDependencies":{"jest":"^29.0.0","react":"19.2.3","turbo":"^2.8.21","eslint":"^9.39.4","del-cli":"^7.0.0","lefthook":"^2.1.4","prettier":"^3.8.1","@eslint/js":"^10.0.1","commitlint":"^20.5.0","release-it":"^19.2.4","typescript":"^6.0.2","@types/react":"^19.2.0","react-native":"0.85.0","@jest/globals":"^29.0.0","@eslint/compat":"^2.0.3","@eslint/eslintrc":"^3.3.5","react-native-web":"~0.21.1","eslint-plugin-ft-flow":"^3.0.11","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","react-native-builder-bob":"^0.41.0","@react-native/jest-preset":"0.85.0","@react-native/babel-preset":"0.85.0","@react-native/eslint-config":"0.85.0","@commitlint/config-conventional":"^20.5.0","@release-it/conventional-changelog":"^10.0.6"},"peerDependencies":{"react":"*","react-native":"*"},"_npmOperationalInternal":{"tmp":"tmp/rn-vad_0.2.4_1781943085428_0.9572848417305657","host":"s3://npm-registry-packages-npm-production"},"react-native-builder-bob":{"output":"lib","source":"src","targets":[["module",{"esm":true}],["typescript",{"project":"tsconfig.build.json"}]]},"create-react-native-library":{"type":"turbo-module","tools":["eslint","jest","lefthook","release-it","vite"],"version":"0.62.0","languages":"kotlin-objc"}},"0.2.5":{"name":"@abhayaku/rn-vad","version":"0.2.5","description":"Real-time Voice Activity Detection (VAD) for React Native — speech, noise, and silence classification using WebRTC VAD with adaptive noise floor.","main":"./lib/module/index.js","types":"./lib/typescript/src/index.d.ts","exports":{".":{"source":"./src/index.tsx","types":"./lib/typescript/src/index.d.ts","default":"./lib/module/index.js"},"./package.json":"./package.json"},"scripts":{"example":"yarn workspace rn-vad-example","clean":"del-cli android/build example/android/build example/android/app/build example/ios/build lib","prepare":"bob build","typecheck":"tsc","lint":"eslint \"**/*.{js,ts,tsx}\"","test":"jest","release":"release-it --only-version","web":"vite","build:web":"vite build"},"keywords":["react-native","ios","android","vad","voice-activity-detection","speech-detection","speech-recognition","webrtc","microphone","audio","turbomodule","new-architecture"],"repository":{"type":"git","url":"git+https://github.com/Abhayaku/rn-vad.git"},"author":{"name":"abhayaku","email":"abhayaku21@gmail.com","url":"https://github.com/Abhayaku"},"license":"UNLICENSED","bugs":{"url":"https://github.com/Abhayaku/rn-vad/issues"},"homepage":"https://github.com/Abhayaku/rn-vad#readme","publishConfig":{"registry":"https://registry.npmjs.org/","access":"public"},"devDependencies":{"@commitlint/config-conventional":"^20.5.0","@eslint/compat":"^2.0.3","@eslint/eslintrc":"^3.3.5","@eslint/js":"^10.0.1","@jest/globals":"^29.0.0","@react-native/babel-preset":"0.85.0","@react-native/eslint-config":"0.85.0","@react-native/jest-preset":"0.85.0","@release-it/conventional-changelog":"^10.0.6","@types/react":"^19.2.0","commitlint":"^20.5.0","del-cli":"^7.0.0","eslint":"^9.39.4","eslint-config-prettier":"^10.1.8","eslint-plugin-ft-flow":"^3.0.11","eslint-plugin-prettier":"^5.5.5","jest":"^29.0.0","lefthook":"^2.1.4","prettier":"^3.8.1","react":"19.2.3","react-native":"0.85.0","react-native-builder-bob":"^0.41.0","react-native-web":"~0.21.1","release-it":"^19.2.4","turbo":"^2.8.21","typescript":"^6.0.2"},"peerDependencies":{"react":"*","react-native":"*"},"workspaces":["example"],"packageManager":"yarn@4.11.0","react-native-builder-bob":{"source":"src","output":"lib","targets":[["module",{"esm":true}],["typescript",{"project":"tsconfig.build.json"}]]},"codegenConfig":{"name":"RnVadSpec","type":"modules","jsSrcsDir":"src","android":{"javaPackageName":"com.rnvad"}},"prettier":{"quoteProps":"consistent","singleQuote":true,"tabWidth":2,"trailingComma":"es5","useTabs":false},"jest":{"preset":"@react-native/jest-preset","modulePathIgnorePatterns":["<rootDir>/example/node_modules","<rootDir>/lib/"]},"commitlint":{"extends":["@commitlint/config-conventional"]},"release-it":{"git":{"commitMessage":"chore: release ${version}","tagName":"v${version}"},"npm":{"publish":false},"github":{"release":true},"plugins":{"@release-it/conventional-changelog":{"preset":{"name":"angular"}}}},"create-react-native-library":{"type":"turbo-module","languages":"kotlin-objc","tools":["eslint","jest","lefthook","release-it","vite"],"version":"0.62.0"},"gitHead":"144e316045450f17076dfe0d9dc3bf036b5b8310","_id":"@abhayaku/rn-vad@0.2.5","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-Y3f7mTp+Uh+SHcSta5HCyVdsKuj7Uu13sCXRcnsjN3z0aiK0c6s66MzgY1q7tQnUwPbDvkiUpSUSFH5hk8Ubtg==","shasum":"4c2b6b238e548568c8eac1374799028dd30f4c78","tarball":"https://registry.npmjs.org/@abhayaku/rn-vad/-/rn-vad-0.2.5.tgz","fileCount":104,"unpackedSize":384078,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQD9ionlHcBk/an3h0oDubyU7cbqrmNTprTCQkqXKud7ugIgajs2brkSo78fcUf3Z8Hzx083TxMW3RD/QDc0/L4ftfI="}]},"_npmUser":{"name":"abhayaku","email":"abhayaku21@gmail.com"},"directories":{},"maintainers":[{"name":"abhayaku","email":"abhayaku21@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/rn-vad_0.2.5_1782991500037_0.816292164389425"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-30T18:54:24.564Z","modified":"2026-07-02T11:25:00.390Z","0.2.2":"2026-05-30T18:54:24.901Z","0.2.3":"2026-05-31T03:16:04.381Z","0.2.4":"2026-06-20T08:11:25.557Z","0.2.5":"2026-07-02T11:25:00.182Z"},"bugs":{"url":"https://github.com/Abhayaku/rn-vad/issues"},"author":{"name":"abhayaku","email":"abhayaku21@gmail.com","url":"https://github.com/Abhayaku"},"license":"UNLICENSED","homepage":"https://github.com/Abhayaku/rn-vad#readme","keywords":["react-native","ios","android","vad","voice-activity-detection","speech-detection","speech-recognition","webrtc","microphone","audio","turbomodule","new-architecture"],"repository":{"type":"git","url":"git+https://github.com/Abhayaku/rn-vad.git"},"description":"Real-time Voice Activity Detection (VAD) for React Native — speech, noise, and silence classification using WebRTC VAD with adaptive noise floor.","maintainers":[{"name":"abhayaku","email":"abhayaku21@gmail.com"}],"readme":"# rn-vad\n\nReal-time Voice Activity Detection (VAD) for React Native. Classifies microphone input as **speech**, **noise**, or **silence** on every audio frame — on both iOS and Android.\n\nBuilt on [libfvad](https://github.com/dpirch/libfvad) (WebRTC VAD C library, BSD-3-Clause). New Architecture (TurboModules) only.\n\n---\n\n## Features\n\n- Real-time speech vs silence detection\n- Three-way classification: `speech` | `noise` | `silence`\n- Adaptive noise floor — threshold adjusts to ambient noise in real time\n- Configurable aggressiveness (4 WebRTC VAD modes)\n- Energy/dBFS level events for waveform meters\n- Raw PCM chunk callbacks (for streaming to STT)\n- Auto-save speech segments as WAV files\n- Microphone permission helper\n- TypeScript — fully typed\n- React hook (`useVAD`) + imperative (`VAD`) API\n- iOS: AVAudioEngine · Android: AudioRecord + NDK/CMake\n\n---\n\n## Requirements\n\n| Requirement | Version |\n|---|---|\n| React Native | >= 0.71 (New Architecture enabled) |\n| iOS | >= 13.0 |\n| Android minSdk | 24 |\n| NDK | r21+ |\n\n---\n\n## Installation\n\n```sh\nnpm install rn-vad\n# or\nyarn add rn-vad\n```\n\n### iOS\n\n```sh\ncd ios && pod install\n```\n\nNo extra setup — libfvad C sources are vendored in the package.\n\nAdd the microphone usage description to your app's `Info.plist`:\n\n```xml\n<key>NSMicrophoneUsageDescription</key>\n<string>Microphone is required for voice detection.</string>\n```\n\n### Android\n\n`RECORD_AUDIO` is declared in the library manifest and merged automatically. No manual changes needed.\n\n---\n\n## Quick Start\n\n### Hook API (recommended for React components)\n\n```tsx\nimport { useVAD } from 'rn-vad';\n\nfunction MicButton() {\n  const { isSpeaking, isNoise, energyDb, isRunning, start, stop } = useVAD({\n    mode: 2,\n    onSpeechStart: () => console.log('speech started'),\n    onSpeechEnd: (e) => {\n      console.log(`ended — ${e.duration}ms`);\n      if (e.segmentPath) console.log('saved to:', e.segmentPath);\n    },\n  });\n\n  return (\n    <>\n      <Text>{isSpeaking ? 'SPEAKING' : isNoise ? 'NOISE' : 'SILENT'}</Text>\n      <Text>{energyDb.toFixed(1)} dBFS</Text>\n      <Button title={isRunning ? 'Stop' : 'Start'} onPress={isRunning ? stop : start} />\n    </>\n  );\n}\n```\n\n### Imperative API (for services / outside components)\n\n```ts\nimport { VAD } from 'rn-vad';\n\nconst granted = await VAD.requestMicPermission();\nif (!granted) return;\n\nawait VAD.configure({\n  mode: 2,\n  silenceTimeoutMs: 500,\n  recordSegments: true,\n});\n\nawait VAD.start();\n\nconst unsub1 = VAD.on('voiceActivity', (e) => {\n  console.log(e.type, e.energyDb); // 'speech' | 'noise' | 'silence'\n});\n\nconst unsub2 = VAD.on('speechEnd', (e) => {\n  console.log('WAV saved at:', e.segmentPath);\n});\n\n// Later:\nawait VAD.stop();\nunsub1();\nunsub2();\n```\n\n---\n\n## API Reference\n\n### `VAD.configure(options): Promise<void>`\n\nMust be called before `start()`. All options are optional — defaults are applied for any omitted field.\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `sampleRate` | `8000\\|16000\\|32000\\|48000` | `16000` | Microphone sample rate (Hz) |\n| `frameMs` | `10\\|20\\|30` | `20` | Frame duration fed to WebRTC VAD |\n| `mode` | `0\\|1\\|2\\|3` | `2` | VAD aggressiveness (see table below) |\n| `silenceTimeoutMs` | `number` | `500` | Silence ms before `speechEnd` fires |\n| `noiseThresholdDb` | `number` | `-30` | Fixed dBFS threshold when `adaptiveThreshold: false` |\n| `speechOnsetMs` | `number` | `150` | Consecutive speech ms required before `speechStart` fires — prevents noise spikes from triggering |\n| `emitPcm` | `boolean` | `false` | Emit raw PCM via `pcmData` event |\n| `recordSegments` | `boolean` | `false` | Auto-save speech segments as WAV files |\n| `segmentOutputDir` | `string` | system temp | Output directory for WAV files |\n| `adaptiveThreshold` | `boolean` | `true` | Adapt noise floor to ambient in real time. When `false`, uses fixed `noiseThresholdDb` |\n| `adaptiveMarginDb` | `number` | `15` | dB above the adaptive noise floor that sets the speech threshold |\n| `adaptationRate` | `number` | `0.995` | EMA alpha for upward floor drift (0–1). Higher = slower adaptation |\n| `initialNoiseFloor` | `number` | `-45` | dBFS starting estimate before adaptation kicks in |\n| `minNoiseFloor` | `number` | `-80` | dBFS floor clamp — noise floor never drops below this |\n\n#### VAD Modes\n\n| Mode | Name | Best for |\n|---|---|---|\n| `0` | Quality | Clean studio audio |\n| `1` | Low bitrate | Telephony / low bandwidth |\n| `2` | Aggressive | General use ← **default** |\n| `3` | Very aggressive | Noisy environments |\n\n### `VAD.start(): Promise<void>`\n\nStarts microphone capture and VAD processing.\n\n### `VAD.stop(): Promise<void>`\n\nStops capture. Native resources kept alive for fast restart.\n\n### `VAD.destroy(): Promise<void>`\n\nStops capture and fully releases all native resources.\n\n### `VAD.isRunning(): Promise<boolean>`\n\n### `VAD.requestMicPermission(): Promise<boolean>`\n\nRequests microphone permission. Returns whether granted.\n- iOS: calls `AVAudioSession.requestRecordPermission`\n- Android: calls `PermissionsAndroid.request(RECORD_AUDIO)` — shows OS permission dialog\n\n### `VAD.on(event, callback): () => void`\n\nSubscribes to an event. Returns an unsubscribe function.\n\n---\n\n## Events\n\n### `voiceActivity` — fires every frame\n\n```ts\nVAD.on('voiceActivity', (e: VADActivity) => {\n  e.isSpeaking  // true for the full duration of a speech segment (speechStart → speechEnd)\n  e.type        // 'speech' | 'noise' | 'silence' — per-frame classification\n  e.energyDb    // dBFS of current frame (typically -160 to 0)\n  e.noiseFloor  // dBFS — current adaptive noise floor estimate\n  e.threshold   // dBFS — active speech threshold (noiseFloor + adaptiveMarginDb)\n  e.timestamp   // epoch ms\n});\n```\n\n> `isSpeaking` reflects the FSM state — it is `true` from `speechStart` through `speechEnd`, regardless of per-frame energy dips. `type` reflects the current frame's per-frame classification.\n\n### `speechStart` — speech segment began\n\n```ts\nVAD.on('speechStart', (e: SpeechStartEvent) => {\n  e.timestamp\n});\n```\n\n### `speechEnd` — speech segment ended\n\nFires after `silenceTimeoutMs` of consecutive non-speech frames.\n\n```ts\nVAD.on('speechEnd', (e: SpeechEndEvent) => {\n  e.duration      // ms of speech segment\n  e.timestamp     // epoch ms\n  e.segmentPath   // absolute path to WAV (only when recordSegments: true)\n});\n```\n\n### `pcmData` — raw PCM (only when `emitPcm: true`)\n\n```ts\nVAD.on('pcmData', (e: PCMDataEvent) => {\n  e.data        // number[] — int16 samples\n  e.sampleRate\n  e.timestamp\n});\n```\n\n### `error`\n\n```ts\nVAD.on('error', (e: VADError) => {\n  e.code\n  e.message\n});\n```\n\n---\n\n## Classification Logic\n\n```\nthreshold = adaptiveThreshold\n  ? noiseFloor + adaptiveMarginDb   ← real-time adaptive (default)\n  : noiseThresholdDb                ← fixed\n\nPer-frame signal (outside a speech segment):\n  energyDb > threshold  AND  webrtcVad == 1  →  type = 'noise'  (speech onset accumulating)\n  energyDb > threshold  AND  webrtcVad == 0  →  type = 'noise'\n  energyDb ≤ threshold                        →  type = 'silence'\n\nDuring a speech segment (after speechStart fires):\n  type = 'speech', isSpeaking = true — until silenceTimeoutMs of non-speech elapses\n```\n\nSpeech onset requires `speechOnsetMs` of consecutive speech signal before `speechStart` fires. A single non-speech frame resets the onset counter.\n\n### Adaptive noise floor\n\nWhen `adaptiveThreshold: true` (default), the noise floor tracks ambient energy using an asymmetric EMA:\n\n- **Downward (room quieter):** α = 0.90 — adapts in ~200ms\n- **Upward (room louder):** α = `adaptationRate` (default 0.995) — resists noise bursts, drifts gradually\n\nThe floor only updates when the VAD is **not in a speech segment** and **not in the post-speech hold window** (30 frames ≈ 600ms after each `speechEnd`). This prevents speech and reverb from corrupting the floor estimate.\n\n---\n\n## `useVAD` Hook\n\n```ts\nconst result = useVAD(options: UseVADOptions): UseVADResult\n```\n\n### Options (`UseVADOptions`)\n\nAll `VADOptions` fields plus:\n\n| Field | Type |\n|---|---|\n| `onSpeechStart` | `(e: SpeechStartEvent) => void` |\n| `onSpeechEnd` | `(e: SpeechEndEvent) => void` |\n| `onVoiceActivity` | `(e: VADActivity) => void` |\n| `onError` | `(e: VADError) => void` |\n\n### Returns (`UseVADResult`)\n\n| Field | Type | Description |\n|---|---|---|\n| `isSpeaking` | `boolean` | `true` for the full duration of a speech segment |\n| `isNoise` | `boolean` | Current frame is above threshold but not in a speech segment |\n| `isSilence` | `boolean` | Current frame is below threshold |\n| `energyDb` | `number` | Current frame energy in dBFS |\n| `isRunning` | `boolean` | VAD is active |\n| `start` | `() => Promise<void>` | Configure and start VAD |\n| `stop` | `() => Promise<void>` | Stop VAD |\n| `error` | `VADError \\| null` | Last error |\n\n---\n\n## Testing / Jest Mock\n\n```ts\njest.mock('rn-vad');\n// All methods return resolved promises. useVAD returns stub state.\n```\n\n---\n\n## Architecture\n\n```\nJS (TypeScript)\n  └── VAD.ts / useVAD.ts\n        └── NativeRnVad.ts  ← TurboModule spec (codegen source of truth)\n              ├── iOS: RNVad.mm  (all audio ops dispatched to main thread)\n              │     ├── AVAudioEngine tap → PCM frames → accumulator\n              │     ├── VADProcessor.mm (libfvad C wrapper, dBFS calc)\n              │     └── WAV segment writer (NSFileHandle)\n              └── Android: RNVadModule.kt\n                    ├── AudioCaptureThread.kt (AudioRecord loop, VAD FSM)\n                    ├── VadProcessor.kt (JNI → libfvad)\n                    └── WAV segment writer\n```\n\n---\n\n## Attribution\n\nThis package vendors [libfvad](https://github.com/dpirch/libfvad) — a standalone WebRTC VAD library by Daniel Pirch, derived from the [WebRTC project](https://webrtc.org/) by Google. Licensed under BSD 3-Clause (see `ios/fvad/LICENSE`).\n\n---\n\n## License\n\nUNLICENSED — Copyright (c) 2026 Abhay Upadhyay. All rights reserved.\n","readmeFilename":"README.md"}