tags:

views:

461

answers:

2

I am working on a real time audio processing dynamically linked library where I have a 2 dimensional C array of floating point data which represents the audio buffer. One dimension is time (samples) and the other is channel. I would like to pass this to a python script as a numpy array for the DSP processing and then I would like to pass this back to C so the data can carry on down the processing chain in C. The member function in C++ which does the processing looks like this:

void myEffect::process (float** inputs, float** outputs, int buffersize)
{
    //Some processing stuff
}

The arrays inputs and outputs are of equal size. The integer buffersize is the number of columns in the inputs and outputs arrays. On the python side I would like the processing to be carried out by a function which looks like the following:

class myPyEffect
    ...
    ...
    def process(self,inBuff):
    #inBuff and outBuff should be numpy arrays
        outBuff = inBuff * self.whatever # some DSP stuff
        return outBuff
    ...
    ...

Now, my question is, how can I go about getting the data in and out of C in the most efficient way possible (avoiding unnecessary memory copying etc.)? So far, for simple parameter changes I have been using C-API calls like the following:

pValue = PyObject_CallMethod(pInstance, "setParameter", "(f)", value);

Do I use something similar for my numpy arrays or is there a better way? Thanks for reading.

+3  A: 

You may be able to avoid dealing with the NumPy C API entirely. Python can call C code using the ctypes module, and you can access pointers into the numpy data using the array's ctypes attribute.

Here's a minimal example showing the process for a 1d sum-of-squares function.

ctsquare.c

#include <stdlib.h>

float mysumsquares(float * array, size_t size) {
    float total = 0.0f;
    size_t idx;
    for (idx = 0; idx < size; ++idx) {
        total += array[idx]*array[idx];
    }
    return total;
}

compilation to ctsquare.so

These command lines are for OS X, your OS may vary.

$ gcc -O3 -fPIC -c ctsquare.c -o ctsquare.o
$ ld -dylib -o ctsquare.so -lc ctsquare.o

ctsquare.py

import numpy
import ctypes

# pointer to float type, for convenience
c_float_p = ctypes.POINTER(ctypes.c_float)

# load the library
ctsquarelib = ctypes.cdll.LoadLibrary("ctsquare.so")

# define the return type and arguments of the function
ctsquarelib.mysumsquares.restype = ctypes.c_float
ctsquarelib.mysumsquares.argtypes = [c_float_p, ctypes.c_size_t]

# python front-end function, takes care of the ctypes interface
def myssq(arr):
    # make sure that the array is contiguous and the right data type
    arr = numpy.ascontiguousarray(arr, dtype='float32')

    # grab a pointer to the array's data
    dataptr = arr.ctypes.data_as(c_float_p)

    # this assumes that the array is 1-dimensional. 2d is more complex.
    datasize = arr.ctypes.shape[0]

    # call the C function
    ret = ctsquarelib.mysumsquares(dataptr, datasize)

    return ret

if __name__ == '__main__':
    a = numpy.array([1,2,3,4])
    print 'sum of squares of [1,2,3,4] =', myssq(a)
Theran
Thanks! This is cool, however, the dynamically linked library I'm creating is a plugin for another application, therefore, the python code must be invoked from the plugin. This means that I need to create a numpy array from whatever is in the input buffer, process it in python, and then return it to the plugin dll. I cannot just specify the numpy array contents like in the above example a = numpy.array([1,2,3,4])Ctypes looks like an excellent tool for using library functions in python scripts, however I'm using python to extend C, and not C to extend python. Can it be done this way?
learnvst
A: 

OK, I think that I need to use something like the following function from the numpy C-API

myarray = PyArray_SimpleNewFromData(2, dim, NPY_FLOAT, inputs);

Which I then send to the python script using something like . . .

    pValue = PyObject_CallMethod(pInstance, "processReplacing",
 "whatever this type is!?", myarray);

I'll than need to change pValue back into a C type array. Any further help would be much appreciated.

learnvst
"whatever this type is!?" should be "O" for Python object. You'll need to ensure that pValue is a numpy array using PyArray_Check. Then the data will be accessible through pValue's data pointer. It may already be in contiguous chunks, or it could be a slice of another numpy object, in which case you'll have to copy it to a contiguous C array.
Theran