@kuralle-syrinx/server-websocket 4.4.1 → 4.5.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/dist/backchannel-cue-fixtures.d.ts +4 -0
- package/dist/backchannel-cue-fixtures.d.ts.map +1 -0
- package/dist/backchannel-cue-fixtures.js +25 -0
- package/dist/backchannel-cue-fixtures.js.map +1 -0
- package/dist/background-audio.d.ts +59 -0
- package/dist/background-audio.d.ts.map +1 -0
- package/dist/background-audio.js +248 -0
- package/dist/background-audio.js.map +1 -0
- package/dist/browser-opus.d.ts +12 -0
- package/dist/browser-opus.d.ts.map +1 -0
- package/dist/browser-opus.js +48 -0
- package/dist/browser-opus.js.map +1 -0
- package/dist/carrier-commands.d.ts +124 -0
- package/dist/carrier-commands.d.ts.map +1 -0
- package/dist/carrier-commands.js +193 -0
- package/dist/carrier-commands.js.map +1 -0
- package/dist/edge-telnyx.d.ts +37 -0
- package/dist/edge-telnyx.d.ts.map +1 -0
- package/dist/edge-telnyx.js +320 -0
- package/dist/edge-telnyx.js.map +1 -0
- package/dist/edge-twilio.d.ts +31 -0
- package/dist/edge-twilio.d.ts.map +1 -0
- package/dist/edge-twilio.js +286 -0
- package/dist/edge-twilio.js.map +1 -0
- package/dist/edge.d.ts +71 -0
- package/dist/edge.d.ts.map +1 -0
- package/dist/edge.js +560 -0
- package/dist/edge.js.map +1 -0
- package/dist/inbound-audio.d.ts +13 -0
- package/dist/inbound-audio.d.ts.map +1 -0
- package/dist/inbound-audio.js +66 -0
- package/dist/inbound-audio.js.map +1 -0
- package/dist/index.d.ts +81 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +718 -0
- package/dist/index.js.map +1 -0
- package/dist/json-message.d.ts +7 -0
- package/dist/json-message.d.ts.map +1 -0
- package/dist/json-message.js +41 -0
- package/dist/json-message.js.map +1 -0
- package/dist/outbound-playout-pipeline.d.ts +53 -0
- package/dist/outbound-playout-pipeline.d.ts.map +1 -0
- package/dist/outbound-playout-pipeline.js +148 -0
- package/dist/outbound-playout-pipeline.js.map +1 -0
- package/dist/paced-playout.d.ts +42 -0
- package/dist/paced-playout.d.ts.map +1 -0
- package/dist/paced-playout.js +151 -0
- package/dist/paced-playout.js.map +1 -0
- package/dist/playout-progress.d.ts +26 -0
- package/dist/playout-progress.d.ts.map +1 -0
- package/dist/playout-progress.js +64 -0
- package/dist/playout-progress.js.map +1 -0
- package/dist/session-store.d.ts +39 -0
- package/dist/session-store.d.ts.map +1 -0
- package/dist/session-store.js +80 -0
- package/dist/session-store.js.map +1 -0
- package/dist/smartpbx.d.ts +47 -0
- package/dist/smartpbx.d.ts.map +1 -0
- package/dist/smartpbx.js +437 -0
- package/dist/smartpbx.js.map +1 -0
- package/dist/telnyx-codec.d.ts +41 -0
- package/dist/telnyx-codec.d.ts.map +1 -0
- package/dist/telnyx-codec.js +114 -0
- package/dist/telnyx-codec.js.map +1 -0
- package/dist/telnyx.d.ts +52 -0
- package/dist/telnyx.d.ts.map +1 -0
- package/dist/telnyx.js +550 -0
- package/dist/telnyx.js.map +1 -0
- package/dist/test-helpers.d.ts +20 -0
- package/dist/test-helpers.d.ts.map +1 -0
- package/dist/test-helpers.js +242 -0
- package/dist/test-helpers.js.map +1 -0
- package/dist/transport-helpers.d.ts +12 -0
- package/dist/transport-helpers.d.ts.map +1 -0
- package/dist/transport-helpers.js +90 -0
- package/dist/transport-helpers.js.map +1 -0
- package/dist/transport-host.d.ts +56 -0
- package/dist/transport-host.d.ts.map +1 -0
- package/dist/transport-host.js +110 -0
- package/dist/transport-host.js.map +1 -0
- package/dist/turn-metrics.d.ts +79 -0
- package/dist/turn-metrics.d.ts.map +1 -0
- package/dist/turn-metrics.js +208 -0
- package/dist/turn-metrics.js.map +1 -0
- package/dist/twilio-auth.d.ts +12 -0
- package/dist/twilio-auth.d.ts.map +1 -0
- package/dist/twilio-auth.js +44 -0
- package/dist/twilio-auth.js.map +1 -0
- package/dist/twilio.d.ts +56 -0
- package/dist/twilio.d.ts.map +1 -0
- package/dist/twilio.js +511 -0
- package/dist/twilio.js.map +1 -0
- package/dist/websocket-close.d.ts +12 -0
- package/dist/websocket-close.d.ts.map +1 -0
- package/dist/websocket-close.js +58 -0
- package/dist/websocket-close.js.map +1 -0
- package/dist/websocket-lifecycle.d.ts +29 -0
- package/dist/websocket-lifecycle.d.ts.map +1 -0
- package/dist/websocket-lifecycle.js +87 -0
- package/dist/websocket-lifecycle.js.map +1 -0
- package/dist/websocket-upgrade.d.ts +9 -0
- package/dist/websocket-upgrade.d.ts.map +1 -0
- package/dist/websocket-upgrade.js +91 -0
- package/dist/websocket-upgrade.js.map +1 -0
- package/dist/wire-carrier-control.d.ts +27 -0
- package/dist/wire-carrier-control.d.ts.map +1 -0
- package/dist/wire-carrier-control.js +116 -0
- package/dist/wire-carrier-control.js.map +1 -0
- package/package.json +32 -11
- package/src/admission-control.test.ts +270 -0
- package/src/background-audio.test.ts +224 -0
- package/src/browser-opus.test.ts +41 -0
- package/src/browser-pacing.test.ts +440 -0
- package/src/carrier-commands.test.ts +198 -0
- package/src/edge-telnyx.test.ts +333 -0
- package/src/edge-twilio.test.ts +293 -0
- package/src/edge.test.ts +727 -0
- package/src/graceful-drain.test.ts +306 -0
- package/src/inbound-audio.test.ts +58 -0
- package/src/index.test.ts +2078 -0
- package/src/outbound-playout-pipeline.test.ts +314 -0
- package/src/paced-playout.test.ts +247 -0
- package/src/playout-progress.test.ts +56 -0
- package/src/session-store.test.ts +274 -0
- package/src/smartpbx.test.ts +986 -0
- package/src/telnyx.test.ts +1633 -0
- package/src/transport-host.test.ts +51 -0
- package/src/turn-metrics.test.ts +503 -0
- package/src/twilio-auth.test.ts +36 -0
- package/src/twilio.test.ts +1274 -0
- package/src/websocket-close.test.ts +63 -0
- package/src/websocket-lifecycle.test.ts +49 -0
package/dist/smartpbx.js
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { WebSocket } from "ws";
|
|
4
|
+
import { Decoder as OpusDecoder, Encoder as OpusEncoder } from "@evan/opus";
|
|
5
|
+
import { Route } from "@kuralle-syrinx/core";
|
|
6
|
+
import { decodeMuLawToPcm16, encodePcm16ToMuLaw, pcm16BytesToSamples, pcm16SamplesToBytes, resamplePcm16Streaming, } from "@kuralle-syrinx/core/audio";
|
|
7
|
+
import { sendJsonCapped } from "./websocket-close.js";
|
|
8
|
+
import { optionalRecord, optionalString, optionalStringOrNumber, parseJsonRecord, requiredString, } from "./json-message.js";
|
|
9
|
+
import { createRoutedWebSocketServer } from "./websocket-upgrade.js";
|
|
10
|
+
import { runWebSocketConnection, TRANSPORT_ADMISSION_REJECTED_METRIC } from "./transport-host.js";
|
|
11
|
+
import { BackgroundAudioMixer } from "./background-audio.js";
|
|
12
|
+
import { wireTelephonyOutboundPipeline, installTelephonyTurnRotation } from "./outbound-playout-pipeline.js";
|
|
13
|
+
import { decodeStrictBase64, nonNegativeInteger, numberFromString, positiveInteger, rawDataToText, } from "./transport-helpers.js";
|
|
14
|
+
const DEFAULT_ENGINE_SAMPLE_RATE_HZ = 16000;
|
|
15
|
+
const DEFAULT_OUTBOUND_FRAME_DURATION_MS = 20;
|
|
16
|
+
// Bounds queue memory + sanity only. TTS providers legitimately burst entire
|
|
17
|
+
// replies ahead of realtime (Deepgram); the pacer drains at wire rate and
|
|
18
|
+
// barge-in clear is O(1), so a generous cap is safe. A tiny cap turns every
|
|
19
|
+
// long reply into dropped audio.
|
|
20
|
+
const DEFAULT_MAX_QUEUED_OUTPUT_AUDIO_MS = 60_000;
|
|
21
|
+
const DEFAULT_HEARTBEAT_INTERVAL_MS = 30_000;
|
|
22
|
+
const DEFAULT_STARTUP_TIMEOUT_MS = 15_000;
|
|
23
|
+
const DEFAULT_MAX_SESSION_DURATION_MS = 30 * 60_000;
|
|
24
|
+
const DEFAULT_MAX_BUFFERED_AMOUNT_BYTES = 8 * 1024 * 1024;
|
|
25
|
+
const DEFAULT_MAX_INBOUND_MESSAGE_BYTES = 256 * 1024;
|
|
26
|
+
export async function createSmartPbxMediaStreamServer(options) {
|
|
27
|
+
const ownsHttpServer = !options.server;
|
|
28
|
+
const httpServer = options.server ?? createServer();
|
|
29
|
+
const routedWebSocket = createRoutedWebSocketServer(httpServer, options.path ?? "/media-stream", {
|
|
30
|
+
maxConcurrentSessions: positiveInteger(options.maxConcurrentSessions) ?? undefined,
|
|
31
|
+
maxConcurrentSessionsScope: options.maxConcurrentSessionsScope,
|
|
32
|
+
onAdmissionRejected: () => options.onTransportMetric?.(TRANSPORT_ADMISSION_REJECTED_METRIC),
|
|
33
|
+
});
|
|
34
|
+
const wsServer = routedWebSocket.wsServer;
|
|
35
|
+
const sessions = new Set();
|
|
36
|
+
const inputSampleRateHz = positiveInteger(options.inputSampleRateHz) ?? DEFAULT_ENGINE_SAMPLE_RATE_HZ;
|
|
37
|
+
const outboundFrameDurationMs = positiveInteger(options.outboundFrameDurationMs) ?? DEFAULT_OUTBOUND_FRAME_DURATION_MS;
|
|
38
|
+
const maxQueuedOutputAudioMs = positiveInteger(options.maxQueuedOutputAudioMs) ?? DEFAULT_MAX_QUEUED_OUTPUT_AUDIO_MS;
|
|
39
|
+
const hostConfig = {
|
|
40
|
+
heartbeatIntervalMs: nonNegativeInteger(options.heartbeatIntervalMs) ?? DEFAULT_HEARTBEAT_INTERVAL_MS,
|
|
41
|
+
startupTimeoutMs: nonNegativeInteger(options.startupTimeoutMs) ?? DEFAULT_STARTUP_TIMEOUT_MS,
|
|
42
|
+
maxSessionDurationMs: nonNegativeInteger(options.maxSessionDurationMs) ?? DEFAULT_MAX_SESSION_DURATION_MS,
|
|
43
|
+
maxBufferedAmountBytes: positiveInteger(options.maxBufferedAmountBytes) ?? DEFAULT_MAX_BUFFERED_AMOUNT_BYTES,
|
|
44
|
+
maxInboundMessageBytes: positiveInteger(options.maxInboundMessageBytes) ?? DEFAULT_MAX_INBOUND_MESSAGE_BYTES,
|
|
45
|
+
};
|
|
46
|
+
const contextIdFn = options.contextId ?? defaultSmartPbxContextId;
|
|
47
|
+
const gracefulCloseRegistry = new Map();
|
|
48
|
+
const adapter = {
|
|
49
|
+
createState: () => ({
|
|
50
|
+
callId: "",
|
|
51
|
+
accountId: "",
|
|
52
|
+
contextId: "",
|
|
53
|
+
contextBase: "",
|
|
54
|
+
turnCounter: 0,
|
|
55
|
+
codec: "g711_ulaw",
|
|
56
|
+
wireSampleRateHz: 8000,
|
|
57
|
+
opusDecoder: null,
|
|
58
|
+
opusEncoder: null,
|
|
59
|
+
opusEncodeRemainder: new Int16Array(0),
|
|
60
|
+
started: false,
|
|
61
|
+
stopped: false,
|
|
62
|
+
clearPlayout: () => undefined,
|
|
63
|
+
streamingResamplers: new Map(),
|
|
64
|
+
}),
|
|
65
|
+
async acquireSession({ request, shouldAbort, onSessionCreated }) {
|
|
66
|
+
const sess = await options.createSession(request);
|
|
67
|
+
onSessionCreated(sess);
|
|
68
|
+
if (shouldAbort()) {
|
|
69
|
+
await sess.close().catch(() => undefined);
|
|
70
|
+
throw new Error("SmartPBX websocket session startup aborted");
|
|
71
|
+
}
|
|
72
|
+
sessions.add(sess);
|
|
73
|
+
await sess.start();
|
|
74
|
+
if (shouldAbort()) {
|
|
75
|
+
sessions.delete(sess);
|
|
76
|
+
await sess.close().catch(() => undefined);
|
|
77
|
+
throw new Error("SmartPBX websocket session startup aborted");
|
|
78
|
+
}
|
|
79
|
+
return { session: sess, resumed: false };
|
|
80
|
+
},
|
|
81
|
+
wireSession(session, socket, state, disposers) {
|
|
82
|
+
const outbound = wireTelephonyOutboundPipeline({
|
|
83
|
+
session,
|
|
84
|
+
socket,
|
|
85
|
+
disposers,
|
|
86
|
+
outboundFrameDurationMs,
|
|
87
|
+
maxQueuedOutputAudioMs,
|
|
88
|
+
...(options.backgroundAudio ? { backgroundAudio: new BackgroundAudioMixer(options.backgroundAudio) } : {}),
|
|
89
|
+
callbacks: {
|
|
90
|
+
carrierLabel: "smartpbx",
|
|
91
|
+
getContextId: () => state.contextId,
|
|
92
|
+
isActive: () => !state.stopped && state.started,
|
|
93
|
+
encodeFrames: (audio, sourceSampleRateHz, contextId) => {
|
|
94
|
+
return encodeOutboundFrames(audio, sourceSampleRateHz, state, outboundFrameDurationMs)
|
|
95
|
+
.map((frame) => ({
|
|
96
|
+
contextId,
|
|
97
|
+
send: () => {
|
|
98
|
+
if (state.stopped)
|
|
99
|
+
return false;
|
|
100
|
+
return sendSmartPbxJson(socket, {
|
|
101
|
+
event: "media",
|
|
102
|
+
callId: state.callId,
|
|
103
|
+
accountId: state.accountId,
|
|
104
|
+
media: { payload: Buffer.from(frame).toString("base64") },
|
|
105
|
+
}, hostConfig.maxBufferedAmountBytes);
|
|
106
|
+
},
|
|
107
|
+
}));
|
|
108
|
+
},
|
|
109
|
+
onInterrupt: (contextId) => {
|
|
110
|
+
state.opusEncodeRemainder = new Int16Array(0);
|
|
111
|
+
session.bus.push(Route.Background, {
|
|
112
|
+
kind: "metric.conversation",
|
|
113
|
+
contextId,
|
|
114
|
+
timestampMs: Date.now(),
|
|
115
|
+
name: "smartpbx.interrupt_no_playback_clear",
|
|
116
|
+
value: "1",
|
|
117
|
+
});
|
|
118
|
+
},
|
|
119
|
+
onDrain: (contextId, playout, progress) => {
|
|
120
|
+
const pendingFrames = encodePendingOpusFrame(state, outboundFrameDurationMs)
|
|
121
|
+
.map((frame) => ({
|
|
122
|
+
contextId,
|
|
123
|
+
send: () => {
|
|
124
|
+
if (state.stopped)
|
|
125
|
+
return false;
|
|
126
|
+
return sendSmartPbxJson(socket, {
|
|
127
|
+
event: "media",
|
|
128
|
+
callId: state.callId,
|
|
129
|
+
accountId: state.accountId,
|
|
130
|
+
media: { payload: Buffer.from(frame).toString("base64") },
|
|
131
|
+
}, hostConfig.maxBufferedAmountBytes);
|
|
132
|
+
},
|
|
133
|
+
}));
|
|
134
|
+
playout.enqueue(pendingFrames);
|
|
135
|
+
playout.enqueueControl(() => {
|
|
136
|
+
if (state.stopped)
|
|
137
|
+
return;
|
|
138
|
+
progress.complete(contextId);
|
|
139
|
+
session.bus.push(Route.Background, {
|
|
140
|
+
kind: "metric.conversation",
|
|
141
|
+
contextId,
|
|
142
|
+
timestampMs: Date.now(),
|
|
143
|
+
name: "smartpbx.playout_drained",
|
|
144
|
+
value: "1",
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
},
|
|
148
|
+
onStop: () => {
|
|
149
|
+
state.stopped = true;
|
|
150
|
+
state.opusEncodeRemainder = new Int16Array(0);
|
|
151
|
+
},
|
|
152
|
+
onClear: () => { state.opusEncodeRemainder = new Int16Array(0); },
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
state.clearPlayout = outbound.clearPlayout;
|
|
156
|
+
installTelephonyTurnRotation(session, disposers, state);
|
|
157
|
+
gracefulCloseRegistry.set(socket, (deadlineMs) => outbound.drainAndClose(socket, deadlineMs));
|
|
158
|
+
disposers.push(() => gracefulCloseRegistry.delete(socket));
|
|
159
|
+
return (reason) => {
|
|
160
|
+
state.opusEncodeRemainder = new Int16Array(0);
|
|
161
|
+
state.clearPlayout(reason);
|
|
162
|
+
};
|
|
163
|
+
},
|
|
164
|
+
processMessage(data, isBinary, session, state) {
|
|
165
|
+
if (isBinary)
|
|
166
|
+
throw new Error("SmartPBX AI Provider messages must be JSON text frames");
|
|
167
|
+
const message = parseSmartPbxMessage(parseJsonRecord(rawDataToText(data), "SmartPBX AI Provider message"));
|
|
168
|
+
if (message.event === "connected")
|
|
169
|
+
return;
|
|
170
|
+
if (message.event === "start") {
|
|
171
|
+
if (state.stopped)
|
|
172
|
+
throw new Error("SmartPBX start event received after stream stop");
|
|
173
|
+
const start = message.start ?? {};
|
|
174
|
+
const format = validateSmartPbxStart(start);
|
|
175
|
+
state.callId = start.callId ?? "";
|
|
176
|
+
state.accountId = start.accountId ?? "";
|
|
177
|
+
if (!state.callId)
|
|
178
|
+
throw new Error("SmartPBX start event is missing callId");
|
|
179
|
+
if (!state.accountId)
|
|
180
|
+
throw new Error("SmartPBX start event is missing accountId");
|
|
181
|
+
state.contextId = contextIdFn(start);
|
|
182
|
+
state.contextBase = state.contextId;
|
|
183
|
+
state.codec = format.codec;
|
|
184
|
+
state.wireSampleRateHz = format.sampleRateHz;
|
|
185
|
+
state.opusDecoder = format.codec === "opus" ? new OpusDecoder({ channels: 1, sample_rate: 48000 }) : null;
|
|
186
|
+
state.opusEncoder = format.codec === "opus" ? new OpusEncoder({ channels: 1, sample_rate: 48000, application: "voip" }) : null;
|
|
187
|
+
state.opusEncodeRemainder = new Int16Array(0);
|
|
188
|
+
state.started = true;
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (message.event === "media") {
|
|
192
|
+
if (state.stopped)
|
|
193
|
+
return;
|
|
194
|
+
if (!state.started || !state.contextId)
|
|
195
|
+
throw new Error("SmartPBX media event received before a valid start event");
|
|
196
|
+
const payload = message.media?.payload;
|
|
197
|
+
if (!payload)
|
|
198
|
+
throw new Error("SmartPBX media event is missing media.payload");
|
|
199
|
+
const wire = decodeStrictBase64(payload, "media.payload");
|
|
200
|
+
const decoded = decodeSmartPbxWireAudio(wire, state);
|
|
201
|
+
const resampled = resamplePcm16Streaming(state.streamingResamplers, decoded, state.wireSampleRateHz, inputSampleRateHz);
|
|
202
|
+
session.bus.push(Route.Main, {
|
|
203
|
+
kind: "user.audio_received",
|
|
204
|
+
contextId: state.contextId,
|
|
205
|
+
timestampMs: Date.now(),
|
|
206
|
+
audio: pcm16SamplesToBytes(resampled),
|
|
207
|
+
sampleRateHz: inputSampleRateHz,
|
|
208
|
+
});
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (message.event === "hangup" || message.event === "stop") {
|
|
212
|
+
state.stopped = true;
|
|
213
|
+
state.started = false;
|
|
214
|
+
state.opusEncodeRemainder = new Int16Array(0);
|
|
215
|
+
state.clearPlayout("stop");
|
|
216
|
+
session.close().catch(() => undefined);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (message.event === "dtmf") {
|
|
220
|
+
if (state.stopped)
|
|
221
|
+
return;
|
|
222
|
+
if (!state.started || !state.contextId) {
|
|
223
|
+
session.bus.push(Route.Background, {
|
|
224
|
+
kind: "metric.conversation",
|
|
225
|
+
contextId: "",
|
|
226
|
+
timestampMs: Date.now(),
|
|
227
|
+
name: "smartpbx.dtmf.before_start",
|
|
228
|
+
value: message.dtmf?.digit ?? message.digit ?? "",
|
|
229
|
+
});
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const rawDigit = message.dtmf?.digit ?? message.digit ?? "";
|
|
233
|
+
const digit = parseDtmfDigit(rawDigit);
|
|
234
|
+
if (!digit) {
|
|
235
|
+
session.bus.push(Route.Background, {
|
|
236
|
+
kind: "metric.conversation",
|
|
237
|
+
contextId: state.contextId,
|
|
238
|
+
timestampMs: Date.now(),
|
|
239
|
+
name: "dtmf.unparsed",
|
|
240
|
+
value: rawDigit,
|
|
241
|
+
});
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
session.bus.push(Route.Critical, {
|
|
245
|
+
kind: "dtmf.received",
|
|
246
|
+
contextId: state.contextId,
|
|
247
|
+
timestampMs: Date.now(),
|
|
248
|
+
digit,
|
|
249
|
+
provider: "smartpbx",
|
|
250
|
+
rawDigit,
|
|
251
|
+
});
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
throw new Error(`Unsupported SmartPBX AI Provider event: ${String(message.event)}`);
|
|
255
|
+
},
|
|
256
|
+
onDisconnect(session) {
|
|
257
|
+
sessions.delete(session);
|
|
258
|
+
void session.close().catch(() => undefined);
|
|
259
|
+
},
|
|
260
|
+
onStartupTimeout(_state, session) {
|
|
261
|
+
sessions.delete(session);
|
|
262
|
+
void session.close().catch(() => undefined);
|
|
263
|
+
},
|
|
264
|
+
sendError(socket, state, message) {
|
|
265
|
+
sendSmartPbxError(socket, state, message, hostConfig.maxBufferedAmountBytes);
|
|
266
|
+
},
|
|
267
|
+
sendStartupError(socket, state, err) {
|
|
268
|
+
sendSmartPbxError(socket, state, err instanceof Error ? err.message : String(err), hostConfig.maxBufferedAmountBytes);
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
wsServer.on("connection", (socket, request) => {
|
|
272
|
+
void runWebSocketConnection(socket, request, hostConfig, adapter);
|
|
273
|
+
});
|
|
274
|
+
if (ownsHttpServer || typeof options.port === "number") {
|
|
275
|
+
await new Promise((resolveListen, reject) => {
|
|
276
|
+
httpServer.once("error", reject);
|
|
277
|
+
httpServer.listen(options.port ?? 0, options.host, () => {
|
|
278
|
+
httpServer.off("error", reject);
|
|
279
|
+
resolveListen();
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
let closing = false;
|
|
284
|
+
return {
|
|
285
|
+
httpServer,
|
|
286
|
+
wsServer,
|
|
287
|
+
address: () => httpServer.address(),
|
|
288
|
+
close: async (opts) => {
|
|
289
|
+
if (closing)
|
|
290
|
+
return;
|
|
291
|
+
closing = true;
|
|
292
|
+
if (opts?.graceful === true && gracefulCloseRegistry.size > 0) {
|
|
293
|
+
const deadline = Date.now() + (opts.drainDeadlineMs ?? 10_000);
|
|
294
|
+
await Promise.allSettled([...gracefulCloseRegistry.values()].map((fn) => fn(deadline)));
|
|
295
|
+
for (const client of wsServer.clients) {
|
|
296
|
+
if (client.readyState === WebSocket.OPEN)
|
|
297
|
+
client.terminate();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
for (const client of wsServer.clients)
|
|
302
|
+
client.terminate();
|
|
303
|
+
}
|
|
304
|
+
gracefulCloseRegistry.clear();
|
|
305
|
+
for (const session of sessions)
|
|
306
|
+
await session.close().catch(() => undefined);
|
|
307
|
+
await new Promise((resolveClose) => { wsServer.close(() => resolveClose()); });
|
|
308
|
+
routedWebSocket.detach();
|
|
309
|
+
if (ownsHttpServer || typeof options.port === "number") {
|
|
310
|
+
await new Promise((resolveClose) => { httpServer.close(() => resolveClose()); });
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
const DTMF_DIGITS = new Set(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "#"]);
|
|
316
|
+
function parseDtmfDigit(raw) {
|
|
317
|
+
const trimmed = raw.trim();
|
|
318
|
+
if (trimmed.length !== 1 || !DTMF_DIGITS.has(trimmed))
|
|
319
|
+
return null;
|
|
320
|
+
return trimmed;
|
|
321
|
+
}
|
|
322
|
+
function parseSmartPbxMessage(value) {
|
|
323
|
+
const start = optionalRecord(value.start, "SmartPBX start");
|
|
324
|
+
const media = optionalRecord(value.media, "SmartPBX media");
|
|
325
|
+
const dtmf = optionalRecord(value.dtmf, "SmartPBX dtmf");
|
|
326
|
+
const mediaFormat = optionalRecord(start?.mediaFormat, "SmartPBX start.mediaFormat");
|
|
327
|
+
return {
|
|
328
|
+
event: requiredString(value.event, "SmartPBX event"),
|
|
329
|
+
digit: optionalString(value.digit, "SmartPBX digit"),
|
|
330
|
+
start: start
|
|
331
|
+
? {
|
|
332
|
+
callId: optionalString(start.callId, "SmartPBX start.callId"),
|
|
333
|
+
otherLegCallId: optionalString(start.otherLegCallId, "SmartPBX start.otherLegCallId"),
|
|
334
|
+
callerIdNumber: optionalString(start.callerIdNumber, "SmartPBX start.callerIdNumber"),
|
|
335
|
+
calleeIdNumber: optionalString(start.calleeIdNumber, "SmartPBX start.calleeIdNumber"),
|
|
336
|
+
accountId: optionalString(start.accountId, "SmartPBX start.accountId"),
|
|
337
|
+
mediaFormat: mediaFormat
|
|
338
|
+
? {
|
|
339
|
+
encoding: optionalString(mediaFormat.encoding, "SmartPBX start.mediaFormat.encoding"),
|
|
340
|
+
sampleRate: optionalStringOrNumber(mediaFormat.sampleRate, "SmartPBX start.mediaFormat.sampleRate"),
|
|
341
|
+
}
|
|
342
|
+
: undefined,
|
|
343
|
+
}
|
|
344
|
+
: undefined,
|
|
345
|
+
media: media ? { payload: optionalString(media.payload, "SmartPBX media.payload") } : undefined,
|
|
346
|
+
dtmf: dtmf ? { digit: optionalString(dtmf.digit, "SmartPBX dtmf.digit") } : undefined,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function validateSmartPbxStart(start) {
|
|
350
|
+
const format = start.mediaFormat;
|
|
351
|
+
if (!format)
|
|
352
|
+
throw new Error("SmartPBX start event is missing mediaFormat");
|
|
353
|
+
const encoding = format.encoding?.trim().toLowerCase();
|
|
354
|
+
const sampleRateHz = numberFromString(format.sampleRate);
|
|
355
|
+
if (encoding !== "g711_ulaw" && encoding !== "pcm16" && encoding !== "opus") {
|
|
356
|
+
throw new Error(`Unsupported SmartPBX media encoding: ${format.encoding ?? "unknown"}`);
|
|
357
|
+
}
|
|
358
|
+
if (encoding === "g711_ulaw" && sampleRateHz !== 8000)
|
|
359
|
+
throw new Error(`Unsupported SmartPBX g711_ulaw sample rate: ${String(format.sampleRate)}`);
|
|
360
|
+
if (encoding === "pcm16" && sampleRateHz !== 24000)
|
|
361
|
+
throw new Error(`Unsupported SmartPBX pcm16 sample rate: ${String(format.sampleRate)}`);
|
|
362
|
+
if (encoding === "opus" && sampleRateHz !== 48000)
|
|
363
|
+
throw new Error(`Unsupported SmartPBX opus sample rate: ${String(format.sampleRate)}`);
|
|
364
|
+
return { codec: encoding, sampleRateHz: sampleRateHz };
|
|
365
|
+
}
|
|
366
|
+
function decodeSmartPbxWireAudio(wire, state) {
|
|
367
|
+
if (state.codec === "g711_ulaw")
|
|
368
|
+
return decodeMuLawToPcm16(wire);
|
|
369
|
+
if (state.codec === "pcm16")
|
|
370
|
+
return pcm16BytesToSamples(wire);
|
|
371
|
+
if (!state.opusDecoder)
|
|
372
|
+
throw new Error("SmartPBX opus decoder is not initialized");
|
|
373
|
+
return pcm16BytesToSamples(state.opusDecoder.decode(wire));
|
|
374
|
+
}
|
|
375
|
+
function encodeOutboundFrames(audio, sourceSampleRateHz, state, frameDurationMs) {
|
|
376
|
+
const samples = pcm16BytesToSamples(audio);
|
|
377
|
+
const resampled = resamplePcm16Streaming(state.streamingResamplers, samples, sourceSampleRateHz, state.wireSampleRateHz);
|
|
378
|
+
if (state.codec === "opus")
|
|
379
|
+
return encodeOpusFrames(resampled, state, frameDurationMs, false);
|
|
380
|
+
const encoded = state.codec === "g711_ulaw" ? encodePcm16ToMuLaw(resampled) : pcm16SamplesToBytes(resampled);
|
|
381
|
+
const bytesPerSample = state.codec === "g711_ulaw" ? 1 : 2;
|
|
382
|
+
const frameBytes = Math.max(1, Math.round((state.wireSampleRateHz * frameDurationMs) / 1000) * bytesPerSample);
|
|
383
|
+
const frames = [];
|
|
384
|
+
for (let offset = 0; offset < encoded.byteLength; offset += frameBytes) {
|
|
385
|
+
frames.push(encoded.subarray(offset, Math.min(encoded.byteLength, offset + frameBytes)));
|
|
386
|
+
}
|
|
387
|
+
return frames;
|
|
388
|
+
}
|
|
389
|
+
function encodePendingOpusFrame(state, frameDurationMs) {
|
|
390
|
+
if (state.codec !== "opus" || state.opusEncodeRemainder.length === 0)
|
|
391
|
+
return [];
|
|
392
|
+
return encodeOpusFrames(new Int16Array(0), state, frameDurationMs, true);
|
|
393
|
+
}
|
|
394
|
+
function encodeOpusFrames(samples, state, frameDurationMs, flush) {
|
|
395
|
+
if (!state.opusEncoder)
|
|
396
|
+
throw new Error("SmartPBX opus encoder is not initialized");
|
|
397
|
+
const frameSamples = Math.round((state.wireSampleRateHz * frameDurationMs) / 1000);
|
|
398
|
+
const pending = new Int16Array(state.opusEncodeRemainder.length + samples.length);
|
|
399
|
+
pending.set(state.opusEncodeRemainder);
|
|
400
|
+
pending.set(samples, state.opusEncodeRemainder.length);
|
|
401
|
+
const completeFrames = Math.floor(pending.length / frameSamples);
|
|
402
|
+
const frames = [];
|
|
403
|
+
for (let index = 0; index < completeFrames; index += 1) {
|
|
404
|
+
const frame = pending.subarray(index * frameSamples, (index + 1) * frameSamples);
|
|
405
|
+
frames.push(state.opusEncoder.encode(pcm16SamplesToBytes(frame)));
|
|
406
|
+
}
|
|
407
|
+
const consumed = completeFrames * frameSamples;
|
|
408
|
+
const remainder = pending.subarray(consumed);
|
|
409
|
+
if (flush && remainder.length > 0) {
|
|
410
|
+
const padded = new Int16Array(frameSamples);
|
|
411
|
+
padded.set(remainder);
|
|
412
|
+
frames.push(state.opusEncoder.encode(pcm16SamplesToBytes(padded)));
|
|
413
|
+
state.opusEncodeRemainder = new Int16Array(0);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
state.opusEncodeRemainder = new Int16Array(remainder);
|
|
417
|
+
}
|
|
418
|
+
return frames;
|
|
419
|
+
}
|
|
420
|
+
function defaultSmartPbxContextId(start) {
|
|
421
|
+
const callId = start.callId?.trim();
|
|
422
|
+
if (callId)
|
|
423
|
+
return `smartpbx-${callId}`;
|
|
424
|
+
return `smartpbx-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
425
|
+
}
|
|
426
|
+
function sendSmartPbxError(socket, state, message, maxBufferedAmountBytes) {
|
|
427
|
+
sendSmartPbxJson(socket, {
|
|
428
|
+
event: "syrinx_error",
|
|
429
|
+
callId: state.callId || undefined,
|
|
430
|
+
accountId: state.accountId || undefined,
|
|
431
|
+
error: { component: "transport", category: "invalid_input", message },
|
|
432
|
+
}, maxBufferedAmountBytes);
|
|
433
|
+
}
|
|
434
|
+
function sendSmartPbxJson(socket, value, maxBufferedAmountBytes) {
|
|
435
|
+
return sendJsonCapped(socket, value, maxBufferedAmountBytes);
|
|
436
|
+
}
|
|
437
|
+
//# sourceMappingURL=smartpbx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smartpbx.js","sourceRoot":"","sources":["../src/smartpbx.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type G722DecoderState, type G722EncoderState } from "@kuralle-syrinx/core/audio";
|
|
2
|
+
/** Telnyx stream codecs. PCMA/G722 unit-tested; live trunk negotiation unverified. */
|
|
3
|
+
export type TelnyxCodec = "PCMU" | "L16" | "PCMA" | "G722";
|
|
4
|
+
export interface TelnyxStartMediaFormat {
|
|
5
|
+
readonly encoding?: string;
|
|
6
|
+
readonly sample_rate?: number | string;
|
|
7
|
+
readonly channels?: number | string;
|
|
8
|
+
}
|
|
9
|
+
export interface TelnyxStartPayload {
|
|
10
|
+
readonly stream_id?: string;
|
|
11
|
+
readonly call_control_id?: string;
|
|
12
|
+
readonly call_session_id?: string;
|
|
13
|
+
readonly media_format?: TelnyxStartMediaFormat;
|
|
14
|
+
}
|
|
15
|
+
/** Stateful G.722 pair for a stream. Other codecs leave both null. */
|
|
16
|
+
export interface TelnyxG722State {
|
|
17
|
+
decoder: G722DecoderState | null;
|
|
18
|
+
encoder: G722EncoderState | null;
|
|
19
|
+
}
|
|
20
|
+
export declare function wireSampleRateForCodec(codec: TelnyxCodec): number;
|
|
21
|
+
/** Alias kept for callers that used the Node-side name. */
|
|
22
|
+
export declare const outboundSampleRateForCodec: typeof wireSampleRateForCodec;
|
|
23
|
+
export declare function createTelnyxG722State(codec: TelnyxCodec): TelnyxG722State;
|
|
24
|
+
export declare function validateTelnyxStart(start: TelnyxStartPayload): {
|
|
25
|
+
readonly codec: TelnyxCodec;
|
|
26
|
+
readonly sampleRateHz: number;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Decode a Telnyx media payload (RTP bytes, already base64-decoded) to PCM16 at
|
|
30
|
+
* the wire sample rate. G.722 keeps 16 kHz — do not downsample before STT.
|
|
31
|
+
*/
|
|
32
|
+
export declare function decodeTelnyxInboundPayload(input: Uint8Array, codec: TelnyxCodec, g722: TelnyxG722State): Int16Array;
|
|
33
|
+
/** Encode PCM16 samples already at the wire sample rate into a Telnyx media payload. */
|
|
34
|
+
export declare function encodeTelnyxOutboundPayload(samples: Int16Array, codec: TelnyxCodec, g722: TelnyxG722State): Uint8Array;
|
|
35
|
+
/**
|
|
36
|
+
* Split an encoded payload into fixed-duration wire frames (Node paced playout).
|
|
37
|
+
* G.722: 1 byte per 2 samples @ 16 kHz; L16: 2 bytes/sample; PCMU/PCMA: 1 byte/sample.
|
|
38
|
+
*/
|
|
39
|
+
export declare function splitTelnyxEncodedFrames(encoded: Uint8Array, codec: TelnyxCodec, wireSampleRateHz: number, frameDurationMs: number): Uint8Array[];
|
|
40
|
+
export declare function defaultTelnyxContextId(start: TelnyxStartPayload): string;
|
|
41
|
+
//# sourceMappingURL=telnyx-codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telnyx-codec.d.ts","sourceRoot":"","sources":["../src/telnyx-codec.ts"],"names":[],"mappings":"AAMA,OAAO,EAWL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACtB,MAAM,4BAA4B,CAAC;AAEpC,sFAAsF;AACtF,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAE3D,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,YAAY,CAAC,EAAE,sBAAsB,CAAC;CAChD;AAED,sEAAsE;AACtE,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAClC;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAEjE;AAED,2DAA2D;AAC3D,eAAO,MAAM,0BAA0B,+BAAyB,CAAC;AAEjE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,WAAW,GAAG,eAAe,CAMzE;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,kBAAkB,GACxB;IAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAkBhE;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,eAAe,GACpB,UAAU,CAaZ;AAED,wFAAwF;AACxF,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,UAAU,EACnB,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,eAAe,GACpB,UAAU,CAaZ;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,UAAU,EACnB,KAAK,EAAE,WAAW,EAClB,gBAAgB,EAAE,MAAM,EACxB,eAAe,EAAE,MAAM,GACtB,UAAU,EAAE,CAWd;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAQxE"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// Workers-safe Telnyx codec helpers (TypedArray only — no Node Buffer / process /
|
|
4
|
+
// node: imports). Shared by the Node host (`telnyx.ts`) and the Workers edge
|
|
5
|
+
// runner (`edge-telnyx.ts`). Live trunk negotiation remains carrier-gated.
|
|
6
|
+
import { bigEndianPcm16BytesToSamples, createG722DecoderState, createG722EncoderState, decodeALawToPcm16, decodeG722, decodeMuLawToPcm16, encodeG722, encodePcm16ToALaw, encodePcm16ToMuLaw, pcm16SamplesToBigEndianBytes, } from "@kuralle-syrinx/core/audio";
|
|
7
|
+
export function wireSampleRateForCodec(codec) {
|
|
8
|
+
return codec === "L16" || codec === "G722" ? 16_000 : 8_000;
|
|
9
|
+
}
|
|
10
|
+
/** Alias kept for callers that used the Node-side name. */
|
|
11
|
+
export const outboundSampleRateForCodec = wireSampleRateForCodec;
|
|
12
|
+
export function createTelnyxG722State(codec) {
|
|
13
|
+
if (codec !== "G722")
|
|
14
|
+
return { decoder: null, encoder: null };
|
|
15
|
+
return {
|
|
16
|
+
decoder: createG722DecoderState(),
|
|
17
|
+
encoder: createG722EncoderState(),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function validateTelnyxStart(start) {
|
|
21
|
+
const format = start.media_format;
|
|
22
|
+
if (!format)
|
|
23
|
+
throw new Error("Telnyx start event is missing media_format");
|
|
24
|
+
const encoding = format.encoding?.trim().toUpperCase();
|
|
25
|
+
if (encoding !== "PCMU" && encoding !== "L16" && encoding !== "PCMA" && encoding !== "G722") {
|
|
26
|
+
throw new Error(`Unsupported Telnyx media encoding: ${format.encoding ?? "unknown"}`);
|
|
27
|
+
}
|
|
28
|
+
const sampleRateHz = numberFromString(format.sample_rate);
|
|
29
|
+
if (sampleRateHz === null)
|
|
30
|
+
throw new Error(`Unsupported Telnyx sample rate: ${String(format.sample_rate)}`);
|
|
31
|
+
if ((encoding === "PCMU" || encoding === "PCMA") && sampleRateHz !== 8000) {
|
|
32
|
+
throw new Error(`Unsupported Telnyx ${encoding} sample rate: ${String(format.sample_rate)}`);
|
|
33
|
+
}
|
|
34
|
+
if ((encoding === "L16" || encoding === "G722") && sampleRateHz !== 16000) {
|
|
35
|
+
throw new Error(`Unsupported Telnyx ${encoding} sample rate: ${String(format.sample_rate)}`);
|
|
36
|
+
}
|
|
37
|
+
const channels = numberFromString(format.channels);
|
|
38
|
+
if (channels !== 1)
|
|
39
|
+
throw new Error(`Unsupported Telnyx channel count: ${String(format.channels)}`);
|
|
40
|
+
return { codec: encoding, sampleRateHz };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Decode a Telnyx media payload (RTP bytes, already base64-decoded) to PCM16 at
|
|
44
|
+
* the wire sample rate. G.722 keeps 16 kHz — do not downsample before STT.
|
|
45
|
+
*/
|
|
46
|
+
export function decodeTelnyxInboundPayload(input, codec, g722) {
|
|
47
|
+
switch (codec) {
|
|
48
|
+
case "PCMU":
|
|
49
|
+
return decodeMuLawToPcm16(input);
|
|
50
|
+
case "PCMA":
|
|
51
|
+
return decodeALawToPcm16(input);
|
|
52
|
+
case "G722": {
|
|
53
|
+
if (!g722.decoder)
|
|
54
|
+
g722.decoder = createG722DecoderState();
|
|
55
|
+
return decodeG722(g722.decoder, input);
|
|
56
|
+
}
|
|
57
|
+
case "L16":
|
|
58
|
+
return bigEndianPcm16BytesToSamples(input);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** Encode PCM16 samples already at the wire sample rate into a Telnyx media payload. */
|
|
62
|
+
export function encodeTelnyxOutboundPayload(samples, codec, g722) {
|
|
63
|
+
switch (codec) {
|
|
64
|
+
case "PCMU":
|
|
65
|
+
return encodePcm16ToMuLaw(samples);
|
|
66
|
+
case "PCMA":
|
|
67
|
+
return encodePcm16ToALaw(samples);
|
|
68
|
+
case "G722": {
|
|
69
|
+
if (!g722.encoder)
|
|
70
|
+
g722.encoder = createG722EncoderState();
|
|
71
|
+
return encodeG722(g722.encoder, samples);
|
|
72
|
+
}
|
|
73
|
+
case "L16":
|
|
74
|
+
return pcm16SamplesToBigEndianBytes(samples);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Split an encoded payload into fixed-duration wire frames (Node paced playout).
|
|
79
|
+
* G.722: 1 byte per 2 samples @ 16 kHz; L16: 2 bytes/sample; PCMU/PCMA: 1 byte/sample.
|
|
80
|
+
*/
|
|
81
|
+
export function splitTelnyxEncodedFrames(encoded, codec, wireSampleRateHz, frameDurationMs) {
|
|
82
|
+
const samplesPerFrame = Math.max(1, Math.round((wireSampleRateHz * frameDurationMs) / 1000));
|
|
83
|
+
const frameBytes = codec === "G722"
|
|
84
|
+
? Math.max(1, samplesPerFrame >> 1)
|
|
85
|
+
: samplesPerFrame * (codec === "L16" ? 2 : 1);
|
|
86
|
+
const frames = [];
|
|
87
|
+
for (let offset = 0; offset < encoded.byteLength; offset += frameBytes) {
|
|
88
|
+
frames.push(encoded.subarray(offset, Math.min(encoded.byteLength, offset + frameBytes)));
|
|
89
|
+
}
|
|
90
|
+
return frames;
|
|
91
|
+
}
|
|
92
|
+
export function defaultTelnyxContextId(start) {
|
|
93
|
+
const callControlId = start.call_control_id?.trim();
|
|
94
|
+
if (callControlId)
|
|
95
|
+
return `telnyx-${callControlId}`;
|
|
96
|
+
const callSessionId = start.call_session_id?.trim();
|
|
97
|
+
if (callSessionId)
|
|
98
|
+
return `telnyx-${callSessionId}`;
|
|
99
|
+
const streamId = start.stream_id?.trim();
|
|
100
|
+
if (streamId)
|
|
101
|
+
return `telnyx-${streamId}`;
|
|
102
|
+
return `telnyx-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
103
|
+
}
|
|
104
|
+
function numberFromString(value) {
|
|
105
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
106
|
+
return value;
|
|
107
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
108
|
+
const parsed = Number(value);
|
|
109
|
+
if (Number.isFinite(parsed))
|
|
110
|
+
return parsed;
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=telnyx-codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telnyx-codec.js","sourceRoot":"","sources":["../src/telnyx-codec.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,kFAAkF;AAClF,6EAA6E;AAC7E,2EAA2E;AAE3E,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,sBAAsB,EACtB,iBAAiB,EACjB,UAAU,EACV,kBAAkB,EAClB,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,4BAA4B,GAG7B,MAAM,4BAA4B,CAAC;AAwBpC,MAAM,UAAU,sBAAsB,CAAC,KAAkB;IACvD,OAAO,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;AAC9D,CAAC;AAED,2DAA2D;AAC3D,MAAM,CAAC,MAAM,0BAA0B,GAAG,sBAAsB,CAAC;AAEjE,MAAM,UAAU,qBAAqB,CAAC,KAAkB;IACtD,IAAI,KAAK,KAAK,MAAM;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC9D,OAAO;QACL,OAAO,EAAE,sBAAsB,EAAE;QACjC,OAAO,EAAE,sBAAsB,EAAE;KAClC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,KAAyB;IAEzB,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC;IAClC,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAC3E,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACvD,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QAC5F,MAAM,IAAI,KAAK,CAAC,sCAAsC,MAAM,CAAC,QAAQ,IAAI,SAAS,EAAE,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,YAAY,GAAG,gBAAgB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC1D,IAAI,YAAY,KAAK,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAC5G,IAAI,CAAC,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,MAAM,CAAC,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;QAC1E,MAAM,IAAI,KAAK,CAAC,sBAAsB,QAAQ,iBAAiB,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAC/F,CAAC;IACD,IAAI,CAAC,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,MAAM,CAAC,IAAI,YAAY,KAAK,KAAK,EAAE,CAAC;QAC1E,MAAM,IAAI,KAAK,CAAC,sBAAsB,QAAQ,iBAAiB,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAC/F,CAAC;IACD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACnD,IAAI,QAAQ,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACpG,OAAO,EAAE,KAAK,EAAE,QAAuB,EAAE,YAAY,EAAE,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CACxC,KAAiB,EACjB,KAAkB,EAClB,IAAqB;IAErB,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,MAAM;YACT,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACnC,KAAK,MAAM;YACT,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAClC,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,IAAI,CAAC,IAAI,CAAC,OAAO;gBAAE,IAAI,CAAC,OAAO,GAAG,sBAAsB,EAAE,CAAC;YAC3D,OAAO,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACzC,CAAC;QACD,KAAK,KAAK;YACR,OAAO,4BAA4B,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,2BAA2B,CACzC,OAAmB,EACnB,KAAkB,EAClB,IAAqB;IAErB,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,MAAM;YACT,OAAO,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACrC,KAAK,MAAM;YACT,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC;QACpC,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,IAAI,CAAC,IAAI,CAAC,OAAO;gBAAE,IAAI,CAAC,OAAO,GAAG,sBAAsB,EAAE,CAAC;YAC3D,OAAO,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC3C,CAAC;QACD,KAAK,KAAK;YACR,OAAO,4BAA4B,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CACtC,OAAmB,EACnB,KAAkB,EAClB,gBAAwB,EACxB,eAAuB;IAEvB,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,gBAAgB,GAAG,eAAe,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IAC7F,MAAM,UAAU,GACd,KAAK,KAAK,MAAM;QACd,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,eAAe,IAAI,CAAC,CAAC;QACnC,CAAC,CAAC,eAAe,GAAG,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,MAAM,IAAI,UAAU,EAAE,CAAC;QACvE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IAC3F,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAyB;IAC9D,MAAM,aAAa,GAAG,KAAK,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IACpD,IAAI,aAAa;QAAE,OAAO,UAAU,aAAa,EAAE,CAAC;IACpD,MAAM,aAAa,GAAG,KAAK,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IACpD,IAAI,aAAa;QAAE,OAAO,UAAU,aAAa,EAAE,CAAC;IACpD,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC;IACzC,IAAI,QAAQ;QAAE,OAAO,UAAU,QAAQ,EAAE,CAAC;IAC1C,OAAO,UAAU,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AAC3E,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAkC;IAC1D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC;IAC7C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|