From 830be783b85534c204530f4fcfcaf2484d387c89 Mon Sep 17 00:00:00 2001 From: Dave Horton Date: Wed, 19 Jul 2023 09:59:34 -0400 Subject: [PATCH] trim trailing silence from azure tts when JAMBONES_TTS_TRIM_SILENCE is set --- lib/synth-audio.js | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/lib/synth-audio.js b/lib/synth-audio.js index ad5d1d8..7cebd84 100644 --- a/lib/synth-audio.js +++ b/lib/synth-audio.js @@ -33,6 +33,24 @@ const debug = require('debug')('jambonz:realtimedb-helpers'); const EXPIRES = (process.env.JAMBONES_TTS_CACHE_DURATION_MINS || 4 * 60) * 60; // cache tts for 4 hours const TMP_FOLDER = '/tmp'; + +const trimTrailingSilence = (buffer) => { + assert.ok(buffer instanceof Buffer, 'trimTrailingSilence - argument is not a Buffer'); + + let offset = buffer.length; + while (offset > 0) { + // Get 16-bit value from the buffer (read in reverse) + const value = buffer.readUInt16BE(offset - 2); + if (value !== 0) { + break; + } + offset -= 2; + } + + // Trim the silence from the end + return offset === buffer.length ? buffer : buffer.subarray(0, offset); +}; + /** * Synthesize speech to an mp3 file, and also cache the generated speech * in redis (base64 format) for 24 hours so as to avoid unnecessarily paying @@ -104,7 +122,12 @@ async function synthAudio(client, logger, stats, { account_sid, text }); let filePath; - if (['nuance', 'nvidia'].includes(vendor)) { + if (['nuance', 'nvidia'].includes(vendor) || + ( + process.env.JAMBONES_TTS_TRIM_SILENCE && + ['microsoft', 'azure'].includes(vendor) + ) + ) { filePath = `${TMP_FOLDER}/${key.replace('tts:', `tts-${salt || ''}`)}.r8`; } else filePath = `${TMP_FOLDER}/${key.replace('tts:', `tts-${salt || ''}`)}.mp3`; @@ -284,6 +307,7 @@ const synthMicrosoft = async(logger, { }) => { try { const {api_key: apiKey, region, use_custom_tts, custom_tts_endpoint} = credentials; + const trimSilence = filePath.endsWith('.r8'); let content = text; const speechConfig = SpeechConfig.fromSubscription(apiKey, region); speechConfig.speechSynthesisLanguage = language; @@ -297,7 +321,9 @@ const synthMicrosoft = async(logger, { */ if (!content.startsWith('${text}`; } - speechConfig.speechSynthesisOutputFormat = SpeechSynthesisOutputFormat.Audio16Khz32KBitRateMonoMp3; + speechConfig.speechSynthesisOutputFormat = trimSilence ? + SpeechSynthesisOutputFormat.Raw8Khz16BitMonoPcm : + SpeechSynthesisOutputFormat.Audio16Khz32KBitRateMonoMp3; const synthesizer = new SpeechSynthesizer(speechConfig); if (content.startsWith('')) { @@ -323,7 +349,9 @@ const synthMicrosoft = async(logger, { reject(cancellation.errorDetails); break; case ResultReason.SynthesizingAudioCompleted: - resolve(Buffer.from(result.audioData)); + let buffer = Buffer.from(result.audioData); + if (trimSilence) buffer = trimTrailingSilence(buffer); + resolve(buffer); synthesizer.close(); stats.increment('tts.count', ['vendor:microsoft', 'accepted:yes']); break;