gpt4 book ai didi

numpy - 将 PCM 波形数据转换为 numpy 数组,反之亦然

转载 作者:行者123 更新时间:2023-12-02 04:22:23 24 4
gpt4 key购买 nike

情况

我正在通过使用 WebRTC-VAD 使用来自 WebRTC 的 VAD(语音事件检测) ,一个 Python 适配器。 example implementation来自 GitHub 存储库使用 Python 的 wave module从文件中读取 PCM 数据。请注意,根据评论,该模块仅适用于单声道音频和 8000、16000 或 32000 Hz 的采样率。

我想做的

从具有不同采样率的任意音频文件(MP3 和 WAV 文件)中读取音频数据,将其转换为 WebRTC-VAD 使用的 PCM 表示,应用 WebRTC-VAD 检测语音事件,最后通过生成 Numpy-Arrays 处理结果再次来自 PCM 数据,因为它们在使用 Librosa 时最容易使用

我的问题

WebRTC-VAD 模块仅在使用 wave 时才能正常工作模块。此模块将 PCM 数据返回为 bytes对象。当给它喂食已经获得的 Numpy 数组时它不起作用,例如通过使用 librosa.load(...) .我还没有找到在两种表示之间进行转换的方法。

到目前为止我做了什么

我编写了以下函数来从音频文件中读取音频数据并自动转换它们:

使用 Librosa 读取/转换任何音频数据的通用函数(--> 返回 Numpy 数组):

def read_audio(file_path, sample_rate=None, mono=False):       
return librosa.load(file_path, sr=sample_rate, mono=mono)

将任意数据读取为 PCM 数据的函数(--> 返回字节):
def read_audio_vad(file_path):
audio, rate = librosa.load(file_path, sr=16000, mono=True)
tmp_file = 'tmp.wav'
sf.write(tmp_file, audio, rate, subtype='PCM_16')
audio, rate = read_pcm16_wave(tmp_file)
remove(tmp_file)
return audio, rate

def read_pcm16_wave(file_path):
with wave.open(file_path, 'rb') as wf:
sample_rate = wf.getframerate()
pcm_data = wf.readframes(wf.getnframes())
return pcm_data, sample_rate

如您所见,我首先使用 librosa 读取/转换音频数据,从而绕道而行。这是必需的,因此我可以读取具有任意编码的 MP3 文件或 WAV 文件,并使用 Librosa 自动将其重新采样为 16kHz 单声道。然后我正在写入一个临时文件。在删除文件之前,我再次读出了内容,但这次使用 wave模块。这给了我 PCM 数据。

我现在有以下代码来提取语音事件并生成 Numpy 数组:
def webrtc_voice(audio, rate):
voiced_frames = webrtc_split(audio, rate)
tmp_file = 'tmp.wav'
for frames in voiced_frames:
voice_audio = b''.join([f.bytes for f in frames])
write_pcm16_wave(tmp_file, voice_audio, rate)
voice_audio, rate = read_audio(tmp_file)
remove(tmp_file)

start_time = frames[0].timestamp
end_time = (frames[-1].timestamp + frames[-1].duration)
start_frame = int(round(start_time * rate / 1e3))
end_frame = int(round(end_time * rate / 1e3))
yield voice_audio, rate, start_frame, end_frame

def write_pcm16_wave(path, audio, sample_rate):
with wave.open(path, 'wb') as wf:
wf.setnchannels(1)
wf.setsampwidth(2)
wf.setframerate(sample_rate)
wf.writeframes(audio)

如您所见,我再次绕过临时文件先写入 PCM 数据,然后使用 Librosa 再次读取临时文件以获得 Numpy 数组。 webrtc_split函数是 example implementation 的实现只有很少的细微变化。为了完整起见,我将其发布在这里:
def webrtc_split(audio, rate, aggressiveness=3, frame_duration_ms=30, padding_duration_ms=300):
vad = Vad(aggressiveness)

num_padding_frames = int(padding_duration_ms / frame_duration_ms)
ring_buffer = collections.deque(maxlen=num_padding_frames)
triggered = False

voiced_frames = []
for frame in generate_frames(audio, rate):
is_speech = vad.is_speech(frame.bytes, rate)

if not triggered:
ring_buffer.append((frame, is_speech))
num_voiced = len([f for f, speech in ring_buffer if speech])
if num_voiced > 0.9 * ring_buffer.maxlen:
triggered = True
for f, s in ring_buffer:
voiced_frames.append(f)
ring_buffer.clear()
else:
voiced_frames.append(frame)
ring_buffer.append((frame, is_speech))
num_unvoiced = len([f for f, speech in ring_buffer if not speech])
if num_unvoiced > 0.9 * ring_buffer.maxlen:
triggered = False
yield voiced_frames
ring_buffer.clear()
voiced_frames = []
if voiced_frames:
yield voiced_frames


class Frame(object):
"""
object holding the audio signal of a fixed time interval (30ms) inside a long audio signal
"""

def __init__(self, bytes, timestamp, duration):
self.bytes = bytes
self.timestamp = timestamp
self.duration = duration


def generate_frames(audio, sample_rate, frame_duration_ms=30):
frame_length = int(sample_rate * frame_duration_ms / 1000) * 2
offset = 0
timestamp = 0.0
duration = (float(frame_length) / sample_rate)
while offset + frame_length < len(audio):
yield Frame(audio[offset:offset + frame_length], timestamp, duration)
timestamp += duration
offset += frame_length

我的问题

我使用 wave 写入/读取临时文件的实现模块并使用 Librosa 读取/写入这些文件以获取 Numpy 数组对我来说似乎过于复杂。然而,尽管花了一整天的时间在这件事上,我没有找到直接在两种编码之间进行转换的方法。我承认我不完全了解 PCM 和 WAVE 文件的所有细节,对 PCM 数据使用 16/24/32 位的影响或字节序。我希望我上面的解释足够详细,而不是太多。有没有更简单的方法在内存中的两种表示之间进行转换?

最佳答案

似乎 WebRTC-VAD 和 Python 包装器 py-webrtcvad , 预计音频数据为 16 位 PCM little-endian - 这是 WAV 文件中最常见的存储格式。
librosa及其底层 I/O 库 pysoundfile但是总是返回 [-1.0, 1.0] 范围内的浮点数组.要将其转换为包含 16 位 PCM 的字节,您可以使用以下 float_to_pcm16功能。

def float_to_pcm16(audio):
import numpy

ints = (audio * 32767).astype(numpy.int16)
little_endian = ints.astype('<u2')
buf = little_endian.tostring()
return buf


def read_pcm16(path):
import soundfile

audio, sample_rate = soundfile.read(path)
assert sample_rate in (8000, 16000, 32000, 48000)
pcm_data = float_to_pcm16(audio)
return pcm_data, sample_rate

关于numpy - 将 PCM 波形数据转换为 numpy 数组,反之亦然,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51486093/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com