Files

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();