【问题标题】:Python How to convert pyaudio bytes into virtual file?Python如何将pyaudio字节转换为虚拟文件?
【发布时间】:2021-04-07 04:55:35
【问题描述】:

简而言之

有没有办法将原始音频数据(通过PyAudio模块获取)转换成虚拟文件的形式(可以使用python的open()函数获取),而无需保存到磁盘并从磁盘?详情如下。

我在做什么

我正在使用PyAudio 录制音频,然后将其输入到张量流模型中进行预测。目前,当我首先将录制的声音保存为磁盘上的.wav文件时,它可以工作,然后再次读取它以将其输入模型。下面是记录和保存的代码:

import pyaudio
import wave

CHUNK_LENGTH = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 16000
RECORD_SECONDS = 1

p = pyaudio.PyAudio()

stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK_LENGTH)

print("* recording")
frames = [stream.read(RATE * RECORD_SECONDS)]  # here is the recorded data, in the form of list of bytes
print("* done recording")

stream.stop_stream()
stream.close()
p.terminate()

在我得到原始音频数据(变量frames)后,可以使用pythonwave模块保存如下。我们可以看到,在保存的时候,必须调用wf.setxxx这样的函数来保存一些元消息。

import os

output_dir = "data/"
output_path = output_dir + "{:%Y%m%d_%H%M%S}.wav".format(datetime.now())

if not os.path.exists(output_dir):
    os.makedirs(output_dir)

# save the recorded data as wav file using python `wave` module
wf = wave.open(output_path, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()

这是使用保存的文件在 tensorflow 模型上运行推理的代码。它只是简单地将其读取为二进制,然后模型将处理其余部分。

import classifier  # my tensorflow model

with open(output_path, 'rb') as f:
    w = f.read()
    classifier.run_graph(w, labels, 5)

问题

为了满足实时需求,我需要不断地流式传输音频并偶尔将其馈送到模型中。但是继续将文件保存在磁盘上然后一遍又一遍地读取它似乎是不合理的,这会浪费在 I/O 上的时间。

我想将数据保存在内存中并直接使用,而不是反复保存和读取。但是pythonwave模块不支持同时读写(参考here)。

如果我直接输入没有一些元数据(例如通道、帧率)的数据(可以在保存过程中由wave 模块添加),如下所示:

w = b''.join(frames)
classifier.run_graph(w, labels, 5)

我会得到如下错误:

2021-04-07 11:05:08.228544: W tensorflow/core/framework/op_kernel.cc:1651] OP_REQUIRES failed at decode_wav_op.cc:55 : Invalid argument: Header mismatch: Expected RIFF but found 
Traceback (most recent call last):
  File "C:\Users\anaconda3\envs\tensorflow\lib\site-packages\tensorflow_core\python\client\session.py", line 1365, in _do_call
    return fn(*args)
  File "C:\Users\anaconda3\envs\tensorflow\lib\site-packages\tensorflow_core\python\client\session.py", line 1350, in _run_fn
    target_list, run_metadata)
  File "C:\Users\anaconda3\envs\tensorflow\lib\site-packages\tensorflow_core\python\client\session.py", line 1443, in _call_tf_sessionrun
    run_metadata)
tensorflow.python.framework.errors_impl.InvalidArgumentError: 2 root error(s) found.
  (0) Invalid argument: Header mismatch: Expected RIFF but found

这里提供了我正在使用的 tensorflow 模型:ML-KWS-for-MCU,希望对您有所帮助。 这是产生错误的代码:(classifier.run_graph())

def run_graph(wav_data, labels, num_top_predictions):
    """Runs the audio data through the graph and prints predictions."""
    with tf.Session() as sess:
        #   Feed the audio data as input to the graph.
        #   predictions  will contain a two-dimensional array, where one
        #   dimension represents the input image count, and the other has
        #   predictions per class
        softmax_tensor = sess.graph.get_tensor_by_name("labels_softmax:0")
        predictions, = sess.run(softmax_tensor, {"wav_data:0": wav_data})

        # Sort to show labels in order of confidence
        top_k = predictions.argsort()[-num_top_predictions:][::-1]
        for node_id in top_k:
            human_string = labels[node_id]
            score = predictions[node_id]
            print('%s (score = %.5f)' % (human_string, score))

        return 0

【问题讨论】:

  • 您是否尝试过使用 BytesIO 对象?
  • @MadPhysicist 如果你的意思是用io.BytesIO() 包装数据,tensorflow 会说tensorflow.python.framework.errors_impl.InternalError: Unsupported object type _io.BytesIO
  • 你在使用前是否倒带文件?
  • 发布你是如何得到错误的。一般来说,Tensorflow 似乎与 io.BytesIO 配合得很好。
  • @MadPhysicist 已发布。对tensorflow不熟悉,只知道需要wav数据,类型为bytes

标签: python tensorflow pyaudio wave


【解决方案1】:

您应该可以使用 io.BytesIO 而不是物理文件,它们共享相同的接口但 BytesIO 只保存在内存中:

import io
container = io.BytesIO()
wf = wave.open(container, 'wb')
wf.setnchannels(4)
wf.setsampwidth(4)
wf.setframerate(4)
wf.writeframes(b'abcdef')

# Read the data up to this point
container.seek(0)
data_package = container.read()

# add some more data...
wf.writeframes(b'ghijk')

# read the data added since last
container.seek(len(data_package))
data_package = container.read()

这应该允许您在使用 TensorFlow 代码读取多余数据的同时将数据连续流式传输到文件中。

【讨论】:

  • 它完全解决了我的问题,非常感谢! :D
  • 对于实时应用程序,我们需要将上面的部分答案包装到一个while循环中。我理解正确吗?
猜你喜欢
  • 2011-05-18
  • 2015-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多