lmc/kokoro.ts

66 lines
1.5 KiB
TypeScript

/* Step before building:
* 1. Copy onnxruntime-node/bin to out/
* 2. Modify onnxruntime-node/dist/binding.js for bun
* 3. Profit */
import os from "node:os";
import { $, which } from "bun";
import chalk from "chalk";
import { KokoroTTS, TextSplitterStream } from "kokoro-js";
const model_id = "onnx-community/Kokoro-82M-v1.0-ONNX";
const tts = await KokoroTTS.from_pretrained(model_id, {
dtype: "fp32", // fp32 and q8 work best; fp16 results in silent segments
device: "cpu",
});
const stream = new TextSplitterStream();
const input = await $`wl-paste`.text();
if (!input) {
console.error("No input");
process.exit(1);
}
stream.push(input);
console.log(chalk.blue("Welcome to aitts!\n"));
console.log(input);
await $`rm -r ${os.homedir()}/.aitts/`.nothrow();
await $`mkdir ${os.homedir()}/.aitts/`.nothrow();
let fileId = 0;
const files: string[] = [];
const processText = async (text: string) => {
const audio = await tts.generate(text, {
voice: "af_bella",
speed: 1.2,
});
const file = `${os.homedir()}/.aitts/${fileId}.wav`;
audio.save(file);
files.push(file);
fileId++;
};
const play = async () => {
for (const file of files) {
console.log(chalk.green(`Playing ${file}`));
await Bun.spawn({
cmd: ["mpv", file],
stdout: "inherit",
}).exited;
process.stdout.moveCursor(-9999, -5);
process.stdout.clearScreenDown();
}
};
let playing: Promise<void> | undefined;
for (const text of stream) {
await processText(text);
if (!playing && which("mpv")) {
playing = play();
}
}
await playing;