我正在开发一个项目,其中涉及大量文本转语音的音频处理任务,但我遇到了一个小障碍。我将处理可能数百个 TTS 音频片段,因此我想尽可能减少文件 IO。 我需要使用 Coqui TTS 合成语音,使用 AudioTSM 对其进行时间拉伸,然后使用 PyDub 进行附加处理和拼接。
我正在使用 Coqui TTS 生成如下文本:
from TTS.api import TTS
tts = TTS()
audio = tts.tts("Hello StackOverflow! Please help me!")
这将返回一个 Float32 值列表,需要将其转换为与 AudioTSM 的 ArrayReader
一起使用像这样:
audio_array = np.array(tts_audio)
# Reshape the array to (channels, samples)
samples = len(audio_array)
channels = 1
sample_rate = 22050
audio_array = audio_array.reshape(channels, samples)
from audiotsm.io.array import ArrayReader, ArrayWriter
reader = ArrayReader(audio_array)
tsm = wsola(reader.channels, speed=2) # increase the speed by 2x
rate_adjusted = ArrayWriter(channels=channels)
tsm.run(reader, rate_adjusted)
到目前为止,一切都很顺利。问题出在 Pydub
如果我使用 AudioTSM 的
WavWriter
来代替,则音频会正确地进行时间拉伸,就像我先完成 tts_to_file
然后 WavReader()
一样
如果我直接传入
ArrayWriter
的rate_adjusted.data.tobytes()
的结果,像这样,我们会得到严重失真的音频
from pydub import AudioSegment
# Convert the processed audio data to a PyDub AudioSegment
processed_audio_segment = AudioSegment(
rate_adjusted.data.tobytes(),
frame_rate=samplerate,
sample_width=2,
channels=channels
)
# Perform additional audio processing
processed_audio_segment.export('tts_output.wav', format='wav')
我找不到支持这一点的文档,但是查看
AudioSegment
__init__
的源代码,我怀疑它与 Coqui 输出有关 float32
AudioSegment 想要缩放 int16
转换数组实际上似乎会产生一些有用的结果
# Scale the floats to whole numbers and convert
converted_audio = (rate_adjusted.data * 2**15).astype(np.int16).tobytes()
这会生成一个不失真但质量明显下降的音频文件,导出时实际上比 AudioTSM
WavWriter
未经任何处理导出的文件小约 25KB。我猜这是因为 Int16 使用较少的数据。我尝试像这样转换为 Int32:
converted_audio = (rate_adjusted.data * 2**31).astype(np.int32).tobytes()
但这实际上听起来并没有更好,而且占用了更多的空间。我在这里缺少什么?
如果我只是用
WavWriter
导出到 wav 并用 AudioSegment.from_wav()
读入,则不会出现失真,导出是相同的,而且我不必转换,但同样,文件 IO 既昂贵又痛苦。
除了将内容转换为 wav 之外,是否有任何方法可以在这些数组格式之间正确进行转换,而不会导致失真、质量损失或理智?我也可以尝试其他库,但我的项目已经大量使用 PyDub,尽管它被证明是一个巨大的眼中钉。我的目标只是在内存中执行所有音频操作,并在库之间提供尽可能多的互操作性。
我怀疑您可能会遇到失真,因为原始
float32
音频阵列和要转换为的 int16
格式之间的数值范围存在差异。在浮点 wav 文件中,值通常在 -1.0 和 1.0 之间缩放。但是,为了避免音频削波,录音(尤其是语音录音)通常会使用较窄的范围,例如 -0.1 到 0.1 之间。
值得注意的是,
float32
最多可以容纳 2^31
不同的值,因此仅利用该范围的一部分并不是一个重要问题。另一方面,在处理 int16
时,如果您仅使用可用范围的十分之一,则实际上将可用存储空间减少了大约 6%
。范围的减小可能会导致音频质量明显下降。
我怀疑这种范围减小可能是您观察到的质量损失的原因。为了解决这个问题,我建议在将其转换为
int16
之前重新调整音频阵列以跨越 -1 到 1 的整个范围。这种调整应该会提高数据分辨率。所以类似:
data = data / np.max(np.abs(data))
除以数据中的最大绝对值可确保整个范围得到充分利用,从而减轻转换过程中潜在的质量损失。