我有一个C ++库,可以对音频数据进行分析,并提供一个C API。 C API函数之一采用const int16_t*指向数据的指针并返回分析结果。

我正在尝试为此API建立一个Python接口,并且大多数接口都在工作,但是我很难让ctypes指针用作该函数的参数。由于C侧的指针指向const,所以我觉得应该可以对任何连续的数据进行良好的处理。但是,以下操作无效:

import ctypes
import wave

_native_lib = ctypes.cdll.LoadLibrary('libsound.so')
_native_function = _native_lib.process_sound_data
_native_function.argtypes = [ctypes.POINTER(ctypes.c_int16),
                             ctypes.c_size_t]
_native_function.restype = ctypes.c_int

wav_path = 'hello.wav'

with wave.open(wav_path, mode='rb') as wav_file:
    wav_bytes = wav_file.readframes(wav_file.getnframes())

data_start = ctypes.POINTER(ctypes.c_int16).from_buffer(wav_bytes) # ERROR: data is immutable
_native_function(data_start, len(wav_bytes)//2)


手动将wav_bytes复制到bytearray允许构造指针,但会导致本机代码出现段错误,表明接收到的地址是错误的(它通过单元测试,并从C ++中读取数据)。通过正确地解决地址问题可以从技术上解决问题,但是我觉得有更好的方法。

当然有可能只是获取一些数据的地址并保证它是正确的格式并且不会被更改吗?我不想不必将所有以Python方式存储的音频数据深度复制到ctypes格式,因为如果我能得到指向它们的指针,那么大概字节就在那儿!

理想情况下,我希望能够做这样的事情

data_start = cast_to(address_of(data[0]), c_int16_pointer)
_native_function(data_start, len(data))


然后可以使用具有[0]len的任何内容。有没有办法在ctypes中做类似的事情?如果不是,是否有技术原因解释为什么它不可能,还有其他我应该使用的东西吗?

最佳答案

这应该为您工作。将array用于可写缓冲区,并创建引用该缓冲区的ctypes数组。

data = array.array('h',wav_bytes)
addr,size = data.buffer_info()
arr = (c_short * size).from_address(addr)
_native_function(arr,size)


另外,要跳过将wav_bytes的副本复制到data数组中,则可以使用argtypes中的指针类型。 ctypes知道如何将字节字符串转换为c_char_p。指针只是一个地址,因此_native_function将接收该地址,但在内部将其用作int*

_native_function.argtypes = c_char_p,c_size_t
_native_function(wav_bytes,len(wav_bytes) // 2)


解决“底层缓冲区不可写”错误的另一种方法是利用c_char_p,它允许使用不可变的字节字符串,然后将其显式转换为所需的指针类型:

_native_function.argtypes = POINTER(c_short),c_size_t
p = cast(c_char_p(wav_bytes),POINTER(c_short))
_native_function(p,len(wav_bytes) // 2)


在后一种情况下,您必须确保实际上没有写入缓冲区,因为它会破坏保存数据的不变Python对象。

关于python - 使用ctypes将音频数据从Python传递到C,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56845109/

10-12 21:48
查看更多