Add local MeloTTS support
This commit is contained in:
54
src/index.ts
54
src/index.ts
@@ -6,6 +6,7 @@ import { Logger } from "./logger.js";
|
||||
import { printAudioDevices, spawnLoopbackCapture } from "./audio/capture.js";
|
||||
import { RealtimeSegmenter } from "./audio/realtime-segmenter.js";
|
||||
import { FasterWhisperSttService } from "./services/faster-whisper-stt.js";
|
||||
import { MeloTtsService } from "./services/melo-tts.js";
|
||||
import { OllamaLlmService } from "./services/ollama-llm.js";
|
||||
|
||||
const mode = process.argv[2] ?? "test-stt";
|
||||
@@ -15,8 +16,10 @@ async function runSttTest(enableLlm: boolean): Promise<void> {
|
||||
const logger = new Logger(config.DEBUG ? config.LOG_LEVEL : "error");
|
||||
const stt = new FasterWhisperSttService(config, logger);
|
||||
const llm = enableLlm ? new OllamaLlmService(config, logger) : null;
|
||||
let tts = enableLlm && config.TTS_ENABLED ? new MeloTtsService(config, logger) : null;
|
||||
let capture = null as ReturnType<typeof spawnLoopbackCapture> | null;
|
||||
let shuttingDown: Promise<void> | null = null;
|
||||
let suppressCapture = false;
|
||||
let receivedChunks = 0;
|
||||
let receivedBytes = 0;
|
||||
let maxPeak = 0;
|
||||
@@ -79,6 +82,22 @@ async function runSttTest(enableLlm: boolean): Promise<void> {
|
||||
logger.info("LLM warmup finished");
|
||||
console.log("LLM 준비 완료");
|
||||
}
|
||||
if (tts) {
|
||||
console.log("TTS 준비중...");
|
||||
try {
|
||||
await tts.warmup();
|
||||
logger.info("TTS warmup finished", {
|
||||
image: config.TTS_IMAGE,
|
||||
language: config.TTS_LANGUAGE,
|
||||
speaker: config.TTS_SPEAKER,
|
||||
});
|
||||
console.log("TTS 준비 완료");
|
||||
} catch (error) {
|
||||
logger.warn("TTS warmup failed", error);
|
||||
console.log("TTS 비활성화: bun run setup:tts 를 먼저 실행하세요.");
|
||||
tts = null;
|
||||
}
|
||||
}
|
||||
|
||||
const transcriptionQueue: Array<{ pcm16: Buffer; queuedAt: number; index: number }> = [];
|
||||
let transcribing = false;
|
||||
@@ -155,6 +174,20 @@ async function runSttTest(enableLlm: boolean): Promise<void> {
|
||||
} else {
|
||||
console.log(`답변> ${reply}`);
|
||||
}
|
||||
|
||||
if (tts) {
|
||||
suppressCapture = true;
|
||||
segmenter.reset();
|
||||
try {
|
||||
await tts.speak(reply);
|
||||
} catch (error) {
|
||||
logger.warn("TTS playback failed", error);
|
||||
} finally {
|
||||
suppressCapture = false;
|
||||
sawSpeechStart = false;
|
||||
maxPeak = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -227,6 +260,9 @@ async function runSttTest(enableLlm: boolean): Promise<void> {
|
||||
receivedChunks += 1;
|
||||
receivedBytes += chunk.length;
|
||||
lastChunkAt = Date.now();
|
||||
if (suppressCapture) {
|
||||
return;
|
||||
}
|
||||
segmenter.pushChunk(chunk);
|
||||
});
|
||||
capture.stderr.on("data", (chunk: Buffer) => {
|
||||
@@ -330,6 +366,19 @@ async function runLlmCli(): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
async function runTtsTest(): Promise<void> {
|
||||
const text = process.argv.slice(3).join(" ").trim() || "안녕하세요. 로컬 티티에스 테스트입니다.";
|
||||
const config = loadConfig();
|
||||
const logger = new Logger(config.DEBUG ? config.LOG_LEVEL : "error");
|
||||
const tts = new MeloTtsService(config, logger);
|
||||
|
||||
console.log("TTS 준비중...");
|
||||
await tts.warmup();
|
||||
console.log("TTS 준비 완료");
|
||||
console.log(`재생 문장: ${text}`);
|
||||
await tts.speak(text);
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
switch (mode) {
|
||||
case "devices":
|
||||
@@ -344,8 +393,11 @@ async function main(): Promise<void> {
|
||||
case "test-llm":
|
||||
await runLlmCli();
|
||||
return;
|
||||
case "test-tts":
|
||||
await runTtsTest();
|
||||
return;
|
||||
default:
|
||||
throw new Error(`알 수 없는 실행 모드입니다: ${mode}. 사용 가능: test-stt, test-sttllm, test-llm, devices`);
|
||||
throw new Error(`알 수 없는 실행 모드입니다: ${mode}. 사용 가능: test-stt, test-sttllm, test-llm, test-tts, devices`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user