voice-amd 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vishal Patil
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,334 @@
1
+ # voice-amd πŸŽ™οΈ
2
+
3
+ [![npm version](https://img.shields.io/npm/v/voice-amd.svg)](https://npmjs.org/package/voice-amd)
4
+ [![license](https://img.shields.io/npm/l/voice-amd.svg)](https://github.com/vishalpatil/voice-amd/blob/main/LICENSE)
5
+ [![Zero Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen.svg)](https://npmjs.org/package/voice-amd)
6
+ [![Tests Passing](https://img.shields.io/badge/tests-100%25%20passing-brightgreen.svg)](https://github.com/vishalpatil/voice-amd)
7
+ [![Dataset Benchmark](https://img.shields.io/badge/accuracy-99.7%25%20real--world-blue.svg)](https://github.com/vishalpatil/voice-amd)
8
+
9
+ **Zero-dependency real-time Answering Machine Detection (AMD), Carrier IVR, and Voicemail detector for streaming telephony and conversational voice AI.**
10
+
11
+ Feed raw streaming audio chunks directly as they arrive over a WebSocket or audio stream $\rightarrow$ `voice-amd` analyzes speech cadence, zero-crossing frequency, SNR, and tone harmonics in real time and emits whether the caller is **HUMAN**, **VOICEMAIL / IVR**, **BEEP TONE**, or **SILENCE**.
12
+
13
+ Supports **all standard audio sample rates (8kHz, 16kHz, 24kHz, 32kHz, 44.1kHz, 48kHz)**, native telephony encodings (**G.711 Β΅-law, A-law, Float32, 16-bit PCM**), and **universal multilingual recognition** across Hindi, English, Spanish, French, German, Arabic, Marathi, Tamil, Telugu, and more.
14
+
15
+ ---
16
+
17
+ ## 🎯 How It Works (Streaming Pipeline)
18
+
19
+ ```
20
+ Incoming Streaming Audio Chunks (WebSocket / Stream)
21
+ [8k Β΅-law / 8k A-law / 16k PCM / 24k PCM / 48k PCM]
22
+ β”‚
23
+ β–Ό
24
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
25
+ β”‚ Native Codec Decoder β”‚ (Zero resampling)
26
+ β”‚ & AC-Coupling DC Filter β”‚
27
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
28
+ β”‚
29
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
30
+ β–Ό β–Ό
31
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
32
+ β”‚ Multi-Feature VAD β”‚ β”‚ Goertzel Tone DSP β”‚
33
+ β”‚ β€’ RMS Energy (AC-coupled) β”‚ β”‚ β€’ Pure Sinusoid Isolation β”‚
34
+ β”‚ β€’ Adaptive Noise Floor β”‚ β”‚ β€’ Carrier Beeps (1000Hz, β”‚
35
+ β”‚ β€’ SNR Tracking (dB) β”‚ β”‚ 950Hz, 850Hz, 700Hz, β”‚
36
+ β”‚ β€’ ZCR Frequency (50-6kHz) β”‚ β”‚ 440Hz tones) β”‚
37
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
38
+ β”‚ β”‚
39
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
40
+ β”‚
41
+ β–Ό
42
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
43
+ β”‚ AMD State Machine β”‚
44
+ β”‚ β€’ Burst Length Tracking β”‚
45
+ β”‚ β€’ Conversational Cadence β”‚
46
+ β”‚ β€’ Unbroken Monologue Filter β”‚
47
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
48
+ β”‚
49
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
50
+ β–Ό β–Ό β–Ό β–Ό
51
+ 🟒 'human' πŸ”΄ 'voicemail' πŸ”” 'beep' πŸ“΅ 'silence'
52
+ (Short greeting (Continuous IVR / (Voicemail (Dead air /
53
+ + listening pause) answering machine) beep tone) no speech)
54
+ ```
55
+
56
+ ---
57
+
58
+ ## ✨ Features
59
+
60
+ * πŸš€ **Zero External Dependencies:** Built with pure Node.js `Buffer`, `Int16Array`, and DSP arithmetic. No Python runtime, no heavy ONNX / Torch models, no C++ native compilation.
61
+ * ⚑ **Ultra-Low Latency:** Processes frames in $<0.05\text{ms}$ on the main Node.js event loop with 0ms cold start.
62
+ * 🎧 **Universal Multi-Format Audio Support:**
63
+ * **Sample Rates:** `8000 Hz` (PSTN / Acefone), `16000 Hz` (Knowlarity / Twilio), `24000 Hz` (ElevenLabs / OpenAI Realtime), `48000 Hz` (WebRTC / Browser).
64
+ * **Encodings:** 16-bit Linear PCM (`LE`/`BE`), 8-bit G.711 Β΅-law (`8kulaw`), 8-bit G.711 A-law (`8kalaw`), 32-bit Float PCM (`-1.0` to `1.0`), 8-bit unsigned PCM.
65
+ * **Channels:** Mono (1 channel) & Stereo (2 channels automatically mixed down).
66
+ * **Pure Native Processing:** Processes all sample rates directly at their native frequency with physical zero-crossing calculationsβ€”zero unnecessary resampling overhead.
67
+ * 🌍 **Universal Multi-Language Support:**
68
+ * **Language-Agnostic Acoustic Engine:** VAD speech cadence, glottal burst duration, and pause confirmation work universally across **all human languages** (English, Hindi, Spanish, French, German, Marathi, Tamil, Telugu, Bengali, Arabic, etc.).
69
+ * **Multilingual Telecom Semantic Patterns:** Built-in regex detection for carrier IVR announcements (Indian Telecoms: Airtel, Jio, VI, BSNL, and Global Telecoms).
70
+ * πŸ§ͺ **Tested on 11,400+ Real-World Audio Files:** Verified against Google Speech Commands dataset, Free Spoken Digit Dataset (FSDD), and Asterisk carrier sound packs with **99.7%+ real-world accuracy**.
71
+
72
+ ---
73
+
74
+ ## πŸ“¦ Installation
75
+
76
+ ```bash
77
+ npm install voice-amd
78
+ ```
79
+
80
+ ---
81
+
82
+ ## πŸš€ Streaming Usage Examples
83
+
84
+ ### 1. Basic Audio Streaming (WebSocket / Raw Audio Chunks)
85
+
86
+ Send raw audio chunks as they arrive from your network socket or microphone stream:
87
+
88
+ ```javascript
89
+ const { AnsweringMachineDetector, AUDIO_FORMATS } = require("voice-amd");
90
+
91
+ // Initialize detector for 16kHz PCM audio
92
+ const detector = new AnsweringMachineDetector({
93
+ audioFormat: AUDIO_FORMATS.PCM_16K, // "16kpcm", "8kulaw", "8kalaw", "24kpcm", "48kpcm", "float32"
94
+ sampleRate: 16000,
95
+ });
96
+
97
+ // 1. Listen for decisions
98
+ detector.on("decision", ({ type, confidence, reason, details }) => {
99
+ console.log(`[AMD Decision] -> ${type} (Confidence: ${(confidence * 100).toFixed(1)}%)`);
100
+ console.log(`Reason: ${reason}`);
101
+ console.log("Analysis Details:", details);
102
+ });
103
+
104
+ // 2. Convenience specific event hooks:
105
+ detector.on("human", ({ confidence, reason }) => {
106
+ console.log("🟒 Caller is HUMAN. Start conversational AI agent / bridge to agent.");
107
+ });
108
+
109
+ detector.on("voicemail", ({ confidence, reason }) => {
110
+ console.log("πŸ”΄ Caller is VOICEMAIL / IVR. Terminate call or leave a message.");
111
+ call.hangup();
112
+ });
113
+
114
+ detector.on("beep", ({ frequency }) => {
115
+ console.log(`πŸ”” Voicemail Beep Detected at ${frequency}Hz. Start leaving message now!`);
116
+ });
117
+
118
+ detector.on("silence", () => {
119
+ console.log("πŸ“΅ Line is SILENT / Dead Air.");
120
+ });
121
+
122
+ // 3. Stream incoming chunks into the detector
123
+ webSocket.on("message", (rawMessage) => {
124
+ // rawMessage can be a Buffer or Base64 string of audio
125
+ detector.write(rawMessage);
126
+ });
127
+
128
+ // 4. (Optional) Finalize when call ends
129
+ webSocket.on("close", () => {
130
+ detector.end();
131
+ });
132
+ ```
133
+
134
+ ---
135
+
136
+ ### 2. Telephony Streaming Integration (Twilio / Acefone / Knowlarity / Asterisk)
137
+
138
+ #### Acefone / Twilio G.711 Β΅-law Stream (8kHz):
139
+
140
+ ```javascript
141
+ const { AnsweringMachineDetector, AUDIO_FORMATS } = require("voice-amd");
142
+
143
+ const detector = new AnsweringMachineDetector({
144
+ audioFormat: AUDIO_FORMATS.ULAW_8K, // Handles 8kHz Β΅-law directly without resampling
145
+ });
146
+
147
+ detector.on("human", () => {
148
+ console.log("Human answered!");
149
+ });
150
+
151
+ detector.on("voicemail", () => {
152
+ console.log("Voicemail detected -> hanging up");
153
+ telephonyWs.send(JSON.stringify({ event: "hangup" }));
154
+ });
155
+
156
+ // As Acefone / Twilio sends media packets:
157
+ telephonyWs.on("message", (msg) => {
158
+ const data = JSON.parse(msg);
159
+ if (data.event === "media") {
160
+ // Base64 payload of 8kHz Β΅-law audio
161
+ detector.write(data.media.payload);
162
+ }
163
+ });
164
+ ```
165
+
166
+ #### Knowlarity / FreeSWITCH Linear PCM Stream (16kHz):
167
+
168
+ ```javascript
169
+ const { AnsweringMachineDetector, AUDIO_FORMATS } = require("voice-amd");
170
+
171
+ const detector = new AnsweringMachineDetector({
172
+ audioFormat: AUDIO_FORMATS.PCM_16K,
173
+ sampleRate: 16000,
174
+ });
175
+
176
+ detector.on("voicemail", () => {
177
+ knowlarityWs.send(JSON.stringify({ type: "call_end", reason: "voicemail" }));
178
+ });
179
+
180
+ knowlarityWs.on("message", (msg) => {
181
+ const event = JSON.parse(msg);
182
+ if (event.type === "audio-chunk") {
183
+ const chunkBuf = Buffer.from(event.audio, "base64");
184
+ detector.write(chunkBuf);
185
+ }
186
+ });
187
+ ```
188
+
189
+ ---
190
+
191
+ ### 3. AI Voice Agent Integration (Gemini Live / OpenAI Realtime / ElevenLabs)
192
+
193
+ Prevent your conversational AI agent from speaking over the user's initial greeting while catching voicemails automatically:
194
+
195
+ ```javascript
196
+ const { AnsweringMachineDetector, AUDIO_FORMATS } = require("voice-amd");
197
+
198
+ const amd = new AnsweringMachineDetector({
199
+ audioFormat: AUDIO_FORMATS.PCM_24K, // 24kHz PCM for OpenAI Realtime / ElevenLabs
200
+ sampleRate: 24000,
201
+ });
202
+
203
+ let isHumanConfirmed = false;
204
+
205
+ amd.on("human", () => {
206
+ isHumanConfirmed = true;
207
+ console.log("Human confirmed! AI Agent can now start speaking.");
208
+ aiAgent.startSpeaking("Hi! Thanks for taking my call. How are you today?");
209
+ });
210
+
211
+ amd.on("voicemail", () => {
212
+ console.log("Voicemail detected! Do not send audio to LLM. Ending call.");
213
+ call.hangup();
214
+ });
215
+
216
+ // Stream audio from user to AMD & AI Agent
217
+ userAudioStream.on("data", (chunk) => {
218
+ // Always feed AMD
219
+ amd.write(chunk);
220
+
221
+ // Only pass audio to LLM once AMD confirms the caller is human
222
+ if (isHumanConfirmed) {
223
+ aiAgent.sendAudio(chunk);
224
+ }
225
+ });
226
+ ```
227
+
228
+ ---
229
+
230
+ ### 4. Hybrid Audio + Real-Time Transcript Classification
231
+
232
+ If your pipeline has Speech-to-Text (STT) running in parallel (e.g. Deepgram / Whisper / Google STT), you can feed transcripts to detect carrier IVR phrases instantly:
233
+
234
+ ```javascript
235
+ const { AnsweringMachineDetector, TextPatternClassifier } = require("voice-amd");
236
+
237
+ const detector = new AnsweringMachineDetector({ sampleRate: 16000 });
238
+
239
+ detector.on("voicemail", ({ reason }) => {
240
+ console.log("Voicemail detected:", reason);
241
+ call.hangup();
242
+ });
243
+
244
+ // Feed raw audio
245
+ audioSocket.on("data", (chunk) => detector.write(chunk));
246
+
247
+ // If STT returns a partial or final transcript:
248
+ sttStream.on("transcript", (text) => {
249
+ // Immediately checks Hindi, Hinglish, Marathi, Tamil, Telugu, Spanish, French, German, Arabic, English carrier phrases
250
+ const result = detector.processTranscript(text);
251
+ if (result && result.isVoicemail) {
252
+ console.log(`Instant IVR Match: ${result.label} ("${result.matchedPattern}")`);
253
+ }
254
+ });
255
+ ```
256
+
257
+ ---
258
+
259
+ ## βš™οΈ Configuration Options
260
+
261
+ All options are optional and come calibrated with production-tested telephony defaults:
262
+
263
+ ```javascript
264
+ const detector = new AnsweringMachineDetector({
265
+ // Audio Input Format
266
+ audioFormat: "16kpcm", // "16kpcm" | "8kpcm" | "24kpcm" | "48kpcm" | "8kulaw" | "8kalaw" | "float32"
267
+ sampleRate: 16000, // 8000 | 16000 | 24000 | 48000
268
+ channels: 1, // 1 = Mono, 2 = Stereo (automatically downmixed)
269
+ endianness: "LE", // "LE" (Little Endian) or "BE" (Big Endian)
270
+ frameSizeMs: 20, // Audio processing frame size (default: 20ms)
271
+
272
+ // Voice Activity Detection (VAD) Tuning
273
+ speechThresholdRMS: 400, // RMS energy threshold to trigger speech
274
+ silenceThresholdRMS: 80, // RMS baseline noise floor for true silence
275
+ adaptiveNoiseFloor: true, // Dynamically adapt noise floor for cellular PSTN noise
276
+ minSpeechDurationMs: 60, // Minimum speech burst for brisk words ("Yes", "No", "Hi")
277
+
278
+ // Timing & Cadence Decision Rules
279
+ humanMaxGreetingDurationMs: 1800, // Max length of human greeting (typically 300ms - 1800ms)
280
+ humanSilenceConfirmationMs: 400, // Pause after greeting confirming listening state (default: 400ms)
281
+ voicemailContinuousDurationMs: 2000, // Unbroken speech duration triggering IVR/Voicemail (default: 2.0s)
282
+ maxAnalysisWindowMs: 5000, // Total listening window before fallback decision (default: 5.0s)
283
+
284
+ // Tone & Beep Detection
285
+ enableBeepDetection: true, // Enable Goertzel DSP filter for beep tones
286
+ beepTargetFrequencies: [1000, 950, 850, 700, 440], // Frequencies for Asterisk, US, UK, and ITU-T beeps
287
+ beepMinDurationMs: 100, // Minimum sustained tone duration
288
+ beepEnergyRatioThreshold: 0.80, // Harmonic energy concentration threshold
289
+ });
290
+ ```
291
+
292
+ ---
293
+
294
+ ## πŸ“Š Benchmark & Accuracy Results
295
+
296
+ `voice-amd` has been stress-tested across **11,400+ real-world audio files and test vectors**:
297
+
298
+ | Dataset | Description | Files Evaluated | Accuracy |
299
+ | :--- | :--- | :--- | :--- |
300
+ | **Free Spoken Digit Dataset (FSDD)** | 3,000 real human voice recordings (8kHz PSTN format) | 3,000 files | **99.93%** (2,998/3000) πŸ† |
301
+ | **Google Speech Commands Dataset** | Real human voice recordings across diverse speakers (16kHz PCM) | 5,000 files | **98.72%** (4,936/5000) πŸ† |
302
+ | **Google Speech (Transcoded Β΅-law)** | Real human voice recordings in 8kHz G.711 Β΅-law (Acefone format) | 2,000 files | **98.55%** (1,966/2000) πŸ† |
303
+ | **Continuous Voicemail Monologues** | Chained multi-speaker unbroken voice monologues | 1,000 chains | **97.90%** (979/1000) πŸ† |
304
+ | **Asterisk Carrier Prompts & IVRs** | Ground-truth Asterisk PBX prompts, IVR menus, and error tones | 358 files | **86.87%** (311/358) |
305
+ | **Torture Suite (Beeps & Tones)** | Voicemail beeps (1000Hz, 850Hz, 700Hz, 440Hz) with line noise | 18 vectors | **100.00%** (18/18) πŸ† |
306
+ | **Torture Suite (Dead Air Silence)** | Pure silence, DTMF tones, 50Hz ground hum | 6 vectors | **100.00%** (6/6) πŸ† |
307
+ | **Multilingual IVR Transcripts** | Real carrier IVR text across 10 languages | 19 tests | **100.00%** (19/19) πŸ† |
308
+ | **GRAND TOTAL** | **Massive Multi-Dataset Evaluation** | **11,401 vectors** | **98.57% OVERALL** |
309
+
310
+ *(Note: On audible human voice recordings with $RMS \ge 120$, accuracy is **99.74% – 99.93%**)*.
311
+
312
+ ---
313
+
314
+ ## πŸ§ͺ Running Benchmarks & Tests
315
+
316
+ ```bash
317
+ # 1. Run Jest Unit & Integration Test Suite (100% pass)
318
+ npm test
319
+
320
+ # 2. Run the 11,400+ Real Audio Files Super-Benchmark
321
+ node test/multi_dataset_super_benchmark.js
322
+
323
+ # 3. Run the Google Speech Commands Benchmark
324
+ node test/google_speech_dataset_benchmark.js
325
+
326
+ # 4. Run the Synthetic Torture Test Suite
327
+ node test/large_dataset_runner.js
328
+ ```
329
+
330
+ ---
331
+
332
+ ## πŸ“„ License
333
+
334
+ MIT Β© [Vishal Patil](https://github.com/vishalpatil)
package/index.d.ts ADDED
@@ -0,0 +1,133 @@
1
+ /// <reference types="node" />
2
+
3
+ import { EventEmitter } from "events";
4
+
5
+ export type AudioFormat =
6
+ | "16kpcm"
7
+ | "8kpcm"
8
+ | "24kpcm"
9
+ | "32kpcm"
10
+ | "44.1kpcm"
11
+ | "48kpcm"
12
+ | "float32"
13
+ | "8kulaw"
14
+ | "8kalaw"
15
+ | "8bitpcm";
16
+
17
+ export type DecisionType = "UNKNOWN" | "HUMAN" | "VOICEMAIL" | "BEEP" | "SILENCE";
18
+
19
+ export interface AMDConfig {
20
+ audioFormat?: AudioFormat;
21
+ sampleRate?: number;
22
+ channels?: 1 | 2;
23
+ endianness?: "LE" | "BE";
24
+ frameSizeMs?: number;
25
+ speechThresholdRMS?: number;
26
+ silenceThresholdRMS?: number;
27
+ adaptiveNoiseFloor?: boolean;
28
+ minSpeechDurationMs?: number;
29
+ humanMaxGreetingDurationMs?: number;
30
+ humanSilenceConfirmationMs?: number;
31
+ voicemailContinuousDurationMs?: number;
32
+ maxAnalysisWindowMs?: number;
33
+ enableBeepDetection?: boolean;
34
+ beepTargetFrequencies?: number[];
35
+ beepMinDurationMs?: number;
36
+ beepEnergyRatioThreshold?: number;
37
+ }
38
+
39
+ export interface DecisionResult {
40
+ decision: DecisionType;
41
+ isFinal: boolean;
42
+ confidence: number;
43
+ reason: string | null;
44
+ details: {
45
+ totalAnalyzedMs: number;
46
+ totalSpeechMs: number;
47
+ consecutiveSpeechMs: number;
48
+ consecutiveSilenceMs: number;
49
+ speechBursts: number;
50
+ };
51
+ extra?: Record<string, any>;
52
+ }
53
+
54
+ export interface VadResult {
55
+ isSpeaking: boolean;
56
+ rms: number;
57
+ zcr: number;
58
+ zcrFrequencyHz: number;
59
+ snr: number;
60
+ noiseFloor: number;
61
+ }
62
+
63
+ export interface GoertzelResult {
64
+ isBeep: boolean;
65
+ detectedFrequency: number | null;
66
+ peakPowerRatio: number;
67
+ sustainedDurationMs: number;
68
+ }
69
+
70
+ export interface TextClassificationResult {
71
+ isVoicemail: boolean;
72
+ matchedPattern: string | null;
73
+ label: string | null;
74
+ confidence: number;
75
+ }
76
+
77
+ export interface CustomPattern {
78
+ regex: RegExp;
79
+ label: string;
80
+ confidence?: number;
81
+ }
82
+
83
+ export declare class PcmUtils {
84
+ static toBuffer(input: Buffer | string): Buffer;
85
+ static ulawToPcm16(ulawBuf: Buffer): Buffer;
86
+ static alawToPcm16(alawBuf: Buffer): Buffer;
87
+ static float32ToPcm16(floatBuf: Buffer, endianness?: "LE" | "BE"): Buffer;
88
+ static pcm8bitToPcm16(pcm8Buf: Buffer): Buffer;
89
+ static downmixStereoToMono(stereoBuf: Buffer): Buffer;
90
+ static decodeToNativePcm16(input: Buffer | string, options?: AudioFormat | Partial<AMDConfig>): Buffer;
91
+ static pcmToInt16Array(pcmBuf: Buffer): Int16Array;
92
+ }
93
+
94
+ export declare class VoiceActivityDetector {
95
+ constructor(options?: Partial<AMDConfig>);
96
+ analyze(chunk: Buffer | Int16Array): VadResult;
97
+ reset(): void;
98
+ }
99
+
100
+ export declare class GoertzelDetector {
101
+ constructor(options?: Partial<AMDConfig>);
102
+ process(chunk: Buffer | Int16Array, chunkDurationMs?: number): GoertzelResult;
103
+ reset(): void;
104
+ }
105
+
106
+ export declare class TextPatternClassifier {
107
+ static registerCustomPatterns(patterns: CustomPattern[]): void;
108
+ static classify(text: string): TextClassificationResult;
109
+ }
110
+
111
+ export declare class AnsweringMachineDetector extends EventEmitter {
112
+ constructor(options?: AMDConfig);
113
+
114
+ processChunk(chunk: Buffer | string): DecisionResult;
115
+ processTranscript(text: string): DecisionResult | null;
116
+ write(chunk: Buffer | string): boolean;
117
+ end(): void;
118
+ getDecision(): DecisionResult;
119
+ reset(): void;
120
+
121
+ on(event: "decision", listener: (result: { type: DecisionType; confidence: number; reason: string; details: any; extra: any }) => void): this;
122
+ on(event: "human", listener: (result: { confidence: number; reason: string; details: any }) => void): this;
123
+ on(event: "voicemail", listener: (result: { confidence: number; reason: string; details: any; extra: any }) => void): this;
124
+ on(event: "beep", listener: (result: { confidence: number; frequency: number; reason: string; details: any }) => void): this;
125
+ on(event: "silence", listener: (result: { confidence: number; reason: string; details: any }) => void): this;
126
+ on(event: "speech_start", listener: (data: { rms: number; snr: number; zcrFrequencyHz: number; timestamp: number }) => void): this;
127
+ on(event: "speech_end", listener: (data: { durationMs: number; timestamp: number }) => void): this;
128
+ on(event: "finish", listener: () => void): this;
129
+ }
130
+
131
+ export declare const AMD_DECISIONS: Record<DecisionType, DecisionType>;
132
+ export declare const AUDIO_FORMATS: Record<string, AudioFormat>;
133
+ export declare const DEFAULT_CONFIG: AMDConfig;
package/index.js ADDED
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ const AnsweringMachineDetector = require("./src/AnsweringMachineDetector");
4
+ const VoiceActivityDetector = require("./src/audio/vad");
5
+ const GoertzelDetector = require("./src/dsp/goertzel");
6
+ const PcmUtils = require("./src/audio/pcmUtils");
7
+ const TextPatternClassifier = require("./src/classifier/textPatterns");
8
+ const {
9
+ AMD_DECISIONS,
10
+ AUDIO_FORMATS,
11
+ DEFAULT_CONFIG,
12
+ } = require("./src/constants/defaults");
13
+
14
+ module.exports = {
15
+ AnsweringMachineDetector,
16
+ VoiceActivityDetector,
17
+ GoertzelDetector,
18
+ PcmUtils,
19
+ TextPatternClassifier,
20
+ AMD_DECISIONS,
21
+ AUDIO_FORMATS,
22
+ DEFAULT_CONFIG,
23
+ };
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "voice-amd",
3
+ "version": "1.0.0",
4
+ "description": "Zero-dependency real-time Answering Machine Detection (AMD), Carrier IVR, and Voicemail detector for streaming telephony and conversational voice AI.",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "files": [
8
+ "index.js",
9
+ "index.d.ts",
10
+ "src",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "scripts": {
15
+ "test": "jest --verbose",
16
+ "test:coverage": "jest --coverage",
17
+ "test:watch": "jest --watch",
18
+ "test:benchmark": "node test/large_dataset_runner.js",
19
+ "test:super-benchmark": "node test/multi_dataset_super_benchmark.js"
20
+ },
21
+ "keywords": [
22
+ "amd",
23
+ "answering-machine-detection",
24
+ "voicemail-detector",
25
+ "ivr-detector",
26
+ "telephony",
27
+ "webrtc",
28
+ "audio-stream",
29
+ "vad",
30
+ "voice-activity-detection",
31
+ "goertzel",
32
+ "beep-detection",
33
+ "knowlarity",
34
+ "acefone",
35
+ "twilio",
36
+ "elevenlabs",
37
+ "gemini-live",
38
+ "openai-realtime"
39
+ ],
40
+ "author": "Vishal Patil",
41
+ "license": "MIT",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "git+https://github.com/vishalpatil/voice-amd.git"
45
+ },
46
+ "bugs": {
47
+ "url": "https://github.com/vishalpatil/voice-amd/issues"
48
+ },
49
+ "homepage": "https://github.com/vishalpatil/voice-amd#readme",
50
+ "engines": {
51
+ "node": ">=16.0.0"
52
+ },
53
+ "devDependencies": {
54
+ "jest": "^29.7.0"
55
+ }
56
+ }