/* py/pyext - python script object for PD and Max/MSP Copyright (c)2002-2008 Thomas Grill (gr@grrrr.org) For information on usage and redistribution, and for a DISCLAIMER OF ALL WARRANTIES, see the file, "license.txt," in this distribution. $LastChangedRevision: 26 $ $LastChangedDate: 2008-01-03 17:20:03 +0100 (Thu, 03 Jan 2008) $ $LastChangedBy: thomas $ */ #include "pybase.h" #undef PY_ARRAYS #if defined(PY_NUMERIC) || defined(PY_NUMPY) || defined(PY_NUMARRAY) #define PY_ARRAYS 1 #endif #ifdef PY_ARRAYS #ifdef PY_NUMARRAY # if FLEXT_OS == FLEXT_OS_MAC # include # else # include # endif static NumarrayType numtype = tAny; inline bool arrsupport() { return numtype != tAny; } #else # if defined(PY_NUMPY) # include # else # if FLEXT_OS == FLEXT_OS_MAC # include # else # include # endif # endif static PyArray_TYPES numtype = PyArray_NOTYPE; inline bool arrsupport() { return numtype != PyArray_NOTYPE; } #endif #endif PyObject *pybase::py_arraysupport(PyObject *self,PyObject *args) { PyObject *ret; #ifdef PY_ARRAYS ret = Py_True; #else ret = Py_False; #endif Py_INCREF(ret); return ret; } // PD defines a T_OBJECT symbol #undef T_OBJECT #if FLEXT_OS == FLEXT_OS_MAC #include "Python/bufferobject.h" #include "Python/structmember.h" #else #include "bufferobject.h" #include "structmember.h" #endif static PyObject *buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { pySamplebuffer *self = (pySamplebuffer *)pySamplebuffer_Type.tp_alloc(&pySamplebuffer_Type, 0); self->sym = NULL; self->buf = NULL; self->dirty = false; return (PyObject *)self; } static void buffer_dealloc(PyObject *obj) { pySamplebuffer *self = (pySamplebuffer *)obj; if(self->buf) { self->buf->Unlock(self->lock); if(self->dirty) self->buf->Dirty(true); delete self->buf; } obj->ob_type->tp_free(obj); } static int buffer_init(PyObject *obj, PyObject *args, PyObject *kwds) { FLEXT_ASSERT(pySamplebuffer_Check(obj)); PyObject *arg = PySequence_GetItem(args,0); // new reference if(!arg) return -1; int ret = 0; pySamplebuffer *self = (pySamplebuffer *)obj; FLEXT_ASSERT(!self->sym && !self->buf); if(pySymbol_Check(arg)) self->sym = pySymbol_AS_SYMBOL(arg); else if(PyString_Check(arg)) self->sym = flext::MakeSymbol(PyString_AS_STRING(arg)); else ret = -1; Py_DECREF(arg); if(self->sym) { flext::buffer *b = new flext::buffer(self->sym); if(b->Ok() && b->Valid()) self->lock = (self->buf = b)->Lock(); else delete b; } return ret; } static PyObject *buffer_repr(PyObject *self) { FLEXT_ASSERT(pySamplebuffer_Check(self)); return (PyObject *)PyString_FromFormat("",pySamplebuffer_AS_STRING(self)); } static long buffer_hash(PyObject *self) { FLEXT_ASSERT(pySamplebuffer_Check(self)); return (long)(((pySamplebuffer *)self)->buf); } static PyObject *buffer_getsymbol(pySamplebuffer* self,void *closure) { if(self->sym) return pySymbol_FromSymbol(self->sym); else { Py_INCREF(Py_None); return Py_None; } } static PyGetSetDef buffer_getseters[] = { {"symbol",(getter)buffer_getsymbol, NULL, NULL}, {NULL} /* Sentinel */ }; static PyObject *buffer_dirty(PyObject *obj) { ((pySamplebuffer *)obj)->dirty = true; Py_INCREF(Py_None); return Py_None; } static PyObject *buffer_resize(PyObject *obj,PyObject *args,PyObject *kwds) { flext::buffer *b = ((pySamplebuffer *)obj)->buf; if(b) { int frames,keep = 1,zero = 1; static char *kwlist[] = {"frames", "keep", "zero", NULL}; if(!PyArg_ParseTupleAndKeywords(args, kwds, "i|ii", kwlist, &frames, &keep, &zero)) return NULL; b->Frames(frames,keep != 0,zero != 0); Py_INCREF(obj); return obj; } else { PyErr_SetString(PyExc_RuntimeError,"Invalid buffer"); return NULL; } } static PyMethodDef buffer_methods[] = { {"dirty", (PyCFunction)buffer_dirty,METH_NOARGS,"Mark buffer as dirty"}, {"resize", (PyCFunction)buffer_resize,METH_VARARGS|METH_KEYWORDS,"Resize buffer"}, {NULL} /* Sentinel */ }; // support the buffer protocol #if PY_VERSION_HEX >= 0x02050000 static Py_ssize_t buffer_readbuffer(PyObject *obj, Py_ssize_t segment, void **ptrptr) #else static int buffer_readbuffer(PyObject *obj, int segment, void **ptrptr) #endif { flext::buffer *b = ((pySamplebuffer *)obj)->buf; ptrptr[0] = b->Data(); return b->Channels()*b->Frames()*sizeof(t_sample); } #if PY_VERSION_HEX >= 0x02050000 static Py_ssize_t buffer_writebuffer(PyObject *obj, Py_ssize_t segment, void **ptrptr) #else static int buffer_writebuffer(PyObject *obj, int segment, void **ptrptr) #endif { flext::buffer *b = ((pySamplebuffer *)obj)->buf; ptrptr[0] = b->Data(); return b->Channels()*b->Frames()*sizeof(t_sample); } #if PY_VERSION_HEX >= 0x02050000 static Py_ssize_t buffer_segcount(PyObject *obj, Py_ssize_t *lenp) #else static int buffer_segcount(PyObject *obj, int *lenp) #endif { flext::buffer *b = ((pySamplebuffer *)obj)->buf; if(lenp) lenp[0] = b->Channels()*b->Frames()*sizeof(t_sample); return 1; } #if PY_VERSION_HEX >= 0x02050000 static Py_ssize_t buffer_charbuffer(PyObject *obj, Py_ssize_t segment, char **ptrptr) #else static int buffer_charbuffer(PyObject *obj, int segment, const char **ptrptr) #endif { flext::buffer *b = ((pySamplebuffer *)obj)->buf; ptrptr[0] = (char *)b->Data(); return b->Channels()*b->Frames()*sizeof(t_sample); } static PyBufferProcs buffer_as_buffer = { buffer_readbuffer, buffer_writebuffer, buffer_segcount, buffer_charbuffer }; static int buffer_length(pySamplebuffer *self) { return self->buf?self->buf->Frames():0; } static PyObject *buffer_item(pySamplebuffer *self, int i) { PyObject *ret; if(self->buf) { if (i < 0 || i >= self->buf->Frames()) { PyErr_SetString(PyExc_IndexError,"Index out of range"); ret = NULL; } else { if(self->buf->Channels() == 1) ret = PyFloat_FromDouble(self->buf->Data()[i]); else { PyErr_SetString(PyExc_NotImplementedError,"Multiple channels not implemented yet"); ret = NULL; } } } else { Py_INCREF(Py_None); ret = Py_None; } return ret; } PyObject *arrayfrombuffer(PyObject *buf,int c,int n) { #ifdef PY_ARRAYS if(arrsupport()) { PyObject *arr; int shape[2]; shape[0] = n; shape[1] = c; #ifdef PY_NUMARRAY arr = (PyObject *)NA_NewAllFromBuffer(c == 1?1:2,shape,numtype,buf,0,0,NA_ByteOrder(),1,1); #else void *data; int len; int err = PyObject_AsWriteBuffer(buf,&data,&len); if(!err) { FLEXT_ASSERT(len <= n*c*sizeof(t_sample)); Py_INCREF(buf); // \todo change to new API! arr = PyArray_FromDimsAndData(c == 1?1:2,shape,numtype,(char *)data); } else { // exception string is already set arr = NULL; } #endif return arr; } else #endif return NULL; } static PyObject *buffer_slice(pySamplebuffer *self,int ilow = 0,int ihigh = 1<<(sizeof(int)*8-2)) { PyObject *ret; #ifdef PY_ARRAYS if(arrsupport()) { if(self->buf) { const int n = self->buf->Frames(); const int c = self->buf->Channels(); if(ilow < 0) ilow += n; if(ilow >= n) ilow = n-1; if(ihigh < 0) ihigh += n; if(ihigh > n) ihigh = n; PyObject *nobj = arrayfrombuffer((PyObject *)self,c,n); if(ilow != 0 || ihigh != n) { ret = PySequence_GetSlice(nobj,ilow,ihigh); Py_DECREF(nobj); } else ret = nobj; } else { Py_INCREF(Py_None); ret = Py_None; } } else #endif { PyErr_SetString(PyExc_RuntimeError,"No numarray support"); ret = NULL; } return ret; } static int buffer_ass_item(pySamplebuffer *self,int i,PyObject *v) { int ret; if(self->buf) { if (i < 0 || i >= self->buf->Frames()) { PyErr_Format(PyExc_IndexError,"Index out of range"); ret = -1; } else { if(self->buf->Channels() == 1) { self->buf->Data()[i] = (t_sample)PyFloat_AsDouble(v); if(PyErr_Occurred()) { // cast to double failed PyErr_SetString(PyExc_TypeError,"Value must be a numarray"); ret = -1; } else { self->dirty = true; ret = 0; } } else { PyErr_SetString(PyExc_NotImplementedError,"Multiple channels not implemented yet"); ret = -1; } } } else ret = -1; return ret; } static int buffer_ass_slice(pySamplebuffer *self,int ilow,int ihigh,PyObject *value) { int ret; #ifdef PY_ARRAYS if(arrsupport()) { if(!value) { PyErr_SetString(PyExc_TypeError,"Object doesn't support item deletion"); ret = -1; } else if(self->buf) { const int n = self->buf->Frames(); const int c = self->buf->Channels(); if(ilow < 0) ilow += n; if(ilow >= n) ilow = n-1; if(ihigh < 0) ihigh += n; if(ihigh > n) ihigh = n; #ifdef PY_NUMARRAY PyArrayObject *out = NA_InputArray(value,numtype,NUM_C_ARRAY); #else PyArrayObject *out = (PyArrayObject *)PyArray_ContiguousFromObject(value,numtype,0,0); #endif if(!out) { PyErr_SetString(PyExc_TypeError,"Assigned object must be a numarray"); ret = -1; } else if(out->nd != 1) { PyErr_SetString(PyExc_NotImplementedError,"Multiple dimensions not supported yet"); ret = -1; } else { int dlen = ihigh-ilow; int slen = out->dimensions[0]; #ifdef PY_NUMARRAY flext::CopySamples(self->buf->Data()+ilow,(t_sample *)NA_OFFSETDATA(out),slen < dlen?slen:dlen); #else flext::CopySamples(self->buf->Data()+ilow,(t_sample *)out->data,slen < dlen?slen:dlen); #endif self->dirty = true; ret = 0; } Py_XDECREF(out); } else { PyErr_SetString(PyExc_ValueError,"Buffer is not assigned"); ret = -1; } } else #endif { PyErr_SetString(PyExc_RuntimeError,"No numarray support"); ret = -1; } return ret; } static PyObject *buffer_concat(pySamplebuffer *self,PyObject *op) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PySequence_Concat(nobj,op); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PyObject *buffer_repeat(pySamplebuffer *self,int rep) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PySequence_Repeat(nobj,rep); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PySequenceMethods buffer_as_seq = { (inquiry)buffer_length, /* inquiry sq_length; __len__ */ (binaryfunc)buffer_concat, /* __add__ */ (intargfunc)buffer_repeat, /* __mul__ */ (intargfunc)buffer_item, /* intargfunc sq_item; __getitem__ */ (intintargfunc)buffer_slice, /* intintargfunc sq_slice; __getslice__ */ (intobjargproc)buffer_ass_item, /* intobjargproc sq_ass_item; __setitem__ */ (intintobjargproc)buffer_ass_slice, /* intintobjargproc sq_ass_slice; __setslice__ */ }; static PyObject *buffer_iter(PyObject *obj) { pySamplebuffer *self = (pySamplebuffer *)obj; PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *it = PyObject_GetIter(nobj); Py_DECREF(nobj); return it; } else return NULL; } static PyObject *buffer_add(pySamplebuffer *self,PyObject *op) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_Add(nobj,op); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PyObject *buffer_subtract(pySamplebuffer *self,PyObject *op) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_Subtract(nobj,op); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PyObject *buffer_multiply(pySamplebuffer *self,PyObject *op) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_Multiply(nobj,op); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PyObject *buffer_divide(pySamplebuffer *self,PyObject *op) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_Divide(nobj,op); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PyObject *buffer_remainder(pySamplebuffer *self,PyObject *op) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_Remainder(nobj,op); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PyObject *buffer_divmod(pySamplebuffer *self,PyObject *op) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_Divmod(nobj,op); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PyObject *buffer_power(pySamplebuffer *self,PyObject *op1,PyObject *op2) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_Power(nobj,op1,op2); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PyObject *buffer_negative(pySamplebuffer *self) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_Negative(nobj); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PyObject *buffer_pos(pySamplebuffer *self) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_Positive(nobj); Py_DECREF(nobj); return ret; } else return NULL; } static PyObject *buffer_absolute(pySamplebuffer *self) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_Absolute(nobj); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static int buffer_coerce(pySamplebuffer **pm, PyObject **pw) { if(pySamplebuffer_Check(*pw)) { Py_INCREF(*pm); Py_INCREF(*pw); return 0; } else return 1; } static PyObject *buffer_inplace_add(pySamplebuffer *self,PyObject *op) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_InPlaceAdd(nobj,op); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PyObject *buffer_inplace_subtract(pySamplebuffer *self,PyObject *op) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_InPlaceSubtract(nobj,op); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PyObject *buffer_inplace_multiply(pySamplebuffer *self,PyObject *op) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_InPlaceMultiply(nobj,op); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PyObject *buffer_inplace_divide(pySamplebuffer *self,PyObject *op) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_InPlaceDivide(nobj,op); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PyObject *buffer_inplace_remainder(pySamplebuffer *self,PyObject *op) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_InPlaceRemainder(nobj,op); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PyObject *buffer_inplace_power(pySamplebuffer *self,PyObject *op1,PyObject *op2) { PyObject *nobj = buffer_slice(self); if(nobj) { PyObject *ret = PyNumber_InPlacePower(nobj,op1,op2); if(ret == nobj) self->dirty = true; Py_DECREF(nobj); return ret; } else return NULL; } static PyNumberMethods buffer_as_number = { (binaryfunc)buffer_add, /*nb_add*/ (binaryfunc)buffer_subtract, /*nb_subtract*/ (binaryfunc)buffer_multiply, /*nb_multiply*/ (binaryfunc)buffer_divide, /*nb_divide*/ (binaryfunc)buffer_remainder, /*nb_remainder*/ (binaryfunc)buffer_divmod, /*nb_divmod*/ (ternaryfunc)buffer_power, /*nb_power*/ (unaryfunc)buffer_negative, (unaryfunc)buffer_pos, /*nb_pos*/ (unaryfunc)buffer_absolute, /* (unaryfunc)buffer_abs, */ 0, //(inquiry)buffer_nonzero, /*nb_nonzero*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ 0, /*nb_and*/ 0, /*nb_xor*/ 0, /*nb_or*/ (coercion)buffer_coerce, /*nb_coerce*/ 0, /*nb_int*/ 0, /*nb_long*/ 0, /*nb_float*/ 0, /*nb_oct*/ 0, /*nb_hex*/ (binaryfunc)buffer_inplace_add, /* nb_inplace_add */ (binaryfunc)buffer_inplace_subtract, /* nb_inplace_subtract */ (binaryfunc)buffer_inplace_multiply, /* nb_inplace_multiply */ (binaryfunc)buffer_inplace_divide, /* nb_inplace_divide */ (binaryfunc)buffer_inplace_remainder, /* nb_inplace_remainder */ (ternaryfunc)buffer_inplace_power, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ 0, /* nb_inplace_rshift */ 0, /* nb_inplace_and */ 0, /* nb_inplace_xor */ 0, /* nb_inplace_or */ // buffer_floor_div, /* nb_floor_divide */ // buffer_div, /* nb_true_divide */ // buffer_inplace_floor_div, /* nb_inplace_floor_divide */ // buffer_inplace_div, /* nb_inplace_true_divide */ }; PyTypeObject pySamplebuffer_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "Buffer", /*tp_name*/ sizeof(pySamplebuffer), /*tp_basicsize*/ 0, /*tp_itemsize*/ buffer_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ buffer_repr, /*tp_repr*/ &buffer_as_number, /*tp_as_number*/ &buffer_as_seq, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ buffer_hash, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ &buffer_as_buffer, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT /*| Py_TPFLAGS_BASETYPE*/, /*tp_flags*/ "Samplebuffer objects", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0 /*buffer_richcompare*/, /* tp_richcompare */ 0, /* tp_weaklistoffset */ buffer_iter, /* tp_iter */ 0, /* tp_iternext */ buffer_methods, /* tp_methods */ 0, /* tp_members */ buffer_getseters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ buffer_init, /* tp_init */ 0, /* tp_alloc */ buffer_new, /* tp_new */ }; // Must have this as a function because the import_array macro in numpy version 1.01 strangely has a return statement included. // Furthermore the import error printout from this macro is ugly, but we accept that for more, waiting for later numpy updates to fix all of this. static void __import_array__() { #ifdef PY_NUMARRAY import_libnumarray(); #else import_array(); #endif } void initsamplebuffer() { #ifdef PY_ARRAYS __import_array__(); if(PyErr_Occurred()) // catch import error PyErr_Clear(); else { // numarray support ok #ifdef PY_NUMARRAY numtype = sizeof(t_sample) == 4?tFloat32:tFloat64; #else numtype = sizeof(t_sample) == 4?PyArray_FLOAT:PyArray_DOUBLE; #endif post(""); post("Python array support enabled"); } #endif if(PyType_Ready(&pySamplebuffer_Type) < 0) FLEXT_ASSERT(false); else Py_INCREF(&pySamplebuffer_Type); }