-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathrealtime-translation-microphone.js
More file actions
93 lines (80 loc) · 2.84 KB
/
Copy pathrealtime-translation-microphone.js
File metadata and controls
93 lines (80 loc) · 2.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// Realtime speech-to-speech translation from the microphone.
//
// Speak into your mic in the source language; the translated speech is played
// back through your speakers in near real time, and the translated text is
// printed as it arrives. Audio is PCM16 mono at 24 kHz in both directions.
//
// Requires:
// npm install @camb-ai/sdk node-record-lpcm16
// sox on the host — checked at startup (mic capture + speaker playback)
//
// Run with:
// export CAMB_API_KEY=...
// node examples/realtime-translation-microphone.js [en-us] [de-de]
import {
CambClient,
Microphone,
RealtimeServerEventType,
SoxRequiredError,
assertSoxAvailable,
createSoxPcmSpeakerChecked,
} from "@camb-ai/sdk";
const SAMPLE_RATE = 24000; // PCM16 mono, both directions
const apiKey = process.env.CAMB_API_KEY;
if (!apiKey) {
console.error("Missing CAMB_API_KEY environment variable.");
process.exit(1);
}
async function main() {
try {
await assertSoxAvailable({ playback: true });
} catch (err) {
if (err instanceof SoxRequiredError) {
console.error(err.message);
process.exit(1);
}
throw err;
}
const sourceLanguage = process.argv[2] ?? "en-us";
const targetLanguage = process.argv[3] ?? "de-de";
const client = new CambClient({ apiKey });
const session = await client.realtime.connect({
sourceLanguage,
targetLanguage,
});
const speaker = await createSoxPcmSpeakerChecked({ sampleRate: SAMPLE_RATE });
session.on(RealtimeServerEventType.SessionStarting, () =>
console.log("Booting the translation pipeline (this can take 30s+)..."),
);
session.on(RealtimeServerEventType.SessionCreated, () =>
console.log(`Ready. Speak in ${sourceLanguage}; you'll hear ${targetLanguage}. Ctrl-C to stop.`),
);
session.on(RealtimeServerEventType.TranscriptCompleted, (event) =>
console.log(`\n[you] ${event.transcript}`),
);
session.on(RealtimeServerEventType.TextDone, (event) =>
console.log(`[translation] ${event.text}`),
);
session.on(RealtimeServerEventType.AudioDelta, (event) => speaker.feed(Buffer.from(event.data)));
session.on(RealtimeServerEventType.Error, (err) =>
console.error(`\nServer error: ${err.message}`),
);
session.on(RealtimeServerEventType.Closed, (info) =>
console.log(`\nClosed: code=${info.code} reason=${info.reason}`),
);
await session.waitUntilReady();
const mic = Microphone.fromNode({ sampleRate: SAMPLE_RATE });
await mic.start();
process.on("SIGINT", async () => {
await mic.stop();
await speaker.close();
await session.close();
process.exit(0);
});
await session.stream(mic);
await speaker.close();
}
main().catch((err) => {
console.error(err);
process.exit(1);
});