读取 QAudioProbe 缓冲区

Reading QAudioProbe buffer

Qt 文档 (https://doc.qt.io/qtforpython-5/PySide2/QtMultimedia/QAudioBuffer.html) 说我们应该像这样从 QAudioProbe 读取缓冲区:

// With a 16bit sample buffer:
quint16 *data = buffer->data<quint16>(); // May cause deep copy

这是 C++,但我需要在 Python 中编写它。

我不确定如何使用 Qt quint16 数据类型,甚至不知道如何导入它。

这是我的完整代码:

#!/bin/python3

from PySide2.QtMultimedia import QMediaPlayer, QMediaContent, QAudioProbe, QAudioBuffer
from PySide2.QtCore import QUrl, QCoreApplication, QObject, Signal, Slot
import sys


def main():

    app = QCoreApplication()
    player = QMediaPlayer()
    url = QUrl.fromLocalFile("/home/ubuntu/sound.wav")
    content = QMediaContent(url)
    player.setMedia(content)
    player.setVolume(50)

    probe = QAudioProbe()
    probe.setSource(player)
    probe.audioBufferProbed.connect(processProbe)

    player.play()


def processProbe(probe):
    print(probe.data())


if __name__ == "__main__":
    main()

输出:

shiboken2.shiboken2.VoidPtr(Address 0x2761000, Size 0, isWritable False)
shiboken2.shiboken2.VoidPtr(Address 0x2761000, Size 0, isWritable False)
shiboken2.shiboken2.VoidPtr(Address 0x2761000, Size 0, isWritable False)
shiboken2.shiboken2.VoidPtr(Address 0x2761000, Size 0, isWritable False)
...

我 运行 在一个新的 PySide2 5.13.2 环境中遇到了同样的问题,并且 运行 print(probe.data().toBytes()) 返回了大小为 0 的块,我知道这不可能因为其他内置功能正在访问数据。

我和其他人一样讨厌这个 hack,但是如果你想 test 可以通过这种方式访问​​缓冲区内容(请 do不要在生产代码中使用它):

  1. 通过 format 了解缓冲区的数据类型、字节顺序等,并推断出您需要的正确 C 类型(例如 signed int 16)。

  2. VoidPtr打印输出中提取打印地址,并将其转换为整数

  3. 通过在给定地址、给定类型和给定帧数读取来创建一个 numpy 数组。


代码:

首先,在您应用的某处,您将通过 setSource 将您的 QAudioProbe 连接到您的源,然后将 audioBufferProbed 信号连接到一个方法,例如:

self.audio_probe.audioBufferProbed.connect(self.on_audio_probed)

然后,以下 on_audio_probed 功能将获取 numpy 数组并打印其范数,该范数应在声音出现时增加:

import numpy as np
import ctypes

def get_buffer_info(buf):
    """
    """
    num_bytes = buf.byteCount()
    num_frames = buf.frameCount()
    #
    fmt = buf.format()
    sample_type = fmt.sampleType()  # float, int, uint
    bytes_per_frame = fmt.bytesPerFrame()
    sample_rate = fmt.sampleRate()
    #
    if sample_type == fmt.Float and bytes_per_frame == 4:
        dtype = np.float32
        ctype = ctypes.c_float
    elif sample_type == fmt.SignedInt and bytes_per_frame == 2:
        dtype = np.int16
        ctype = ctypes.c_int16
    elif sample_type == fmt.UnsignedInt and bytes_per_frame == 2:
        dtype = np.uint16
        ctype = ctypes.c_uint16
    #
    return dtype, ctype, num_bytes, num_frames, bytes_per_frame, sample_rate

def on_audio_probed(audio_buffer):
    """
    """
    cdata = audio_buffer.constData()
    (dtype, ctype, num_bytes, num_frames,
     bytes_per_frame, sample_rate) = get_buffer_info(audio_buffer)
    pointer_addr_str = str(cdata).split("Address ")[1].split(", Size")[0]
    pointer_addr = int(pointer_addr_str, 16)
    arr = np.array((ctype * num_frames).from_address(pointer_addr))
    print(np.linalg.norm(arr))  # should increase in presence of sound

我刚刚使用 QAudioRecorder 使用 16 位无符号 wavs 对其进行了测试,它工作“很好”(音频看起来和听起来都不错,请参见下面的屏幕截图)。再次强调,这基本上是一个模因代码,所以任何上面向你的堂兄弟展示你的花哨的音频缓冲应用程序的东西都是非常危险的,不要在严肃的代码中使用。但无论如何,让我知道是否有任何其他解决方法对您有用,或者这是否也适用于不同的环境!希望如果开发人员看到人们实际上在使用这种方法,他们会尽快解决问题 :)

干杯!
安德烈斯