94 lines
3.5 KiB
JavaScript
94 lines
3.5 KiB
JavaScript
(async function () {
|
|
"use strict";
|
|
const sdk = await import("microsoft-cognitiveservices-speech-sdk");
|
|
const readline = require("readline");
|
|
const { exec } = require("child_process");
|
|
const path = require("path");
|
|
const fs = require("fs");
|
|
|
|
const rawFile = "tts_raw.wav";
|
|
const asteriskFile = "tts_asterisk.wav";
|
|
|
|
// Aufruf:
|
|
// node tts.js -> background.wav, falls vorhanden
|
|
// node tts.js meine-musik.mp3 -> diese Musik
|
|
// node tts.js --no-music -> ohne Musik (auch: none, -n)
|
|
const arg = process.argv[2];
|
|
const noMusic = ["--no-music", "none", "-n"].includes(arg);
|
|
const musicFile = noMusic ? null : (arg || "background.wav");
|
|
const musicVolume = 0.15;
|
|
const introMs = 2000; // Musik-Vorlauf vor der Sprache
|
|
|
|
const speechConfig = sdk.SpeechConfig.fromSubscription(
|
|
"c0d12660f15c4869bf2572fda3cb91ab",
|
|
"germanywestcentral"
|
|
);
|
|
const audioConfig = sdk.AudioConfig.fromAudioFileOutput(rawFile);
|
|
const voiceName = "de-AT-IngridNeural";
|
|
const language = "de-AT";
|
|
speechConfig.speechSynthesisVoiceName = voiceName;
|
|
const synthesizer = new sdk.SpeechSynthesizer(speechConfig, audioConfig);
|
|
|
|
const rl = readline.createInterface({
|
|
input: process.stdin,
|
|
output: process.stdout
|
|
});
|
|
|
|
rl.question("Enter text with SSML break tags:\n> ", function (userInput) {
|
|
rl.close();
|
|
const ssml = `
|
|
<speak version="1.0"
|
|
xmlns="http://www.w3.org/2001/10/synthesis"
|
|
xml:lang="${language}">
|
|
<voice name="${voiceName}">
|
|
${userInput}
|
|
</voice>
|
|
</speak>`;
|
|
synthesizer.speakSsmlAsync(
|
|
ssml,
|
|
function (result) {
|
|
synthesizer.close();
|
|
if (result.reason !== sdk.ResultReason.SynthesizingAudioCompleted) {
|
|
console.error("TTS failed:", result.errorDetails);
|
|
return;
|
|
}
|
|
|
|
const hasMusic = !noMusic && fs.existsSync(musicFile);
|
|
if (!noMusic && !hasMusic) {
|
|
console.warn(`Hinweis: "${musicFile}" nicht gefunden -> ohne Musik.`);
|
|
}
|
|
console.log(
|
|
"Azure TTS done → converting for Asterisk" +
|
|
(hasMusic ? ` (mit Musik: ${musicFile})...` : " (ohne Musik)...")
|
|
);
|
|
|
|
let cmd;
|
|
if (hasMusic) {
|
|
cmd =
|
|
`ffmpeg -y -i "${rawFile}" -stream_loop -1 -i "${musicFile}" ` +
|
|
`-filter_complex "[0:a]adelay=${introMs}:all=1[sp];` +
|
|
`[1:a]volume=${musicVolume}[bg];` +
|
|
`[sp][bg]amix=inputs=2:duration=first:dropout_transition=0,` +
|
|
`areverse,afade=t=in:d=2,areverse" ` +
|
|
`-acodec pcm_s16le -ar 8000 -ac 1 "${asteriskFile}"`;
|
|
} else {
|
|
cmd = `ffmpeg -y -i "${rawFile}" -acodec pcm_s16le -ar 8000 -ac 1 "${asteriskFile}"`;
|
|
}
|
|
|
|
exec(cmd, (error, stdout, stderr) => {
|
|
if (error) {
|
|
console.error("FFmpeg error:", error);
|
|
return;
|
|
}
|
|
console.log("Asterisk-compatible file created:", asteriskFile);
|
|
fs.unlinkSync(rawFile);
|
|
});
|
|
},
|
|
function (err) {
|
|
console.error("Error:", err);
|
|
synthesizer.close();
|
|
}
|
|
);
|
|
console.log("Synthesizing...");
|
|
});
|
|
})(); |