VonageAudioSocket
Duplex audio provider for the Vonage Voice API WebSocket endpoint.
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:310
Duplex audio provider for the Vonage Voice API WebSocket endpoint.
Remarks
VonageAudioSocket covers BOTH the 'input' and 'output' pipeline roles for a phone call bridged to your server via an NCCO connect action with a websocket endpoint:
[
{
"action": "connect",
"endpoint": [
{
"type": "websocket",
"uri": "wss://example.com/vonage",
"content-type": "audio/l16;rate=16000"
}
]
}
]
Your application accepts the WebSocket connection and passes the socket to attach(). The provider then:
- parses the sample rate from the
websocket:connectedJSON event (8000 / 16000 / 24000 Hz, default 16000), - emits every inbound binary frame as a linear16 AudioChunk,
- converts and re-chunks TTS audio into 20 ms linear16 frames at the negotiated rate, sent on a paced 20 ms timer (Vonage rejects large unpaced bursts),
- surfaces DTMF key presses via onDtmf().
Data-flow diagram:
socket binary frame ──> [input role] ──callback(chunk)──> InputQueue ──> STT
TTS chunk ──enqueue()──> convert to linear16@rate ──> byte queue
│ 20 ms pump
v
socket.send(640-byte frame)
Duplex semantics — one stop(), one pause(): because a single instance fills both the input and output pipeline slots, the shared lifecycle methods follow the semantics CompositeVoice relies on at runtime:
- stop() implements the OUTPUT contract (barge-in): it clears the outbound queue and pump and settles any pending flush(). It deliberately does NOT deactivate inbound capture — CompositeVoice calls
output.stop()when the caller interrupts the agent, and silencing the microphone there would leave the call permanently deaf. Inbound capture ends on detach(), socket close (remote hangup), or dispose(). - pause() / resume() gate the INPUT side only. The turn-taking controller pauses input while the agent speaks and then awaits
output.flush(); pausing the outbound pump too would deadlock that flush.
Output formats: linear16 at the negotiated rate passes straight through; linear16 at any other rate is resampled (linear interpolation); mulaw/alaw are G.711-decoded then resampled. Compressed formats (opus, mp3) are rejected by VonageAudioSocket.configure | configure() with instructions to reconfigure the TTS provider — e.g. new DeepgramTTS({ encoding: 'linear16', sampleRate: 16000 }).
STT auto-configuration caveat: VonageAudioSocket.getMetadata | getMetadata() is read at pipeline initialization, usually before Vonage has connected, so it reports the default 16000 Hz until the websocket:connected event arrives. Set content-type in your NCCO to audio/l16;rate=16000 (recommended) or configure your STT provider’s sample rate to match your NCCO rate.
Example
import { WebSocketServer } from 'ws';
import { VonageAudioSocket } from 'composite-voice';
const vonage = new VonageAudioSocket({ debug: true });
await vonage.initialize();
vonage.onDtmf((digit, duration) => {
console.log(`Caller pressed ${digit} for ${duration} ms`);
});
const wss = new WebSocketServer({ port: 3000, path: '/vonage' });
wss.on('connection', (socket) => vonage.attach(socket));
See
- AudioInputProvider and AudioOutputProvider for the interface contracts
- VonageAudioSocketConfig for configuration options
- VonageSocket for the socket duck type
Implements
Constructors
Constructor
new VonageAudioSocket(config?): VonageAudioSocket;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:425
Creates a new VonageAudioSocket instance.
Parameters
| Parameter | Type | Description |
|---|---|---|
config | VonageAudioSocketConfig | Optional configuration (see VonageAudioSocketConfig). |
Returns
VonageAudioSocket
Remarks
Construction is cheap and performs no I/O. Call initialize before use and attach once Vonage has opened its WebSocket to your server.
Example
const vonage = new VonageAudioSocket({ debug: true });
Properties
| Property | Modifier | Type | Default value | Description | Defined in |
|---|---|---|---|---|---|
roles | readonly | readonly ProviderRole[] | undefined | Pipeline roles covered by this provider. Remarks VonageAudioSocket is a duplex provider covering the 'input' and 'output' slots. Separate STT, LLM, and TTS providers are still required. | src/providers/io/vonage/VonageAudioSocket.ts:329 |
type | readonly | ProviderType | 'websocket' | Communication type for this provider. Remarks 'websocket' — the provider holds (a reference to) a persistent WebSocket for the duration of the call, even though the connection is accepted by the application rather than dialed by the provider. | src/providers/io/vonage/VonageAudioSocket.ts:319 |
Methods
attach()
attach(socket): void;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:521
Attach the WebSocket that Vonage opened to your server.
Parameters
| Parameter | Type | Description |
|---|---|---|
socket | VonageSocket | The accepted WebSocket (see VonageSocket). |
Returns
void
Remarks
Wires message and close handlers using whichever event style the socket supports (on(...) for Node ws, addEventListener(...) for browser-style sockets) and resets the per-call negotiation state — the sample rate returns to the 16000 Hz default until the new call’s websocket:connected event arrives. If another socket is already attached it is detached first (its listeners are removed and any queued outbound audio is dropped).
When the socket exposes a binaryType property it is set to 'arraybuffer' so browser-style sockets deliver binary frames as ArrayBuffer rather than Blob.
The provider never closes the socket — your application owns its lifecycle.
Throws
ConfigurationError if the socket supports neither on() nor addEventListener().
Example
const wss = new WebSocketServer({ port: 3000 });
wss.on('connection', (socket) => vonage.attach(socket));
See
detach for the inverse operation
configure()
configure(metadata): void;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:811
Configure the output with the TTS provider’s audio format.
Parameters
| Parameter | Type | Description |
|---|---|---|
metadata | AudioMetadata | Format description for incoming TTS chunks. |
Returns
void
Remarks
Accepted encodings:
'linear16'— passed through when the rate matches the negotiated rate, otherwise resampled with linear interpolation.'mulaw'/'alaw'— G.711-decoded to linear16, then resampled if needed.
Compressed encodings ('opus', 'mp3') cannot be decoded server-side without heavy dependencies and are rejected. Configure your TTS provider for raw PCM instead — e.g. new DeepgramTTS({ encoding: 'linear16', sampleRate: 16000 }).
Throws
ConfigurationError if the encoding is unsupported or the audio is not mono.
Implementation of
detach()
detach(): void;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:586
Detach the current socket, if any.
Returns
void
Remarks
Removes the provider’s message/close listeners, stops the outbound frame pump, drops any queued outbound audio, and settles pending flush promises (they resolve — the audio is cancelled, not failed). The socket itself is NOT closed; the application owns it. Inbound emission naturally ceases because no socket is delivering frames.
A no-op when no socket is attached — audio enqueued before the first attach stays queued and starts draining once a socket arrives.
See
dispose()
dispose(): Promise<void>;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:456
Dispose of the provider and release all resources.
Returns
Promise<void>
Remarks
Detaches the socket (listeners removed, pump stopped, queue cleared, pending flush settled), clears every registered callback, and resets the sequence counter. The instance may be re-initialized afterwards.
Implementation of
enqueue()
enqueue(chunk): void;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:848
Enqueue a TTS audio chunk for playback into the call.
Parameters
| Parameter | Type | Description |
|---|---|---|
chunk | AudioChunk | Audio data from the TTS stage. |
Returns
void
Remarks
The chunk is converted to linear16 at the negotiated sample rate (see configure) and appended to the outbound byte queue. A 20 ms interval pump chunks the queue into fixed-size frames (sampleRate / 50 samples) and sends one per tick — Vonage requires paced frames rather than a single burst.
Per-chunk metadata overrides the format set by configure. If neither is present the chunk is assumed to already be linear16 at the negotiated rate.
Implementation of
flush()
flush(): Promise<void>;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:892
Wait for all enqueued audio to be delivered into the call.
Returns
Promise<void>
Remarks
Vonage has no mark/acknowledgement mechanism, so completion is timed: the paced pump sends one 20 ms frame per 20 ms tick, and flush() resolves one tick after the final frame is sent — i.e. after the total queued duration has elapsed. flush() also marks the end of the response, allowing the pump to send a final zero-padded partial frame if the queued bytes do not divide evenly into 20 ms frames.
Resolves immediately when nothing is queued or playing. Also resolves (as cancelled) on stop, detach, socket close, or dispose.
Implementation of
getContentType()
getContentType(): string | null;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:634
Get the raw content-type announced by the websocket:connected event.
Returns
string | null
The content type (e.g. 'audio/l16;rate=16000'), or null if the connected event has not arrived yet.
Example
vonage.getContentType(); // 'audio/l16;rate=16000'
getMetadata()
getMetadata(): AudioMetadata;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:779
Get the format of the audio this provider emits.
Returns
AudioMetadata describing the inbound audio format.
Remarks
Always linear16 mono, 16-bit, at the negotiated sample rate. Until the websocket:connected event arrives this reports the 16000 Hz default — the pipeline reads it at initialization time to auto-configure STT, so either use audio/l16;rate=16000 in your NCCO (recommended) or configure the STT provider’s sample rate to match your NCCO explicitly.
Implementation of
AudioInputProvider.getMetadata
initialize()
initialize(): Promise<void>;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:441
Initialize the provider.
Returns
Promise<void>
Remarks
No external resources are acquired — the WebSocket is created by Vonage and supplied via attach. If already initialized, this is a no-op.
Implementation of
AudioOutputProvider.initialize
isActive()
isActive(): boolean;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:749
Check whether inbound audio is actively being emitted.
Returns
boolean
true when started and not paused.
Implementation of
isPlaying()
isPlaying(): boolean;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:908
Check whether audio is currently being delivered into the call.
Returns
boolean
true between onPlaybackStart and onPlaybackEnd.
Implementation of
isReady()
isReady(): boolean;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:483
Check whether the provider has been initialized.
Returns
boolean
true when initialize has completed and dispose has not yet been called.
Implementation of
onAudio()
onAudio(callback): void;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:763
Register a callback to receive inbound audio chunks.
Parameters
| Parameter | Type | Description |
|---|---|---|
callback | (chunk) => void | Invoked with each inbound AudioChunk (raw linear16 PCM at the negotiated sample rate). |
Returns
void
Remarks
Only one callback can be registered at a time; subsequent calls replace the previous callback. Must be called before start.
Implementation of
onDtmf()
onDtmf(callback): void;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:619
Register a callback for DTMF key presses on the call.
Parameters
| Parameter | Type | Description |
|---|---|---|
callback | VonageDtmfCallback | Invoked with the digit and (when present) its duration in milliseconds. |
Returns
void
Remarks
Vonage forwards keypad presses as JSON text frames {"event":"websocket:dtmf","digit":"5","duration":260}. Only one callback can be registered at a time; subsequent calls replace it.
Example
vonage.onDtmf((digit) => {
if (digit === '0') transferToHuman();
});
onPlaybackEnd()
onPlaybackEnd(callback): void;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:931
Register a callback invoked when the outbound queue fully drains.
Parameters
| Parameter | Type | Description |
|---|---|---|
callback | () => void | Function called when playback ends. |
Returns
void
Remarks
Also fired when playback is cancelled by stop, detach, or a socket close while audio was being delivered.
Implementation of
AudioOutputProvider.onPlaybackEnd
onPlaybackError()
onPlaybackError(callback): void;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:940
Register a callback invoked when sending audio to Vonage fails.
Parameters
| Parameter | Type | Description |
|---|---|---|
callback | (error) => void | Function called with the error. |
Returns
void
Implementation of
AudioOutputProvider.onPlaybackError
onPlaybackStart()
onPlaybackStart(callback): void;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:918
Register a callback invoked when audio delivery begins after being idle.
Parameters
| Parameter | Type | Description |
|---|---|---|
callback | () => void | Function called when the first frame of a response is sent to Vonage. |
Returns
void
Implementation of
AudioOutputProvider.onPlaybackStart
pause()
pause(): void;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:727
Pause inbound audio emission without dropping the call.
Returns
void
Remarks
Used by the turn-taking system to mute capture while the agent speaks. Frames received while paused are silently dropped. Outbound playback is intentionally unaffected — the turn-taking controller awaits flush() right after pausing input, and pausing the outbound pump here would deadlock that flush.
Implementation of
resume()
resume(): void;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:738
Resume inbound audio emission after pause.
Returns
void
See
Implementation of
start()
start(): void;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:648
Start emitting inbound call audio as AudioChunk objects.
Returns
void
Remarks
Binary frames received before start() is called are silently dropped. Must be called after initialize; audio additionally requires an attached socket to flow.
Implementation of
stop()
stop(): void;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:673
Stop outbound playback immediately and clear buffered audio (barge-in).
Returns
void
Remarks
This is the shared stop() of a duplex provider, and it implements the OUTPUT role’s contract: the outbound frame pump is cleared, queued audio is dropped, pending flush promises resolve (cancelled, not completed), and onPlaybackEnd fires if delivery was in progress.
As a duplex provider, the same stop() is invoked by CompositeVoice for two different reasons, so the provider dispatches on playback state:
- Barge-in (
output.stop()) — while outbound audio is queued or being delivered, only the output side is stopped. Inbound capture is deliberately kept alive: barge-in happens because the caller is speaking, and that speech must keep flowing to STT. - Stop listening (
input.stop()) — with no outbound audio in flight, inbound emission is halted instead (restart with start()).
Implementation of
stopCapture()
stopCapture(): void;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:711
Stop emitting caller audio, leaving outbound delivery alone.
Returns
void
Remarks
The pipeline calls this when it means “stop listening”. Restart with start().
Implementation of
AudioInputProvider.stopCapture
stopPlayback()
stopPlayback(): void;
Defined in: src/providers/io/vonage/VonageAudioSocket.ts:698
Stop outbound delivery, leaving caller audio flowing.
Returns
void
Remarks
The pipeline calls this for barge-in, so the provider never has to infer which side was meant. Clears the paced queue and settles any pending flush() as cancelled, so the pipeline cannot hang waiting on audio that will never be sent.
Capture is deliberately untouched: barge-in fires because the caller is speaking, and that speech has to reach STT. Safe when nothing is playing — barge-in is also raised while the agent is still thinking.