@actrun_ai/tastekit-voice 0.1.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/__tests__/elevenlabs-smoke.d.ts +7 -0
- package/dist/__tests__/elevenlabs-smoke.d.ts.map +1 -0
- package/dist/__tests__/elevenlabs-smoke.js +157 -0
- package/dist/__tests__/elevenlabs-smoke.js.map +1 -0
- package/dist/audio/player.d.ts +27 -0
- package/dist/audio/player.d.ts.map +1 -0
- package/dist/audio/player.js +69 -0
- package/dist/audio/player.js.map +1 -0
- package/dist/audio/recorder.d.ts +22 -0
- package/dist/audio/recorder.d.ts.map +1 -0
- package/dist/audio/recorder.js +45 -0
- package/dist/audio/recorder.js.map +1 -0
- package/dist/config.d.ts +99 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +36 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/resolve.d.ts +16 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +77 -0
- package/dist/resolve.js.map +1 -0
- package/dist/stt/elevenlabs-stt.d.ts +25 -0
- package/dist/stt/elevenlabs-stt.d.ts.map +1 -0
- package/dist/stt/elevenlabs-stt.js +140 -0
- package/dist/stt/elevenlabs-stt.js.map +1 -0
- package/dist/stt/provider.d.ts +25 -0
- package/dist/stt/provider.d.ts.map +1 -0
- package/dist/stt/provider.js +8 -0
- package/dist/stt/provider.js.map +1 -0
- package/dist/stt/whisper-stt.d.ts +27 -0
- package/dist/stt/whisper-stt.d.ts.map +1 -0
- package/dist/stt/whisper-stt.js +95 -0
- package/dist/stt/whisper-stt.js.map +1 -0
- package/dist/tts/elevenlabs-tts.d.ts +24 -0
- package/dist/tts/elevenlabs-tts.d.ts.map +1 -0
- package/dist/tts/elevenlabs-tts.js +67 -0
- package/dist/tts/elevenlabs-tts.js.map +1 -0
- package/dist/tts/piper-tts.d.ts +23 -0
- package/dist/tts/piper-tts.d.ts.map +1 -0
- package/dist/tts/piper-tts.js +59 -0
- package/dist/tts/piper-tts.js.map +1 -0
- package/dist/tts/provider.d.ts +17 -0
- package/dist/tts/provider.d.ts.map +1 -0
- package/dist/tts/provider.js +8 -0
- package/dist/tts/provider.js.map +1 -0
- package/dist/voice-io.d.ts +48 -0
- package/dist/voice-io.d.ts.map +1 -0
- package/dist/voice-io.js +110 -0
- package/dist/voice-io.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { STTProvider, TranscriptEvent } from './provider.js';
|
|
2
|
+
export interface ElevenLabsSTTOptions {
|
|
3
|
+
apiKey: string;
|
|
4
|
+
language?: string;
|
|
5
|
+
/** VAD silence threshold in seconds (default 1.5) */
|
|
6
|
+
vadSilenceThreshold?: number;
|
|
7
|
+
/** Base WebSocket URL (default: wss://api.elevenlabs.io) */
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
sampleRate?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* ElevenLabs Scribe v2 realtime speech-to-text provider.
|
|
13
|
+
*
|
|
14
|
+
* Opens a WebSocket to the ElevenLabs realtime STT endpoint.
|
|
15
|
+
* Sends base64-encoded PCM audio chunks and yields transcript events.
|
|
16
|
+
* Built-in VAD commits transcription when the user stops speaking.
|
|
17
|
+
*/
|
|
18
|
+
export declare class ElevenLabsSTT implements STTProvider {
|
|
19
|
+
readonly name = "elevenlabs-stt";
|
|
20
|
+
private options;
|
|
21
|
+
constructor(options: ElevenLabsSTTOptions);
|
|
22
|
+
transcribe(audio: AsyncIterable<Buffer>): AsyncIterable<TranscriptEvent>;
|
|
23
|
+
dispose(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=elevenlabs-stt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elevenlabs-stt.d.ts","sourceRoot":"","sources":["../../src/stt/elevenlabs-stt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAElE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,qBAAa,aAAc,YAAW,WAAW;IAC/C,QAAQ,CAAC,IAAI,oBAAoB;IACjC,OAAO,CAAC,OAAO,CAAiH;gBAEpH,OAAO,EAAE,oBAAoB;IAUlC,UAAU,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,aAAa,CAAC,eAAe,CAAC;IA6HzE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import WebSocket from 'ws';
|
|
2
|
+
/**
|
|
3
|
+
* ElevenLabs Scribe v2 realtime speech-to-text provider.
|
|
4
|
+
*
|
|
5
|
+
* Opens a WebSocket to the ElevenLabs realtime STT endpoint.
|
|
6
|
+
* Sends base64-encoded PCM audio chunks and yields transcript events.
|
|
7
|
+
* Built-in VAD commits transcription when the user stops speaking.
|
|
8
|
+
*/
|
|
9
|
+
export class ElevenLabsSTT {
|
|
10
|
+
name = 'elevenlabs-stt';
|
|
11
|
+
options;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.options = {
|
|
14
|
+
apiKey: options.apiKey,
|
|
15
|
+
language: options.language ?? 'en',
|
|
16
|
+
vadSilenceThreshold: options.vadSilenceThreshold ?? 1.5,
|
|
17
|
+
baseUrl: options.baseUrl ?? 'wss://api.elevenlabs.io',
|
|
18
|
+
sampleRate: options.sampleRate ?? 16000,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
async *transcribe(audio) {
|
|
22
|
+
const { apiKey, language, vadSilenceThreshold, baseUrl, sampleRate } = this.options;
|
|
23
|
+
const url = `${baseUrl}/v1/speech-to-text/realtime?model_id=scribe_v2_realtime&language_code=${language}&audio_format=pcm_${sampleRate}`;
|
|
24
|
+
const ws = new WebSocket(url, {
|
|
25
|
+
headers: { 'xi-api-key': apiKey },
|
|
26
|
+
});
|
|
27
|
+
// Queue for transcript events received from the server
|
|
28
|
+
const eventQueue = [];
|
|
29
|
+
let resolveWaiting = null;
|
|
30
|
+
let done = false;
|
|
31
|
+
let error = null;
|
|
32
|
+
ws.on('message', (data) => {
|
|
33
|
+
try {
|
|
34
|
+
const msg = JSON.parse(data.toString());
|
|
35
|
+
if (msg.message_type === 'partial_transcript' && msg.text) {
|
|
36
|
+
eventQueue.push({ type: 'partial', text: msg.text });
|
|
37
|
+
}
|
|
38
|
+
else if (msg.message_type === 'committed_transcript' && msg.text) {
|
|
39
|
+
eventQueue.push({ type: 'final', text: msg.text });
|
|
40
|
+
}
|
|
41
|
+
else if (msg.message_type === 'error' || msg.message_type === 'auth_error') {
|
|
42
|
+
error = new Error(`ElevenLabs STT error: ${msg.message ?? msg.error ?? 'unknown'}`);
|
|
43
|
+
}
|
|
44
|
+
if (resolveWaiting) {
|
|
45
|
+
resolveWaiting();
|
|
46
|
+
resolveWaiting = null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Ignore malformed messages
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
ws.on('error', (err) => {
|
|
54
|
+
error = err instanceof Error ? err : new Error(String(err));
|
|
55
|
+
if (resolveWaiting) {
|
|
56
|
+
resolveWaiting();
|
|
57
|
+
resolveWaiting = null;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
ws.on('close', () => {
|
|
61
|
+
done = true;
|
|
62
|
+
if (resolveWaiting) {
|
|
63
|
+
resolveWaiting();
|
|
64
|
+
resolveWaiting = null;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
// Wait for connection to open
|
|
68
|
+
await new Promise((resolve, reject) => {
|
|
69
|
+
ws.on('open', resolve);
|
|
70
|
+
ws.on('error', reject);
|
|
71
|
+
});
|
|
72
|
+
// Start sending audio in the background
|
|
73
|
+
const sendAudio = (async () => {
|
|
74
|
+
try {
|
|
75
|
+
let isFirst = true;
|
|
76
|
+
for await (const chunk of audio) {
|
|
77
|
+
if (ws.readyState !== WebSocket.OPEN)
|
|
78
|
+
break;
|
|
79
|
+
const message = {
|
|
80
|
+
message_type: 'input_audio_chunk',
|
|
81
|
+
audio_base_64: chunk.toString('base64'),
|
|
82
|
+
sample_rate: sampleRate,
|
|
83
|
+
commit: false,
|
|
84
|
+
};
|
|
85
|
+
// Only send previous_text on the first chunk
|
|
86
|
+
if (isFirst) {
|
|
87
|
+
message.previous_text = '';
|
|
88
|
+
isFirst = false;
|
|
89
|
+
}
|
|
90
|
+
ws.send(JSON.stringify(message));
|
|
91
|
+
}
|
|
92
|
+
// Send a final commit to flush any remaining audio
|
|
93
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
94
|
+
ws.send(JSON.stringify({
|
|
95
|
+
message_type: 'input_audio_chunk',
|
|
96
|
+
audio_base_64: '',
|
|
97
|
+
sample_rate: sampleRate,
|
|
98
|
+
commit: true,
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Audio stream ended or WebSocket closed
|
|
104
|
+
}
|
|
105
|
+
})();
|
|
106
|
+
// Yield transcript events as they arrive
|
|
107
|
+
try {
|
|
108
|
+
while (!done || eventQueue.length > 0) {
|
|
109
|
+
if (error)
|
|
110
|
+
throw error;
|
|
111
|
+
if (eventQueue.length > 0) {
|
|
112
|
+
yield eventQueue.shift();
|
|
113
|
+
// If we got a final transcript, we're done with this utterance
|
|
114
|
+
if (eventQueue.length === 0) {
|
|
115
|
+
const lastEvent = eventQueue[eventQueue.length];
|
|
116
|
+
// Check if the last yielded event was final
|
|
117
|
+
// Actually, let's check: we already yielded it above, so we need
|
|
118
|
+
// to track differently. Let's just continue and let the caller
|
|
119
|
+
// decide when to stop.
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else if (!done) {
|
|
123
|
+
await new Promise((resolve) => {
|
|
124
|
+
resolveWaiting = resolve;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
await sendAudio;
|
|
131
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
132
|
+
ws.close();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async dispose() {
|
|
137
|
+
// No persistent state to clean up — each transcribe() call manages its own WebSocket
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=elevenlabs-stt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elevenlabs-stt.js","sourceRoot":"","sources":["../../src/stt/elevenlabs-stt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Speech-to-Text Provider Interface
|
|
3
|
+
*
|
|
4
|
+
* Consumes an audio stream and yields transcription events.
|
|
5
|
+
* Implementations: ElevenLabs Scribe v2, Whisper.cpp
|
|
6
|
+
*/
|
|
7
|
+
export interface TranscriptEvent {
|
|
8
|
+
/** 'partial' = interim result, 'final' = committed transcription */
|
|
9
|
+
type: 'partial' | 'final';
|
|
10
|
+
/** Transcribed text */
|
|
11
|
+
text: string;
|
|
12
|
+
/** Confidence score 0-1, if available */
|
|
13
|
+
confidence?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface STTProvider {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
/**
|
|
18
|
+
* Transcribe an audio stream into text events.
|
|
19
|
+
* @param audio PCM audio chunks (16-bit, 16kHz mono)
|
|
20
|
+
*/
|
|
21
|
+
transcribe(audio: AsyncIterable<Buffer>): AsyncIterable<TranscriptEvent>;
|
|
22
|
+
/** Clean up resources (close WebSocket, etc.) */
|
|
23
|
+
dispose?(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/stt/provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;IAC1B,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,UAAU,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC;IACzE,iDAAiD;IACjD,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/stt/provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { STTProvider, TranscriptEvent } from './provider.js';
|
|
2
|
+
export interface WhisperSTTOptions {
|
|
3
|
+
/** Path to whisper.cpp main binary (default: 'whisper-cpp') */
|
|
4
|
+
binaryPath?: string;
|
|
5
|
+
/** Whisper model name/path (default: 'base.en') */
|
|
6
|
+
model?: string;
|
|
7
|
+
/** Language for transcription */
|
|
8
|
+
language?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Local Whisper.cpp speech-to-text provider.
|
|
12
|
+
*
|
|
13
|
+
* Records the full utterance, writes to a temp WAV file,
|
|
14
|
+
* then runs whisper.cpp for batch transcription.
|
|
15
|
+
* No partial transcripts — yields a single final event.
|
|
16
|
+
*/
|
|
17
|
+
export declare class WhisperSTT implements STTProvider {
|
|
18
|
+
readonly name = "whisper-stt";
|
|
19
|
+
private binaryPath;
|
|
20
|
+
private model;
|
|
21
|
+
private language;
|
|
22
|
+
constructor(options?: WhisperSTTOptions);
|
|
23
|
+
transcribe(audio: AsyncIterable<Buffer>): AsyncIterable<TranscriptEvent>;
|
|
24
|
+
private runWhisper;
|
|
25
|
+
dispose(): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=whisper-stt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whisper-stt.d.ts","sourceRoot":"","sources":["../../src/stt/whisper-stt.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,qBAAa,UAAW,YAAW,WAAW;IAC5C,QAAQ,CAAC,IAAI,iBAAiB;IAC9B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAS;gBAEb,OAAO,CAAC,EAAE,iBAAiB;IAMhC,UAAU,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,aAAa,CAAC,eAAe,CAAC;IA0B/E,OAAO,CAAC,UAAU;IAoBZ,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { execFile } from 'child_process';
|
|
2
|
+
import { writeFileSync, unlinkSync } from 'fs';
|
|
3
|
+
import { tmpdir } from 'os';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
/**
|
|
6
|
+
* Local Whisper.cpp speech-to-text provider.
|
|
7
|
+
*
|
|
8
|
+
* Records the full utterance, writes to a temp WAV file,
|
|
9
|
+
* then runs whisper.cpp for batch transcription.
|
|
10
|
+
* No partial transcripts — yields a single final event.
|
|
11
|
+
*/
|
|
12
|
+
export class WhisperSTT {
|
|
13
|
+
name = 'whisper-stt';
|
|
14
|
+
binaryPath;
|
|
15
|
+
model;
|
|
16
|
+
language;
|
|
17
|
+
constructor(options) {
|
|
18
|
+
this.binaryPath = options?.binaryPath ?? 'whisper-cpp';
|
|
19
|
+
this.model = options?.model ?? 'base.en';
|
|
20
|
+
this.language = options?.language ?? 'en';
|
|
21
|
+
}
|
|
22
|
+
async *transcribe(audio) {
|
|
23
|
+
// Collect all audio chunks into a single buffer
|
|
24
|
+
const chunks = [];
|
|
25
|
+
for await (const chunk of audio) {
|
|
26
|
+
chunks.push(chunk);
|
|
27
|
+
}
|
|
28
|
+
const rawPcm = Buffer.concat(chunks);
|
|
29
|
+
if (rawPcm.length === 0) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// Write raw PCM as a WAV file (16-bit, 16kHz, mono)
|
|
33
|
+
const tempPath = join(tmpdir(), `tastekit-voice-${Date.now()}.wav`);
|
|
34
|
+
writeWav(tempPath, rawPcm, 16000);
|
|
35
|
+
try {
|
|
36
|
+
const text = await this.runWhisper(tempPath);
|
|
37
|
+
if (text.trim()) {
|
|
38
|
+
yield { type: 'final', text: text.trim() };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
try {
|
|
43
|
+
unlinkSync(tempPath);
|
|
44
|
+
}
|
|
45
|
+
catch { /* ignore */ }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
runWhisper(wavPath) {
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const args = [
|
|
51
|
+
'-m', this.model,
|
|
52
|
+
'-l', this.language,
|
|
53
|
+
'-f', wavPath,
|
|
54
|
+
'--no-timestamps',
|
|
55
|
+
'--print-progress', 'false',
|
|
56
|
+
];
|
|
57
|
+
execFile(this.binaryPath, args, { timeout: 30000 }, (err, stdout, stderr) => {
|
|
58
|
+
if (err) {
|
|
59
|
+
reject(new Error(`Whisper failed: ${stderr || err.message}`));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
resolve(stdout);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async dispose() {
|
|
68
|
+
// Stateless
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Write raw 16-bit PCM data as a WAV file. */
|
|
72
|
+
function writeWav(path, pcm, sampleRate) {
|
|
73
|
+
const numChannels = 1;
|
|
74
|
+
const bitsPerSample = 16;
|
|
75
|
+
const byteRate = sampleRate * numChannels * (bitsPerSample / 8);
|
|
76
|
+
const blockAlign = numChannels * (bitsPerSample / 8);
|
|
77
|
+
const dataSize = pcm.length;
|
|
78
|
+
const headerSize = 44;
|
|
79
|
+
const header = Buffer.alloc(headerSize);
|
|
80
|
+
header.write('RIFF', 0);
|
|
81
|
+
header.writeUInt32LE(dataSize + headerSize - 8, 4);
|
|
82
|
+
header.write('WAVE', 8);
|
|
83
|
+
header.write('fmt ', 12);
|
|
84
|
+
header.writeUInt32LE(16, 16); // fmt chunk size
|
|
85
|
+
header.writeUInt16LE(1, 20); // PCM format
|
|
86
|
+
header.writeUInt16LE(numChannels, 22);
|
|
87
|
+
header.writeUInt32LE(sampleRate, 24);
|
|
88
|
+
header.writeUInt32LE(byteRate, 28);
|
|
89
|
+
header.writeUInt16LE(blockAlign, 32);
|
|
90
|
+
header.writeUInt16LE(bitsPerSample, 34);
|
|
91
|
+
header.write('data', 36);
|
|
92
|
+
header.writeUInt32LE(dataSize, 40);
|
|
93
|
+
writeFileSync(path, Buffer.concat([header, pcm]));
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=whisper-stt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whisper-stt.js","sourceRoot":"","sources":["../../src/stt/whisper-stt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { TTSProvider } from './provider.js';
|
|
2
|
+
export interface ElevenLabsTTSOptions {
|
|
3
|
+
apiKey: string;
|
|
4
|
+
/** ElevenLabs voice ID (default: Rachel - 21m00Tcm4TlvDq8ikWAM) */
|
|
5
|
+
voiceId?: string;
|
|
6
|
+
/** Model ID (default: eleven_multilingual_v2) */
|
|
7
|
+
modelId?: string;
|
|
8
|
+
/** Base URL (default: https://api.elevenlabs.io) */
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* ElevenLabs streaming text-to-speech provider.
|
|
13
|
+
*
|
|
14
|
+
* Uses the REST streaming endpoint to convert text to audio.
|
|
15
|
+
* Returns raw PCM audio chunks for streaming playback.
|
|
16
|
+
*/
|
|
17
|
+
export declare class ElevenLabsTTS implements TTSProvider {
|
|
18
|
+
readonly name = "elevenlabs-tts";
|
|
19
|
+
private options;
|
|
20
|
+
constructor(options: ElevenLabsTTSOptions);
|
|
21
|
+
synthesize(text: string): Promise<AsyncIterable<Buffer>>;
|
|
22
|
+
dispose(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=elevenlabs-tts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elevenlabs-tts.d.ts","sourceRoot":"","sources":["../../src/tts/elevenlabs-tts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,WAAW;IAC/C,QAAQ,CAAC,IAAI,oBAAoB;IACjC,OAAO,CAAC,OAAO,CAAqF;gBAExF,OAAO,EAAE,oBAAoB;IASnC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAmDxD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ElevenLabs streaming text-to-speech provider.
|
|
3
|
+
*
|
|
4
|
+
* Uses the REST streaming endpoint to convert text to audio.
|
|
5
|
+
* Returns raw PCM audio chunks for streaming playback.
|
|
6
|
+
*/
|
|
7
|
+
export class ElevenLabsTTS {
|
|
8
|
+
name = 'elevenlabs-tts';
|
|
9
|
+
options;
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.options = {
|
|
12
|
+
apiKey: options.apiKey,
|
|
13
|
+
voiceId: options.voiceId ?? '21m00Tcm4TlvDq8ikWAM',
|
|
14
|
+
modelId: options.modelId ?? 'eleven_multilingual_v2',
|
|
15
|
+
baseUrl: options.baseUrl ?? 'https://api.elevenlabs.io',
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async synthesize(text) {
|
|
19
|
+
const { apiKey, voiceId, modelId, baseUrl } = this.options;
|
|
20
|
+
// output_format is a query parameter, not a body field
|
|
21
|
+
const url = `${baseUrl}/v1/text-to-speech/${voiceId}/stream?output_format=pcm_22050`;
|
|
22
|
+
const response = await fetch(url, {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers: {
|
|
25
|
+
'Content-Type': 'application/json',
|
|
26
|
+
'xi-api-key': apiKey,
|
|
27
|
+
},
|
|
28
|
+
body: JSON.stringify({
|
|
29
|
+
text,
|
|
30
|
+
model_id: modelId,
|
|
31
|
+
voice_settings: {
|
|
32
|
+
stability: 0.5,
|
|
33
|
+
similarity_boost: 0.75,
|
|
34
|
+
style: 0.0,
|
|
35
|
+
use_speaker_boost: true,
|
|
36
|
+
},
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
const errorText = await response.text();
|
|
41
|
+
throw new Error(`ElevenLabs TTS error (${response.status}): ${errorText}`);
|
|
42
|
+
}
|
|
43
|
+
if (!response.body) {
|
|
44
|
+
throw new Error('ElevenLabs TTS returned no body');
|
|
45
|
+
}
|
|
46
|
+
// Convert the ReadableStream to AsyncIterable<Buffer>
|
|
47
|
+
const reader = response.body.getReader();
|
|
48
|
+
async function* streamToAsyncIterable() {
|
|
49
|
+
try {
|
|
50
|
+
while (true) {
|
|
51
|
+
const { done, value } = await reader.read();
|
|
52
|
+
if (done)
|
|
53
|
+
break;
|
|
54
|
+
yield Buffer.from(value);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
reader.releaseLock();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return streamToAsyncIterable();
|
|
62
|
+
}
|
|
63
|
+
async dispose() {
|
|
64
|
+
// Stateless — nothing to clean up
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=elevenlabs-tts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elevenlabs-tts.js","sourceRoot":"","sources":["../../src/tts/elevenlabs-tts.ts"],"names":[],"mappings":"AAYA;;;;;GAKG;AACH,MAAM,OAAO,aAAa;IACf,IAAI,GAAG,gBAAgB,CAAC;IACzB,OAAO,CAAqF;IAEpG,YAAY,OAA6B;QACvC,IAAI,CAAC,OAAO,GAAG;YACb,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,sBAAsB;YAClD,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,wBAAwB;YACpD,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,2BAA2B;SACxD,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAY;QAC3B,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QAE3D,uDAAuD;QACvD,MAAM,GAAG,GAAG,GAAG,OAAO,sBAAsB,OAAO,iCAAiC,CAAC;QAErF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,YAAY,EAAE,MAAM;aACrB;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,IAAI;gBACJ,QAAQ,EAAE,OAAO;gBACjB,cAAc,EAAE;oBACd,SAAS,EAAE,GAAG;oBACd,gBAAgB,EAAE,IAAI;oBACtB,KAAK,EAAE,GAAG;oBACV,iBAAiB,EAAE,IAAI;iBACxB;aACF,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,CAAC,MAAM,MAAM,SAAS,EAAE,CAAC,CAAC;QAC7E,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrD,CAAC;QAED,sDAAsD;QACtD,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QAEzC,KAAK,SAAS,CAAC,CAAC,qBAAqB;YACnC,IAAI,CAAC;gBACH,OAAO,IAAI,EAAE,CAAC;oBACZ,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;oBAC5C,IAAI,IAAI;wBAAE,MAAM;oBAChB,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;oBAAS,CAAC;gBACT,MAAM,CAAC,WAAW,EAAE,CAAC;YACvB,CAAC;QACH,CAAC;QAED,OAAO,qBAAqB,EAAE,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,OAAO;QACX,kCAAkC;IACpC,CAAC;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { TTSProvider } from './provider.js';
|
|
2
|
+
export interface PiperTTSOptions {
|
|
3
|
+
/** Path to piper binary (default: 'piper') */
|
|
4
|
+
binaryPath?: string;
|
|
5
|
+
/** Path to voice model ONNX file */
|
|
6
|
+
model?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Local Piper text-to-speech provider.
|
|
10
|
+
*
|
|
11
|
+
* Runs piper via child process to generate WAV audio.
|
|
12
|
+
* Returns the audio as a single-chunk AsyncIterable for playback.
|
|
13
|
+
*/
|
|
14
|
+
export declare class PiperTTS implements TTSProvider {
|
|
15
|
+
readonly name = "piper-tts";
|
|
16
|
+
private binaryPath;
|
|
17
|
+
private model;
|
|
18
|
+
constructor(options?: PiperTTSOptions);
|
|
19
|
+
synthesize(text: string): Promise<AsyncIterable<Buffer>>;
|
|
20
|
+
private runPiper;
|
|
21
|
+
dispose(): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=piper-tts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"piper-tts.d.ts","sourceRoot":"","sources":["../../src/tts/piper-tts.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,qBAAa,QAAS,YAAW,WAAW;IAC1C,QAAQ,CAAC,IAAI,eAAe;IAC5B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,KAAK,CAAqB;gBAEtB,OAAO,CAAC,EAAE,eAAe;IAK/B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAkB9D,OAAO,CAAC,QAAQ;IAuBV,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { execFile } from 'child_process';
|
|
2
|
+
import { readFileSync, unlinkSync } from 'fs';
|
|
3
|
+
import { tmpdir } from 'os';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
/**
|
|
6
|
+
* Local Piper text-to-speech provider.
|
|
7
|
+
*
|
|
8
|
+
* Runs piper via child process to generate WAV audio.
|
|
9
|
+
* Returns the audio as a single-chunk AsyncIterable for playback.
|
|
10
|
+
*/
|
|
11
|
+
export class PiperTTS {
|
|
12
|
+
name = 'piper-tts';
|
|
13
|
+
binaryPath;
|
|
14
|
+
model;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.binaryPath = options?.binaryPath ?? 'piper';
|
|
17
|
+
this.model = options?.model;
|
|
18
|
+
}
|
|
19
|
+
async synthesize(text) {
|
|
20
|
+
const tempPath = join(tmpdir(), `tastekit-tts-${Date.now()}.wav`);
|
|
21
|
+
await this.runPiper(text, tempPath);
|
|
22
|
+
const audioData = readFileSync(tempPath);
|
|
23
|
+
try {
|
|
24
|
+
unlinkSync(tempPath);
|
|
25
|
+
}
|
|
26
|
+
catch { /* ignore */ }
|
|
27
|
+
// Strip the WAV header (44 bytes) to get raw PCM
|
|
28
|
+
const pcm = audioData.subarray(44);
|
|
29
|
+
async function* singleChunk() {
|
|
30
|
+
yield pcm;
|
|
31
|
+
}
|
|
32
|
+
return singleChunk();
|
|
33
|
+
}
|
|
34
|
+
runPiper(text, outputPath) {
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
const args = ['--output_file', outputPath];
|
|
37
|
+
if (this.model) {
|
|
38
|
+
args.push('--model', this.model);
|
|
39
|
+
}
|
|
40
|
+
const child = execFile(this.binaryPath, args, { timeout: 30000 }, (err) => {
|
|
41
|
+
if (err) {
|
|
42
|
+
reject(new Error(`Piper failed: ${err.message}`));
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
resolve();
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
// Pipe text to piper's stdin
|
|
49
|
+
if (child.stdin) {
|
|
50
|
+
child.stdin.write(text);
|
|
51
|
+
child.stdin.end();
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async dispose() {
|
|
56
|
+
// Stateless
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=piper-tts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"piper-tts.js","sourceRoot":"","sources":["../../src/tts/piper-tts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAU5B;;;;;GAKG;AACH,MAAM,OAAO,QAAQ;IACV,IAAI,GAAG,WAAW,CAAC;IACpB,UAAU,CAAS;IACnB,KAAK,CAAqB;IAElC,YAAY,OAAyB;QACnC,IAAI,CAAC,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,OAAO,CAAC;QACjD,IAAI,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAY;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,gBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAElE,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAEpC,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,CAAC;YAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QAEpD,iDAAiD;QACjD,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAEnC,KAAK,SAAS,CAAC,CAAC,WAAW;YACzB,MAAM,GAAG,CAAC;QACZ,CAAC;QAED,OAAO,WAAW,EAAE,CAAC;IACvB,CAAC;IAEO,QAAQ,CAAC,IAAY,EAAE,UAAkB;QAC/C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,IAAI,GAAG,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;YAC3C,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC;YAED,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE;gBACxE,IAAI,GAAG,EAAE,CAAC;oBACR,MAAM,CAAC,IAAI,KAAK,CAAC,iBAAiB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBACpD,CAAC;qBAAM,CAAC;oBACN,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,6BAA6B;YAC7B,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;gBAChB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACxB,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YACpB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO;QACX,YAAY;IACd,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text-to-Speech Provider Interface
|
|
3
|
+
*
|
|
4
|
+
* Converts text into an audio stream for playback.
|
|
5
|
+
* Implementations: ElevenLabs, Piper
|
|
6
|
+
*/
|
|
7
|
+
export interface TTSProvider {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
/**
|
|
10
|
+
* Synthesize text into an audio stream.
|
|
11
|
+
* @returns AsyncIterable of PCM or MP3 audio chunks
|
|
12
|
+
*/
|
|
13
|
+
synthesize(text: string): Promise<AsyncIterable<Buffer>>;
|
|
14
|
+
/** Clean up resources */
|
|
15
|
+
dispose?(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/tts/provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;IACzD,yBAAyB;IACzB,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/tts/provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { STTProvider } from './stt/provider.js';
|
|
2
|
+
import type { TTSProvider } from './tts/provider.js';
|
|
3
|
+
import { MicRecorder } from './audio/recorder.js';
|
|
4
|
+
import { AudioPlayer } from './audio/player.js';
|
|
5
|
+
export interface VoiceIOOptions {
|
|
6
|
+
/** Show live transcription while user speaks */
|
|
7
|
+
showTranscription?: boolean;
|
|
8
|
+
/** Called with partial transcription updates */
|
|
9
|
+
onPartial?: (text: string) => void;
|
|
10
|
+
/** Called when the user's final transcription is ready */
|
|
11
|
+
onFinalTranscript?: (text: string) => void;
|
|
12
|
+
/** Print interviewer text to console alongside audio */
|
|
13
|
+
printText?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Voice I/O orchestrator for TasteKit onboarding.
|
|
17
|
+
*
|
|
18
|
+
* Composes STT + TTS + mic recorder + audio player into the two
|
|
19
|
+
* callbacks the Interviewer expects: getUserInput() and onInterviewerMessage().
|
|
20
|
+
*
|
|
21
|
+
* Drop-in replacement for the text-based inquirer callbacks.
|
|
22
|
+
*/
|
|
23
|
+
export declare class VoiceIO {
|
|
24
|
+
private stt;
|
|
25
|
+
private tts;
|
|
26
|
+
private recorder;
|
|
27
|
+
private player;
|
|
28
|
+
private options;
|
|
29
|
+
constructor(stt: STTProvider, tts: TTSProvider, recorder?: MicRecorder, player?: AudioPlayer, options?: VoiceIOOptions);
|
|
30
|
+
/**
|
|
31
|
+
* Drop-in replacement for Interviewer's getUserInput callback.
|
|
32
|
+
* Records from mic → runs STT → returns transcribed text.
|
|
33
|
+
*/
|
|
34
|
+
getUserInput(): Promise<string>;
|
|
35
|
+
/**
|
|
36
|
+
* Drop-in replacement for Interviewer's onInterviewerMessage callback.
|
|
37
|
+
* Prints text AND plays it as audio via TTS.
|
|
38
|
+
*/
|
|
39
|
+
onInterviewerMessage(message: string): Promise<void>;
|
|
40
|
+
/** Clean up all resources. */
|
|
41
|
+
dispose(): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Wrap an async iterable so it stops yielding when a condition is met.
|
|
44
|
+
* This lets us stop the mic recording when STT returns a final transcript.
|
|
45
|
+
*/
|
|
46
|
+
private createControlledStream;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=voice-io.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voice-io.d.ts","sourceRoot":"","sources":["../src/voice-io.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAmB,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gDAAgD;IAChD,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,0DAA0D;IAC1D,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,wDAAwD;IACxD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,GAAG,CAAc;IACzB,OAAO,CAAC,GAAG,CAAc;IACzB,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,OAAO,CAA2B;gBAGxC,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,WAAW,EAChB,QAAQ,CAAC,EAAE,WAAW,EACtB,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,cAAc;IAc1B;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IA8CrC;;;OAGG;IACG,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB1D,8BAA8B;IACxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAO9B;;;OAGG;YACY,sBAAsB;CAStC"}
|