/* dyn_py - Python interface to the dyn library Copyright (c)2004-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. */ #define DYNPY_VERSION_MAJOR 0 #define DYNPY_VERSION_MINOR 2 #define DYNPY_VERSION (DYNPY_VERSION_MAJOR*100+DYNPY_VERSION_MINOR) #ifdef _MSC_VER #pragma warning(disable:4267) #endif #include #include #include #include using namespace Py; static Object dynpatcher(const Tuple &a,dyn_id ident = DYN_ID_NONE); static Object dynobject(const Tuple &a,dyn_id ident = DYN_ID_NONE); static Object dynmessage(const Tuple &a,dyn_id ident = DYN_ID_NONE); /*! Convert a Python tuple to PD atoms \note we don't need to check for $-arguments here... symbols will do */ static int tuple2atoms(const Tuple &s,int offs,const t_symbol *&hdr,t_atom *lst,int sz,bool listonly = false) { hdr = listonly?flext::sym_list:NULL; int i = 0,len = s.length(); for(int it = offs; it < len && i < sz; ++it) { const Object &o = s[it]; if(o.isNumeric()) { if(!hdr) hdr = flext::sym_list; float f = (float)Float(o); int fi = (int)f; if(fi == f) flext::SetInt(lst[i++],fi); else flext::SetFloat(lst[i++],f); } else if(pySymbol_Check(o.ptr())) { const t_symbol *s = pySymbol_AS_SYMBOL(o.ptr()); if(!hdr) hdr = s; else flext::SetSymbol(lst[i++],s); } else if(o.isString()) { const t_symbol *s = flext::MakeSymbol(o.as_string().c_str()); if(!hdr) hdr = s; else flext::SetSymbol(lst[i++],s); } else { hdr = NULL; return 0; } } if(hdr == flext::sym_list && i == 1) { if(flext::IsFloat(*lst)) hdr = flext::sym_float; else if(flext::IsInt(*lst)) hdr = flext::sym_int; else if(flext::IsSymbol(*lst)) hdr = flext::sym_symbol; } return i; } static Tuple atoms2tuple(const t_symbol *sym,int argc,const t_atom *argv) { bool hd = sym != flext::sym_list && sym != flext::sym_float; Tuple args(argc+hd?1:0); int o = 0; if(hd) args[o++] = String(flext::GetString(sym)); for(int i = 0; i < argc; ++i,++o) { if(flext::IsFloat(argv[i])) args[o] = Float(flext::GetFloat(argv[i])); else if(flext::IsInt(argv[i])) args[o] = Int(flext::GetInt(argv[i])); else if(flext::IsSymbol(argv[i])) args[o] = asObject(pySymbol_FromSymbol(flext::GetSymbol(argv[i]))); else args[o] = Nothing(); } return args; } typedef std::map ListenMap; // all listeners static ListenMap listen; static void callback(dyn_id ident,int signal,void *data) { if(dyn_GetType(ident) == DYN_TYPE_LISTENER && signal == DYN_SIGNAL_FREE) { // clear listener data ListenMap::iterator it = listen.find(ident); if(it != listen.end()) { delete it->second; listen.erase(it); } } #ifdef FLEXT_DEBUG // flext::post("dyn callback - signal %i, object %x",signal,ident); #endif } class dynIdent : public PythonExtension { public: dyn_id ident; dynIdent(dyn_id id = DYN_ID_NONE): ident(id) {} static void init_type() { behaviors().name("ID"); behaviors().doc("documentation for dyn::Ident" ); add_varargs_method("Patcher", &dynIdent::f_patcher, "Create a new sub patcher"); add_varargs_method("Object", &dynIdent::f_object, "Create a new dyn object"); add_varargs_method("Message", &dynIdent::f_message, "Create a new dyn message object"); add_varargs_method("Free", &dynIdent::f_free, "Free the dyn object"); add_varargs_method("Inlets", &dynIdent::f_inlets, "Get inlet count of a dyn object"); add_varargs_method("Outlets", &dynIdent::f_outlets, "Get outlet count of a dyn object"); add_varargs_method("InletType", &dynIdent::f_inlettype, "Get type of an inlet"); add_varargs_method("OutletType", &dynIdent::f_outlettype, "Get type of an outlet"); add_varargs_method("Connect", &dynIdent::f_connect, "Create a connection between two dyn objects"); add_varargs_method("Send", &dynIdent::f_send, "Send a message to a dyn object"); add_varargs_method("Listen", &dynIdent::f_listen, "Create a listener to messages of a dyn object"); } private: void Check() const { if(ident == DYN_ID_NONE) throw RuntimeError("Dyn object not initialized"); } Object f_patcher(const Tuple &a) { Check(); return dynpatcher(a,ident); } Object f_object(const Tuple &a) { Check(); return dynobject(a,ident); } Object f_message(const Tuple &a) { Check(); return dynmessage(a,ident); } Object f_free(const Tuple &a) { // don't Check() object to free... if(ident != DYN_ID_NONE) { if(dyn_Free(DYN_SCHED_AUTO,ident) != DYN_ID_NONE) throw RuntimeError("Could not free dyn object"); ident = DYN_ID_NONE; } return Nothing(); } Object f_inlets(const Tuple &a) { Check(); if(a.length()) throw RuntimeError("No arguments allowed"); int ret = dyn_GetInletCount(ident); return ret >= 0?Int(ret):Nothing(); } Object f_outlets(const Tuple &a) { Check(); if(a.length()) throw RuntimeError("No arguments allowed"); int ret = dyn_GetOutletCount(ident); return ret >= 0?Int(ret):Nothing(); } Object f_inlettype(const Tuple &a) { Check(); int ret = dyn_GetInletType(ident,Int(a[0])); if(ret >= 0) return Int(ret == DYN_INOUT_SIGNAL); else throw RuntimeError("Error querying inlet type"); } Object f_outlettype(const Tuple &a) { Check(); int ret = dyn_GetOutletType(ident,Int(a[0])); if(ret >= 0) return Int(ret == DYN_INOUT_SIGNAL); else throw RuntimeError("Error querying outlet type"); } Object f_connect(const Tuple &a) { if(a.length() == 3) { dyn_id obj; ExtensionObject dst(a[1]); if(dyn_NewConnection(DYN_SCHED_AUTO,&obj,callback,ident,Int(a[0]),dst.extensionObject()->ident,Int(a[2])) == DYN_ID_NONE) return asObject(new dynIdent(obj)); else throw RuntimeError("Error connecting dyn objects"); } else throw RuntimeError("Syntax: Connect(src-outlet,dst-ID,dst-inlet)"); } Object f_send(const Tuple &a) { Check(); int err = DYN_ERROR_GENERAL; if(a.length() == 2 && a[1].isString()) { std::string msg(a[1].as_string()); err = dyn_SendStr(DYN_SCHED_AUTO,ident,Int(a[0]),msg.c_str()); } else if(a.length() >= 1) { t_atom lst[256],*argv = lst; t_symbol *sym; int argc = tuple2atoms(a,1,sym,lst,256); if(argc || sym) err = dyn_Send(DYN_SCHED_AUTO,ident,Int(a[0]),sym,argc,argv); } if(err == DYN_ERROR_NONE) return Nothing(); else throw RuntimeError("Could not send to dyn object"); } Object f_listen(const Tuple &a) { Check(); if(a.length() == 2) { dyn_id obj; Callable *func = new Callable(a[1]); if(dyn_Listen(DYN_SCHED_AUTO,&obj,ident,Int(a[0]),listener,func) == DYN_ID_NONE) { listen[obj] = func; return asObject(new dynIdent(obj)); } else { delete func; throw RuntimeError("Error creating dyn listener"); } } else throw RuntimeError("Syntax: Listen(inlet,function)"); } static void listener(dyn_id id,dyn_id oid,int outlet,const t_symbol *sym,int argc,const t_atom *argv,void *data) { #ifdef FLEXT_DEBUG // flext::post("dyn listener - object %x, outlet %i, sym %s",oid,outlet,sym->s_name); #endif Callable *func = (Callable *)data; // convert PD atoms to Python tuple Tuple args(atoms2tuple(sym,argc,argv)); Tuple tpl(3); tpl[0] = asObject(new dynIdent(oid)); tpl[1] = Int(outlet); tpl[2] = args.length() == 1?args[0]:args; try { func->apply(tpl); // call method } catch(Exception e) { PyErr_Print(); e.clear(); } catch(...) { PyErr_Clear(); } } }; class dynModule : public flext , public ExtensionModule { public: dynModule() : ExtensionModule("dyn") { flext::Setup(); dynIdent::init_type(); add_varargs_method("Patcher", &dynModule::f_patcher, "Create a new dyn patcher"); add_varargs_method("Object", &dynModule::f_object, "Create a new dyn object"); add_varargs_method("Message", &dynModule::f_message, "Create a new dyn message object"); add_varargs_method("Version", &dynModule::f_version, "Return version number"); add_varargs_method("DynVersion", &dynModule::f_dynversion, "Return dyn version number"); // must be last initialize( "documentation for the dyn module" ); } protected: Object f_dynversion(const Tuple &a) { return Int(DYN_VERSION); } Object f_version(const Tuple &a) { return Int(DYNPY_VERSION); } Object f_patcher(const Tuple &a) { return dynpatcher(a); } Object f_object(const Tuple &a) { return dynobject(a); } Object f_message(const Tuple &a) { return dynmessage(a); } }; extern "C" #ifdef _MSC_VER _declspec(dllexport) #endif void initdyn() { #if defined(PY_WIN32_DELAYLOAD_PYTHON_DLL) Py::InitialisePythonIndirectInterface(); #endif static dynModule* module = new dynModule; } static Object dynpatcher(const Tuple &a,dyn_id ident) { if(!a.length()) { dyn_id obj; if(dyn_NewPatcher(DYN_SCHED_AUTO,&obj,callback,ident) == DYN_ERROR_NONE) return asObject(new dynIdent(obj)); else throw RuntimeError("Error creating dyn patcher"); } else throw RuntimeError("Syntax: Patcher()"); } static Object dynobject(const Tuple &a,dyn_id ident) { dyn_id obj; int err = DYN_ERROR_GENERAL; if(a.length() == 1 && a[0].isString()) err = dyn_NewObjectStr(DYN_SCHED_AUTO,&obj,callback,ident,a[0].as_string().c_str()); else { t_atom lst[256],*argv = lst; t_symbol *sym; int argc = tuple2atoms(a,0,sym,lst,256); if(argc || sym) err = dyn_NewObject(DYN_SCHED_AUTO,&obj,callback,ident,sym,argc,argv); else throw RuntimeError("Invalid arguments for dyn object"); } if(err == DYN_ERROR_NONE) return asObject(new dynIdent(obj)); else throw RuntimeError("Error creating dyn object"); } static Object dynmessage(const Tuple &a,dyn_id ident) { dyn_id obj; int err = DYN_ERROR_GENERAL; if(a.length() == 1 && a[0].isString()) err = dyn_NewMessageStr(DYN_SCHED_AUTO,&obj,callback,ident,a[0].as_string().c_str()); else { t_atom argv[256]; t_symbol *hdr; int argc = tuple2atoms(a,0,hdr,argv,256,true); if(argc) err = dyn_NewMessage(DYN_SCHED_AUTO,&obj,callback,ident,argc,argv); else throw RuntimeError("Invalid arguments for dyn message"); } if(err == DYN_ERROR_NONE) return asObject(new dynIdent(obj)); else throw RuntimeError("Error creating dyn message"); }