@micdrop/server 2.3.0 → 2.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/README.md +1 -1
- package/dist/index.d.mts +58 -1
- package/dist/index.d.ts +58 -1
- package/dist/index.js +71 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +71 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -9
- package/LICENSE +0 -9
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 🖐️🎤 Micdrop: Real-Time Voice Conversations with AI
|
|
2
2
|
|
|
3
|
-
[Micdrop website](https://micdrop.dev) | [Documentation](https://micdrop.dev/docs/server) | [Demo](../../examples/
|
|
3
|
+
[Micdrop website](https://micdrop.dev) | [Documentation](https://micdrop.dev/docs/server) | [Basic example](../../examples/basic) | [Demo](../../examples/advanced)
|
|
4
4
|
|
|
5
5
|
Micdrop is a set of open source Typescript packages to build real-time voice conversations with AI agents. It handles all the complexities on the browser and server side (microphone, speaker, VAD, network communication, etc) and provides ready-to-use implementations for various AI providers.
|
|
6
6
|
|
package/dist/index.d.mts
CHANGED
|
@@ -21,6 +21,28 @@ declare enum MicdropServerCommands {
|
|
|
21
21
|
EndCall = "EndCall",
|
|
22
22
|
ToolCall = "ToolCall"
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Hears whether a sentence has landed, where voice activity detection only
|
|
26
|
+
* hears whether someone is speaking.
|
|
27
|
+
*
|
|
28
|
+
* `SmartTurn` from `@micdrop/smart-turn` implements it, and so can anything
|
|
29
|
+
* else, a call to a service included. Both sides of a call can hold one, the
|
|
30
|
+
* client to decide when its turn ends and the server to decide when to answer.
|
|
31
|
+
*/
|
|
32
|
+
interface TurnDetector {
|
|
33
|
+
/**
|
|
34
|
+
* Feeds the audio received since the last call
|
|
35
|
+
* @param samples - Mono samples, in the -1..1 range
|
|
36
|
+
* @param sampleRate - Sample rate of `samples`, in Hz
|
|
37
|
+
*/
|
|
38
|
+
push(samples: Float32Array, sampleRate?: number): void;
|
|
39
|
+
/** Answers whether the turn pushed so far sounds finished */
|
|
40
|
+
predict(): Promise<{
|
|
41
|
+
complete: boolean;
|
|
42
|
+
}>;
|
|
43
|
+
/** Starts a new turn, forgetting the previous one */
|
|
44
|
+
reset(): void;
|
|
45
|
+
}
|
|
24
46
|
interface MicdropCallSummary {
|
|
25
47
|
conversation: MicdropConversation;
|
|
26
48
|
duration: number;
|
|
@@ -327,6 +349,7 @@ declare abstract class SentenceTTS extends TTS {
|
|
|
327
349
|
private controller?;
|
|
328
350
|
private generation;
|
|
329
351
|
private counter;
|
|
352
|
+
private synthesizing;
|
|
330
353
|
/**
|
|
331
354
|
* Turns one sentence into PCM16 audio at the client's sample rate.
|
|
332
355
|
*
|
|
@@ -335,6 +358,16 @@ declare abstract class SentenceTTS extends TTS {
|
|
|
335
358
|
* nothing emits nothing, which is how a cancelled synthesis reports back.
|
|
336
359
|
*/
|
|
337
360
|
protected abstract synthesize(text: string, signal: AbortSignal): Promise<Buffer | undefined>;
|
|
361
|
+
/**
|
|
362
|
+
* Emits a piece of the sentence being synthesized.
|
|
363
|
+
*
|
|
364
|
+
* A model that generates progressively can hand its chunks over as they
|
|
365
|
+
* come rather than waiting for the sentence to be finished, which brings
|
|
366
|
+
* the first word forward by the duration of that sentence. The false it
|
|
367
|
+
* returns says the utterance was cancelled or replaced, so the generation
|
|
368
|
+
* it comes from can be stopped there.
|
|
369
|
+
*/
|
|
370
|
+
protected emitAudio(audio: Buffer): boolean;
|
|
338
371
|
speak(textStream: Readable): void;
|
|
339
372
|
cancel(): void;
|
|
340
373
|
private enqueue;
|
|
@@ -352,6 +385,17 @@ interface MicdropConfig {
|
|
|
352
385
|
agent: Agent;
|
|
353
386
|
stt: STT;
|
|
354
387
|
tts: TTS;
|
|
388
|
+
/**
|
|
389
|
+
* Waits for the rest of the sentence when the speaker paused in the middle
|
|
390
|
+
* of one, instead of answering an unfinished question.
|
|
391
|
+
*
|
|
392
|
+
* Prefer running the detector in the client, which reaches the same decision
|
|
393
|
+
* without the round trip and can then close its turns sooner. This is the
|
|
394
|
+
* option for the browsers where the model has nowhere to run.
|
|
395
|
+
*/
|
|
396
|
+
turnDetector?: TurnDetector;
|
|
397
|
+
/** How long to wait for the rest of a sentence, 4000 ms by default */
|
|
398
|
+
turnMaxWait?: number;
|
|
355
399
|
}
|
|
356
400
|
declare class MicdropServer extends EventEmitter<MicdropServerEvents> {
|
|
357
401
|
socket: WebSocket$1 | null;
|
|
@@ -363,6 +407,8 @@ declare class MicdropServer extends EventEmitter<MicdropServerEvents> {
|
|
|
363
407
|
private isProcessingQueue;
|
|
364
408
|
private currentUserStream?;
|
|
365
409
|
private userSpeechChunks;
|
|
410
|
+
private turnComplete?;
|
|
411
|
+
private heldTurnTimer?;
|
|
366
412
|
constructor(socket: WebSocket$1, config: MicdropConfig);
|
|
367
413
|
private log;
|
|
368
414
|
private processQueue;
|
|
@@ -374,6 +420,17 @@ declare class MicdropServer extends EventEmitter<MicdropServerEvents> {
|
|
|
374
420
|
private onMute;
|
|
375
421
|
private onStartSpeaking;
|
|
376
422
|
private onStopSpeaking;
|
|
423
|
+
private predictTurnComplete;
|
|
424
|
+
/** Answers, unless the sentence sounds like it has more coming */
|
|
425
|
+
private answerUserTurn;
|
|
426
|
+
/**
|
|
427
|
+
* Answers anyway if the rest of the sentence never comes.
|
|
428
|
+
*
|
|
429
|
+
* Without it, a detector that hears an unfinished sentence where there is
|
|
430
|
+
* none leaves the call silent for good.
|
|
431
|
+
*/
|
|
432
|
+
private holdTurn;
|
|
433
|
+
private releaseHeldTurn;
|
|
377
434
|
private onTranscriptSTT;
|
|
378
435
|
private onAudioTTS;
|
|
379
436
|
private sendFirstMessage;
|
|
@@ -416,4 +473,4 @@ declare class MicdropRecorder extends EventEmitter<MicdropRecorderEvents> {
|
|
|
416
473
|
|
|
417
474
|
declare function waitForParams<CallParams>(socket: WebSocket$1, validate: (params: any) => CallParams): Promise<CallParams>;
|
|
418
475
|
|
|
419
|
-
export { AUTO_END_CALL_PROMPT, AUTO_END_CALL_TOOL_NAME, AUTO_IGNORE_USER_NOISE_PROMPT, AUTO_IGNORE_USER_NOISE_TOOL_NAME, AUTO_SEMANTIC_TURN_PROMPT, AUTO_SEMANTIC_TURN_TOOL_NAME, Agent, type AgentEvents, type AgentOptions, type AudioMessage, type DeepPartial, type ExtractJsonOptions, type ExtractOptions, type ExtractTagOptions, FallbackAgent, type FallbackAgentOptions, FallbackSTT, type FallbackSTTOptions, FallbackTTS, type FallbackTTSOptions, Logger, type MicdropAnswerMetadata, type MicdropCallSummary, MicdropClientCommands, type MicdropConfig, type MicdropConversation, type MicdropConversationItem, type MicdropConversationMessage, type MicdropConversationToolCall, type MicdropConversationToolResult, MicdropError, MicdropErrorCode, MicdropRecorder, type MicdropRecorderEvents, MicdropServer, MicdropServerCommands, type MicdropServerEvents, type MicdropToolCall, MockAgent, MockSTT, MockTTS, Pcm16Resampler, STT, type STTEvents, SentenceSplitter, SentenceTTS, TTS, type TTSEvents, type Tool, float32ToPcm16, handleError, pcm16ToFloat32, waitForParams };
|
|
476
|
+
export { AUTO_END_CALL_PROMPT, AUTO_END_CALL_TOOL_NAME, AUTO_IGNORE_USER_NOISE_PROMPT, AUTO_IGNORE_USER_NOISE_TOOL_NAME, AUTO_SEMANTIC_TURN_PROMPT, AUTO_SEMANTIC_TURN_TOOL_NAME, Agent, type AgentEvents, type AgentOptions, type AudioMessage, type DeepPartial, type ExtractJsonOptions, type ExtractOptions, type ExtractTagOptions, FallbackAgent, type FallbackAgentOptions, FallbackSTT, type FallbackSTTOptions, FallbackTTS, type FallbackTTSOptions, Logger, type MicdropAnswerMetadata, type MicdropCallSummary, MicdropClientCommands, type MicdropConfig, type MicdropConversation, type MicdropConversationItem, type MicdropConversationMessage, type MicdropConversationToolCall, type MicdropConversationToolResult, MicdropError, MicdropErrorCode, MicdropRecorder, type MicdropRecorderEvents, MicdropServer, MicdropServerCommands, type MicdropServerEvents, type MicdropToolCall, MockAgent, MockSTT, MockTTS, Pcm16Resampler, STT, type STTEvents, SentenceSplitter, SentenceTTS, TTS, type TTSEvents, type Tool, type TurnDetector, float32ToPcm16, handleError, pcm16ToFloat32, waitForParams };
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,28 @@ declare enum MicdropServerCommands {
|
|
|
21
21
|
EndCall = "EndCall",
|
|
22
22
|
ToolCall = "ToolCall"
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Hears whether a sentence has landed, where voice activity detection only
|
|
26
|
+
* hears whether someone is speaking.
|
|
27
|
+
*
|
|
28
|
+
* `SmartTurn` from `@micdrop/smart-turn` implements it, and so can anything
|
|
29
|
+
* else, a call to a service included. Both sides of a call can hold one, the
|
|
30
|
+
* client to decide when its turn ends and the server to decide when to answer.
|
|
31
|
+
*/
|
|
32
|
+
interface TurnDetector {
|
|
33
|
+
/**
|
|
34
|
+
* Feeds the audio received since the last call
|
|
35
|
+
* @param samples - Mono samples, in the -1..1 range
|
|
36
|
+
* @param sampleRate - Sample rate of `samples`, in Hz
|
|
37
|
+
*/
|
|
38
|
+
push(samples: Float32Array, sampleRate?: number): void;
|
|
39
|
+
/** Answers whether the turn pushed so far sounds finished */
|
|
40
|
+
predict(): Promise<{
|
|
41
|
+
complete: boolean;
|
|
42
|
+
}>;
|
|
43
|
+
/** Starts a new turn, forgetting the previous one */
|
|
44
|
+
reset(): void;
|
|
45
|
+
}
|
|
24
46
|
interface MicdropCallSummary {
|
|
25
47
|
conversation: MicdropConversation;
|
|
26
48
|
duration: number;
|
|
@@ -327,6 +349,7 @@ declare abstract class SentenceTTS extends TTS {
|
|
|
327
349
|
private controller?;
|
|
328
350
|
private generation;
|
|
329
351
|
private counter;
|
|
352
|
+
private synthesizing;
|
|
330
353
|
/**
|
|
331
354
|
* Turns one sentence into PCM16 audio at the client's sample rate.
|
|
332
355
|
*
|
|
@@ -335,6 +358,16 @@ declare abstract class SentenceTTS extends TTS {
|
|
|
335
358
|
* nothing emits nothing, which is how a cancelled synthesis reports back.
|
|
336
359
|
*/
|
|
337
360
|
protected abstract synthesize(text: string, signal: AbortSignal): Promise<Buffer | undefined>;
|
|
361
|
+
/**
|
|
362
|
+
* Emits a piece of the sentence being synthesized.
|
|
363
|
+
*
|
|
364
|
+
* A model that generates progressively can hand its chunks over as they
|
|
365
|
+
* come rather than waiting for the sentence to be finished, which brings
|
|
366
|
+
* the first word forward by the duration of that sentence. The false it
|
|
367
|
+
* returns says the utterance was cancelled or replaced, so the generation
|
|
368
|
+
* it comes from can be stopped there.
|
|
369
|
+
*/
|
|
370
|
+
protected emitAudio(audio: Buffer): boolean;
|
|
338
371
|
speak(textStream: Readable): void;
|
|
339
372
|
cancel(): void;
|
|
340
373
|
private enqueue;
|
|
@@ -352,6 +385,17 @@ interface MicdropConfig {
|
|
|
352
385
|
agent: Agent;
|
|
353
386
|
stt: STT;
|
|
354
387
|
tts: TTS;
|
|
388
|
+
/**
|
|
389
|
+
* Waits for the rest of the sentence when the speaker paused in the middle
|
|
390
|
+
* of one, instead of answering an unfinished question.
|
|
391
|
+
*
|
|
392
|
+
* Prefer running the detector in the client, which reaches the same decision
|
|
393
|
+
* without the round trip and can then close its turns sooner. This is the
|
|
394
|
+
* option for the browsers where the model has nowhere to run.
|
|
395
|
+
*/
|
|
396
|
+
turnDetector?: TurnDetector;
|
|
397
|
+
/** How long to wait for the rest of a sentence, 4000 ms by default */
|
|
398
|
+
turnMaxWait?: number;
|
|
355
399
|
}
|
|
356
400
|
declare class MicdropServer extends EventEmitter<MicdropServerEvents> {
|
|
357
401
|
socket: WebSocket$1 | null;
|
|
@@ -363,6 +407,8 @@ declare class MicdropServer extends EventEmitter<MicdropServerEvents> {
|
|
|
363
407
|
private isProcessingQueue;
|
|
364
408
|
private currentUserStream?;
|
|
365
409
|
private userSpeechChunks;
|
|
410
|
+
private turnComplete?;
|
|
411
|
+
private heldTurnTimer?;
|
|
366
412
|
constructor(socket: WebSocket$1, config: MicdropConfig);
|
|
367
413
|
private log;
|
|
368
414
|
private processQueue;
|
|
@@ -374,6 +420,17 @@ declare class MicdropServer extends EventEmitter<MicdropServerEvents> {
|
|
|
374
420
|
private onMute;
|
|
375
421
|
private onStartSpeaking;
|
|
376
422
|
private onStopSpeaking;
|
|
423
|
+
private predictTurnComplete;
|
|
424
|
+
/** Answers, unless the sentence sounds like it has more coming */
|
|
425
|
+
private answerUserTurn;
|
|
426
|
+
/**
|
|
427
|
+
* Answers anyway if the rest of the sentence never comes.
|
|
428
|
+
*
|
|
429
|
+
* Without it, a detector that hears an unfinished sentence where there is
|
|
430
|
+
* none leaves the call silent for good.
|
|
431
|
+
*/
|
|
432
|
+
private holdTurn;
|
|
433
|
+
private releaseHeldTurn;
|
|
377
434
|
private onTranscriptSTT;
|
|
378
435
|
private onAudioTTS;
|
|
379
436
|
private sendFirstMessage;
|
|
@@ -416,4 +473,4 @@ declare class MicdropRecorder extends EventEmitter<MicdropRecorderEvents> {
|
|
|
416
473
|
|
|
417
474
|
declare function waitForParams<CallParams>(socket: WebSocket$1, validate: (params: any) => CallParams): Promise<CallParams>;
|
|
418
475
|
|
|
419
|
-
export { AUTO_END_CALL_PROMPT, AUTO_END_CALL_TOOL_NAME, AUTO_IGNORE_USER_NOISE_PROMPT, AUTO_IGNORE_USER_NOISE_TOOL_NAME, AUTO_SEMANTIC_TURN_PROMPT, AUTO_SEMANTIC_TURN_TOOL_NAME, Agent, type AgentEvents, type AgentOptions, type AudioMessage, type DeepPartial, type ExtractJsonOptions, type ExtractOptions, type ExtractTagOptions, FallbackAgent, type FallbackAgentOptions, FallbackSTT, type FallbackSTTOptions, FallbackTTS, type FallbackTTSOptions, Logger, type MicdropAnswerMetadata, type MicdropCallSummary, MicdropClientCommands, type MicdropConfig, type MicdropConversation, type MicdropConversationItem, type MicdropConversationMessage, type MicdropConversationToolCall, type MicdropConversationToolResult, MicdropError, MicdropErrorCode, MicdropRecorder, type MicdropRecorderEvents, MicdropServer, MicdropServerCommands, type MicdropServerEvents, type MicdropToolCall, MockAgent, MockSTT, MockTTS, Pcm16Resampler, STT, type STTEvents, SentenceSplitter, SentenceTTS, TTS, type TTSEvents, type Tool, float32ToPcm16, handleError, pcm16ToFloat32, waitForParams };
|
|
476
|
+
export { AUTO_END_CALL_PROMPT, AUTO_END_CALL_TOOL_NAME, AUTO_IGNORE_USER_NOISE_PROMPT, AUTO_IGNORE_USER_NOISE_TOOL_NAME, AUTO_SEMANTIC_TURN_PROMPT, AUTO_SEMANTIC_TURN_TOOL_NAME, Agent, type AgentEvents, type AgentOptions, type AudioMessage, type DeepPartial, type ExtractJsonOptions, type ExtractOptions, type ExtractTagOptions, FallbackAgent, type FallbackAgentOptions, FallbackSTT, type FallbackSTTOptions, FallbackTTS, type FallbackTTSOptions, Logger, type MicdropAnswerMetadata, type MicdropCallSummary, MicdropClientCommands, type MicdropConfig, type MicdropConversation, type MicdropConversationItem, type MicdropConversationMessage, type MicdropConversationToolCall, type MicdropConversationToolResult, MicdropError, MicdropErrorCode, MicdropRecorder, type MicdropRecorderEvents, MicdropServer, MicdropServerCommands, type MicdropServerEvents, type MicdropToolCall, MockAgent, MockSTT, MockTTS, Pcm16Resampler, STT, type STTEvents, SentenceSplitter, SentenceTTS, TTS, type TTSEvents, type Tool, type TurnDetector, float32ToPcm16, handleError, pcm16ToFloat32, waitForParams };
|
package/dist/index.js
CHANGED
|
@@ -511,6 +511,8 @@ var MicdropServerCommands = /* @__PURE__ */ ((MicdropServerCommands2) => {
|
|
|
511
511
|
})(MicdropServerCommands || {});
|
|
512
512
|
|
|
513
513
|
// src/MicdropServer.ts
|
|
514
|
+
var USER_SAMPLE_RATE = 16e3;
|
|
515
|
+
var DEFAULT_TURN_MAX_WAIT = 4e3;
|
|
514
516
|
var MicdropServer = class extends import_eventemitter32.EventEmitter {
|
|
515
517
|
constructor(socket, config) {
|
|
516
518
|
super();
|
|
@@ -522,6 +524,7 @@ var MicdropServer = class extends import_eventemitter32.EventEmitter {
|
|
|
522
524
|
this.isProcessingQueue = false;
|
|
523
525
|
this.userSpeechChunks = 0;
|
|
524
526
|
this.onClose = () => {
|
|
527
|
+
this.releaseHeldTurn();
|
|
525
528
|
if (!this.config) return;
|
|
526
529
|
this.log("Connection closed");
|
|
527
530
|
const duration = Math.round((Date.now() - this.startTime) / 1e3);
|
|
@@ -565,8 +568,7 @@ var MicdropServer = class extends import_eventemitter32.EventEmitter {
|
|
|
565
568
|
this.config.agent.addUserMessage(transcript);
|
|
566
569
|
if (!this.currentUserStream) {
|
|
567
570
|
this.log("User stopped speaking, answering");
|
|
568
|
-
this.
|
|
569
|
-
this.answer();
|
|
571
|
+
this.answerUserTurn();
|
|
570
572
|
}
|
|
571
573
|
};
|
|
572
574
|
this.onAudioTTS = (audio) => {
|
|
@@ -639,6 +641,7 @@ var MicdropServer = class extends import_eventemitter32.EventEmitter {
|
|
|
639
641
|
this.log(`Received chunk (${chunk.byteLength} bytes)`);
|
|
640
642
|
this.currentUserStream?.write(chunk);
|
|
641
643
|
this.userSpeechChunks++;
|
|
644
|
+
this.config?.turnDetector?.push(pcm16ToFloat32(chunk), USER_SAMPLE_RATE);
|
|
642
645
|
this.emit("UserAudio", chunk);
|
|
643
646
|
}
|
|
644
647
|
onMute() {
|
|
@@ -652,6 +655,9 @@ var MicdropServer = class extends import_eventemitter32.EventEmitter {
|
|
|
652
655
|
this.userSpeechChunks = 0;
|
|
653
656
|
this.currentUserStream?.end();
|
|
654
657
|
this.currentUserStream = new import_stream2.PassThrough();
|
|
658
|
+
this.config.turnDetector?.reset();
|
|
659
|
+
this.turnComplete = void 0;
|
|
660
|
+
this.releaseHeldTurn();
|
|
655
661
|
this.config.stt.transcribe(this.currentUserStream);
|
|
656
662
|
this.cancel();
|
|
657
663
|
}
|
|
@@ -664,15 +670,60 @@ var MicdropServer = class extends import_eventemitter32.EventEmitter {
|
|
|
664
670
|
this.socket?.send("SkipAnswer" /* SkipAnswer */);
|
|
665
671
|
return;
|
|
666
672
|
}
|
|
673
|
+
this.turnComplete = this.predictTurnComplete();
|
|
667
674
|
const conversation = this.config?.agent.conversation;
|
|
668
675
|
const lastMessage = conversation?.[conversation.length - 1];
|
|
669
676
|
if (lastMessage?.role === "user" && this.lastMessageSpeeched !== lastMessage) {
|
|
670
677
|
this.log(
|
|
671
678
|
"User stopped speaking and a transcript already exists, answering"
|
|
672
679
|
);
|
|
680
|
+
this.answerUserTurn();
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
async predictTurnComplete() {
|
|
684
|
+
const detector = this.config?.turnDetector;
|
|
685
|
+
if (!detector) return true;
|
|
686
|
+
try {
|
|
687
|
+
const { complete } = await detector.predict();
|
|
688
|
+
this.log(`Turn sounds ${complete ? "finished" : "unfinished"}`);
|
|
689
|
+
return complete;
|
|
690
|
+
} catch (error) {
|
|
691
|
+
this.log(`Turn detection failed: ${error}`);
|
|
692
|
+
return true;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
/** Answers, unless the sentence sounds like it has more coming */
|
|
696
|
+
async answerUserTurn() {
|
|
697
|
+
const complete = await (this.turnComplete ?? Promise.resolve(true));
|
|
698
|
+
if (!complete) {
|
|
699
|
+
this.log("Waiting for the rest of the sentence");
|
|
700
|
+
this.socket?.send("SkipAnswer" /* SkipAnswer */);
|
|
701
|
+
this.holdTurn();
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
this.releaseHeldTurn();
|
|
705
|
+
this.cancel();
|
|
706
|
+
this.answer();
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Answers anyway if the rest of the sentence never comes.
|
|
710
|
+
*
|
|
711
|
+
* Without it, a detector that hears an unfinished sentence where there is
|
|
712
|
+
* none leaves the call silent for good.
|
|
713
|
+
*/
|
|
714
|
+
holdTurn() {
|
|
715
|
+
this.releaseHeldTurn();
|
|
716
|
+
this.heldTurnTimer = setTimeout(() => {
|
|
717
|
+
this.heldTurnTimer = void 0;
|
|
718
|
+
this.log("Nothing more came, answering");
|
|
673
719
|
this.cancel();
|
|
674
720
|
this.answer();
|
|
675
|
-
}
|
|
721
|
+
}, this.config?.turnMaxWait ?? DEFAULT_TURN_MAX_WAIT);
|
|
722
|
+
}
|
|
723
|
+
releaseHeldTurn() {
|
|
724
|
+
if (!this.heldTurnTimer) return;
|
|
725
|
+
clearTimeout(this.heldTurnTimer);
|
|
726
|
+
this.heldTurnTimer = void 0;
|
|
676
727
|
}
|
|
677
728
|
sendFirstMessage() {
|
|
678
729
|
if (!this.config) return;
|
|
@@ -1101,6 +1152,22 @@ var SentenceTTS = class extends TTS {
|
|
|
1101
1152
|
// whether it is still the one that should be heard.
|
|
1102
1153
|
this.generation = 0;
|
|
1103
1154
|
this.counter = 0;
|
|
1155
|
+
// Identifies the current speak() call
|
|
1156
|
+
this.synthesizing = 0;
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Emits a piece of the sentence being synthesized.
|
|
1160
|
+
*
|
|
1161
|
+
* A model that generates progressively can hand its chunks over as they
|
|
1162
|
+
* come rather than waiting for the sentence to be finished, which brings
|
|
1163
|
+
* the first word forward by the duration of that sentence. The false it
|
|
1164
|
+
* returns says the utterance was cancelled or replaced, so the generation
|
|
1165
|
+
* it comes from can be stopped there.
|
|
1166
|
+
*/
|
|
1167
|
+
emitAudio(audio) {
|
|
1168
|
+
if (this.synthesizing !== this.counter) return false;
|
|
1169
|
+
if (audio.length) this.emit("Audio", audio);
|
|
1170
|
+
return true;
|
|
1104
1171
|
}
|
|
1105
1172
|
speak(textStream) {
|
|
1106
1173
|
const generation = ++this.generation;
|
|
@@ -1146,6 +1213,7 @@ var SentenceTTS = class extends TTS {
|
|
|
1146
1213
|
this.draining = true;
|
|
1147
1214
|
while (this.queue.length > 0) {
|
|
1148
1215
|
const counter = this.counter;
|
|
1216
|
+
this.synthesizing = counter;
|
|
1149
1217
|
const text = this.queue.shift();
|
|
1150
1218
|
const controller = new AbortController();
|
|
1151
1219
|
this.controller = controller;
|