// Start keeping track of edits and connections in the patcher void PackStartTracking(PackPtr self) { ObjectPtr patcher = NULL; ObjectPtr parent = NULL; ObjectPtr patcherview = NULL; MaxErr err; Atom result; // first find the top-level patcher err = object_obex_lookup(self, gensym("#P"), &patcher); parent = patcher; while (parent) { patcher = parent; parent = object_attr_getobj(patcher, _sym_parentpatcher); } // now iterate recursively from the top-level patcher down through all of the subpatchers object_method(patcher, gensym("iterate"), (method)PackIterateResetCallback, self, PI_DEEP, &result); object_method(patcher, gensym("iterate"), (method)PackIterateSetupCallback, self, PI_DEEP, &result); // now let's attach to the patcherview to get notifications about any further changes to the patch cords // the patcher 'dirty' attribute is not modified for each change, but the patcherview 'dirty' attribute is if (!self->patcherview) { patcherview = jpatcher_get_firstview(patcher); self->patcherview = patcherview; self->patcher = patcher; object_attach_byptr_register(self, patcherview, _sym_nobox); } // now we want to go a step further and attach to all of the patch cords // this is how we will know if one is deleted PackAttachToPatchlinesForPatcher(self, self->patcher); }
// Don't pass memory in for this function! It will allocate what it needs // -- then the caller is responsible for freeing void jamoma_patcher_getargs(t_object *patcher, long *argc, t_atom **argv) { t_symbol *context = jamoma_patcher_getcontext(patcher); t_object *box = object_attr_getobj(patcher, jps_box); t_object *textfield = NULL; char *text = NULL; unsigned long textlen = 0; if(context == gensym("bpatcher")) object_attr_getvalueof(box, gensym("args"), argc, argv); else if(context == gensym("subpatcher")){ textfield = object_attr_getobj(box, gensym("textfield")); object_method(textfield, gensym("gettextptr"), &text, &textlen); atom_setparse(argc, argv, text); } else{ *argc = 0; *argv = NULL; } }
void UnpackAttachToPatchlinesForPatcher(UnpackPtr self, ObjectPtr patcher) { ObjectPtr patchline = object_attr_getobj(patcher, _sym_firstline); ObjectPtr box = jpatcher_get_firstobject(patcher); while (patchline) { object_attach_byptr_register(self, patchline, _sym_nobox); patchline = object_attr_getobj(patchline, _sym_nextline); } while (box) { SymbolPtr classname = jbox_get_maxclass(box); if (classname == _sym_jpatcher) { ObjectPtr subpatcher = jbox_get_object(box); UnpackAttachToPatchlinesForPatcher(self, subpatcher); } box = jbox_get_nextobject(box); } }
void OutAttachToPatchlinesForPatcher(OutPtr self, t_object* patcher) { t_object* patchline = object_attr_getobj(patcher, _sym_firstline); t_object* box = jpatcher_get_firstobject(patcher); while (patchline) { object_attach_byptr_register(self, patchline, _sym_nobox); patchline = object_attr_getobj(patchline, _sym_nextline); } while (box) { t_symbol *classname = jbox_get_maxclass(box); if (classname == _sym_jpatcher) { t_object* subpatcher = jbox_get_object(box); OutAttachToPatchlinesForPatcher(self, subpatcher); } box = jbox_get_nextobject(box); } }
// Registering with the jcom.hub object t_object *jcom_core_subscribe(t_object *x, t_symbol *name, t_object *container, t_symbol *object_type) { t_object *patcher = container; t_object *box; t_symbol *objclass = NULL; t_object *hub = NULL; again5: box = object_attr_getobj(patcher, _sym_firstobject); while (box) { objclass = object_attr_getsym(box, _sym_maxclass); if (objclass == jps_jcom_hub) { hub = object_attr_getobj(box, _sym_object); object_method(hub, jps_subscribe, name, x, object_type); return hub; } box = object_attr_getobj(box, _sym_nextobject); } patcher = object_attr_getobj(patcher, _sym_parentpatcher); if (patcher) goto again5; return NULL; }
t_symbol *jamoma_patcher_getcontext(t_object *patcher) { t_object *box = object_attr_getobj(patcher, jps_box); t_symbol *objclass = NULL; if(box) objclass = object_classname(box); if(objclass == gensym("bpatcher")) return objclass; else if(objclass == gensym("newobj")) return gensym("subpatcher"); else return gensym("toplevel"); }
void iterator_attach(t_iterator *x) { t_atom *av = NULL; long ac = 0; x->a_patcherview = object_attr_getobj(x->a_patcher, gensym("firstview")); object_attach_byptr_register(x, x->a_patcherview, CLASS_NOBOX); // get the bounds of the first patcherview and cache them object_attr_getvalueof(x->a_patcherview, gensym("rect"), &ac, &av); if (ac && av) { x->a_cached.x = atom_getfloat(av+2); // width x->a_cached.y = atom_getfloat(av+3); // height freebytes(av, sizeof(t_atom) * ac); // or sysmem_freeptr() } }
TTErr PlugOutBuildGraph(PlugOutPtr self) { MaxErr err; ObjectPtr patcher = NULL; ObjectPtr parent = NULL; long result = 0; err = object_obex_lookup(self, GENSYM("#P"), &patcher); // first find the top-level patcher err = object_obex_lookup(self, GENSYM("#P"), &patcher); parent = patcher; while (parent) { patcher = parent; parent = object_attr_getobj(patcher, _sym_parentpatcher); } //object_method(patcher, gensym("iterate"), (method)PlugOutIterateResetCallback, self, PI_DEEP, &result); object_method(patcher, GENSYM("iterate"), (method)PlugOutIterateSetupCallback, self, PI_DEEP, &result); return kTTErrNone; }
// TODO: When running in the debugger, it seems like we are iterating through this function a whole bunch of times! // Can we put it in a qelem or something so that it only gets called once? [TAP] // But actually, maybe it is just a Max 4.6 funky Runtime thing? Let's take a look again when we get to Max 5 void hub_examine_context(t_hub *x) { AtomCount argc = 0; AtomPtr argv = NULL; SymbolPtr context = jamoma_patcher_getcontext(x->container); // Try to get OSC Name of module from an argument jamoma_patcher_getargs(x->container, &argc, &argv); // <-- this call allocates memory for argv if(argc){ x->osc_name = atom_getsym(argv+(argc-1)); sysmem_freeptr(argv); } else x->osc_name = _sym_nothing; // Try to get OSC Name of module from scripting name if(x->osc_name == _sym_nothing) x->osc_name = jamoma_patcher_getvarname(x->container); // In this case we overwrite whatever happened above if(context == gensym("toplevel")){ x->osc_name = gensym("/editing_this_module"); x->editing = true; } else{ t_object* patcher = jamoma_object_getpatcher((t_object*)x); t_object* box = object_attr_getobj(patcher, jps_box); t_object* ui = NULL; t_symbol* objclass = NULL; x->editing = false; ui = object_attr_getobj(patcher, gensym("firstobject")); while(ui){ objclass = object_attr_getsym(ui, gensym("maxclass")); if(objclass == gensym("jcom.ui")) break; ui = object_attr_getobj(ui, gensym("nextobject")); } if(ui){ t_rect boxRect; t_rect uiRect; if(context == gensym("bpatcher")){ object_attr_get_rect(ui, _sym_presentation_rect, &uiRect); object_attr_get_rect(box, _sym_patching_rect, &boxRect); boxRect.width = uiRect.width; boxRect.height = uiRect.height; object_attr_set_rect(box, _sym_patching_rect, &boxRect); object_attr_get_rect(box, _sym_presentation_rect, &boxRect); boxRect.width = uiRect.width; boxRect.height = uiRect.height; object_attr_set_rect(box, _sym_presentation_rect, &boxRect); } else if(context == gensym("subpatcher")){ object_attr_get_rect(ui, _sym_presentation_rect, &uiRect); object_attr_get_rect(patcher, _sym_defrect, &boxRect); boxRect.width = uiRect.width; boxRect.height = uiRect.height; object_attr_set_rect(patcher, _sym_defrect, &boxRect); object_attr_setchar(patcher, _sym_toolbarvisible, 0); } } } object_attr_setsym(x, _sym_name, x->osc_name); hub_subscriptions_refresh(x); hub_internals_create(x); qelem_unset(x->init_qelem); // clear the last thing to make sure we don't call into this a bunch of times qelem_set(x->init_qelem); // flag the queue for initialization }
// DSP Method void UnpackDsp64(UnpackPtr self, ObjectPtr dsp64, short *count, double samplerate, long maxvectorsize, long flags) { TTUInt16 i; MaxErr err; long result = 0; self->vectorSize = maxvectorsize; #ifdef DEBUG_NOTIFICATIONS object_post(SELF, "dsp method called"); #endif // DEBUG_NOTIFICATIONS /* We need to figure out what objects are connected to what inlets to build the graph: 1. Broadcast 'audio.reset' to every object in the patcher, to remove all existing connections. 2. Broadcast 'audio.setup' to every object in the patcher, to tell objects to then send 'audio.connect' messages to any objects below them. 3. When an object received 'audio.connect', then it makes the connection. At this point, the graph is configured and we just need to execute it. We execute the graph from our perform method, which MSP calls once per signal vector. 5. Crawl the graph from bottom to top, calling the audio graph preprocess method (prepare for process) 6. Crawl the graph from bottom to top, calling the audio graph process method (calculate the samples) 7. (Maybe) crawl the graph from bottom to top, calling a audio graph postprocess method For steps 1 & 2, we have to traverse thge patcher twice, because we have to clear all connections first, then add connections. It won't work to do them both during the same traversal because situations arise Where we setup the chain and then it gets reset again by another object (since the order in which we traverse objects is undefined). */ if (!self->hasReset) { ObjectPtr patcher = NULL; ObjectPtr parent = NULL; ObjectPtr patcherview = NULL; // first find the top-level patcher err = object_obex_lookup(self, gensym("#P"), &patcher); parent = patcher; while (parent) { patcher = parent; parent = object_attr_getobj(patcher, _sym_parentpatcher); } // now iterate recursively from the top-level patcher down through all of the subpatchers object_method(patcher, gensym("iterate"), (method)UnpackIterateResetCallback, self, PI_DEEP, &result); object_method(patcher, gensym("iterate"), (method)UnpackIterateSetupCallback, self, PI_DEEP, &result); // now let's attach to the patcherview to get notifications about any further changes to the patch cords // the patcher 'dirty' attribute is not modified for each change, but the patcherview 'dirty' attribute is if (!self->patcherview) { patcherview = jpatcher_get_firstview(patcher); self->patcherview = patcherview; self->patcher = patcher; object_attach_byptr_register(self, patcherview, _sym_nobox); } } // now we want to go a step further and attach to all of the patch cords // this is how we will know if one is deleted UnpackAttachToPatchlinesForPatcher(self, self->patcher); self->numChannels = 0; for (i=1; i <= self->maxNumChannels; i++) { self->numChannels++; } self->audioGraphObject->getUnitGenerator()->setAttributeValue(kTTSym_sampleRate, samplerate); self->audioGraphObject->resetSampleStamp(); self->sampleStamp = 0; self->initData.vectorSize = self->vectorSize; object_method(dsp64, gensym("dsp_add64"), self, UnpackPerform64, 0, NULL); //dsp_add64(dsp64, (ObjectPtr)self, (t_perfroutine64)UnpackPerform64, 0, NULL); }
void connect_attach(t_connect *x) { object_obex_lookup(x, gensym("#P"), &x->f_patcher); x->f_patcherview = object_attr_getobj(x->f_patcher, hoa_sym_firstview); object_attach_byptr_register(x, x->f_patcherview, CLASS_NOBOX); }
// DSP Method void OutDsp(OutPtr self, t_signal** sp, short* count) { //TTUInt16 i; TTUInt16 k=0; void **audioVectors = NULL; t_max_err err; long result = 0; self->vectorSize = sp[0]->s_n; #ifdef DEBUG_NOTIFICATIONS object_post(SELF, "dsp method called"); #endif // DEBUG_NOTIFICATIONS /* We need to figure out what objects are connected to what inlets to build the graph: 1. Broadcast 'audio.reset' to every object in the patcher, to remove all existing connections. 2. Broadcast 'audio.setup' to every object in the patcher, to tell objects to then send 'audio.connect' messages to any objects below them. 3. When an object received 'audio.connect', then it makes the connection. At this point, the graph is configured and we just need to execute it. We execute the graph from our perform method, which MSP calls once per signal vector. 5. Crawl the graph from bottom to top, calling the audio graph preprocess method (prepare for process) 6. Crawl the graph from bottom to top, calling the audio graph process method (calculate the samples) 7. (Maybe) crawl the graph from bottom to top, calling a audio graph postprocess method For steps 1 & 2, we have to traverse thge patcher twice, because we have to clear all connections first, then add connections. It won't work to do them both during the same traversal because situations arise Where we setup the chain and then it gets reset again by another object (since the order in which we traverse objects is undefined). */ if (!self->hasReset) { t_object* patcher = NULL; t_object* parent = NULL; t_object* patcherview = NULL; // first find the top-level patcher err = object_obex_lookup(self, gensym("#P"), &patcher); parent = patcher; while (parent) { patcher = parent; parent = object_attr_getobj(patcher, _sym_parentpatcher); } // now iterate recursively from the top-level patcher down through all of the subpatchers object_method(patcher, gensym("iterate"), (method)OutIterateResetCallback, self, PI_DEEP, &result); object_method(patcher, gensym("iterate"), (method)OutIterateSetupCallback, self, PI_DEEP, &result); // now let's attach to the patcherview to get notifications about any further changes to the patch cords // the patcher 'dirty' attribute is not modified for each change, but the patcherview 'dirty' attribute is if (!self->patcherview) { patcherview = jpatcher_get_firstview(patcher); self->patcherview = patcherview; self->patcher = patcher; object_attach_byptr_register(self, patcherview, _sym_nobox); } } // now we want to go a step further and attach to all of the patch cords // this is how we will know if one is deleted OutAttachToPatchlinesForPatcher(self, self->patcher); // Setup the perform method //audioVectors = (void**)sysmem_newptr(sizeof(void*) * (self->maxNumChannels + 1)); audioVectors = (void**)sysmem_newptr(sizeof(void*) * (1)); audioVectors[k] = self; k++; //self->numChannels = 0; /*for (i=1; i <= self->maxNumChannels; i++) { self->numChannels++; audioVectors[k] = sp[i]->s_vec; k++; }*/ self->numChannels = self->maxNumChannels; //[np] self->audioGraphObject->getUnitGenerator().get(kTTSym_sampleRate, sp[0]->s_sr); self->audioGraphObject->resetSampleStamp(); self->sampleCount = 0; dsp_addv(OutPerform, k, audioVectors); sysmem_freeptr(audioVectors); self->initData.vectorSize = self->vectorSize; }
void ww_attach(t_ww *x) { x->w_patcherview = object_attr_getobj(x->w_patcher, gensym("firstview")); object_attach_byptr_register(x, x->w_patcherview, CLASS_NOBOX); }