Passing a float array pointer through a python extension/wrapper – SndObj-library - python

So I'm feeling that Google is getting tired of trying to help me with this.
I've been trying to experiment some with the SndObj library as of late, and more specifically the python wrapper of it.
The library is kind enough to include a python example to play around with, the only issue being it to get it to work. The last line below is giving me a world of hurt:
from sndobj import SndObj, SndRTIO, HarmTable, Oscili, SND_OUTPUT
from scipy import zeros, pi, sin, float32
import numpy
sine = numpy.array([256],float32)
for i in range(sine.size):
sine[i] = 0.5 * sin((2 * pi * i) / sine.size)
sine *= 32768
obj = SndObj()
obj.PushIn(sine,256)
In the original code it was:
obj.PushIn(sine)
That gave me the error
TypeError: SndObj_PushIn() takes exactly 3 arguments (2 given)
Alright, fair enough. I check the (automatically generated) documentation and some example code around the web and find that it also wants an integer size. Said and done (I like how they have, what I'm guessing is at least, dated code in the example).
Anyway, new argument; new error:
TypeError: in method 'SndObj_PushIn', argument 2 of type 'float *'
I'm not experienced at all in c++, which I believe is the library's "native" (excuse my lack of proper terminology) language, but I'm pretty sure I've picked up that it wants a float array/vector as its second argument (the first being self). However, I am having a hard time accomplishing that. Isn't what I've got a float array/vector already? I've also, among other things, tried using float instead of float32 in the first line and float(32768) in the fourth to no avail.
Any help, suggestion or tip would be much appreciated!
EDIT:
Became unsure of the float vector/array part and went to the auto-docs again:
int SndObj::PushIn ( float * vector,
int size
)
So I'd say that at least the c++ wants a float array/vector, although I can of course still be wrong about the python wrapper.
UPDATE
As per Prune's request (saying that the error message isn't asking for a float vector, but saying that that's the error), I tried inputing different integer (int,int32, etc.) vectors instead. However, seeing that I still got the same error message and keeping the EDIT above in mind, I'd say that its actually supposed to be a float vector after all.
UPDATE2
After some hints from saulspatz I've changed the question title and tags to better formulate my problem. I did some further googling according to this as well, but am yet to dig out anything useful.
UDATE3
SOLVED

Actually, the problem is the opposite: PushIn takes an array of integers. The error message is complaining that you gave it floats. Try this in place of your call to PushIn
int_sine = numpy.array([256],int32)
int_sine = [int(x) for x in sine]
and then feed int_sine instead of sine to PushIn.

I don't really have an answer to your question, but I have some information for you that's too long to fit in a comment, and that I think may prove useful. I looked at the source of what I take to be the latest version, SndObj 2.6.7. In SndObj.h the definition of PushIn is
int PushIn(float *in_vector, int size){
for(int i = 0; i<size; i++){
if(m_vecpos >= m_vecsize) m_vecpos = 0;
m_output[m_vecpos++] = in_vector[i];
}
return m_vecpos;
}
so it's clear that size is the number of elements to push. (I presume this would be the number of elements in your array, and 256 is right.) The float* means a pointer to float; in_vector is just an identifier. I read the error message to mean that the function received a float when it was expecting a pointer to float. In a C++ program, you might pass a pointer to float by passing the name of an array of floats, though this is not the only way to do it.
I don't know anything about how python extensions are programmed, I'm sorry to say. From what I'm seeing, obj.PushIn(sine,256) looks right, but that's a naive view.
Perhaps with this information, you can formulate another question (or find another tag) that will attract the attention of someone who knows about writing python extensions in C/C++.
I hope this helps.

So finally managed to get it working (with some assistance the very friendly wrapper author)!
It turns out that there is a floatArray class in the sandbox-library which is used for passing float arrays to the c++-functions. I'm guessing that they included that after the numpy-test.py was written which threw me for a loop.
Functioning code:
from sndobj import SndObj, SndRTIO, SND_OUTPUT, floatArray
from scipy import pi, sin
# ---------------------------------------------------------------------------
# Test PushIn
# Create 1 frame of a sine wave in a numpy array
sine = floatArray(256)
for i in range(256):
sine[i] = float(32768*0.5 * sin((2 * pi * i) / 256))
obj = SndObj()
obj.PushIn(sine,256)
outp = SndRTIO(1, SND_OUTPUT)
outp.SetOutput(1, obj)
# Repeatedly output the 1 frame of sine wave
duration = outp.GetSr() * 2 # 2 seconds
i = 0
vector_size = outp.GetVectorSize()
while i < duration:
outp.Write()
i += vector_size

Related

Hypothesis strategy generating inf when specifically asked not to

from functools import partial
import hypothesis as h
import hypothesis.strategies as hs
import hypothesis.extra.numpy as hnp
import numpy as np
floats_notnull = partial(hs.floats, allow_nan=False, allow_infinity=False)
complex_notnull = partial(hs.complex_numbers, allow_nan=False, allow_infinity=False)
data_strategy_real = hnp.arrays(
np.float64,
hs.tuples(hs.integers(min_value=2, max_value=50),
hs.integers(min_value=2, max_value=5)),
floats_notnull()
)
data_strategy_complex = hnp.arrays(
np.complex64,
hs.tuples(hs.integers(min_value=2, max_value=50), hs.just(1)),
complex_notnull()
)
data_strategy = hs.one_of(data_strategy_real, data_strategy_complex)
If you run data_strategy.example() a couple times, you'll notice that some of the values in the result have infinite real or imaginary parts. My intention here was to specifically disallow infinite or NaN parts.
What am I doing wrong?
Update: if I use
data_strategy = hs.lists(complex_notnull, min_size=2, max_size=50)
and convert that to an array inside my test, the problem appears to go away. Are the complex numbers overflowing? I'm not getting the usual deprecation warning about overflow from Hypothesis.
And if I use
data_strategy = data_strategy_real
no infs appear.
The complex64 type is too small and it's overflowing. Somehow Hypothesis is failing to catch this.
Yep, the root cause of this problem is that you're generating 64-bit finite floats, then casting them to 32-bit (because complex64 is a pair of 32-bit floats). You can fix that with the width=32 argument to floats():
floats_notnull_32 = partial(hs.floats, allow_nan=False, allow_infinity=False, width=32)
And you're not getting the usual overflow check because it's only implemented for floats and integers at the moment. I've opened (edit: and fixed) issue #1591 to check complex and string types too.
The complex64 type is too small and it's overflowing. Somehow Hypothesis is failing to catch this.
Switching to complex128 fixed the problem for now.

Python. How to call a function that expect ctypes array. Strand/Straus7

I'm using python scripting for Strand/Straus7 importing his DLL.
I'm trying to call a function for set the units, called St7SetUnits, following the manual (Img.1) and watching the .py scripting of the DLL that I imported (Img.2). The function expected a c_long and a ctypes.POINTER.(c_long), as specify in the script (Img.3)
Here the complete manual strand7.com/downloads/Strand7%20R246%20API%20Manual%20TOC.pdf
and here the .py scripting https://www.dropbox.com/s/88plz2funjqy1vb/St7API.py?dl=0
As specify at the beginning of the manual I have to convert the list in ctypes array (Img.4).
The function I call is the same of the example, but I can't call it correctly.
I write
import St7API as SA
import ctypes
SA.St7Init()
unitsArray = ctypes.c_int * SA.kLastUnit
units = unitsArray()
units[0] = 0
units[1] = 1
units[2] = 3
units[3] = 1
units[4] = 1
units[5] = 2
SA.St7SetUnits(1, units)
But returns error
expected c_long, got c_long_Array_6
If I try something else, for example an int from the array
SA.St7SetUnits(1, units[0])
the error change in
expected LP_c_long, got int
I tried many solution, but no one works.
Can anyone help me?
Thanks a lot
I know its been a while, but this works for me:
units_type=ctypes.c_long*6
Units = units_type(0,1,3,1,1,2)
St7API.St7SetUnits(1,Units)
From your screenshots it looks like you might be using Grasshopper. If so, you might need to explicitly cast the units array to a pointer by adding this line to the top of your script:
PI = ctypes.POINTER(ctypes.c_long)
And do this whenever you pass an array from IronPython to St7API:
SA.St7SetUnits(1, PI(units))
This answer has slightly more.

Python interface to C++ COM dll

So I'm trying to interface with a COM object with Python and having some difficulty as I'm not much of a programmer. I've interface Python with c dlls before but not with COM dlls. But that may not necessarily be the source of the problem. Any help or suggestions would be very much appreciated.
I've been able to load the library with Python with:
CiGenUsb = pythoncom.MakeIID(CiGenUsb_string)
win32com.client.pythoncom.CoInitialize()
disp = win32com.client.gencache.EnsureDispatch(CiGenUsb)
I've been able to call some functions okay but not the following one. The function is defined in C++ as:
CIUsb_SendFrame([in] LONG nDevId, [in] BYTE* pFrameData, [in] LONG nSize, [out] LONG* pStatus);
The data I want to send with CIUsb_SendFrame -- the pFrameData array -- is first read in as an array of 160 integers with python. I then put that into a byte array (of 320 bytes):
frame_bytes_type = ctypes.c_ubyte * 320
frame_bytes = frame_bytes_type()
j=0
for i in range(0,320,2):
frame_bytes[i] = intData[j]&0xff
frame_bytes[i+1] = (intData[j]>>8)&0xff
j=j+1
disp.CIUsb_SendFrame(0, ctypes.addressof(frame_bytes), ctypes.sizeof(frame_bytes),0)
The code runs but the frame that is output to the hardware which the code controls looks very wrong. And it seems to change run to run. So I assume the data being sent is not what's in frame_bytes but something random.
The COM library also has variant versions of all the functions for use in ActiveX Automation environments but I have even less of a clue on how to use that.
Thanks.
Edit: I have called CoInitialize which I've now included in the snippet above. nDevId set to 0 is correct. I'll try using something more correct for *pStatus. The hardware being controlled consists of 160 pixels which can each take a 2-byte value. But since the CIUsb_SendFrame function takes a BYTE array, I create the 320 element array and pass that.

Differences in ctypes between Python 2 and 3

I have a working python 2.7 program that calls a DLL. I am trying to port the script to python 3.2. The DLL call seems to work (i.e. there is no error upon calling) but the returned data does not make sense.
Just in case it could be useful:
- The call takes three arguments: two int (input) and a pointer to a ushort array (output).
I have tried using both python and numpy arrays without success.
Can anyone enumerate the differences between Python 2.7 and 3.2 respecting ctypes?
Thanks in advance
EDIT
Here is some example code. The DLL is propietary so I do not have the code. But I do have the C header:
void example (int width, int height, unsigned short* pointer)
The python code is:
width, height = 40, 100
imagearray = np.zeros((width,height), dtype=np.dtype(np.ushort))
image = np.ascontiguousarray(imagearray)
ptrimage = image.ctypes.data_as(ct.POINTER(ct.c_ushort))
DLL.example(width, height, ptrimage)
This works in python 2.7 but not in 3.2.
EDIT 2
If the changes in ctypes are only those pointed out by Cedric, it does not make sense that python 3.2 will not work. So looking again at the code, I have found that there is a preparation function called before the function that I am mentioning. The signature is:
void prepare(char *table)
In python, I am calling by:
table = str(aNumber)
DLL.prepare(table)
Is it possible that the problem is due to the change in the Python string handling?
In Python 2.7, strings are byte-strings by default. In Python 3.x, they are unicode by default. Try explicitly making your string a byte string using .encode('ascii') before handing it to DLL.prepare.
Edit:
#another way of saying table=str(aNumber).encode('ascii')
table = bytes(str(aNumber), 'ascii')
DLL.prepare(table)
In our case, we had code looking like:
addr = clib.some_function()
data = some_struct.from_address(addr)
This worked in python 2 but not in 3. The reason turned out not to be any difference in ctypes, but rather a change in memory layout that unmasked a bug in the code above. In python 2, the address returned was always (by chance) small enough to fit inside a C int (32-bit), which is the default return type for all ctypes function calls. In python 3, the addresses were almost always too large, which caused the pointer address to become corrupted as it was coerced to int.
The solution is to set the function restype to a 64-bit integer type to ensure that it can accommodate the whole address, like:
clib.some_function.restype = c_longlong
or:
clib.some_function.restype = POINTER(some_struct)
According to the python documentation, the only changes between 2.7 and 3.2 is here
A new type, ctypes.c_ssize_t represents the C ssize_t datatype.
In 2.7, there was some other modifications introduced :
The ctypes module now always converts None to a C NULL pointer for
arguments declared as pointers. (Changed by Thomas Heller; issue
4606.) The underlying libffi library has been updated to version
3.0.9, containing various fixes for different platforms. (Updated by
Matthias Klose; issue 8142.)
I'm not sure it will explain the cause of your problem...

Python c_types .dll functions (pari library)

Alright, so a couple days ago I decided to try and write a primitive wrapper for the PARI library. Ever since then I've been playing with ctypes library in loading the dll and accessing the functions contained using code similar to the following:
from ctypes import *
libcyg=CDLL("<path/cygwin1.dll") #It needs cygwin to be loaded. Not sure why.
pari=CDLL("<path>/libpari-gmp-2.4.dll")
print pari.fibo #fibonacci function
#prints something like "<_FuncPtr object at 0x00BA5828>"
So the functions are there and they can potentially be accessed, but I always receive an access violation no matter what I try. For example:
pari.fibo(5) #access violation
pari.fibo(c_int(5)) #access violation
pari.fibo.argtypes = [c_long] #setting arguments manually
pari.fibo.restype = long #set the return type
pari.fibo(byref(c_int(5))) #access violation reading 0x04 consistently
and any variation on that, including setting argtypes to receive pointers.
The Pari .dll is written in C and the fibonacci function's syntax within the library is GEN fibo(long x).
Could it be the return type that's causing these errors, as it is not a standard int or long but a GEN type, which is unique to the PARI library? Any help would be appreciated. If anyone is able to successfully load the library and use ANY function from within python, please tell; I've been at this for hours now.
EDIT: Seems as though I was simply forgetting to initialize the library. After a quick pari.pari_init(4000000,500000) it stopped erroring. Now my problem lies in the in the fact that it returns a GEN object; which is fine, but whenever I try to reference the address to which it points, it's always 33554435, which I presume is still an address. I'm trying further commands and I'll update if I succeed in getting the correct value of something.
You have two problems here, one give fibo the correct return type and two convert the GEN return type to the value you are looking for.
Poking around the source code a bit, you'll find that GEN is defined as a pointer to a long. Also, at looks like the library provides some converting/printing GENs. I focused in on GENtostr since it would probably be safer for all the pari functions.
import cytpes
pari = ctypes.CDLL("./libpari.so.2.3.5") #I did this under linux
pari.fibo.restype = ctypes.POINTER(ctypes.c_long)
pari.GENtostr.restype = ctypes.POINTER(ctypes.c_char)
pari.pari_init(4000000,500000)
x = pari.fibo(100)
y = pari.GENtostr(x)
ctypes.string_at(y)
Results in:
'354224848179261915075'

Categories

Resources