@voicethere/agent 0.7.11 → 0.7.13
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/agent.js +24 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/protocol.d.ts +24 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js.map +1 -1
- package/dist/runtime.d.ts +3 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +68 -1
- package/dist/runtime.js.map +1 -1
- package/dist/templates/echo/agent.js +26 -2
- package/dist/templates/echo-dc/agent.js +26 -2
- package/dist/templates/game-sync/agent.js +26 -2
- package/dist/templates/positional-tts/agent.js +26 -2
- package/dist/templates/recording-consent/agent.js +26 -2
- package/dist/templates/registry.d.ts.map +1 -1
- package/dist/templates/registry.js +2 -1
- package/dist/templates/registry.js.map +1 -1
- package/dist/templates/spatial-showcase/agent.js +498 -29
- package/dist/templates/voice-showcase/agent.js +26 -2
- package/dist/templates/voice-starter/agent.js +26 -2
- package/dist/templates/webhooks/agent.js +26 -2
- package/dist/templates/webhooks-redis/agent.js +26 -2
- package/package.json +4 -4
- package/templates/README.md +10 -10
- package/templates/spatial-showcase/agent.ts +404 -34
- package/templates/spatial-showcase/protocol.ts +82 -1
- package/templates/spatial-showcase/sine.ts +66 -0
|
@@ -152,6 +152,12 @@ function isPlayStopAckMessage(value) {
|
|
|
152
152
|
const msg = value;
|
|
153
153
|
return msg.type === "play_stop_ack" && typeof msg.requestId === "string";
|
|
154
154
|
}
|
|
155
|
+
function isPlayPoseAckMessage(value) {
|
|
156
|
+
if (!value || typeof value !== "object")
|
|
157
|
+
return false;
|
|
158
|
+
const msg = value;
|
|
159
|
+
return msg.type === "play_pose_ack" && typeof msg.requestId === "string";
|
|
160
|
+
}
|
|
155
161
|
function isRecordingControlAckMessage(value) {
|
|
156
162
|
if (!value || typeof value !== "object")
|
|
157
163
|
return false;
|
|
@@ -212,7 +218,7 @@ function isParentMessage(value) {
|
|
|
212
218
|
if (!value || typeof value !== "object")
|
|
213
219
|
return false;
|
|
214
220
|
const msg = value;
|
|
215
|
-
return msg.type === "session_start" || msg.type === "speech_event" || msg.type === "session_end" || msg.type === "data_channel_message" || msg.type === "data_channel_binary" || msg.type === "idle_timeout" || msg.type === "recording_control_ack" || msg.type === "play_ack" || msg.type === "play_status_ack" || msg.type === "play_stop_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
|
|
221
|
+
return msg.type === "session_start" || msg.type === "speech_event" || msg.type === "session_end" || msg.type === "data_channel_message" || msg.type === "data_channel_binary" || msg.type === "idle_timeout" || msg.type === "recording_control_ack" || msg.type === "play_ack" || msg.type === "play_status_ack" || msg.type === "play_stop_ack" || msg.type === "play_pose_ack" || msg.type === "mix_control_ack" || msg.type === "stt_control_ack" || msg.type === "webhook";
|
|
216
222
|
}
|
|
217
223
|
function isSessionScopedParentMessage(value) {
|
|
218
224
|
return isParentMessage(value) && !isWebhookMessage(value);
|
|
@@ -235,7 +241,9 @@ var pendingRecordingAcks = /* @__PURE__ */ new Map();
|
|
|
235
241
|
var pendingPlayAcks = /* @__PURE__ */ new Map();
|
|
236
242
|
var pendingPlayStatusAcks = /* @__PURE__ */ new Map();
|
|
237
243
|
var pendingPlayStopAcks = /* @__PURE__ */ new Map();
|
|
244
|
+
var pendingPlayPoseAcks = /* @__PURE__ */ new Map();
|
|
238
245
|
var MIX_CONTROL_ACK_TIMEOUT_MS = 5e3;
|
|
246
|
+
var STT_CONTROL_ACK_TIMEOUT_MS = 5e3;
|
|
239
247
|
var pendingMixAcks = /* @__PURE__ */ new Map();
|
|
240
248
|
var pendingSttAcks = /* @__PURE__ */ new Map();
|
|
241
249
|
function handleRecordingControlAck(message) {
|
|
@@ -290,6 +298,19 @@ function handlePlayStopAck(message) {
|
|
|
290
298
|
requestId: message.requestId
|
|
291
299
|
});
|
|
292
300
|
}
|
|
301
|
+
function handlePlayPoseAck(message) {
|
|
302
|
+
const pending = pendingPlayPoseAcks.get(message.requestId);
|
|
303
|
+
if (!pending)
|
|
304
|
+
return;
|
|
305
|
+
clearTimeout(pending.timer);
|
|
306
|
+
pendingPlayPoseAcks.delete(message.requestId);
|
|
307
|
+
pending.resolve({
|
|
308
|
+
ok: message.ok,
|
|
309
|
+
playId: message.playId,
|
|
310
|
+
reason: message.reason,
|
|
311
|
+
requestId: message.requestId
|
|
312
|
+
});
|
|
313
|
+
}
|
|
293
314
|
function handleMixControlAck(message) {
|
|
294
315
|
const pending = pendingMixAcks.get(message.requestId);
|
|
295
316
|
if (!pending)
|
|
@@ -402,7 +423,7 @@ function sendParentMessage(message) {
|
|
|
402
423
|
process.send?.(message);
|
|
403
424
|
return;
|
|
404
425
|
}
|
|
405
|
-
if (msgType === "play" || msgType === "play_status" || msgType === "play_stop") {
|
|
426
|
+
if (msgType === "play" || msgType === "play_status" || msgType === "play_stop" || msgType === "play_pose") {
|
|
406
427
|
process.send?.(message);
|
|
407
428
|
return;
|
|
408
429
|
}
|
|
@@ -588,6 +609,10 @@ function defineAgent(handlers) {
|
|
|
588
609
|
handlePlayStopAck(message);
|
|
589
610
|
return;
|
|
590
611
|
}
|
|
612
|
+
if (isPlayPoseAckMessage(message)) {
|
|
613
|
+
handlePlayPoseAck(message);
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
591
616
|
if (isMixControlAckMessage(message)) {
|
|
592
617
|
handleMixControlAck(message);
|
|
593
618
|
return;
|
|
@@ -778,6 +803,26 @@ async function sendTtsPoseControl(action, payload) {
|
|
|
778
803
|
});
|
|
779
804
|
});
|
|
780
805
|
}
|
|
806
|
+
async function sendSttControl(options) {
|
|
807
|
+
const requestId = randomUUID();
|
|
808
|
+
if (!isVoicethereAgentChild()) {
|
|
809
|
+
return { ok: true, reason: "local_mock", requestId };
|
|
810
|
+
}
|
|
811
|
+
return new Promise((resolve) => {
|
|
812
|
+
const timer = setTimeout(() => {
|
|
813
|
+
pendingSttAcks.delete(requestId);
|
|
814
|
+
resolve({ ok: false, reason: "timeout", requestId });
|
|
815
|
+
}, STT_CONTROL_ACK_TIMEOUT_MS);
|
|
816
|
+
pendingSttAcks.set(requestId, { resolve, timer });
|
|
817
|
+
sendParentMessage({
|
|
818
|
+
type: "stt_control",
|
|
819
|
+
requestId,
|
|
820
|
+
enabled: options.enabled,
|
|
821
|
+
...options.clientId ? { clientId: options.clientId } : {},
|
|
822
|
+
...options.sessionId ? { sessionId: options.sessionId } : {}
|
|
823
|
+
});
|
|
824
|
+
});
|
|
825
|
+
}
|
|
781
826
|
function createMixGroup(options) {
|
|
782
827
|
return sendMixControl("create_group", {
|
|
783
828
|
groupId: options.id,
|
|
@@ -809,6 +854,9 @@ function setListenerMute(options) {
|
|
|
809
854
|
muted: options.muted
|
|
810
855
|
});
|
|
811
856
|
}
|
|
857
|
+
function setSttEnabled(options) {
|
|
858
|
+
return sendSttControl(options);
|
|
859
|
+
}
|
|
812
860
|
function validatePlayOptions(options) {
|
|
813
861
|
const requestId = randomUUID();
|
|
814
862
|
const url = options.url?.trim();
|
|
@@ -928,6 +976,36 @@ function getPlay(playId) {
|
|
|
928
976
|
function stopPlay(playId) {
|
|
929
977
|
return sendPlayStop(playId);
|
|
930
978
|
}
|
|
979
|
+
async function sendPlayPose(playId, pose) {
|
|
980
|
+
const requestId = randomUUID();
|
|
981
|
+
if (!playId?.trim() || !pose) {
|
|
982
|
+
return {
|
|
983
|
+
ok: false,
|
|
984
|
+
playId: playId ?? "",
|
|
985
|
+
reason: "invalid_payload",
|
|
986
|
+
requestId
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
if (!isVoicethereAgentChild()) {
|
|
990
|
+
return { ok: true, playId, reason: "local_mock", requestId };
|
|
991
|
+
}
|
|
992
|
+
return new Promise((resolve) => {
|
|
993
|
+
const timer = setTimeout(() => {
|
|
994
|
+
pendingPlayPoseAcks.delete(requestId);
|
|
995
|
+
resolve({ ok: false, playId, reason: "timeout", requestId });
|
|
996
|
+
}, PLAY_CONTROL_ACK_TIMEOUT_MS);
|
|
997
|
+
pendingPlayPoseAcks.set(requestId, { playId, resolve, timer });
|
|
998
|
+
sendParentMessage({
|
|
999
|
+
type: "play_pose",
|
|
1000
|
+
requestId,
|
|
1001
|
+
playId,
|
|
1002
|
+
pose
|
|
1003
|
+
});
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
function setPlayPose(playId, pose) {
|
|
1007
|
+
return sendPlayPose(playId, pose);
|
|
1008
|
+
}
|
|
931
1009
|
function sendToClient(sessionId, payload) {
|
|
932
1010
|
sendParentMessage({ type: "send_to_client", sessionId, payload });
|
|
933
1011
|
}
|
|
@@ -1059,6 +1137,8 @@ function resolveClipUrl(assetOrigin, clipId) {
|
|
|
1059
1137
|
var SHOWCASE_DEMOS = /* @__PURE__ */ new Set(["orbit", "soundboard", "proximity"]);
|
|
1060
1138
|
var MAX_SAY_TEXT_LENGTH = 200;
|
|
1061
1139
|
var MAX_XZ = 5;
|
|
1140
|
+
var ORBIT_SINE_MIN_FREQUENCY_HZ = 110;
|
|
1141
|
+
var ORBIT_SINE_MAX_FREQUENCY_HZ = 1760;
|
|
1062
1142
|
function isFiniteNumber(value) {
|
|
1063
1143
|
return typeof value === "number" && Number.isFinite(value);
|
|
1064
1144
|
}
|
|
@@ -1068,6 +1148,9 @@ function clampVolume(value) {
|
|
|
1068
1148
|
function isInXzRange(value) {
|
|
1069
1149
|
return value >= -MAX_XZ && value <= MAX_XZ;
|
|
1070
1150
|
}
|
|
1151
|
+
function isOrbitSineFrequencyHz(value) {
|
|
1152
|
+
return value >= ORBIT_SINE_MIN_FREQUENCY_HZ && value <= ORBIT_SINE_MAX_FREQUENCY_HZ;
|
|
1153
|
+
}
|
|
1071
1154
|
function isMixPlacement(value) {
|
|
1072
1155
|
return value === "center" || value === "left" || value === "right" || value === "front" || value === "behind" || value === "below" || value === "above";
|
|
1073
1156
|
}
|
|
@@ -1116,15 +1199,33 @@ function parseShowcaseMessage(message) {
|
|
|
1116
1199
|
if (set.paused !== void 0 && typeof set.paused !== "boolean") {
|
|
1117
1200
|
return null;
|
|
1118
1201
|
}
|
|
1202
|
+
if (set.frequencyHz !== void 0 && (!isFiniteNumber(set.frequencyHz) || !isOrbitSineFrequencyHz(set.frequencyHz))) {
|
|
1203
|
+
return null;
|
|
1204
|
+
}
|
|
1205
|
+
if (set.sinePlaying !== void 0 && typeof set.sinePlaying !== "boolean") {
|
|
1206
|
+
return null;
|
|
1207
|
+
}
|
|
1119
1208
|
return {
|
|
1120
1209
|
type: "orbit",
|
|
1121
1210
|
action: "set",
|
|
1122
1211
|
...set.radius !== void 0 ? { radius: set.radius } : {},
|
|
1123
1212
|
...set.periodSec !== void 0 ? { periodSec: set.periodSec } : {},
|
|
1124
1213
|
...set.elevation !== void 0 ? { elevation: set.elevation } : {},
|
|
1125
|
-
...set.paused !== void 0 ? { paused: set.paused } : {}
|
|
1214
|
+
...set.paused !== void 0 ? { paused: set.paused } : {},
|
|
1215
|
+
...set.frequencyHz !== void 0 ? { frequencyHz: set.frequencyHz } : {},
|
|
1216
|
+
...set.sinePlaying !== void 0 ? { sinePlaying: set.sinePlaying } : {}
|
|
1126
1217
|
};
|
|
1127
1218
|
}
|
|
1219
|
+
if (orbit.action === "place") {
|
|
1220
|
+
const place = message;
|
|
1221
|
+
if (!isFiniteNumber(place.x) || !isInXzRange(place.x)) {
|
|
1222
|
+
return null;
|
|
1223
|
+
}
|
|
1224
|
+
if (!isFiniteNumber(place.z) || !isInXzRange(place.z)) {
|
|
1225
|
+
return null;
|
|
1226
|
+
}
|
|
1227
|
+
return { type: "orbit", action: "place", x: place.x, z: place.z };
|
|
1228
|
+
}
|
|
1128
1229
|
if (orbit.action === "say") {
|
|
1129
1230
|
const say = message;
|
|
1130
1231
|
if (typeof say.text !== "string" || say.text.length === 0 || say.text.length > MAX_SAY_TEXT_LENGTH) {
|
|
@@ -1176,6 +1277,24 @@ function parseShowcaseMessage(message) {
|
|
|
1176
1277
|
}
|
|
1177
1278
|
return { type: "pad_status", playId: status.playId.trim() };
|
|
1178
1279
|
}
|
|
1280
|
+
case "pad_move": {
|
|
1281
|
+
const padMove = message;
|
|
1282
|
+
if (!isNonEmptyString(padMove.playId)) {
|
|
1283
|
+
return null;
|
|
1284
|
+
}
|
|
1285
|
+
if (!isFiniteNumber(padMove.x) || !isInXzRange(padMove.x)) {
|
|
1286
|
+
return null;
|
|
1287
|
+
}
|
|
1288
|
+
if (!isFiniteNumber(padMove.z) || !isInXzRange(padMove.z)) {
|
|
1289
|
+
return null;
|
|
1290
|
+
}
|
|
1291
|
+
return {
|
|
1292
|
+
type: "pad_move",
|
|
1293
|
+
playId: padMove.playId.trim(),
|
|
1294
|
+
x: padMove.x,
|
|
1295
|
+
z: padMove.z
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1179
1298
|
case "move": {
|
|
1180
1299
|
const move = message;
|
|
1181
1300
|
if (!isFiniteNumber(move.x) || !isInXzRange(move.x)) {
|
|
@@ -1286,12 +1405,60 @@ var ProximityRoom = class {
|
|
|
1286
1405
|
}
|
|
1287
1406
|
};
|
|
1288
1407
|
|
|
1408
|
+
// templates/spatial-showcase/sine.ts
|
|
1409
|
+
var ORBIT_SINE_DURATION_MS = 2e3;
|
|
1410
|
+
var ORBIT_SINE_FREQUENCY_HZ = 440;
|
|
1411
|
+
var ORBIT_SINE_AMPLITUDE = 8e3;
|
|
1412
|
+
var ORBIT_SINE_SAMPLE_RATE = 16e3;
|
|
1413
|
+
var ORBIT_SINE_INLINE_URL = "https://example.com/inline-orbit-sine.wav";
|
|
1414
|
+
function orbitSineSampleCount(durationMs = ORBIT_SINE_DURATION_MS, frequencyHz = ORBIT_SINE_FREQUENCY_HZ, sampleRate = ORBIT_SINE_SAMPLE_RATE) {
|
|
1415
|
+
const targetCycles = durationMs / 1e3 * frequencyHz;
|
|
1416
|
+
const cycles = Math.max(1, Math.floor(targetCycles));
|
|
1417
|
+
return Math.round(cycles * sampleRate / frequencyHz);
|
|
1418
|
+
}
|
|
1419
|
+
function orbitSineClipDurationMs(durationMs = ORBIT_SINE_DURATION_MS, frequencyHz = ORBIT_SINE_FREQUENCY_HZ, sampleRate = ORBIT_SINE_SAMPLE_RATE) {
|
|
1420
|
+
const numSamples = orbitSineSampleCount(durationMs, frequencyHz, sampleRate);
|
|
1421
|
+
return numSamples / sampleRate * 1e3;
|
|
1422
|
+
}
|
|
1423
|
+
function buildOrbitSineInlineClipBase64(durationMs = ORBIT_SINE_DURATION_MS, frequencyHz = ORBIT_SINE_FREQUENCY_HZ, amplitude = ORBIT_SINE_AMPLITUDE, phaseRad = 0) {
|
|
1424
|
+
const sampleRate = ORBIT_SINE_SAMPLE_RATE;
|
|
1425
|
+
const channels = 1;
|
|
1426
|
+
const bytesPerSample = 2;
|
|
1427
|
+
const numSamples = orbitSineSampleCount(durationMs, frequencyHz, sampleRate);
|
|
1428
|
+
const dataSize = numSamples * channels * bytesPerSample;
|
|
1429
|
+
const buffer = Buffer.alloc(44 + dataSize);
|
|
1430
|
+
buffer.write("RIFF", 0);
|
|
1431
|
+
buffer.writeUInt32LE(36 + dataSize, 4);
|
|
1432
|
+
buffer.write("WAVE", 8);
|
|
1433
|
+
buffer.write("fmt ", 12);
|
|
1434
|
+
buffer.writeUInt32LE(16, 16);
|
|
1435
|
+
buffer.writeUInt16LE(1, 20);
|
|
1436
|
+
buffer.writeUInt16LE(channels, 22);
|
|
1437
|
+
buffer.writeUInt32LE(sampleRate, 24);
|
|
1438
|
+
buffer.writeUInt32LE(sampleRate * channels * bytesPerSample, 28);
|
|
1439
|
+
buffer.writeUInt16LE(channels * bytesPerSample, 32);
|
|
1440
|
+
buffer.writeUInt16LE(16, 34);
|
|
1441
|
+
buffer.write("data", 36);
|
|
1442
|
+
buffer.writeUInt32LE(dataSize, 40);
|
|
1443
|
+
for (let i = 0; i < numSamples; i++) {
|
|
1444
|
+
const t = i / sampleRate;
|
|
1445
|
+
const sample = Math.round(
|
|
1446
|
+
amplitude * Math.sin(2 * Math.PI * frequencyHz * t + phaseRad)
|
|
1447
|
+
);
|
|
1448
|
+
buffer.writeInt16LE(sample, 44 + i * 2);
|
|
1449
|
+
}
|
|
1450
|
+
return buffer.toString("base64");
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1289
1453
|
// templates/spatial-showcase/agent.ts
|
|
1290
1454
|
var ORBIT_TICK_MS = 50;
|
|
1291
1455
|
var ORBIT_POSE_EMIT_MS = 100;
|
|
1292
1456
|
var ROOM_STATE_TICK_MS = 100;
|
|
1293
1457
|
var LOOP_PAD_POLL_MS = 250;
|
|
1294
|
-
var
|
|
1458
|
+
var ORBIT_SINE_POLL_MS = 250;
|
|
1459
|
+
var ORBIT_SINE_GAPLESS_LEAD_MS = 200;
|
|
1460
|
+
var ORBIT_SINE_VOLUME = 0.25;
|
|
1461
|
+
var MAX_ACTIVE_PLAYS = 12;
|
|
1295
1462
|
var SHOWCASE_ROOM_GROUP_ID = "showcase-room";
|
|
1296
1463
|
var sessions = /* @__PURE__ */ new Map();
|
|
1297
1464
|
var proximityRoom = new ProximityRoom();
|
|
@@ -1314,12 +1481,13 @@ function ack(sessionId, action, extra) {
|
|
|
1314
1481
|
...extra
|
|
1315
1482
|
});
|
|
1316
1483
|
}
|
|
1317
|
-
function ackError(sessionId, action, error) {
|
|
1484
|
+
function ackError(sessionId, action, error, extra) {
|
|
1318
1485
|
sendToClient(sessionId, {
|
|
1319
1486
|
type: "showcase_ack",
|
|
1320
1487
|
action,
|
|
1321
1488
|
ok: false,
|
|
1322
|
-
error
|
|
1489
|
+
error,
|
|
1490
|
+
...extra?.clipId !== void 0 ? { clipId: extra.clipId } : {}
|
|
1323
1491
|
});
|
|
1324
1492
|
}
|
|
1325
1493
|
async function safeMixCall(sessionId, label, fn) {
|
|
@@ -1351,6 +1519,9 @@ function defaultSessionState(demo, assetOrigin) {
|
|
|
1351
1519
|
playIds: /* @__PURE__ */ new Set(),
|
|
1352
1520
|
loopPads: /* @__PURE__ */ new Map(),
|
|
1353
1521
|
loopPollTimers: /* @__PURE__ */ new Map(),
|
|
1522
|
+
orbitSineFrequencyHz: 440,
|
|
1523
|
+
orbitSineEnabled: true,
|
|
1524
|
+
orbitSinePhaseRad: 0,
|
|
1354
1525
|
inProximityRoom: false
|
|
1355
1526
|
};
|
|
1356
1527
|
}
|
|
@@ -1360,6 +1531,14 @@ function clearRoomStateTimer(state) {
|
|
|
1360
1531
|
state.roomStateTimer = void 0;
|
|
1361
1532
|
}
|
|
1362
1533
|
}
|
|
1534
|
+
function stopLoopPadsForClip(sessionId, state, clipId) {
|
|
1535
|
+
for (const [playId, pad] of [...state.loopPads.entries()]) {
|
|
1536
|
+
if (pad.clipId === clipId) {
|
|
1537
|
+
void stopPlay(playId);
|
|
1538
|
+
clearLoopPad(sessionId, state, playId);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1363
1542
|
function clearLoopPad(sessionId, state, playId) {
|
|
1364
1543
|
const timer = state.loopPollTimers.get(playId);
|
|
1365
1544
|
if (timer) {
|
|
@@ -1383,8 +1562,162 @@ function clearAllPlays(sessionId, state) {
|
|
|
1383
1562
|
state.loopPads.clear();
|
|
1384
1563
|
state.loopPollTimers.clear();
|
|
1385
1564
|
}
|
|
1386
|
-
function
|
|
1565
|
+
function currentOrbitPose(state) {
|
|
1566
|
+
if (state.orbitManualPose) {
|
|
1567
|
+
const elevation = state.orbit.elevation ?? 0;
|
|
1568
|
+
return {
|
|
1569
|
+
position: {
|
|
1570
|
+
x: state.orbitManualPose.x,
|
|
1571
|
+
y: elevation,
|
|
1572
|
+
z: state.orbitManualPose.z
|
|
1573
|
+
},
|
|
1574
|
+
orientation: { x: 0, y: 0, z: 0, w: 1 }
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1387
1577
|
if (!state.orbitStartMs) {
|
|
1578
|
+
return orbitTtsPose(0, state.orbit);
|
|
1579
|
+
}
|
|
1580
|
+
const elapsedSec = (Date.now() - state.orbitStartMs) / 1e3;
|
|
1581
|
+
return orbitTtsPose(elapsedSec, state.orbit);
|
|
1582
|
+
}
|
|
1583
|
+
function orbitPoseFromXz(state, x, z) {
|
|
1584
|
+
const elevation = state.orbit.elevation ?? 0;
|
|
1585
|
+
return {
|
|
1586
|
+
position: { x, y: elevation, z },
|
|
1587
|
+
orientation: { x: 0, y: 0, z: 0, w: 1 }
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
function syncOrbitClockFromManualPose(state) {
|
|
1591
|
+
const manual = state.orbitManualPose;
|
|
1592
|
+
if (!manual) {
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
const periodSec = state.orbit.periodSec ?? 2 * Math.PI;
|
|
1596
|
+
const angleRad = Math.atan2(manual.z, manual.x);
|
|
1597
|
+
const elapsedSec = angleRad * periodSec / (2 * Math.PI);
|
|
1598
|
+
state.orbitStartMs = Date.now() - elapsedSec * 1e3;
|
|
1599
|
+
state.orbitManualPose = void 0;
|
|
1600
|
+
}
|
|
1601
|
+
function clearOrbitSinePoll(state) {
|
|
1602
|
+
if (state.orbitSinePollTimer) {
|
|
1603
|
+
clearInterval(state.orbitSinePollTimer);
|
|
1604
|
+
state.orbitSinePollTimer = void 0;
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
function clearOrbitSineRestartTimer(state) {
|
|
1608
|
+
if (state.orbitSineRestartTimer) {
|
|
1609
|
+
clearTimeout(state.orbitSineRestartTimer);
|
|
1610
|
+
state.orbitSineRestartTimer = void 0;
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
async function stopOrbitSine(state) {
|
|
1614
|
+
clearOrbitSinePoll(state);
|
|
1615
|
+
clearOrbitSineRestartTimer(state);
|
|
1616
|
+
const playId = state.orbitSinePlayId;
|
|
1617
|
+
state.orbitSinePlayId = void 0;
|
|
1618
|
+
if (playId) {
|
|
1619
|
+
await stopPlay(playId);
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
function orbitSineClipBytes(state) {
|
|
1623
|
+
return buildOrbitSineInlineClipBase64(
|
|
1624
|
+
ORBIT_SINE_DURATION_MS,
|
|
1625
|
+
state.orbitSineFrequencyHz,
|
|
1626
|
+
void 0,
|
|
1627
|
+
state.orbitSinePhaseRad
|
|
1628
|
+
);
|
|
1629
|
+
}
|
|
1630
|
+
function orbitSineClipDuration(state) {
|
|
1631
|
+
return orbitSineClipDurationMs(
|
|
1632
|
+
ORBIT_SINE_DURATION_MS,
|
|
1633
|
+
state.orbitSineFrequencyHz
|
|
1634
|
+
);
|
|
1635
|
+
}
|
|
1636
|
+
function scheduleOrbitSineGaplessRestart(sessionId, state) {
|
|
1637
|
+
clearOrbitSineRestartTimer(state);
|
|
1638
|
+
const durationMs = orbitSineClipDuration(state);
|
|
1639
|
+
const delayMs = Math.max(0, durationMs - ORBIT_SINE_GAPLESS_LEAD_MS);
|
|
1640
|
+
state.orbitSineRestartTimer = setTimeout(() => {
|
|
1641
|
+
state.orbitSineRestartTimer = void 0;
|
|
1642
|
+
void restartOrbitSineGapless(sessionId, state);
|
|
1643
|
+
}, delayMs);
|
|
1644
|
+
}
|
|
1645
|
+
async function restartOrbitSineGapless(sessionId, state) {
|
|
1646
|
+
if (!state.orbitSineEnabled || !state.orbitSinePlayId) {
|
|
1647
|
+
return;
|
|
1648
|
+
}
|
|
1649
|
+
const previousPlayId = state.orbitSinePlayId;
|
|
1650
|
+
const result = await play({
|
|
1651
|
+
url: ORBIT_SINE_INLINE_URL,
|
|
1652
|
+
bytes: orbitSineClipBytes(state),
|
|
1653
|
+
sessionIds: [sessionId],
|
|
1654
|
+
volume: ORBIT_SINE_VOLUME,
|
|
1655
|
+
pose: currentOrbitPose(state)
|
|
1656
|
+
});
|
|
1657
|
+
if (!result.ok || !result.playId) {
|
|
1658
|
+
agentLog(
|
|
1659
|
+
"warn",
|
|
1660
|
+
`spatial-showcase orbit sine gapless restart failed: ${result.reason ?? "play_failed"}`,
|
|
1661
|
+
sessionId
|
|
1662
|
+
);
|
|
1663
|
+
startOrbitSineLoopPoll(sessionId, state);
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
state.orbitSinePlayId = result.playId;
|
|
1667
|
+
void stopPlay(previousPlayId);
|
|
1668
|
+
scheduleOrbitSineGaplessRestart(sessionId, state);
|
|
1669
|
+
}
|
|
1670
|
+
function startOrbitSineLoopPoll(sessionId, state) {
|
|
1671
|
+
if (state.orbitSineRestartTimer) {
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1674
|
+
clearOrbitSinePoll(state);
|
|
1675
|
+
const playId = state.orbitSinePlayId;
|
|
1676
|
+
if (!playId) {
|
|
1677
|
+
return;
|
|
1678
|
+
}
|
|
1679
|
+
state.orbitSinePollTimer = setInterval(() => {
|
|
1680
|
+
void (async () => {
|
|
1681
|
+
if (!state.orbitSinePlayId || state.orbitSineRestartTimer) {
|
|
1682
|
+
clearOrbitSinePoll(state);
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
const status = await getPlay(state.orbitSinePlayId);
|
|
1686
|
+
if (!status.ok) {
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
if (status.status === "ended") {
|
|
1690
|
+
await restartOrbitSineGapless(sessionId, state);
|
|
1691
|
+
}
|
|
1692
|
+
})();
|
|
1693
|
+
}, ORBIT_SINE_POLL_MS);
|
|
1694
|
+
}
|
|
1695
|
+
async function startOrbitSinePlay(sessionId, state) {
|
|
1696
|
+
if (!state.orbitSineEnabled) {
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
await stopOrbitSine(state);
|
|
1700
|
+
const result = await play({
|
|
1701
|
+
url: ORBIT_SINE_INLINE_URL,
|
|
1702
|
+
bytes: orbitSineClipBytes(state),
|
|
1703
|
+
sessionIds: [sessionId],
|
|
1704
|
+
volume: ORBIT_SINE_VOLUME,
|
|
1705
|
+
pose: currentOrbitPose(state)
|
|
1706
|
+
});
|
|
1707
|
+
if (!result.ok || !result.playId) {
|
|
1708
|
+
agentLog(
|
|
1709
|
+
"warn",
|
|
1710
|
+
`spatial-showcase orbit sine play failed: ${result.reason ?? "play_failed"}`,
|
|
1711
|
+
sessionId
|
|
1712
|
+
);
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
state.orbitSinePlayId = result.playId;
|
|
1716
|
+
scheduleOrbitSineGaplessRestart(sessionId, state);
|
|
1717
|
+
startOrbitSineLoopPoll(sessionId, state);
|
|
1718
|
+
}
|
|
1719
|
+
function emitOrbitPose(sessionId, state) {
|
|
1720
|
+
if (!state.orbitStartMs && !state.orbitManualPose) {
|
|
1388
1721
|
return;
|
|
1389
1722
|
}
|
|
1390
1723
|
const now = Date.now();
|
|
@@ -1392,9 +1725,8 @@ function emitOrbitPose(sessionId, state) {
|
|
|
1392
1725
|
return;
|
|
1393
1726
|
}
|
|
1394
1727
|
state.lastOrbitPoseEmitMs = now;
|
|
1395
|
-
const
|
|
1396
|
-
const
|
|
1397
|
-
const angleRad = 2 * Math.PI * elapsedSec / (state.orbit.periodSec ?? 2 * Math.PI);
|
|
1728
|
+
const pose = currentOrbitPose(state);
|
|
1729
|
+
const angleRad = state.orbitManualPose ? Math.atan2(state.orbitManualPose.z, state.orbitManualPose.x) : 2 * Math.PI * (now - state.orbitStartMs) / 1e3 / (state.orbit.periodSec ?? 2 * Math.PI);
|
|
1398
1730
|
sendToClient(sessionId, {
|
|
1399
1731
|
type: "orbit_pose",
|
|
1400
1732
|
angleDeg: angleRad * 180 / Math.PI,
|
|
@@ -1402,18 +1734,36 @@ function emitOrbitPose(sessionId, state) {
|
|
|
1402
1734
|
z: pose.position.z
|
|
1403
1735
|
});
|
|
1404
1736
|
}
|
|
1737
|
+
function emitOrbitPlacePose(sessionId, state, x, z) {
|
|
1738
|
+
state.lastOrbitPoseEmitMs = Date.now();
|
|
1739
|
+
const angleRad = Math.atan2(z, x);
|
|
1740
|
+
sendToClient(sessionId, {
|
|
1741
|
+
type: "orbit_pose",
|
|
1742
|
+
angleDeg: angleRad * 180 / Math.PI,
|
|
1743
|
+
x,
|
|
1744
|
+
z
|
|
1745
|
+
});
|
|
1746
|
+
}
|
|
1405
1747
|
function startOrbitDemo(sessionId, state) {
|
|
1406
1748
|
stopOrbitInterval(state);
|
|
1407
1749
|
beginOrbitClock(state);
|
|
1750
|
+
void startOrbitSinePlay(sessionId, state);
|
|
1408
1751
|
state.orbitTimer = setInterval(() => {
|
|
1409
|
-
if (state.orbit.paused || !state.orbitStartMs) {
|
|
1752
|
+
if (state.orbit.paused || !state.orbitStartMs && !state.orbitManualPose) {
|
|
1410
1753
|
return;
|
|
1411
1754
|
}
|
|
1412
|
-
const
|
|
1755
|
+
const pose = currentOrbitPose(state);
|
|
1756
|
+
if (state.orbitSinePlayId) {
|
|
1757
|
+
void safeMixCall(
|
|
1758
|
+
sessionId,
|
|
1759
|
+
"setPlayPose",
|
|
1760
|
+
() => setPlayPose(state.orbitSinePlayId, pose)
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1413
1763
|
void safeMixCall(
|
|
1414
1764
|
sessionId,
|
|
1415
1765
|
"setTtsPose",
|
|
1416
|
-
() => setTtsPose(sessionId,
|
|
1766
|
+
() => setTtsPose(sessionId, pose)
|
|
1417
1767
|
);
|
|
1418
1768
|
emitOrbitPose(sessionId, state);
|
|
1419
1769
|
}, ORBIT_TICK_MS);
|
|
@@ -1482,6 +1832,13 @@ async function startLoopPadPoll(sessionId, state, playId, pad) {
|
|
|
1482
1832
|
clearLoopPad(sessionId, state, playId);
|
|
1483
1833
|
state.playIds.add(replay.playId);
|
|
1484
1834
|
state.loopPads.set(replay.playId, pad);
|
|
1835
|
+
ack(sessionId, "pad", {
|
|
1836
|
+
playId: replay.playId,
|
|
1837
|
+
clipId: pad.clipId,
|
|
1838
|
+
x: pad.x,
|
|
1839
|
+
z: pad.z,
|
|
1840
|
+
...pad.placement !== void 0 ? { placement: pad.placement } : {}
|
|
1841
|
+
});
|
|
1485
1842
|
void startLoopPadPoll(sessionId, state, replay.playId, pad);
|
|
1486
1843
|
}
|
|
1487
1844
|
} else if (status.status) {
|
|
@@ -1496,21 +1853,24 @@ async function startLoopPadPoll(sessionId, state, playId, pad) {
|
|
|
1496
1853
|
state.loopPollTimers.set(playId, timer);
|
|
1497
1854
|
}
|
|
1498
1855
|
async function handlePadPlay(sessionId, state, clipId, x, z, volume, placement) {
|
|
1499
|
-
if (state.playIds.size >= MAX_ACTIVE_PLAYS) {
|
|
1500
|
-
ackError(sessionId, "pad", "busy");
|
|
1501
|
-
return;
|
|
1502
|
-
}
|
|
1503
1856
|
const assetOrigin = state.assetOrigin;
|
|
1504
1857
|
if (!assetOrigin) {
|
|
1505
|
-
ackError(sessionId, "pad", "missing_asset_origin");
|
|
1858
|
+
ackError(sessionId, "pad", "missing_asset_origin", { clipId });
|
|
1506
1859
|
return;
|
|
1507
1860
|
}
|
|
1508
1861
|
const url = resolveClipUrl(assetOrigin, clipId);
|
|
1509
1862
|
if (!url) {
|
|
1510
|
-
ackError(sessionId, "pad", "invalid_asset_origin");
|
|
1863
|
+
ackError(sessionId, "pad", "invalid_asset_origin", { clipId });
|
|
1511
1864
|
return;
|
|
1512
1865
|
}
|
|
1513
1866
|
const sound = SHOWCASE_SOUND_FILES[clipId];
|
|
1867
|
+
if (sound.loop) {
|
|
1868
|
+
stopLoopPadsForClip(sessionId, state, clipId);
|
|
1869
|
+
}
|
|
1870
|
+
if (state.playIds.size >= MAX_ACTIVE_PLAYS) {
|
|
1871
|
+
ackError(sessionId, "pad", "busy", { clipId });
|
|
1872
|
+
return;
|
|
1873
|
+
}
|
|
1514
1874
|
const playVolume = volume ?? sound.defaultVolume;
|
|
1515
1875
|
const options = {
|
|
1516
1876
|
url,
|
|
@@ -1520,11 +1880,17 @@ async function handlePadPlay(sessionId, state, clipId, x, z, volume, placement)
|
|
|
1520
1880
|
};
|
|
1521
1881
|
const result = await play(options);
|
|
1522
1882
|
if (!result.ok || !result.playId) {
|
|
1523
|
-
ackError(sessionId, "pad", result.reason ?? "play_failed");
|
|
1883
|
+
ackError(sessionId, "pad", result.reason ?? "play_failed", { clipId });
|
|
1524
1884
|
return;
|
|
1525
1885
|
}
|
|
1526
1886
|
state.playIds.add(result.playId);
|
|
1527
|
-
ack(sessionId, "pad", {
|
|
1887
|
+
ack(sessionId, "pad", {
|
|
1888
|
+
playId: result.playId,
|
|
1889
|
+
clipId,
|
|
1890
|
+
x,
|
|
1891
|
+
z,
|
|
1892
|
+
...placement !== void 0 ? { placement } : {}
|
|
1893
|
+
});
|
|
1528
1894
|
if (sound.loop) {
|
|
1529
1895
|
const pad = {
|
|
1530
1896
|
clipId,
|
|
@@ -1538,6 +1904,24 @@ async function handlePadPlay(sessionId, state, clipId, x, z, volume, placement)
|
|
|
1538
1904
|
void startLoopPadPoll(sessionId, state, result.playId, pad);
|
|
1539
1905
|
}
|
|
1540
1906
|
}
|
|
1907
|
+
async function handlePadMove(sessionId, state, playId, x, z) {
|
|
1908
|
+
if (!state.playIds.has(playId)) {
|
|
1909
|
+
ackError(sessionId, "pad_move", "unknown_play");
|
|
1910
|
+
return;
|
|
1911
|
+
}
|
|
1912
|
+
const poseResult = await setPlayPose(playId, poseAt(x, z));
|
|
1913
|
+
if (!poseResult.ok) {
|
|
1914
|
+
ackError(sessionId, "pad_move", poseResult.reason ?? "set_pose_failed");
|
|
1915
|
+
return;
|
|
1916
|
+
}
|
|
1917
|
+
const loopPad = state.loopPads.get(playId);
|
|
1918
|
+
if (loopPad) {
|
|
1919
|
+
loopPad.x = x;
|
|
1920
|
+
loopPad.z = z;
|
|
1921
|
+
delete loopPad.placement;
|
|
1922
|
+
}
|
|
1923
|
+
ack(sessionId, "pad_move", { playId, x, z });
|
|
1924
|
+
}
|
|
1541
1925
|
async function handleJoin(sessionId, demo, assetOrigin) {
|
|
1542
1926
|
teardownSession(sessionId);
|
|
1543
1927
|
const state = defaultSessionState(demo, assetOrigin);
|
|
@@ -1553,9 +1937,15 @@ async function handleJoin(sessionId, demo, assetOrigin) {
|
|
|
1553
1937
|
}
|
|
1554
1938
|
ack(sessionId, "join");
|
|
1555
1939
|
switch (demo) {
|
|
1556
|
-
case "orbit":
|
|
1940
|
+
case "orbit": {
|
|
1941
|
+
void safeMixCall(
|
|
1942
|
+
sessionId,
|
|
1943
|
+
"setSttEnabled",
|
|
1944
|
+
() => setSttEnabled({ enabled: false, sessionId })
|
|
1945
|
+
);
|
|
1557
1946
|
startOrbitDemo(sessionId, state);
|
|
1558
1947
|
break;
|
|
1948
|
+
}
|
|
1559
1949
|
case "soundboard":
|
|
1560
1950
|
break;
|
|
1561
1951
|
case "proximity": {
|
|
@@ -1600,8 +1990,14 @@ function teardownSession(sessionId) {
|
|
|
1600
1990
|
}
|
|
1601
1991
|
resetOrbitSession(state);
|
|
1602
1992
|
clearRoomStateTimer(state);
|
|
1993
|
+
void stopOrbitSine(state);
|
|
1603
1994
|
clearAllPlays(sessionId, state);
|
|
1604
1995
|
void safeMixCall(sessionId, "clearTtsPose", () => clearTtsPose(sessionId));
|
|
1996
|
+
void safeMixCall(
|
|
1997
|
+
sessionId,
|
|
1998
|
+
"setSttEnabled",
|
|
1999
|
+
() => setSttEnabled({ enabled: true, sessionId })
|
|
2000
|
+
);
|
|
1605
2001
|
if (state.inProximityRoom) {
|
|
1606
2002
|
proximityRoom.leave(sessionId);
|
|
1607
2003
|
void safeMixCall(
|
|
@@ -1652,11 +2048,54 @@ async function dispatchMessage(sessionId, message) {
|
|
|
1652
2048
|
state.orbit.elevation = message.elevation;
|
|
1653
2049
|
}
|
|
1654
2050
|
if (message.paused !== void 0) {
|
|
2051
|
+
if (message.paused === false && state.orbitManualPose) {
|
|
2052
|
+
syncOrbitClockFromManualPose(state);
|
|
2053
|
+
}
|
|
1655
2054
|
state.orbit.paused = message.paused;
|
|
1656
2055
|
}
|
|
2056
|
+
if (message.frequencyHz !== void 0) {
|
|
2057
|
+
state.orbitSineFrequencyHz = message.frequencyHz;
|
|
2058
|
+
if (state.orbitSineEnabled && state.orbitSinePlayId) {
|
|
2059
|
+
void startOrbitSinePlay(sessionId, state);
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
if (message.sinePlaying !== void 0) {
|
|
2063
|
+
state.orbitSineEnabled = message.sinePlaying;
|
|
2064
|
+
if (message.sinePlaying) {
|
|
2065
|
+
if (!state.orbitSinePlayId) {
|
|
2066
|
+
void startOrbitSinePlay(sessionId, state);
|
|
2067
|
+
}
|
|
2068
|
+
} else {
|
|
2069
|
+
void stopOrbitSine(state);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
1657
2072
|
ack(sessionId, "orbit_set");
|
|
1658
2073
|
return;
|
|
1659
2074
|
}
|
|
2075
|
+
if (message.action === "place") {
|
|
2076
|
+
state.orbit.paused = true;
|
|
2077
|
+
state.orbitManualPose = { x: message.x, z: message.z };
|
|
2078
|
+
const pose = orbitPoseFromXz(state, message.x, message.z);
|
|
2079
|
+
if (state.orbitSinePlayId) {
|
|
2080
|
+
void safeMixCall(
|
|
2081
|
+
sessionId,
|
|
2082
|
+
"setPlayPose",
|
|
2083
|
+
() => setPlayPose(state.orbitSinePlayId, pose)
|
|
2084
|
+
);
|
|
2085
|
+
}
|
|
2086
|
+
void safeMixCall(
|
|
2087
|
+
sessionId,
|
|
2088
|
+
"setTtsPose",
|
|
2089
|
+
() => setTtsPose(sessionId, pose)
|
|
2090
|
+
);
|
|
2091
|
+
emitOrbitPlacePose(sessionId, state, message.x, message.z);
|
|
2092
|
+
ack(sessionId, "orbit_place");
|
|
2093
|
+
return;
|
|
2094
|
+
}
|
|
2095
|
+
sendToClient(sessionId, {
|
|
2096
|
+
type: "orbit_say_status",
|
|
2097
|
+
status: "playing"
|
|
2098
|
+
});
|
|
1660
2099
|
speak(sessionId, message.text);
|
|
1661
2100
|
ack(sessionId, "orbit_say");
|
|
1662
2101
|
return;
|
|
@@ -1666,6 +2105,10 @@ async function dispatchMessage(sessionId, message) {
|
|
|
1666
2105
|
ackError(sessionId, "pad", "wrong_demo");
|
|
1667
2106
|
return;
|
|
1668
2107
|
}
|
|
2108
|
+
if (!isShowcaseClipId(message.clipId)) {
|
|
2109
|
+
ackError(sessionId, "pad", "invalid_clip_id");
|
|
2110
|
+
return;
|
|
2111
|
+
}
|
|
1669
2112
|
await handlePadPlay(
|
|
1670
2113
|
sessionId,
|
|
1671
2114
|
state,
|
|
@@ -1709,6 +2152,20 @@ async function dispatchMessage(sessionId, message) {
|
|
|
1709
2152
|
}
|
|
1710
2153
|
return;
|
|
1711
2154
|
}
|
|
2155
|
+
case "pad_move": {
|
|
2156
|
+
if (state.demo !== "soundboard") {
|
|
2157
|
+
ackError(sessionId, "pad_move", "wrong_demo");
|
|
2158
|
+
return;
|
|
2159
|
+
}
|
|
2160
|
+
await handlePadMove(
|
|
2161
|
+
sessionId,
|
|
2162
|
+
state,
|
|
2163
|
+
message.playId,
|
|
2164
|
+
message.x,
|
|
2165
|
+
message.z
|
|
2166
|
+
);
|
|
2167
|
+
return;
|
|
2168
|
+
}
|
|
1712
2169
|
case "move": {
|
|
1713
2170
|
if (state.demo !== "proximity") {
|
|
1714
2171
|
ackError(sessionId, "move", "wrong_demo");
|
|
@@ -1784,13 +2241,6 @@ defineAgent({
|
|
|
1784
2241
|
ok: true
|
|
1785
2242
|
});
|
|
1786
2243
|
},
|
|
1787
|
-
onUserSpeechFinal({ sessionId, text }) {
|
|
1788
|
-
const state = sessions.get(sessionId);
|
|
1789
|
-
if (!state || state.demo !== "orbit") {
|
|
1790
|
-
return;
|
|
1791
|
-
}
|
|
1792
|
-
speak(sessionId, `You said: ${text}`);
|
|
1793
|
-
},
|
|
1794
2244
|
onDataChannelMessage(ctx) {
|
|
1795
2245
|
const message = parseShowcaseMessage(ctx.message);
|
|
1796
2246
|
if (!message) {
|
|
@@ -1798,11 +2248,30 @@ defineAgent({
|
|
|
1798
2248
|
}
|
|
1799
2249
|
void dispatchMessage(ctx.sessionId, message);
|
|
1800
2250
|
},
|
|
2251
|
+
onSpeechEvent(ctx, event) {
|
|
2252
|
+
const state = sessions.get(ctx.sessionId);
|
|
2253
|
+
if (!state || state.demo !== "orbit") {
|
|
2254
|
+
return;
|
|
2255
|
+
}
|
|
2256
|
+
if (event.type === "agent_speaking_start") {
|
|
2257
|
+
sendToClient(ctx.sessionId, {
|
|
2258
|
+
type: "orbit_say_status",
|
|
2259
|
+
status: "playing"
|
|
2260
|
+
});
|
|
2261
|
+
} else if (event.type === "agent_speaking_end") {
|
|
2262
|
+
sendToClient(ctx.sessionId, {
|
|
2263
|
+
type: "orbit_say_status",
|
|
2264
|
+
status: "ended"
|
|
2265
|
+
});
|
|
2266
|
+
}
|
|
2267
|
+
},
|
|
1801
2268
|
onSessionEnd({ sessionId }) {
|
|
1802
2269
|
teardownSession(sessionId);
|
|
1803
2270
|
agentLog("info", `spatial-showcase session_end ${sessionId}`, sessionId);
|
|
1804
2271
|
}
|
|
1805
2272
|
});
|
|
1806
2273
|
export {
|
|
2274
|
+
MAX_ACTIVE_PLAYS,
|
|
2275
|
+
handlePadMove,
|
|
1807
2276
|
handlePadPlay
|
|
1808
2277
|
};
|