111 lines
3.0 KiB
JavaScript
111 lines
3.0 KiB
JavaScript
const fs = require("fs");
|
|
const path = require("path");
|
|
const { exec } = require("node:child_process");
|
|
const { ECDH } = require("crypto");
|
|
|
|
const SAMPLE = path.join(__dirname, "..", "samples", "genericfemale.wav");
|
|
const WORDS_FILE = path.join(__dirname, "..", "words.txt");
|
|
const SOURCE_DIR = path.join(__dirname, "..", "source");
|
|
const OUT_DIR = path.join(__dirname, "..", "out");
|
|
const TTS_MODEL = "tts_models/multilingual/multi-dataset/xtts_v2";
|
|
const TTS_COMMAND = `tts --text "$word" --model_name $model --speaker_wav "$sample" --language_idx en --out_path "$outPath"`;
|
|
const FFMPEG_COMMAND = `ffmpeg -i "$file" -filter:a "volume=2.5, atempo=1.65" "$outPath"`;
|
|
|
|
if (!fs.existsSync(SOURCE_DIR)) {
|
|
fs.mkdirSync(SOURCE_DIR, { recursive: true });
|
|
}
|
|
if (!fs.existsSync(OUT_DIR)) {
|
|
fs.mkdirSync(OUT_DIR, { recursive: true });
|
|
}
|
|
|
|
const temp = SAMPLE.replace(".wav", "").split("\\");
|
|
const sampleName = temp.at(-1);
|
|
const OUTPUT = path.join(OUT_DIR, sampleName);
|
|
const SOURCE = path.join(SOURCE_DIR, sampleName);
|
|
if (!fs.existsSync(OUTPUT)) {
|
|
fs.mkdirSync(OUTPUT, { recursive: true });
|
|
}
|
|
if (!fs.existsSync(SOURCE)) {
|
|
fs.mkdirSync(SOURCE, { recursive: true });
|
|
}
|
|
|
|
const words = fs
|
|
.readFileSync(WORDS_FILE)
|
|
.toString()
|
|
.split("\n")
|
|
.filter((w) => !w.includes("#"));
|
|
function getCookedFiles() {
|
|
return fs.readdirSync(OUTPUT).map((f) => f.replace(".ogg", ""));
|
|
}
|
|
function getSourceFiles() {
|
|
return fs.readdirSync(SOURCE).map((f) => f.replace(".wav", ""));
|
|
}
|
|
|
|
function execCommand(command) {
|
|
return new Promise((resolve, reject) => {
|
|
exec(command, (error, stdout, stderr) => {
|
|
if (error) {
|
|
console.error("Error running TTS: " + stderr);
|
|
reject(stderr);
|
|
}
|
|
resolve(stdout);
|
|
});
|
|
});
|
|
}
|
|
async function runTTS(word) {
|
|
const ttsComamnd = TTS_COMMAND.replace("$word", word)
|
|
.replace("$model", TTS_MODEL)
|
|
.replace("$sample", SAMPLE)
|
|
.replace("$outPath", `${path.join(SOURCE, word + ".wav")}`);
|
|
console.log(`Running tts for ${word} using ${ttsComamnd}`);
|
|
await execCommand(ttsComamnd);
|
|
}
|
|
async function doCommit() {
|
|
const tempLookup = getCookedFiles().reduce((acc, file) => {
|
|
acc[file] = true;
|
|
return acc;
|
|
}, {});
|
|
|
|
for (const stagedFile of getSourceFiles()) {
|
|
if (!tempLookup[stagedFile]) {
|
|
const ffmpegCommand = FFMPEG_COMMAND.replace("$file", path.join(SOURCE, stagedFile + ".wav")).replace(
|
|
"$outPath",
|
|
path.join(OUTPUT, stagedFile + ".ogg")
|
|
);
|
|
console.log(`Running ${ffmpegCommand}`);
|
|
await execCommand(ffmpegCommand);
|
|
}
|
|
// Lesson learnt: Do NOT delete the source...
|
|
// fs.rmSync(path.join(STAGE, stagedFile + ".wav"));
|
|
}
|
|
|
|
console.log("All files committed");
|
|
}
|
|
|
|
async function main() {
|
|
await doCommit();
|
|
|
|
const tempLookup = getSourceFiles().reduce((acc, file) => {
|
|
acc[file] = true;
|
|
return acc;
|
|
}, {});
|
|
const wordToTTS = [];
|
|
for (let word of words) {
|
|
word = word.trim();
|
|
if (word) {
|
|
if (!tempLookup[word]) {
|
|
wordToTTS.push(word);
|
|
} else {
|
|
console.log(`Skipping ${word} since it exists in either stage or out`);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const word of wordToTTS) {
|
|
await runTTS(word);
|
|
}
|
|
await doCommit();
|
|
}
|
|
|
|
main();
|