作为我正在进行的项目的一部分,需要将多个音频数据连接成一个大的音频文件。音频文件是从四个来源生成的,各个文件都存储在Google Cloud存储桶中。每个文件都是mp3文件,可以轻松地验证每个文件是否正确生成(可以单独播放,也可以在自己喜欢的软件中对其进行编辑等)。
为了将音频文件合并在一起,nodejs服务器使用axios POST请求将Google Cloud存储中的文件作为数组缓冲区加载。从那里,它使用将每个数组缓冲区放入节点Buffer中Buffer.from()
,所以现在我们有了一个Buffer对象数组。然后,它用于Buffer.concat()
将Buffer对象连接到一个大的Buffer中,然后我们将其转换为Base64数据并发送到客户端服务器。
这很酷,但是当连接来自不同来源的音频时会出现问题。我上面提到的4个来源是文本到语音软件平台,例如Google Cloud Voice和Amazon Polly。具体来说,我们有来自Google Cloud Voice,Amazon Polly,IBM Watson和Microsoft Azure文本到语音的文件。基本上只有五个文本到语音的解决方案。同样,所有单个文件都可以工作,但是当通过这种方法将它们串联在一起时,会产生一些有趣的效果。
当声音文件被串联时,似乎取决于它们来自哪个平台,声音数据将被包含或将不包含在最终的声音文件中。以下是根据我的测试得出的“兼容性”表:
|------------|--------|--------|-----------|-----|
| Platform / | Google | Amazon | Microsoft | IBM |
|------------|--------|--------|-----------|-----|
| Google | Yes | No | No | No |
|------------|--------|--------|-----------|-----|
| Amazon | | No | No | Yes |
|------------|--------|--------|-----------|-----|
| Microsoft | | | Yes | No |
|------------|--------|--------|-----------|-----|
| IBM | | | | Yes |
|------------|--------|--------|-----------|-----|
效果如下:当播放较大的输出文件时,它将始终开始播放包含的第一个声音文件。从那里开始,如果下一个声音文件兼容,则可以听到,否则将被完全跳过(没有空声或任何声音)。如果跳过该文件,则该文件的“长度”(例如10s长的音频文件)将包含在生成的输出声音文件的末尾。但是,当我的音频播放器到达播放最后一个“兼容”音频的位置时,它会立即跳到结尾。
作为一个方案:
Input:
sound1.mp3 (3s) -> Google
sound2.mp3 (5s) -> Amazon
sound3.mp3 (7s)-> Google
sound4.mp3 (11s) -> IBM
Output:
output.mp3 (26s) -> first 10s is sound1 and sound3, last 16s is skipped.
在这种情况下,输出声音文件的长度为26秒。在开始的10秒钟内,您会听到sound1.mp3
和sound3.mp3
播放的顺序。然后在10秒(至少在firefox中播放此mp3文件)时,播放器立即跳到26秒结束。
我的问题是:有谁知道为什么有时候我可以以这种方式连接音频数据,而有时候却不能呢?在输出文件的末尾为什么会包含“丢失”的数据呢?如果二进制数据在某些情况下可以工作,那么它应该在所有情况下都不能工作吗,因为所有文件都具有mp3编码?如果我输入错了,请让我知道如何成功连接任何mp3文件:)我可以提供我的nodeJS后端代码,但是上面描述了使用的过程和方法。
谢谢阅读?
CD音频中经常使用44.1 kHz的音乐。视频通常使用48 kHz,因为DVD上已经使用了48 kHz。这两个采样率都远远高于语音所要求的采样率,因此您的各种文本语音转换提供程序可能输出的内容有所不同。通常是22.05 kHz(一半为44.1 kHz),那里也有11.025 kHz。
尽管每个帧都指定了自己的采样率,从而可以生成具有不同采样率的流,但我从未见过解码器尝试在流中切换采样率。我怀疑解码器正在跳过这些帧,或者甚至跳过任意块,直到它再次获得一致的数据。
使用FFmpeg(或FFprobe)之类的东西来确定文件的采样率是多少:
ffmpeg -i sound2.mp3
您将获得如下输出:
Duration: 00:13:50.22, start: 0.011995, bitrate: 192 kb/s
Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 192 kb/s
在此示例中,44.1 kHz是采样率。
我希望您的语音MP3可以是单声道,但是检查确定不会有任何伤害。和上面一样,检查FFmpeg的输出。在上面的示例中,它说stereo
。
与采样率一样,从技术上讲,每个帧都可以指定自己的频道数,但我不知道有哪个播放器会中途切换频道数。因此,如果要进行串联,则需要确保所有通道数都相同。
通常在文件的开头(ID3v2)和/或结尾(ID3v1)有ID3元数据。人们不太希望将这些数据放在中间。您需要确保在连接之前全部删除了该元数据。
MP3帧不一定独立存在。如果您具有恒定的比特率流,则编码器可能仍会使用较少的数据来编码一帧,而使用更多的数据来编码另一帧。发生这种情况时,某些帧会包含其他帧的数据。这样,可以从额外带宽中受益的帧可以获得它,同时仍将整个流以恒定的比特率进行适配。这就是“位库”。
如果剪切流并拼接到另一个流中,则可能会拆分一个帧及其从属帧。这通常会导致音频故障,但也可能导致解码器向前跳过。一些表现不佳的解码器将完全停止播放。在您的示例中,您什么都没有减少,所以这可能不是您麻烦的源头……但是我在这里提到它,因为它与您处理这些流的方式绝对相关。
另请参阅:http : //wiki.hydrogenaud.io/index.php?title=Bit_reservoir
如果大多数来源都是完全相同的格式,并且只有一个或两个未解决的来源,则可以转换不合格的文件。从那里,剥离所有内容的ID3标签并连接起来。
要进行转换,我建议将其作为子进程插入FFmpeg 。
child_process.spawn('ffmpeg' [
// Input
'-i', inputFile, // Use '-' to write to STDIN instead
// Set sample rate
'-ar', '44100',
// Set audio channel count
'-ac', '1',
// Audio bitrate... try to match others, but not as critical
'-b:a', '64k',
// Ensure we output an MP3
'-f', 'mp3',
// Output
outputFile // As with input, use '-' to write to STDOUT
]);
最简单,最可靠的解决方案是让FFmpeg为您构建一个全新的流。这将导致您的音频文件被解码为PCM,并产生新的流。您可以添加参数以对这些输入进行重新采样,并根据需要修改通道数。然后输出一个流。使用concat过滤器。
这样,您可以接受任何类型的音频文件,无需编写代码即可将这些流一起黑化,并且一旦安装,您就不必担心。
唯一的缺点是,它将需要对所有内容进行重新编码,这意味着会丢失另一代质量。无论如何,这对于任何不符合要求的文件都是必需的,而这只是语音,因此我不会再考虑了。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句