Files
tts/SpeechSynthesis_SSML.js

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...");
});
})();