Jamoma API  0.6.0.a19
j.pack-/j.pack.cpp
Go to the documentation of this file.
1 /** @file
2  *
3  * @ingroup implementationMaxExternalsGraph
4  *
5  * @brief j.pack# - External object for Max/MSP to bring Max values into a Jamoma Graph.
6  *
7  * @details
8  *
9  * @authors Tim Place, Trond Lossius
10  *
11  * @copyright Copyright © 2010 by Timothy Place @n
12  * This code is licensed under the terms of the "New BSD License" @n
13  * http://creativecommons.org/licenses/BSD/
14  */
15 
16 
17 #include "maxGraph.h"
18 #include "TTGraphInput.h"
19 
20 
21 // Data Structure for this object
22 struct Pack {
23  t_object obj;
24  TTGraphObjectBasePtr graphObject; // this _must_ be second
25  TTPtr graphOutlets[16]; // this _must_ be third (for the setup call)
26  TTDictionaryPtr graphDictionary;
27  t_object* patcher;
28  t_object* patcherview;
29  TTPtr qelem; // for clumping dirty events together
30 };
31 typedef Pack* PackPtr;
32 
33 
34 // Prototypes for methods
35 PackPtr PackNew (t_symbol* msg, long argc, t_atom* argv);
36 void PackFree (PackPtr self);
37 void PackStartTracking(PackPtr self);
38 t_max_err PackNotify (PackPtr self, t_symbol* s, t_symbol* msg, t_object* sender, TTPtr data);
39 void PackQFn (PackPtr self);
40 void PackAssist (PackPtr self, void* b, long msg, long arg, char* dst);
41 void PackInt (PackPtr self, long value);
42 void PackFloat (PackPtr self, double value);
43 void PackList (PackPtr self, t_symbol* s, long ac, t_atom* ap);
44 void PackAnything (PackPtr self, t_symbol* s, long ac, t_atom* ap);
45 void PackMessage (PackPtr self, t_symbol* s, long ac, t_atom* ap);
46 void PackAttribute (PackPtr self, t_symbol* s, long ac, t_atom* ap);
47 TTErr PackReset (PackPtr self, long vectorSize);
48 TTErr PackSetup (PackPtr self);
49 
50 
51 // Globals
52 static t_class* sPackClass;
53 
54 
55 /************************************************************************************/
56 // Main() Function
57 
58 int C74_EXPORT main(void)
59 {
60  t_class* c;
61 
62  TTGraphInit();
63  common_symbols_init();
64 
65  c = class_new("j.pack-", (method)PackNew, (method)PackFree, sizeof(Pack), (method)0L, A_GIMME, 0);
66 
67  class_addmethod(c, (method)PackInt, "int", A_LONG, 0);
68  class_addmethod(c, (method)PackFloat, "float", A_LONG, 0);
69  class_addmethod(c, (method)PackList, "list", A_GIMME, 0);
70  class_addmethod(c, (method)PackAnything, "anything", A_GIMME, 0);
71  class_addmethod(c, (method)PackMessage, "message", A_GIMME, 0);
72  class_addmethod(c, (method)PackAttribute, "attribute", A_GIMME, 0);
73 
74  class_addmethod(c, (method)MaxGraphReset, "graph.reset", A_CANT, 0);
75  class_addmethod(c, (method)MaxGraphSetup, "graph.setup", A_CANT, 0);
76  class_addmethod(c, (method)MaxGraphDrop, "graph.drop", A_CANT, 0);
77  class_addmethod(c, (method)MaxGraphObject, "graph.object", A_CANT, 0);
78 
79  class_addmethod(c, (method)PackNotify, "notify", A_CANT, 0);
80  class_addmethod(c, (method)PackAssist, "assist", A_CANT, 0);
81  class_addmethod(c, (method)object_obex_dumpout, "dumpout", A_CANT, 0);
82 
83  class_register(_sym_box, c);
84  sPackClass = c;
85  return 0;
86 }
87 
88 
89 /************************************************************************************/
90 // Object Creation Method
91 
92 PackPtr PackNew(t_symbol* msg, long argc, t_atom* argv)
93 {
94  PackPtr self;
95  TTValue v;
96  TTErr err;
97 
98  self = PackPtr(object_alloc(sPackClass));
99  if (self) {
100  object_obex_store((void*)self, _sym_dumpout, (t_object*)outlet_new(self, NULL));
101  self->graphOutlets[0] = outlet_new(self, "graph.connect");
102 
103  v.resize(2);
104  v[0] = "graph.input";
105  v[1] = 1;
106  err = TTObjectBaseInstantiate(TT("graph.object"), (TTObjectBasePtr*)&self->graphObject, v);
107  ((TTGraphInput*)self->graphObject->mKernel.instance())->setOwner(self->graphObject);
108 
109  if (!self->graphObject->mKernel.valid()) {
110  object_error(SELF, "cannot load Jamoma object");
111  return NULL;
112  }
113 
114  self->graphDictionary = new TTDictionary;
115  self->graphDictionary->setSchema(TT("none"));
116  self->graphDictionary->append(TT("outlet"), 0);
117 
118  attr_args_process(self, argc, argv);
119 
120  self->qelem = qelem_new(self, (method)PackQFn);
121 
122  // PackStartTracking(self);
123  defer_low(self, (method)PackStartTracking, NULL, 0, NULL);
124  }
125  return self;
126 }
127 
128 
129 // Memory Deallocation
130 void PackFree(PackPtr self)
131 {
132  TTObjectBaseRelease((TTObjectBasePtr*)&self->graphObject);
133  qelem_free(self->qelem);
134 }
135 
136 
137 /************************************************************************************/
138 
139 t_max_err PackNotify(PackPtr self, t_symbol* s, t_symbol* msg, t_object* sender, TTPtr data)
140 {
141  if (sender == self->patcherview) {
142  if (msg == _sym_attr_modified) {
143  t_symbol* name = (t_symbol*)object_method((t_object*)data, _sym_getname);
144  if (name == _sym_dirty) {
145  qelem_set(self->qelem);
146  }
147  }
148  else if (msg == _sym_free)
149  self->patcherview = NULL;
150  }
151  else {
152  if (msg == _sym_free) {
153  t_object* sourceBox;
154  t_object* sourceObject;
155  long sourceOutlet;
156  t_object* destBox;
157  t_object* destObject;
158  long destInlet;
159 
160  #ifdef DEBUG_NOTIFICATIONS
161  object_post(SELF, "patch line deleted");
162  #endif // DEBUG_NOTIFICATIONS
163 
164  // get boxes and inlets
165  sourceBox = jpatchline_get_box1(sender);
166  if (!sourceBox)
167  goto out;
168 
169  sourceObject = jbox_get_object(sourceBox);
170  sourceOutlet = jpatchline_get_outletnum(sender);
171  destBox = jpatchline_get_box2(sender);
172  if (!destBox)
173  goto out;
174  destObject = jbox_get_object(destBox);
175  destInlet = jpatchline_get_inletnum(sender);
176 
177  // if both boxes are graph objects
178  if ( zgetfn(sourceObject, gensym("graph.object")) && zgetfn(destObject, gensym("graph.object")) ) {
179  #ifdef DEBUG_NOTIFICATIONS
180  object_post(SELF, "deleting graph patchline!");
181  #endif // DEBUG_NOTIFICATIONS
182 
183  object_method(destObject, gensym("graph.drop"), destInlet, sourceObject, sourceOutlet);
184  }
185  out:
186  ;
187  }
188  }
189  return MAX_ERR_NONE;
190 }
191 
192 
193 void PackIterateResetCallback(PackPtr self, t_object* obj)
194 {
195  t_max_err err = MAX_ERR_NONE;
196  method graphResetMethod = zgetfn(obj, gensym("graph.reset"));
197 
198  if (graphResetMethod)
199  err = (t_max_err)graphResetMethod(obj);
200 }
201 
202 
203 void PackIterateSetupCallback(PackPtr self, t_object* obj)
204 {
205  t_max_err err = MAX_ERR_NONE;
206  method graphSetupMethod = zgetfn(obj, gensym("graph.setup"));
207 
208  if (graphSetupMethod)
209  err = (t_max_err)graphSetupMethod(obj);
210 }
211 
212 
213 void PackAttachToPatchlinesForPatcher(PackPtr self, t_object* patcher)
214 {
215  t_object* patchline = object_attr_getobj(patcher, _sym_firstline);
216  t_object* box = jpatcher_get_firstobject(patcher);
217 
218  while (patchline) {
219  object_attach_byptr_register(self, patchline, _sym_nobox);
220  patchline = object_attr_getobj(patchline, _sym_nextline);
221  }
222 
223  while (box) {
224  t_symbol* classname = jbox_get_maxclass(box);
225 
226  if (classname == _sym_jpatcher) {
227  t_object* subpatcher = jbox_get_object(box);
228 
229  PackAttachToPatchlinesForPatcher(self, subpatcher);
230  }
231  box = jbox_get_nextobject(box);
232  }
233 }
234 
235 
236 // Qelem function, which clumps together dirty notifications before making the new connections
237 void PackQFn(PackPtr self)
238 {
239  t_atom result;
240 
241  #ifdef DEBUG_NOTIFICATIONS
242  object_post(SELF, "patcher dirtied");
243  #endif // DEBUG_NOTIFICATIONS
244 
245  object_method(self->patcher, gensym("iterate"), (method)PackIterateSetupCallback, self, PI_DEEP, &result);
246 
247  // attach to all of the patch cords so we will know if one is deleted
248  // we are not trying to detach first -- hopefully this is okay and multiple attachments will be filtered (?)
249  PackAttachToPatchlinesForPatcher(self, self->patcher);
250 }
251 
252 
253 // Start keeping track of edits and connections in the patcher
254 void PackStartTracking(PackPtr self)
255 {
256  t_object* patcher = NULL;
257  t_object* parent = NULL;
258  t_object* patcherview = NULL;
259  t_max_err err;
260  t_atom result;
261 
262  // first find the top-level patcher
263  err = object_obex_lookup(self, gensym("#P"), &patcher);
264  parent = patcher;
265  while (parent) {
266  patcher = parent;
267  parent = object_attr_getobj(patcher, _sym_parentpatcher);
268  }
269 
270  // now iterate recursively from the top-level patcher down through all of the subpatchers
271  object_method(patcher, gensym("iterate"), (method)PackIterateResetCallback, self, PI_DEEP, &result);
272  object_method(patcher, gensym("iterate"), (method)PackIterateSetupCallback, self, PI_DEEP, &result);
273 
274 
275  // now let's attach to the patcherview to get notifications about any further changes to the patch cords
276  // the patcher 'dirty' attribute is not modified for each change, but the patcherview 'dirty' attribute is
277  if (!self->patcherview) {
278  patcherview = jpatcher_get_firstview(patcher);
279  self->patcherview = patcherview;
280  self->patcher = patcher;
281  object_attach_byptr_register(self, patcherview, _sym_nobox);
282  }
283 
284  // now we want to go a step further and attach to all of the patch cords
285  // this is how we will know if one is deleted
286  PackAttachToPatchlinesForPatcher(self, self->patcher);
287 }
288 
289 
290 /************************************************************************************/
291 // Methods bound to input/inlets
292 
293 // Method for Assistance Messages
294 void PackAssist(PackPtr self, void* b, long msg, long arg, char* dst)
295 {
296  if (msg==1) // Inlets
297  strcpy (dst, "multichannel input and control messages");
298  else if (msg==2) { // Outlets
299  if (arg == 0)
300  strcpy(dst, "multichannel output");
301  else
302  strcpy(dst, "dumpout");
303  }
304 }
305 
306 
307 void PackInt(PackPtr self, long value)
308 {
309  TTValue v = int(value);
310 
311  self->graphDictionary->setSchema(TT("number"));
312  self->graphDictionary->setValue(v);
313  ((TTGraphInput*)self->graphObject->mKernel.instance())->push(*self->graphDictionary);
314 }
315 
316 
317 void PackFloat(PackPtr self, double value)
318 {
319  TTValue v = value;
320 
321  self->graphDictionary->setSchema(TT("number"));
322  self->graphDictionary->setValue(v);
323  ((TTGraphInput*)self->graphObject->mKernel.instance())->push(*self->graphDictionary);
324 }
325 
326 
327 void PackList(PackPtr self, t_symbol* s, long ac, t_atom* ap)
328 {
329  TTValue v;
330 
331  v.resize(ac);
332  for (int i=0; i<ac; i++) {
333  switch (atom_gettype(ap+i)) {
334  case A_LONG:
335  v[i] = (int)atom_getlong(ap+i);
336  break;
337  case A_FLOAT:
338  v[i] = atom_getfloat(ap+i);
339  break;
340  case A_SYM:
341  v[i] = TT(atom_getsym(ap+i)->s_name);
342  break;
343  default:
344  break;
345  }
346  }
347  self->graphDictionary->setSchema(TT("array"));
348  self->graphDictionary->setValue(v);
349  ((TTGraphInput*)self->graphObject->mKernel.instance())->push(*self->graphDictionary);
350 }
351 
352 
353 void PackAnything(PackPtr self, t_symbol* s, long ac, t_atom* ap)
354 {
355  TTValue v;
356 
357  v.resize(ac+1);
358  if (ac > 0) {
359  self->graphDictionary->setSchema(TT("array"));
360  v[0] = TT(s->s_name);
361  for (int i=0; i<ac; i++) {
362  switch (atom_gettype(ap+i)) {
363  case A_LONG:
364  v[i+1] = (int)atom_getlong(ap+i);
365  break;
366  case A_FLOAT:
367  v[i+1] = atom_getfloat(ap+i);
368  break;
369  case A_SYM:
370  v[i+1] = TT(atom_getsym(ap+i)->s_name);
371  break;
372  default:
373  break;
374  }
375  }
376  }
377  else {
378  self->graphDictionary->setSchema(TT("symbol"));
379  v[0] = TT(s->s_name);
380  }
381 
382  self->graphDictionary->setValue(v);
383  ((TTGraphInput*)self->graphObject->mKernel.instance())->push(*self->graphDictionary);
384 }
385 
386 
387 void PackMessage(PackPtr self, t_symbol* s, long ac, t_atom* ap)
388 {
389  TTValue v;
390 
391  if (ac < 1)
392  return;
393 
394  self->graphDictionary->setSchema(TT("message"));
395  self->graphDictionary->append(TT("name"), TT(atom_getsym(ap)->s_name));
396 
397  v.resize(ac-1);
398  if (ac > 2) {
399 
400  for (int i=0; i < ac-1; i++) {
401  switch (atom_gettype(ap+i)) {
402  case A_LONG:
403  v[i+1] = (int)atom_getlong(ap+i);
404  break;
405  case A_FLOAT:
406  v[i+1] = atom_getfloat(ap+i);
407  break;
408  case A_SYM:
409  v[i+1] = TT(atom_getsym(ap+i)->s_name);
410  break;
411  default:
412  break;
413  }
414  }
415  }
416  else if (ac > 1) {
417  if (atom_gettype(ap+1) == A_SYM)
418  v[0] = TT(s->s_name);
419  else if (atom_gettype(ap+1) == A_LONG || atom_gettype(ap+1) == A_FLOAT)
420  v[0] = atom_getfloat(ap+1);
421  }
422 
423  self->graphDictionary->setValue(v);
424  ((TTGraphInput*)self->graphObject->mKernel.instance())->push(*self->graphDictionary);
425 }
426 
427 
428 void PackAttribute(PackPtr self, t_symbol* s, long ac, t_atom* ap)
429 {
430  TTValue v;
431 
432  if (ac < 1)
433  return;
434 
435  self->graphDictionary->setSchema(TT("attribute"));
436  self->graphDictionary->append(TT("name"), TT(atom_getsym(ap)->s_name));
437 
438  v.resize(ac-1);
439  if (ac > 2) {
440 
441  for (int i=0; i < ac-1; i++) {
442  switch (atom_gettype(ap+i)) {
443  case A_LONG:
444  v[i+1] = (int)atom_getlong(ap+i);
445  break;
446  case A_FLOAT:
447  v[i+1] = atom_getfloat(ap+i);
448  break;
449  case A_SYM:
450  v[i+1] = TT(atom_getsym(ap+i)->s_name);
451  break;
452  default:
453  break;
454  }
455  }
456  }
457  else if (ac > 1) {
458  if (atom_gettype(ap+1) == A_SYM)
459  v[0] = TT(s->s_name);
460  else if (atom_gettype(ap+1) == A_LONG || atom_gettype(ap+1) == A_FLOAT)
461  v[0] = atom_getfloat(ap+1);
462  }
463 
464  self->graphDictionary->setValue(v);
465  ((TTGraphInput*)self->graphObject->mKernel.instance())->push(*self->graphDictionary);
466 }
467 
The TTGraphObjectBase wraps a TTDSP object such that it is possible to build a dynamic graph of audio...
TTErr TTObjectBaseRelease(TTObjectBasePtr *anObject)
DEPRECATED.
Base class for all first-class Jamoma objects.
Definition: TTObjectBase.h:109
Pack * PackPtr
Pointer to a j.pack= instance.
Definition: j.pack.h:30
#define TT
This macro is defined as a shortcut for doing a lookup in the symbol table.
Definition: TTSymbol.h:155
A type that represents the key as a C-String and the value as a pointer to the matching TTSymbol obje...
Definition: TTDictionary.h:47
void append(const T &anElementValueToAppend)
Insert a single TTElement at the end.
Definition: TTValue.h:243
PackPtr PackNew(t_symbol *msg, long argc, t_atom *argv)
Create a new instance of the j.in= object.
void * TTPtr
A generic pointer.
Definition: TTBase.h:248
void PackAssist(PackPtr self, void *b, long msg, long arg, char *dst)
Provides assist strings in Max for object inlets and outlets.
int C74_EXPORT main(void)
Set up this class as a Max external the first time an object of this kind is instantiated.
TTErr TTObjectBaseInstantiate(const TTSymbol className, TTObjectBasePtr *returnedObjectPtr, const TTValue arguments)
DEPRECATED.
TTErr setSchema(const TTSymbol aSchemaName)
TODO: Add documentation schemaName TODO: Add documentation.
Definition: TTDictionary.h:172
TTErr
Jamoma Error Codes Enumeration of error codes that might be returned by any of the TTBlue functions a...
Definition: TTBase.h:342
An object that serves as the source driving an object/graph.
Definition: TTGraphInput.h:26
Data Structure for the j.pack= Max object.
Jamoma Asynchronous Object Graph Layer.
void resize(size_type n)
Change the number of elements.
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34
void PackFree(PackPtr self)
Called when the object is freed (destroyed), ensuring that memory is properly freed up...