From 3ce0fb7e8ad57909fadcd4072817d69bc54e3a66 Mon Sep 17 00:00:00 2001 From: Thomas Grill Date: Sun, 13 Mar 2005 04:59:47 +0000 Subject: pydsp: share dsp buffer objects at inplace operation DSP support for py/pyext: new objects pyext~,pyx~,pyext.~,pyx.~ new base class for py and pyext classes preset sys.argv for module loading support for buffer objects (preliminary) py: bang in left inlet now really triggers without arguments fixes for detached operation and single-threaded version little restructuring adjust pd and py files for correct argument passing more optimizations update for new flext callback naming use lock count instead of message queuing to avoid py->py messaging deadlock pyext: fix for inlet count svn path=/trunk/; revision=2624 --- externals/grill/py/package.txt | 2 +- externals/grill/py/pd/script-1.pd | 13 +-- externals/grill/py/pd/sig-1.pd | 28 ++++++ externals/grill/py/pd/sig-2.pd | 23 +++++ externals/grill/py/py.vcproj | 3 + externals/grill/py/readme.txt | 1 + externals/grill/py/scripts/script.py | 12 +-- externals/grill/py/scripts/sig.py | 64 ++++++++++++ externals/grill/py/source/bound.cpp | 6 +- externals/grill/py/source/clmeth.cpp | 59 ++++++++++- externals/grill/py/source/main.cpp | 111 +++++++++----------- externals/grill/py/source/main.h | 47 +++------ externals/grill/py/source/modmeth.cpp | 34 +++---- externals/grill/py/source/py.cpp | 83 +++++++++++---- externals/grill/py/source/pyargs.cpp | 4 +- externals/grill/py/source/pybuffer.cpp | 22 +++- externals/grill/py/source/pydsp.cpp | 179 +++++++++++++++++++++++++++++++++ externals/grill/py/source/pyext.cpp | 104 +++++++++++++------ externals/grill/py/source/pyext.h | 63 +++++++++--- externals/grill/py/source/register.cpp | 18 ++-- 20 files changed, 665 insertions(+), 211 deletions(-) create mode 100644 externals/grill/py/pd/sig-1.pd create mode 100644 externals/grill/py/pd/sig-2.pd create mode 100644 externals/grill/py/scripts/sig.py create mode 100644 externals/grill/py/source/pydsp.cpp diff --git a/externals/grill/py/package.txt b/externals/grill/py/package.txt index 208fe827..cad479b6 100644 --- a/externals/grill/py/package.txt +++ b/externals/grill/py/package.txt @@ -10,6 +10,6 @@ SRCS= \ main.cpp \ py.cpp pyext.cpp modmeth.cpp clmeth.cpp \ register.cpp bound.cpp pyargs.cpp \ - pysymbol.cpp pybuffer.cpp + pysymbol.cpp pybuffer.cpp pydsp.cpp HDRS= pyprefix.h main.h pyext.h pysymbol.h pybuffer.h diff --git a/externals/grill/py/pd/script-1.pd b/externals/grill/py/pd/script-1.pd index b748857d..27b63d43 100644 --- a/externals/grill/py/pd/script-1.pd +++ b/externals/grill/py/pd/script-1.pd @@ -1,4 +1,4 @@ -#N canvas 297 17 688 530 12; +#N canvas 297 17 692 534 12; #X obj 39 278 print; #X obj 345 251 print; #X msg 499 149 freakhole; @@ -8,16 +8,16 @@ #X msg 102 367 0 1 2 3 4; #X msg 197 367 5 67 3; #X obj 350 456 print; -#X obj 326 365 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1 +#X obj 316 358 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1 -1; #X obj 515 455 print; -#X obj 514 386 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1 +#X obj 484 381 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1 -1; #X msg 188 204 1 3; #X msg 345 155 help; -#X msg 360 327 set ret1; -#X msg 379 351 set ret2; -#X text 434 326 functions can be set; +#X msg 350 320 set ret1; +#X msg 369 344 set ret2; +#X text 424 319 functions can be set; #X msg 421 120 somewhere_past_mars; #X text 152 101 reload with new arguments; #X msg 40 104 reload 1 2 3; @@ -37,6 +37,7 @@ file.; #X text 235 32 http://grrrr.org/ext; #X msg 509 178 a b c; #X text 556 181 too many args; +#X text 505 372 just trigger without arguments; #X connect 2 0 25 1; #X connect 3 0 21 1; #X connect 4 0 21 1; diff --git a/externals/grill/py/pd/sig-1.pd b/externals/grill/py/pd/sig-1.pd new file mode 100644 index 00000000..aaba2fc7 --- /dev/null +++ b/externals/grill/py/pd/sig-1.pd @@ -0,0 +1,28 @@ +#N canvas 56 67 651 303 12; +#X obj 56 234 dac~; +#X msg 523 211 \; pd dsp 1; +#X obj 524 184 loadbang; +#X obj 194 114 hsl 128 15 0.01 1 1 1 empty empty gain -2 -6 0 8 -225271 +-1 -1 4400 1; +#X obj 89 116 noise~; +#X msg 28 117 reload; +#X obj 16 13 cnv 15 600 40 empty empty py/pyext 10 22 0 24 -260818 +-1 0; +#X text 213 32 http://grrrr.org/ext; +#X text 213 16 Python script objects \, (C)2003-2005 Thomas Grill; +#X text 17 66 This demonstrates signal support. See the sig.py file. +; +#X obj 191 131 nbx 5 14 0.001 1 1 0 empty empty empty 0 -6 0 10 -225271 +-1 -1 0.0493075 256; +#X obj 67 181 pyext~ 0 0 1 1 sig gain; +#X msg 192 148 set gain \$1; +#X text 123 202 message inlets \, outlets; +#X text 123 217 signal inlets \, outlets; +#X connect 2 0 1 0; +#X connect 3 0 10 0; +#X connect 4 0 11 0; +#X connect 5 0 11 0; +#X connect 10 0 12 0; +#X connect 11 0 0 0; +#X connect 11 0 0 1; +#X connect 12 0 11 0; diff --git a/externals/grill/py/pd/sig-2.pd b/externals/grill/py/pd/sig-2.pd new file mode 100644 index 00000000..0e641600 --- /dev/null +++ b/externals/grill/py/pd/sig-2.pd @@ -0,0 +1,23 @@ +#N canvas 56 67 659 311 12; +#X obj 121 246 dac~; +#X msg 523 211 \; pd dsp 1; +#X obj 524 184 loadbang; +#X obj 266 134 hsl 128 15 0 1 0 1 empty empty pan -2 -6 0 8 -225271 +-1 -1 6500 1; +#X obj 92 126 noise~; +#X msg 31 127 reload; +#X obj 16 13 cnv 15 600 40 empty empty py/pyext 10 22 0 24 -260818 +-1 0; +#X text 213 32 http://grrrr.org/ext; +#X text 213 16 Python script objects \, (C)2003-2005 Thomas Grill; +#X text 17 66 This demonstrates signal support. See the sig.py file. +; +#X obj 92 179 pyext~ 1 0 1 2 sig pan; +#X text 185 202 message inlets \, outlets; +#X text 183 218 signal inlets \, outlets; +#X connect 2 0 1 0; +#X connect 3 0 10 1; +#X connect 4 0 10 0; +#X connect 5 0 10 0; +#X connect 10 0 0 0; +#X connect 10 1 0 1; diff --git a/externals/grill/py/py.vcproj b/externals/grill/py/py.vcproj index 1541f6b0..328c89f0 100644 --- a/externals/grill/py/py.vcproj +++ b/externals/grill/py/py.vcproj @@ -1131,6 +1131,9 @@ + + py messaging problem with lock count instead of message queuing - ADD: buffer handling with optional numarray support (if present) +- ADD: new objects for dsp processing: pyext~,pyx~,pyext.~,pyx.~ 0.1.4: - ADD: better (and independent) handling of inlet and outlet count (as class variables or dynamically initialized in __init__) diff --git a/externals/grill/py/scripts/script.py b/externals/grill/py/scripts/script.py index 88f7afd2..ff41730f 100644 --- a/externals/grill/py/scripts/script.py +++ b/externals/grill/py/scripts/script.py @@ -28,17 +28,11 @@ def strlen(arg): def strcat(*args): """Concatenate several symbols""" - s = "" - for si in args: - s += str(si) - return s - + return reduce(lambda a,b: a+str(b), args,"") def addall(*args): # variable argument list - s = 0 - for si in args: - s += si - return s + """Add a couple of numbers""" + return reduce(lambda a,b: a+b, args,0) def ret1(): diff --git a/externals/grill/py/scripts/sig.py b/externals/grill/py/scripts/sig.py new file mode 100644 index 00000000..8dccf5fe --- /dev/null +++ b/externals/grill/py/scripts/sig.py @@ -0,0 +1,64 @@ +# py/pyext - python script objects for PD and MaxMSP +# +# Copyright (c) 2002-2005 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. +# + +"""This is an example script for the py/pyext signal support. + +For numarray see http://numeric.scipy.org +It will probably once be replaced by Numeric(3) +""" + +try: + import pyext +except: + print "ERROR: This script must be loaded by the PD/Max py/pyext external" + +try: + import psyco + psyco.full() + print "Using JIT compilation" +except: + # don't care + pass + +import sys,math + +try: + import numarray +except: + print "Failed importing numarray module:",sys.exc_value + + +class gain(pyext._class): + """Just a simple gain stage""" + + gain = 0 + + def _signal(self): + # Multiply input vector by gain and copy to output + self._outvec(0)[:] = self._invec(0)*self.gain + + +class pan(pyext._class): + """Stereo panning""" + + def __init__(self): + self.float_1(0.5) + + def float_1(self,pos): + """pos ranges from 0 to 1""" + x = pos*math.pi/2 + self.fl = math.cos(x) + self.fr = math.sin(x) + + def _signal(self): + # Multiply input vector by gain and copy to output + iv = self._invec(0) + # first process right output channel because left one could be + # identical to input + # we could also test with 'self._outvec(1)[:] is iv' + self._outvec(1)[:] = iv*self.fr + self._outvec(0)[:] = iv*self.fl diff --git a/externals/grill/py/source/bound.cpp b/externals/grill/py/source/bound.cpp index 5690e0b1..1d10c2d3 100644 --- a/externals/grill/py/source/bound.cpp +++ b/externals/grill/py/source/bound.cpp @@ -62,7 +62,7 @@ struct bounddata bool pyext::boundmeth(flext_base *th,t_symbol *sym,int argc,t_atom *argv,void *data) { bounddata *obj = (bounddata *)data; - py *pyth = static_cast(th); + pyext *pyth = static_cast(th); PyThreadState *state = pyth->PyLock(); @@ -92,7 +92,7 @@ PyObject *pyext::pyext_bind(PyObject *,PyObject *args) post("py/pyext - Wrong argument types!"); } else { - py *th = GetThis(self); + pyext *th = GetThis(self); FLEXT_ASSERT(th); const t_symbol *recv = pyObject_AsSymbol(name); @@ -134,7 +134,7 @@ PyObject *pyext::pyext_unbind(PyObject *,PyObject *args) post("py/pyext - Wrong argument types!"); } else { - py *th = GetThis(self); + pyext *th = GetThis(self); FLEXT_ASSERT(th); const t_symbol *recv = pyObject_AsSymbol(name); diff --git a/externals/grill/py/source/clmeth.cpp b/externals/grill/py/source/clmeth.cpp index 6062eda9..cfd8604a 100644 --- a/externals/grill/py/source/clmeth.cpp +++ b/externals/grill/py/source/clmeth.cpp @@ -29,6 +29,9 @@ PyMethodDef pyext::meth_tbl[] = { "_stop", pyext::pyext_stop, METH_VARARGS,"Stop running threads" }, #endif { "_isthreaded", pyext::pyext_isthreaded, METH_O,"Query whether threading is enabled" }, + + { "_invec", pyext::pyext_invec, METH_VARARGS,"Get input vector" }, + { "_outvec", pyext::pyext_outvec, METH_VARARGS,"Get output vector" }, {NULL, NULL, 0, NULL} /* Sentinel */ }; @@ -36,7 +39,7 @@ PyMethodDef pyext::attr_tbl[] = { { "__setattr__", pyext::pyext_setattr, METH_VARARGS,"Set class attribute" }, { "__getattr__", pyext::pyext_getattr, METH_VARARGS,"Get class attribute" }, - { NULL, NULL,0,NULL }, + { NULL, NULL,0,NULL }, }; @@ -174,6 +177,9 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) if(lst) { int o = PyInt_AsLong(outl); if(o >= 1 && o <= ext->Outlets()) { + // offset outlet by signal outlets + o += ext->sigoutlets; + // by using the queue there is no immediate call of the next object // deadlock would occur if this was another py/pyext object! if(lst->Count() && IsSymbol((*lst)[0])) @@ -195,7 +201,10 @@ PyObject *pyext::pyext_outlet(PyObject *,PyObject *args) if(!tp) Py_DECREF(val); } - if(!ok) post("pyext - Syntax: _outlet(self,outlet,args...)"); + if(!ok) { + PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _outlet(self,outlet,args...)"); + return NULL; + } Py_INCREF(Py_None); return Py_None; @@ -211,7 +220,8 @@ PyObject *pyext::pyext_detach(PyObject *,PyObject *args) int val; if(!PyArg_ParseTuple(args, "Oi:pyext_detach",&self,&val)) { // handle error - post("pyext - Syntax: _detach(self,[0/1])"); + PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _detach(self,[0/1])"); + return NULL; } else { pyext *ext = GetThis(self); @@ -229,7 +239,8 @@ PyObject *pyext::pyext_stop(PyObject *,PyObject *args) int val = -1; if(!PyArg_ParseTuple(args, "O|i:pyext_stop",&self,&val)) { // handle error - post("pyext - Syntax: _stop(self,{wait time}"); + PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _stop(self,{wait time})"); + return NULL; } else { pyext *ext = GetThis(self); @@ -305,12 +316,50 @@ PyObject *pyext::pyext_tocanvas(PyObject *,PyObject *args) if(!tp) Py_DECREF(val); } - if(!ok) post("pyext - Syntax: _tocanvas(self,args...)"); + if(!ok) { + PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _tocanvas(self,args...)"); + return NULL; + } Py_INCREF(Py_None); return Py_None; } #endif +PyObject *pyext::pyext_invec(PyObject *,PyObject *args) +{ + PyObject *self; + int val = -1; + if(!PyArg_ParseTuple(args, "O|i:pyext_invec",&self,&val)) { + // handle error + PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _invec(self,inlet)"); + return NULL; + } + else { + pyext *ext = GetThis(self); + PyObject *b = ext->GetSig(val,true); + if(b) return b; + } + Py_INCREF(Py_None); + return Py_None; +} +PyObject *pyext::pyext_outvec(PyObject *,PyObject *args) +{ + PyObject *self; + int val = -1; + if(!PyArg_ParseTuple(args, "O|i:pyext_outvec",&self,&val)) { + // handle error + PyErr_SetString(PyExc_SyntaxError,"pyext - Syntax: _outvec(self,inlet)"); + return NULL; + } + else { + pyext *ext = GetThis(self); + PyObject *b = ext->GetSig(val,false); + if(b) return b; + } + + Py_INCREF(Py_None); + return Py_None; +} diff --git a/externals/grill/py/source/main.cpp b/externals/grill/py/source/main.cpp index e32b9ba8..aa234cc4 100644 --- a/externals/grill/py/source/main.cpp +++ b/externals/grill/py/source/main.cpp @@ -13,7 +13,7 @@ WARRANTIES, see the file, "license.txt," in this distribution. static PyMethodDef StdOut_Methods[] = { - { "write", py::StdOut_Write, 1 }, + { "write", pybase::StdOut_Write, 1 }, { NULL, NULL, } }; @@ -27,9 +27,9 @@ static PyInterpreterState *pymain = NULL; static PyThreadState *pythrmain = NULL; static PyThrMap pythrmap; -int py::lockcount = 0; +int pybase::lockcount = 0; -PyThreadState *py::FindThreadState() +PyThreadState *pybase::FindThreadState() { flext::thrid_t id = flext::GetThreadId(); PyThrMap::iterator it = pythrmap.find(id); @@ -43,7 +43,7 @@ PyThreadState *py::FindThreadState() return it->second; } -void py::FreeThreadState() +void pybase::FreeThreadState() { flext::thrid_t id = flext::GetThreadId(); PyThrMap::iterator it = pythrmap.find(id); @@ -59,10 +59,16 @@ void py::FreeThreadState() #endif +PyObject *pybase::module_obj = NULL; +PyObject *pybase::module_dict = NULL; + +PyObject *pybase::emptytuple = NULL; + + void initsymbol(); void initsamplebuffer(); -void py::lib_setup() +void pybase::lib_setup() { post(""); post("------------------------------------------------"); @@ -137,10 +143,13 @@ void py::lib_setup() initsamplebuffer(); PyModule_AddObject(module_obj,"Buffer",(PyObject *)&pySamplebuffer_Type); + emptytuple = PyTuple_New(0); + // ------------------------------------------------------------- FLEXT_SETUP(pyobj); FLEXT_SETUP(pyext); + FLEXT_DSP_SETUP(pydsp); #ifdef FLEXT_THREADS // release global lock @@ -151,24 +160,10 @@ void py::lib_setup() post(""); } -FLEXT_LIB_SETUP(py,py::lib_setup) +FLEXT_LIB_SETUP(py,pybase::lib_setup) -PyObject *py::module_obj = NULL; -PyObject *py::module_dict = NULL; - - -void py::Setup(t_classid c) -{ - FLEXT_CADDMETHOD_(c,0,"doc",m_doc); - FLEXT_CADDMETHOD_(c,0,"dir",m_dir); -#ifdef FLEXT_THREADS - FLEXT_CADDATTR_VAR1(c,"detach",detach); - FLEXT_CADDMETHOD_(c,0,"stop",m_stop); -#endif -} - -py::py() +pybase::pybase() : module(NULL),detach(0) #ifdef FLEXT_THREADS , shouldexit(false),thrcount(0),stoptick(0) @@ -177,23 +172,16 @@ py::py() PyThreadState *state = PyLock(); Py_INCREF(module_obj); PyUnlock(state); - -#ifdef FLEXT_THREADS - FLEXT_ADDTIMER(stoptmr,tick); - - // launch thread worker - FLEXT_CALLMETHOD(threadworker); -#endif } -py::~py() +pybase::~pybase() { PyThreadState *state = PyLock(); Py_XDECREF(module_obj); PyUnlock(state); } -void py::Exit() +void pybase::Exit() { #ifdef FLEXT_THREADS shouldexit = true; @@ -204,16 +192,15 @@ void py::Exit() Sleep(PY_STOP_TICK*0.001f); if(thrcount) { // Wait forever - post("%s - Waiting for thread termination!",thisName()); + post("py/pyext - Waiting for thread termination!"); while(thrcount) Sleep(PY_STOP_TICK*0.001f); - post("%s - Okay, all threads have terminated",thisName()); + post("py/pyext - Okay, all threads have terminated"); } } #endif - flext_base::Exit(); } -void py::GetDir(PyObject *obj,AtomList &lst) +void pybase::GetDir(PyObject *obj,AtomList &lst) { if(obj) { PyThreadState *state = PyLock(); @@ -227,7 +214,7 @@ void py::GetDir(PyObject *obj,AtomList &lst) lst = *l; delete l; } else - post("%s - %s: List could not be created",thisName(),GetString(thisTag())); + post("py/pyext - Argument list could not be created"); Py_DECREF(pvar); } @@ -235,15 +222,15 @@ void py::GetDir(PyObject *obj,AtomList &lst) } } -void py::m__dir(PyObject *obj) +void pybase::m__dir(PyObject *obj) { AtomList lst; GetDir(obj,lst); // dump dir to attribute outlet - ToOutAnything(GetOutAttr(),thisTag(),lst.Count(),lst.Atoms()); + DumpOut(NULL,lst.Count(),lst.Atoms()); } -void py::m__doc(PyObject *obj) +void pybase::m__doc(PyObject *obj) { if(obj) { PyThreadState *state = PyLock(); @@ -279,19 +266,19 @@ void py::m__doc(PyObject *obj) } } -void py::m_click() +void pybase::OpenEditor() { // this should once open the editor.... } -void py::SetArgs(int argc,const t_atom *argv) +void pybase::SetArgs(int argc,const t_atom *argv) { // script arguments char **sargv = new char *[argc+1]; for(int i = 0; i <= argc; ++i) { sargv[i] = new char[256]; if(!i) - strcpy(sargv[i],thisName()); + strcpy(sargv[i],"py/pyext"); else GetAString(argv[i-1],sargv[i],255); } @@ -303,7 +290,7 @@ void py::SetArgs(int argc,const t_atom *argv) delete[] sargv; } -void py::ImportModule(const char *name) +void pybase::ImportModule(const char *name) { if(!name) return; @@ -316,7 +303,7 @@ void py::ImportModule(const char *name) dict = PyModule_GetDict(module); } -void py::UnimportModule() +void pybase::UnimportModule() { if(!module) return; @@ -332,7 +319,7 @@ void py::UnimportModule() dict = NULL; } -void py::ReloadModule() +void pybase::ReloadModule() { if(module) { PyObject *newmod = PyImport_ReloadModule(module); @@ -348,10 +335,10 @@ void py::ReloadModule() } } else - post("%s - No module to reload",thisName()); + post("py/pyext - No module to reload"); } -void py::GetModulePath(const char *mod,char *dir,int len) +void pybase::GetModulePath(const char *mod,char *dir,int len) { #if FLEXT_SYS == FLEXT_SYS_PD // uarghh... pd doesn't show its path for extra modules @@ -394,7 +381,7 @@ void py::GetModulePath(const char *mod,char *dir,int len) #endif } -void py::AddToPath(const char *dir) +void pybase::AddToPath(const char *dir) { if(dir && *dir) { PyObject *pobj = PySys_GetObject("path"); @@ -410,12 +397,12 @@ void py::AddToPath(const char *dir) static const t_symbol *sym_response = flext::MakeSymbol("response"); -void py::Respond(bool b) +void pybase::Respond(bool b) { if(respond) { t_atom a; SetBool(a,b); - ToOutAnything(GetOutAttr(),sym_response,1,&a); + DumpOut(sym_response,1,&a); } } @@ -424,7 +411,7 @@ void py::Respond(bool b) static PyObject *output = NULL; // post to the console -PyObject* py::StdOut_Write(PyObject* self, PyObject* args) +PyObject* pybase::StdOut_Write(PyObject* self, PyObject* args) { // should always be a tuple FLEXT_ASSERT(PyTuple_Check(args)); @@ -475,7 +462,7 @@ public: PyObject *fun,*args; }; -bool py::gencall(PyObject *pmeth,PyObject *pargs) +bool pybase::gencall(PyObject *pmeth,PyObject *pargs) { bool ret = false; @@ -494,18 +481,18 @@ bool py::gencall(PyObject *pmeth,PyObject *pargs) case 2: // each call a new thread if(!shouldexit) { - ret = FLEXT_CALLMETHOD_X(work_wrapper,new work_data(pmeth,pargs)); - if(!ret) post("%s - Failed to launch thread!",thisName()); + ret = thrcall(new work_data(pmeth,pargs)); + if(!ret) post("py/pyext - Failed to launch thread!"); } break; #endif default: - post("%s - Unknown detach mode",thisName()); + post("py/pyext - Unknown detach mode"); } return ret; } -void py::work_wrapper(void *data) +void pybase::work_wrapper(void *data) { FLEXT_ASSERT(data); @@ -528,7 +515,7 @@ void py::work_wrapper(void *data) } #ifdef FLEXT_THREADS -bool py::qucall(PyObject *fun,PyObject *args) +bool pybase::qucall(PyObject *fun,PyObject *args) { FifoEl *el = qufifo.New(); el->Set(fun,args); @@ -537,7 +524,7 @@ bool py::qucall(PyObject *fun,PyObject *args) return true; } -void py::threadworker() +void pybase::threadworker() { FifoEl *el; PyThreadState *state; @@ -573,7 +560,7 @@ void py::threadworker() #endif #if FLEXT_SYS == FLEXT_SYS_MAX -short py::patcher_myvol(t_patcher *x) +short pybase::patcher_myvol(t_patcher *x) { t_box *w; if (x->p_vol) @@ -585,12 +572,12 @@ short py::patcher_myvol(t_patcher *x) } #endif -bool py::collect() +bool pybase::collect() { if(gcollect) { - PyObject *args = PyTuple_New(0); - PyObject *ret = PyObject_Call(gcollect,args,NULL); - Py_DECREF(args); + Py_INCREF(emptytuple); + PyObject *ret = PyObject_Call(gcollect,emptytuple,NULL); + Py_DECREF(emptytuple); if(ret) { #ifdef FLEXT_DEBUG int refs = PyInt_AsLong(ret); diff --git a/externals/grill/py/source/main.h b/externals/grill/py/source/main.h index 7079dc2f..c76d43fa 100644 --- a/externals/grill/py/source/main.h +++ b/externals/grill/py/source/main.h @@ -41,23 +41,25 @@ public: typedef PooledFifo PyFifo; -class py: - public flext_base -{ - FLEXT_HEADER_S(py,flext_base,Setup) +class pybase + : public flext +{ public: - py(); - ~py(); - static void lib_setup(); + pybase(); + virtual ~pybase(); - virtual void Exit(); + void Exit(); static PyObject *MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet = -1,bool withself = false); static AtomList *GetPyArgs(PyObject *pValue,PyObject **self = NULL); + static void lib_setup(); + protected: + virtual void DumpOut(const t_symbol *sym,int argc,const t_atom *argv) = 0; + void m__dir(PyObject *obj); void m__doc(PyObject *obj); @@ -83,12 +85,15 @@ protected: void Reregister(const char *reg); virtual void Reload() = 0; + void OpenEditor(); void Respond(bool b); static bool IsAnything(const t_symbol *s) { return s && s != sym_float && s != sym_int && s != sym_symbol && s != sym_list && s != sym_pointer; } enum retval { nothing,atom,sequ }; + static PyObject *emptytuple; + // --- module stuff ----- static PyObject *module_obj,*module_dict; @@ -131,6 +136,7 @@ protected: int detach; bool gencall(PyObject *fun,PyObject *args); + virtual bool thrcall(void *data) = 0; virtual bool callpy(PyObject *fun,PyObject *args) = 0; #if FLEXT_SYS == FLEXT_SYS_MAX @@ -139,7 +145,7 @@ protected: static bool collect(); -private: +protected: void work_wrapper(void *data); @@ -151,10 +157,6 @@ private: static PyThreadState *FindThreadState(); static void FreeThreadState(); - - FLEXT_THREAD_X(work_wrapper) -#else - FLEXT_CALLBACK_X(work_wrapper) #endif public: @@ -189,25 +191,6 @@ public: #endif static PyObject* StdOut_Write(PyObject* Self, PyObject* Args); - -protected: - - virtual void m_click(); - - static void Setup(t_classid c); - - // callbacks - FLEXT_ATTRVAR_I(detach) - FLEXT_ATTRVAR_B(respond) - FLEXT_CALLBACK_V(m_stop) - FLEXT_CALLBACK(m_dir) - FLEXT_CALLGET_V(mg_dir) - FLEXT_CALLBACK(m_doc) - -#ifdef FLEXT_THREADS - FLEXT_CALLBACK_T(tick) - FLEXT_THREAD(threadworker) -#endif }; #endif diff --git a/externals/grill/py/source/modmeth.cpp b/externals/grill/py/source/modmeth.cpp index 4054d3ce..bbbe64b1 100644 --- a/externals/grill/py/source/modmeth.cpp +++ b/externals/grill/py/source/modmeth.cpp @@ -12,24 +12,24 @@ WARRANTIES, see the file, "license.txt," in this distribution. // function table for module -PyMethodDef py::func_tbl[] = +PyMethodDef pybase::func_tbl[] = { - { "_send", py::py_send, METH_VARARGS,"Send message to a named object" }, + { "_send", pybase::py_send, METH_VARARGS,"Send message to a named object" }, #ifdef FLEXT_THREADS - { "_priority", py::py_priority, METH_VARARGS,"Set priority of current thread" }, + { "_priority", pybase::py_priority, METH_VARARGS,"Set priority of current thread" }, #endif - { "_samplerate", py::py_samplerate, METH_NOARGS,"Get system sample rate" }, - { "_blocksize", py::py_blocksize, METH_NOARGS,"Get system block size" }, + { "_samplerate", pybase::py_samplerate, METH_NOARGS,"Get system sample rate" }, + { "_blocksize", pybase::py_blocksize, METH_NOARGS,"Get system block size" }, #if FLEXT_SYS == FLEXT_SYS_PD - { "_getvalue", py::py_getvalue, METH_VARARGS,"Get value of a 'value' object" }, - { "_setvalue", py::py_setvalue, METH_VARARGS,"Set value of a 'value' object" }, + { "_getvalue", pybase::py_getvalue, METH_VARARGS,"Get value of a 'value' object" }, + { "_setvalue", pybase::py_setvalue, METH_VARARGS,"Set value of a 'value' object" }, #endif {NULL, NULL, 0, NULL} // sentinel }; -const char *py::py_doc = +const char *pybase::py_doc = "py/pyext - python external object for PD and Max/MSP, (C)2002-2005 Thomas Grill\n" "\n" "This is the pyext module. Available function:\n" @@ -45,7 +45,7 @@ const char *py::py_doc = #ifdef FLEXT_THREADS -void py::tick(void *) +void pybase::tick(void *) { Lock(); @@ -57,7 +57,7 @@ void py::tick(void *) else { // still active threads if(!--stoptick) { - post("%s - Threads couldn't be stopped entirely - %i remaining",thisName(),thrcount); + post("py/pyext - Threads couldn't be stopped entirely - %i remaining",thrcount); shouldexit = false; } else @@ -69,7 +69,7 @@ void py::tick(void *) } #endif -void py::m_stop(int argc,const t_atom *argv) +void pybase::m_stop(int argc,const t_atom *argv) { #ifdef FLEXT_THREADS if(thrcount) { @@ -93,17 +93,17 @@ void py::m_stop(int argc,const t_atom *argv) #endif } -PyObject *py::py_samplerate(PyObject *self,PyObject *args) +PyObject *pybase::py_samplerate(PyObject *self,PyObject *args) { return PyFloat_FromDouble(sys_getsr()); } -PyObject *py::py_blocksize(PyObject *self,PyObject *args) +PyObject *pybase::py_blocksize(PyObject *self,PyObject *args) { return PyLong_FromLong(sys_getblksize()); } -PyObject *py::py_send(PyObject *,PyObject *args) +PyObject *pybase::py_send(PyObject *,PyObject *args) { // should always be a tuple FLEXT_ASSERT(PyTuple_Check(args)); @@ -153,7 +153,7 @@ PyObject *py::py_send(PyObject *,PyObject *args) } #ifdef FLEXT_THREADS -PyObject *py::py_priority(PyObject *self,PyObject *args) +PyObject *pybase::py_priority(PyObject *self,PyObject *args) { int val; if(!PyArg_ParseTuple(args, "i:py_priority", &val)) { @@ -168,7 +168,7 @@ PyObject *py::py_priority(PyObject *self,PyObject *args) #endif #if FLEXT_SYS == FLEXT_SYS_PD -PyObject *py::py_getvalue(PyObject *self,PyObject *args) +PyObject *pybase::py_getvalue(PyObject *self,PyObject *args) { FLEXT_ASSERT(PyTuple_Check(args)); @@ -195,7 +195,7 @@ PyObject *py::py_getvalue(PyObject *self,PyObject *args) return ret; } -PyObject *py::py_setvalue(PyObject *self,PyObject *args) +PyObject *pybase::py_setvalue(PyObject *self,PyObject *args) { FLEXT_ASSERT(PyTuple_Check(args)); diff --git a/externals/grill/py/source/py.cpp b/externals/grill/py/source/py.cpp index 3856739e..bf3ff8fb 100644 --- a/externals/grill/py/source/py.cpp +++ b/externals/grill/py/source/py.cpp @@ -11,19 +11,23 @@ WARRANTIES, see the file, "license.txt," in this distribution. #include "main.h" -class pyobj: - public py +class pyobj + : public pybase + , public flext_base { - FLEXT_HEADER_S(pyobj,py,Setup) + FLEXT_HEADER_S(pyobj,flext_base,Setup) public: pyobj(int argc,const t_atom *argv); ~pyobj(); protected: - bool m_method_(int n,const t_symbol *s,int argc,const t_atom *argv); + virtual void Exit(); - bool work(const t_symbol *s,int argc,const t_atom *argv); + virtual bool CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv); + virtual void CbClick(); + + void m_help(); void m_reload(); void m_reload_(int argc,const t_atom *argv); @@ -31,16 +35,14 @@ protected: void m_dir_() { m__dir(function); } void m_doc_() { m__doc(function); } - virtual void m_help(); - // methods for python arguments void callwork(const t_symbol *s,int argc,const t_atom *argv); - void m_bang() { callwork(sym_bang,0,NULL); } - void m_py_list(int argc,const t_atom *argv) { callwork(sym_list,argc,argv); } - void m_py_float(int argc,const t_atom *argv) { callwork(sym_float,argc,argv); } - void m_py_int(int argc,const t_atom *argv) { callwork(sym_int,argc,argv); } - void m_py_any(const t_symbol *s,int argc,const t_atom *argv) { callwork(s,argc,argv); } + inline void m_bang() { callwork(NULL,0,NULL); } + inline void m_py_list(int argc,const t_atom *argv) { callwork(sym_list,argc,argv); } + inline void m_py_float(int argc,const t_atom *argv) { callwork(sym_float,argc,argv); } + inline void m_py_int(int argc,const t_atom *argv) { callwork(sym_int,argc,argv); } + inline void m_py_any(const t_symbol *s,int argc,const t_atom *argv) { callwork(s,argc,argv); } const t_symbol *funname; PyObject *function; @@ -51,12 +53,16 @@ protected: void SetFunction(const char *func); void ResetFunction(); + virtual bool thrcall(void *data); + virtual void DumpOut(const t_symbol *sym,int argc,const t_atom *argv); + private: virtual bool callpy(PyObject *fun,PyObject *args); static void Setup(t_classid c); + FLEXT_CALLBACK(m_help) FLEXT_CALLBACK(m_bang) FLEXT_CALLBACK(m_reload) FLEXT_CALLBACK_V(m_reload_) @@ -69,10 +75,20 @@ private: FLEXT_CALLBACK_V(m_py_int) FLEXT_CALLBACK_A(m_py_any) + // callbacks + FLEXT_ATTRVAR_I(detach) + FLEXT_ATTRVAR_B(respond) + FLEXT_CALLBACK_V(m_stop) + FLEXT_CALLBACK(m_dir) + FLEXT_CALLGET_V(mg_dir) + FLEXT_CALLBACK(m_doc) + #ifdef FLEXT_THREADS - FLEXT_THREAD_A(work) + FLEXT_CALLBACK_T(tick) + FLEXT_THREAD(threadworker) + FLEXT_THREAD_X(work_wrapper) #else - FLEXT_CALLBACK_A(work) + FLEXT_CALLBACK_X(work_wrapper) #endif }; @@ -81,6 +97,14 @@ FLEXT_LIB_V("py",pyobj) void pyobj::Setup(t_classid c) { + FLEXT_CADDMETHOD_(c,0,"doc",m_doc); + FLEXT_CADDMETHOD_(c,0,"dir",m_dir); +#ifdef FLEXT_THREADS + FLEXT_CADDATTR_VAR1(c,"detach",detach); + FLEXT_CADDMETHOD_(c,0,"stop",m_stop); +#endif + + FLEXT_CADDMETHOD_(c,0,"help",m_help); FLEXT_CADDMETHOD_(c,0,"reload",m_reload_); FLEXT_CADDMETHOD_(c,0,"reload.",m_reload); FLEXT_CADDMETHOD_(c,0,"doc+",m_doc_); @@ -100,11 +124,17 @@ void pyobj::Setup(t_classid c) pyobj::pyobj(int argc,const t_atom *argv): function(NULL),funname(NULL),withfunction(false) { - PyThreadState *state = PyLock(); - AddInAnything(2); AddOutAnything(); +#ifdef FLEXT_THREADS + FLEXT_ADDTIMER(stoptmr,tick); + // launch thread worker + FLEXT_CALLMETHOD(threadworker); +#endif + + PyThreadState *state = PyLock(); + if(argc > 2) SetArgs(argc-2,argv+2); else @@ -163,10 +193,13 @@ pyobj::~pyobj() PyUnlock(state); } +void pyobj::Exit() +{ + pybase::Exit(); + flext_base::Exit(); +} - - -bool pyobj::m_method_(int n,const t_symbol *s,int argc,const t_atom *argv) +bool pyobj::CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv) { if(n == 1) post("%s - no method for type %s",thisName(),GetString(s)); @@ -346,3 +379,15 @@ void pyobj::callwork(const t_symbol *s,int argc,const t_atom *argv) Respond(ret); } + +void pyobj::CbClick() { pybase::OpenEditor(); } + +void pyobj::DumpOut(const t_symbol *sym,int argc,const t_atom *argv) +{ + ToOutAnything(GetOutAttr(),sym?sym:thisTag(),argc,argv); +} + +bool pyobj::thrcall(void *data) +{ + return FLEXT_CALLMETHOD_X(work_wrapper,data); +} diff --git a/externals/grill/py/source/pyargs.cpp b/externals/grill/py/source/pyargs.cpp index a040feac..56b0123a 100644 --- a/externals/grill/py/source/pyargs.cpp +++ b/externals/grill/py/source/pyargs.cpp @@ -29,7 +29,7 @@ static PyObject *MakePyAtom(const t_atom &at) return NULL; } -PyObject *py::MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet,bool withself) +PyObject *pybase::MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet,bool withself) { PyObject *pArgs; @@ -84,7 +84,7 @@ PyObject *py::MakePyArgs(const t_symbol *s,int argc,const t_atom *argv,int inlet return pArgs; } -flext::AtomList *py::GetPyArgs(PyObject *pValue,PyObject **self) +flext::AtomList *pybase::GetPyArgs(PyObject *pValue,PyObject **self) { if(pValue == NULL) return NULL; AtomList *ret = NULL; diff --git a/externals/grill/py/source/pybuffer.cpp b/externals/grill/py/source/pybuffer.cpp index 96f2efae..6e7e571b 100644 --- a/externals/grill/py/source/pybuffer.cpp +++ b/externals/grill/py/source/pybuffer.cpp @@ -178,6 +178,21 @@ static PyObject *buffer_item(pySamplebuffer *self, int i) return ret; } +PyObject *NAFromBuffer(PyObject *buf,int c,int n) +{ +#ifdef PY_NUMARRAY + if(nasupport) { + maybelong shape[2]; + shape[0] = n; + shape[1] = c; + PyArrayObject *na = NA_NewAllFromBuffer(c == 1?1:2,shape,numtype,buf,0,0,NA_ByteOrder(),1,1); + return (PyObject *)na; + } + else +#endif + return NULL; +} + static PyObject *buffer_slice(pySamplebuffer *self,int ilow = 0,int ihigh = 1<<(sizeof(int)*8-2)) { PyObject *ret; @@ -191,10 +206,7 @@ static PyObject *buffer_slice(pySamplebuffer *self,int ilow = 0,int ihigh = 1<<( if(ihigh < 0) ihigh += n; if(ihigh > n) ihigh = n; - maybelong shape[2]; - shape[0] = n; - shape[1] = c; - PyObject *nobj = (PyObject *)NA_NewAllFromBuffer(c == 1?1:2,shape,numtype,(PyObject *)self,0,0,NA_ByteOrder(),1,1); + PyObject *nobj = NAFromBuffer((PyObject *)self,c,n); if(ilow != 0 || ihigh != n) { ret = PySequence_GetSlice(nobj,ilow,ihigh); Py_DECREF(nobj); @@ -657,7 +669,7 @@ void initsamplebuffer() import_libnumarray(); if(PyErr_Occurred()) // catch import error - PyErr_Print(); + PyErr_Clear(); else { // numarray support ok nasupport = true; diff --git a/externals/grill/py/source/pydsp.cpp b/externals/grill/py/source/pydsp.cpp new file mode 100644 index 00000000..b1f50c0d --- /dev/null +++ b/externals/grill/py/source/pydsp.cpp @@ -0,0 +1,179 @@ +/* + +py/pyext - python script object for PD and Max/MSP + +Copyright (c)2002-2005 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. + +*/ + +#include "pyext.h" + +class pydsp + : public pyext +{ + FLEXT_HEADER(pydsp,pyext) +public: + pydsp(int argc,const t_atom *argv); + +protected: + virtual bool CbDsp(); + virtual void CbSignal(); + + virtual bool DoInit(); + virtual void DoExit(); + + virtual PyObject *GetSig(int ix,bool in); + + void NewBuffers(bool update = false); + void FreeBuffers(); + + PyObject *dspfun,*sigfun; + PyObject **buffers; +}; + +FLEXT_LIB_DSP_V("pyext~ pyext.~ pyx~ pyx.~",pydsp) + +pydsp::pydsp(int argc,const t_atom *argv) + : pyext(argc,argv,true) + , dspfun(NULL),sigfun(NULL) +{} + +bool pydsp::DoInit() +{ + if(!pyext::DoInit()) return false; + + if(pyobj) + { + NewBuffers(); + + dspfun = PyObject_GetAttrString(pyobj,"_dsp"); // get ref + if(dspfun && !PyMethod_Check(dspfun)) { + Py_DECREF(dspfun); + dspfun = NULL; + } + sigfun = PyObject_GetAttrString(pyobj,"_signal"); // get ref + if(sigfun && !PyMethod_Check(sigfun)) { + Py_DECREF(sigfun); + sigfun = NULL; + } + } + return true; +} + +void pydsp::DoExit() +{ + if(dspfun) { Py_DECREF(dspfun); dspfun = NULL; } + if(sigfun) { Py_DECREF(sigfun); sigfun = NULL; } + + FreeBuffers(); +} + +PyObject *NAFromBuffer(PyObject *buf,int c,int n); + +void pydsp::NewBuffers(bool update) +{ + int i,n = Blocksize(); + const int ins = CntInSig(),outs = CntOutSig(); + t_sample *const *insigs = InSig(); + t_sample *const *outsigs = OutSig(); + + if(!buffers) { + int cnt = ins+outs; + if(cnt) { + buffers = new PyObject *[cnt]; + memset(buffers,0,cnt*sizeof(*buffers)); + } + } + + for(i = 0; i < ins; ++i) { + if(update) Py_XDECREF(buffers[i]); + PyObject *b = PyBuffer_FromReadWriteMemory(insigs[i],n*sizeof(t_sample)); + buffers[i] = NAFromBuffer(b,1,n); + Py_DECREF(b); + } + for(i = 0; i < outs; ++i) { + if(update) Py_XDECREF(buffers[ins+i]); + if(i < ins && outsigs[i] == insigs[i]) { + // same vectors - share the objects! + buffers[ins+i] = buffers[i]; + Py_XINCREF(buffers[i]); + } + else { + PyObject *b = PyBuffer_FromReadWriteMemory(outsigs[i],n*sizeof(t_sample)); + buffers[ins+i] = NAFromBuffer(b,1,n); + Py_DECREF(b); + } + } +} + +void pydsp::FreeBuffers() +{ + if(buffers) { + int cnt = CntInSig()+CntOutSig(); + for(int i = 0; i < cnt; ++i) Py_XDECREF(buffers[i]); + delete[] buffers; + buffers = NULL; + } +} + +bool pydsp::CbDsp() +{ + if(CntInSig() || CntOutSig()) + { + NewBuffers(true); + + if(dspfun) { + PyThreadState *state = PyLock(); +// Py_INCREF(emptytuple); + PyObject *ret = PyObject_Call(dspfun,emptytuple,NULL); +// Py_DECREF(emptytuple); + if(ret) + Py_DECREF(ret); + else { +#ifdef FLEXT_DEBUG + PyErr_Print(); +#else + PyErr_Clear(); +#endif + } + PyUnlock(state); + } + return true; + } + else + // switch on dsp only if there are signal inlets or outlets + return false; +} + +void pydsp::CbSignal() +{ + if(sigfun) { + PyThreadState *state = PyLock(); +// Py_INCREF(emptytuple); + PyObject *ret = PyObject_Call(sigfun,emptytuple,NULL); +// Py_DECREF(emptytuple); + + if(ret) + Py_DECREF(ret); + else { +#ifdef FLEXT_DEBUG + PyErr_Print(); +#else + PyErr_Clear(); +#endif + } + PyUnlock(state); + } + else + flext_dsp::CbSignal(); +} + +PyObject *pydsp::GetSig(int ix,bool in) +{ + PyObject *r = buffers[in?ix:CntInSig()+ix]; + Py_XINCREF(r); + return r; +} + diff --git a/externals/grill/py/source/pyext.cpp b/externals/grill/py/source/pyext.cpp index 72ae41fd..0ebeab68 100644 --- a/externals/grill/py/source/pyext.cpp +++ b/externals/grill/py/source/pyext.cpp @@ -20,6 +20,15 @@ void pyext::Setup(t_classid c) { sym_get = flext::MakeSymbol("get"); + FLEXT_CADDMETHOD_(c,0,"doc",m_doc); + FLEXT_CADDMETHOD_(c,0,"dir",m_dir); +#ifdef FLEXT_THREADS + FLEXT_CADDATTR_VAR1(c,"detach",detach); + FLEXT_CADDMETHOD_(c,0,"stop",m_stop); +#endif + + FLEXT_CADDMETHOD_(c,0,"help",m_help); + FLEXT_CADDMETHOD_(c,0,"reload",m_reload_); FLEXT_CADDMETHOD_(c,0,"reload.",m_reload); FLEXT_CADDMETHOD_(c,0,"doc+",m_doc_); @@ -95,19 +104,31 @@ void pyext::SetThis() PyObject *pyext::class_obj = NULL; PyObject *pyext::class_dict = NULL; -pyext::pyext(int argc,const t_atom *argv): +pyext::pyext(int argc,const t_atom *argv,bool sig): pyobj(NULL),pythr(NULL), inlets(-1),outlets(-1), + siginlets(0),sigoutlets(0), methname(NULL) { - int apre = 0; +#ifdef FLEXT_THREADS + FLEXT_ADDTIMER(stoptmr,tick); + // launch thread worker + FLEXT_CALLMETHOD(threadworker); +#endif + int apre = 0; if(argc >= apre+2 && CanbeInt(argv[apre]) && CanbeInt(argv[apre+1])) { inlets = GetAInt(argv[apre]); outlets = GetAInt(argv[apre+1]); apre += 2; } + if(sig && argc >= apre+2 && CanbeInt(argv[apre]) && CanbeInt(argv[apre+1])) { + siginlets = GetAInt(argv[apre]); + sigoutlets = GetAInt(argv[apre+1]); + apre += 2; + } + const t_atom *clname = NULL; PyThreadState *state = PyLock(); @@ -162,38 +183,46 @@ pyext::pyext(int argc,const t_atom *argv): if(argc > apre) args(argc-apre,argv+apre); + PyUnlock(state); +} + +bool pyext::Init() +{ + PyThreadState *state = PyLock(); + if(methname) { MakeInstance(); - - if(pyobj) - InitInOut(inlets,outlets); + if(pyobj) InitInOut(inlets,outlets); } - else + else inlets = outlets = 0; - PyUnlock(state); - if(inlets < 0 || outlets < 0) InitProblem(); else { - AddInAnything(1+inlets); + AddInSignal(siginlets); + AddInAnything((siginlets?0:1)+inlets); + AddOutSignal(sigoutlets); AddOutAnything(outlets); } - if(!pyobj) - InitProblem(); + PyUnlock(state); + + return pyobj && flext_dsp::Init(); } -pyext::~pyext() -{ - PyThreadState *state = PyLock(); +void pyext::Exit() +{ + pybase::Exit(); // exit threads + PyThreadState *state = PyLock(); DoExit(); - Unregister("_pyext"); UnimportModule(); PyUnlock(state); + + flext_dsp::Exit(); } bool pyext::DoInit() @@ -230,13 +259,15 @@ void pyext::DoExit() // try to run del to clean up the class instance PyObject *objdel = PyObject_GetAttrString(pyobj,"_del"); if(objdel) { - PyObject *args = PyTuple_New(0); - PyObject *ret = PyObject_Call(objdel,args,NULL); - if(!ret) - post("%s - Could not call _del method",thisName()); - else + Py_INCREF(emptytuple); + PyObject *ret = PyObject_Call(objdel,emptytuple,NULL); + if(ret) Py_DECREF(ret); - Py_DECREF(args); +#ifdef FLEXT_DEBUG + else + post("%s - Could not call _del method",thisName()); +#endif + Py_DECREF(emptytuple); Py_DECREF(objdel); } else @@ -269,7 +300,7 @@ void pyext::InitInOut(int &inl,int &outl) if(inl < 0) { // get number of inlets - inl = 1; + inl = inlets; PyObject *res = PyObject_GetAttrString(pyobj,"_inlets"); // get ref if(res) { if(PyCallable_Check(res)) { @@ -286,7 +317,7 @@ void pyext::InitInOut(int &inl,int &outl) } if(outl < 0) { // get number of outlets - outl = 1; + outl = outlets; PyObject *res = PyObject_GetAttrString(pyobj,"_outlets"); // get ref if(res) { if(PyCallable_Check(res)) { @@ -436,14 +467,12 @@ void pyext::m_set(int argc,const t_atom *argv) } -bool pyext::m_method_(int n,const t_symbol *s,int argc,const t_atom *argv) +bool pyext::CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv) { - bool ret = false; if(pyobj && n >= 1) - ret = work(n,s,argc,argv); + return work(n,s,argc,argv); else - post("%s - no method for type '%s' into inlet %i",thisName(),GetString(s),n); - return ret; + return flext_dsp::CbMethodResort(n,s,argc,argv); } @@ -522,6 +551,10 @@ bool pyext::work(int n,const t_symbol *s,int argc,const t_atom *argv) bool isfloat = s == sym_float && argc == 1; + // offset inlet index by signal inlets + // \note first one is shared with messages! + if(siginlets) n += siginlets-1; + // if float equals an integer, try int_* method if(isfloat && GetAFloat(argv[0]) == GetAInt(argv[0])) { sprintf(str,"int_%i",n); @@ -585,3 +618,18 @@ bool pyext::work(int n,const t_symbol *s,int argc,const t_atom *argv) Respond(ret); return ret; } + +PyObject *pyext::GetSig(int ix,bool in) { return NULL; } + +void pyext::CbClick() { pybase::OpenEditor(); } +bool pyext::CbDsp() { return false; } + +void pyext::DumpOut(const t_symbol *sym,int argc,const t_atom *argv) +{ + ToOutAnything(GetOutAttr(),sym?sym:thisTag(),argc,argv); +} + +bool pyext::thrcall(void *data) +{ + return FLEXT_CALLMETHOD_X(work_wrapper,data); +} diff --git a/externals/grill/py/source/pyext.h b/externals/grill/py/source/pyext.h index f94b2f19..3c6a86b5 100644 --- a/externals/grill/py/source/pyext.h +++ b/externals/grill/py/source/pyext.h @@ -13,14 +13,14 @@ WARRANTIES, see the file, "license.txt," in this distribution. #include "main.h" -class pyext: - public py +class pyext + : public pybase + , public flext_dsp { - FLEXT_HEADER_S(pyext,py,Setup) + FLEXT_HEADER_S(pyext,flext_dsp,Setup) public: - pyext(int argc,const t_atom *argv); - ~pyext(); + pyext(int argc,const t_atom *argv,bool sig = false); static PyObject *pyext__doc__(PyObject *,PyObject *args); static PyObject *pyext__init__(PyObject *,PyObject *args); @@ -38,21 +38,35 @@ public: static PyObject *pyext_stop(PyObject *,PyObject *args); static PyObject *pyext_isthreaded(PyObject *,PyObject *); + static PyObject *pyext_inbuf(PyObject *,PyObject *args); + static PyObject *pyext_invec(PyObject *,PyObject *args); + static PyObject *pyext_outbuf(PyObject *,PyObject *args); + static PyObject *pyext_outvec(PyObject *,PyObject *args); + int Inlets() const { return inlets; } int Outlets() const { return outlets; } protected: - virtual bool m_method_(int n,const t_symbol *s,int argc,const t_atom *argv); + + virtual bool Init(); + virtual void Exit(); + + virtual bool CbMethodResort(int n,const t_symbol *s,int argc,const t_atom *argv); + virtual void CbClick(); + virtual bool CbDsp(); + + virtual void DumpOut(const t_symbol *sym,int argc,const t_atom *argv); bool work(int n,const t_symbol *s,int argc,const t_atom *argv); + void m_help(); + void m_reload(); void m_reload_(int argc,const t_atom *argv); void ms_args(const AtomList &a) { m_reload_(a.Count(),a.Atoms()); } void m_dir_() { m__dir(pyobj); } void mg_dir_(AtomList &lst) { GetDir(pyobj,lst); } void m_doc_() { m__doc(((PyInstanceObject *)pyobj)->in_class->cl_dict); } - virtual void m_help(); void m_get(const t_symbol *s); void m_set(int argc,const t_atom *argv); @@ -60,23 +74,27 @@ protected: const t_symbol *methname; PyObject *pyobj; int inlets,outlets; + int siginlets,sigoutlets; + + virtual void Reload(); + virtual bool DoInit(); + virtual void DoExit(); + + virtual PyObject *GetSig(int ix,bool in); + + static pyext *GetThis(PyObject *self); private: static void Setup(t_classid); - static pyext *GetThis(PyObject *self); void SetThis(); void ClearBinding(); bool MakeInstance(); - bool DoInit(); - void DoExit(); void InitInOut(int &inlets,int &outlets); AtomList args; - virtual void Reload(); - static PyObject *class_obj,*class_dict; static PyMethodDef attr_tbl[],meth_tbl[]; static const char *pyext_doc; @@ -89,6 +107,7 @@ private: bool call(const char *meth,int inlet,const t_symbol *s,int argc,const t_atom *argv); + virtual bool thrcall(void *data); virtual bool callpy(PyObject *fun,PyObject *args); static bool stcallpy(PyObject *fun,PyObject *args); @@ -96,7 +115,9 @@ private: private: static bool boundmeth(flext_base *,t_symbol *sym,int argc,t_atom *argv,void *data); - + + FLEXT_CALLBACK(m_help) + FLEXT_CALLBACK(m_reload) FLEXT_CALLBACK_V(m_reload_) FLEXT_CALLBACK(m_dir_) @@ -108,6 +129,22 @@ private: FLEXT_CALLBACK_S(m_get) FLEXT_CALLBACK_V(m_set) + + // callbacks + FLEXT_ATTRVAR_I(detach) + FLEXT_ATTRVAR_B(respond) + FLEXT_CALLBACK_V(m_stop) + FLEXT_CALLBACK(m_dir) + FLEXT_CALLGET_V(mg_dir) + FLEXT_CALLBACK(m_doc) + +#ifdef FLEXT_THREADS + FLEXT_CALLBACK_T(tick) + FLEXT_THREAD(threadworker) + FLEXT_THREAD_X(work_wrapper) +#else + FLEXT_CALLBACK_X(work_wrapper) +#endif }; #endif diff --git a/externals/grill/py/source/register.cpp b/externals/grill/py/source/register.cpp index ae273955..bc2563ae 100644 --- a/externals/grill/py/source/register.cpp +++ b/externals/grill/py/source/register.cpp @@ -11,7 +11,7 @@ WARRANTIES, see the file, "license.txt," in this distribution. #include "main.h" -void py::Register(const char *regnm) +void pybase::Register(const char *regnm) { if(module) { // add this to module registry @@ -20,7 +20,7 @@ void py::Register(const char *regnm) PyObject *add = Py_BuildValue("[i]",(long)this); if(!reg || !PyList_Check(reg)) { if(PyDict_SetItemString(dict,(char *)regnm,add)) { - post("%s - Could not set registry",thisName()); + post("py/pyext - Could not set registry"); } } else { @@ -29,7 +29,7 @@ void py::Register(const char *regnm) } } -void py::Unregister(const char *regnm) +void pybase::Unregister(const char *regnm) { if(module) { // remove this from module registry @@ -37,11 +37,11 @@ void py::Unregister(const char *regnm) PyObject *reg = PyDict_GetItemString(dict,(char *)regnm); // borrowed!!! PyObject *add = Py_BuildValue("i",(int)this); if(!reg || !PySequence_Check(reg)) - post("%s - Internal error: Registry not found!?",thisName()); + post("py/pyext - Internal error: Registry not found!?"); else { int ix = PySequence_Index(reg,add); if(ix < 0) { - post("%s - Internal error: object not found in registry?!",thisName()); + post("py/pyext - Internal error: object not found in registry?!"); } else { PySequence_DelItem(reg,ix); @@ -51,7 +51,7 @@ void py::Unregister(const char *regnm) } } -void py::Reregister(const char *regnm) +void pybase::Reregister(const char *regnm) { if(module) { // remove this from module registry @@ -59,16 +59,16 @@ void py::Reregister(const char *regnm) PyObject *reg = PyDict_GetItemString(dict,(char *)regnm); // borrowed!!! if(!reg || !PySequence_Check(reg)) - post("%s - Internal error: Registry not found!?",thisName()); + post("py/pyext - Internal error: Registry not found!?"); else { int cnt = PySequence_Size(reg); for(int i = 0; i < cnt; ++i) { PyObject *it = PySequence_GetItem(reg,i); // new reference if(!it || !PyInt_Check(it)) { - post("%s - Internal error: Corrupt registry?!",thisName()); + post("py/pyext - Internal error: Corrupt registry?!"); } else { - py *th = (py *)PyInt_AsLong(it); + pybase *th = (pybase *)PyInt_AsLong(it); th->module = module; th->dict = dict; th->Reload(); -- cgit v1.2.1