【问题标题】:How to concatenate/join audio buffer arrays (text-to-speech results) into one on nodejs?如何在nodejs上将音频缓冲区数组(文本到语音结果)连接/加入一个?
【发布时间】:2021-11-03 08:05:11
【问题描述】:

我想将许多文本转换为一个音频,但我很困惑如何将多个音频连接成一个音频文件(由于 5k 字符/请求限制,您无法将长文本转换为音频)。

我当前的代码如下。它生成多个音频字节数组,但无法合并 mp3 音频,因为它忽略了头/元信息。是否建议在 TTS 领域使用 LINEAR16?我很高兴听到任何建议。谢谢。

  const client = new textToSpeech.TextToSpeechClient();
  const promises = ['hi','world'].map(text => {
    const requestBody = {
      audioConfig: {
        audioEncoding: 'MP3'
      },
      input: {
        text: text,
      },
      voice: {
        languageCode: 'en-US',
        ssmlGender: 'NEUTRAL'
      },
    };
    return client.synthesizeSpeech(requestBody)
  })
  const responses = await Promise.all(promises)
  console.log(responses)
  const audioContents = responses.map(res => res[0].audioContent)
  const audioContent = audioContents.join() // this line has a problem

标准输出

[
  [
    {
      audioContent: <Buffer ff f3 44 c4 00 12 a0 01 24 01 40 00 01 7c 06 43 fa 7f 80 38 46 63 fe 1f 00 33 3f c7 f0 03 03 33 1f c1 f0 0c eb fa 3f 03 20 7e 63 f3 78 03 ba 64 73 e0 ... 2638 more bytes>
    },
    null,
    null
  ],
  [
    {
      audioContent: <Buffer ff f3 44 c4 00 12 58 05 24 01 41 00 01 1e 02 23 9e 1f e0 1f 83 83 df ef 80 e8 ff 99 f0 0c 00 e8 7f c3 68 03 cf fd f8 8f ff 0f 3c 7f 88 f8 8c 87 e0 23 ... 2926 more bytes>
    },
    null,
    null
  ]
]

【问题讨论】:

  • node.js 中有一个 google-tts-concat-ssml 包可以满足您的要求,但不是 Google 官方包,如果它适合您的用例,请告诉我?
  • 谢谢。我来检查一下。我以为我可以不用 3rd 方库,但事情似乎比我预期的要难。
  • 你能测试google-tts-concat-ssml 包吗?你得到了预期的结果吗?
  • 不,我检查过但没有。我最终使用 ffmpeg 连接 mp3。我会看看它是否在生产环境中运行良好。

标签: node.js google-text-to-speech


【解决方案1】:

解决方法 1

正如我在评论中提到的,节点中有一个google-tts-concat-ssml 包以满足您的要求,这不是谷歌官方包。它会根据 API 的 5K 字符限制自动发出多个请求,并将生成的音频连接到单个音频文件中。在执行代码之前,安装以下客户端库:

npm install @google-cloud/text-to-speech
npm install google-text-to-speech-concat --save

通过在 API 的每个请求的 &lt;p&gt;&lt;/p&gt; 标记之间添加少于 5k 个字符来尝试以下代码。例如,如果您有 9K 个字符,则需要将其拆分为 2 个或更多请求,因此在 &lt;p&gt;&lt;/p&gt; 标签之间添加前 5K 个字符,然后在新的 &lt;p&gt;&lt;/p&gt; 标签之间添加剩余的 4k 个字符。因此,通过使用 google-text-to-speech-concat 包,API 返回的音频文件被连接成一个音频文件。

const textToSpeech =require('@google-cloud/text-to-speech');
const testSynthesize =require('google-text-to-speech-concat');
const fs = require('fs');
const path= require('path');
(async () => {
 const request = {
   voice: {
     languageCode: 'en-US',
     ssmlGender: 'FEMALE'
   },
   input: {
     ssml: `
     <speak>
     <p>add less than 5k chars between paragraph tags</p>
     <p>add less than 5k chars between paragraph tags</p>
     </speak>`
   },
   audioConfig: {
     audioEncoding: 'MP3'
   }
 };
 try {
   // Create your Text To Speech client
   // More on that here: https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application
   const textToSpeechClient = new textToSpeech.TextToSpeechClient({
     keyFilename: path.join(__dirname, 'google-cloud-credentials.json')
   });
   // Synthesize the text, resulting in an audio buffer
   const buffer = await testSynthesize.synthesize(textToSpeechClient, request);
   // Handle the buffer
   // For example write it to a file or directly upload it to storage, like S3 or Google Cloud Storage
   const outputFile = path.join(__dirname, 'Output.mp3');
   // Write the file
   fs.writeFile(outputFile, buffer, 'binary', (err) => {
     if (err) throw err;
     console.log('Got audio!', outputFile);
   });
 } catch (err) {
   console.log(err);
 }
})();

解决方法 2

尝试使用以下代码将整个文本拆分为 5K 字符集并将它们发送到 API 进行转换。如您所知,这会创建多个音频文件。在执行代码之前,在您当前的工作目录中创建一个文件夹来存储输出的音频文件。

const textToSpeech = require('@google-cloud/text-to-speech');
const fs = require('fs');
const util = require('util');
 
// Creates a client
const client = new textToSpeech.TextToSpeechClient();
 
(async function () {
 
 // The text to synthesize
 var text = fs.readFileSync('./text.txt', 'utf8');
 var newArr = text.match(/[^\.]+\./g);
 
 var charCount = 0;
 var textChunk = "";
 var index = 0;
 
 for (var n = 0; n < newArr.length; n++) {
 
   charCount += newArr[n].length;
   textChunk = textChunk + newArr[n];
 
   console.log(charCount);
 
   if (charCount > 4600 || n == newArr.length - 1) {
 
     console.log(textChunk);
 
     // Construct the request
     const request = {
       input: {
         text: textChunk
       },
       // Select the language and SSML voice gender (optional)
       voice: {
         languageCode: 'en-US',
         ssmlGender: 'MALE',
         name: "en-US-Wavenet-B"
       },
       // select the type of audio encoding
       audioConfig: {
         effectsProfileId: [
           "headphone-class-device"
         ],
         pitch: -2,
         speakingRate: 1.1,
         audioEncoding: "MP3"
       },
     };
 
     // Performs the text-to-speech request
     const [response] = await client.synthesizeSpeech(request);
 
     console.log(response);
 
     // Write the binary audio content to a local file
     const writeFile = util.promisify(fs.writeFile);
     await writeFile('result/Output' + index + '.mp3', response.audioContent, 'binary');
     console.log('Audio content written to file: output.mp3');
 
     index++;
 
     charCount = 0;
     textChunk = "";
   }
 }
}());

要将输出的音频文件合并为一个音频文件,可以使用audioconcat 包,它不是谷歌官方包。您还可以使用其他类似的可用包来连接音频文件。

要使用这个 audioconcat 库,需要已经安装了 ffmpeg 应用程序(不是 ffmpeg NPM 包)。因此,请根据您的操作系统安装ffmpeg 工具,并在执​​行连接音频文件的代码之前安装以下客户端库:

npm install audioconcat
npm install ffmpeg --enable-libmp3lame

试试下面的代码,它会连接您输出目录中的所有音频文件,并将单个连接的 output.mp3 音频文件存储在您当前的工作目录中。

const audioconcat = require('audioconcat')
const testFolder = 'result/';
const fs = require('fs');
var array=[];
fs.readdirSync(testFolder).forEach(songs => {
 array.push("result/"+songs);
 console.log(songs);
});
 
audioconcat(array)
 .concat('output.mp3')
 .on('start', function (command) {
   console.log('ffmpeg process started:', command)
 })
 .on('error', function (err, stdout, stderr) {
   console.error('Error:', err)
   console.error('ffmpeg stderr:', stderr)
 })
 .on('end', function (output) {
   console.error('Audio successfully created', output)
 })

对于这两种解决方法,我测试了来自各种 GitHub 链接的代码,并根据您的要求修改了代码。以下是供您参考的链接。

  1. Workaround-1
  2. Workaround-2

【讨论】:

  • 这个答案将帮助其他有同样问题的人。我发现 google-tts-concat-ssml 维护得不好,所以我避免了。我最终将文本分成几部分,然后在一个临时目录中创建小的音频文件,然后与audioconcat 连接,它只是在服务器上调用 ffmpeg。我使用 firebase 函数,幸运的是它支持 temp dir 和 ffmpeg。感谢您帮助我比较多个解决方案。
猜你喜欢
  • 1970-01-01
  • 2014-04-03
  • 1970-01-01
  • 2014-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多