Jamoma API  0.6.0.a19
PureDataGraph.cpp
1 /*
2  * MaxGraph
3  * A thin wrapper of the Jamoma Graph system for use in the Cycling '74 Max/MSP environment.
4  * Includes an automated class wrapper to make TTFoundation objects available as objects for Max/MSP.
5  * Copyright © 2010 by Timothy Place
6  *
7  * License: This code is licensed under the terms of the "New BSD License"
8  * http://creativecommons.org/licenses/BSD/
9  */
10 
11 #include "maxGraph.h"
12 #include "ext_hashtab.h"
13 
14 #define MAX_NUM_INLETS 16
15 
16 // Data Structure for this object
17 typedef struct _wrappedInstance {
18  t_object obj; ///< Max audio object header
19  TTGraphObjectBasePtr graphObject; ///< ** must be second **
20  TTPtr graphOutlets[MAX_NUM_INLETS]; ///< Array of outlets, may eventually want this to be more dynamic
21  TTPtr inlets[MAX_NUM_INLETS]; ///< Array of proxy inlets beyond the first inlet
22  WrappedClassPtr wrappedClassDefinition; ///< A pointer to the class definition
23 } WrappedInstance;
24 
25 typedef WrappedInstance* WrappedInstancePtr; ///< Pointer to a wrapped instance of our object.
26 
27 
28 /** A hash of all wrapped clases, keyed on the Max class name. */
29 static t_hashtab* wrappedMaxClasses = NULL;
30 
31 
32 t_object* wrappedClass_new(t_symbol* name, long argc, t_atom* argv)
33 {
34  WrappedClass* wrappedMaxClass = NULL;
35  WrappedInstancePtr self = NULL;
36  TTValue v;
37  TTErr err = kTTErrNone;
38  TTUInt8 numInputs = 1;
39  TTUInt8 numOutputs = 1;
40  long attrstart = attr_args_offset(argc, argv); // support normal arguments
41 
42  // Find the WrappedClass
43  hashtab_lookup(wrappedMaxClasses, name, (t_object**)&wrappedMaxClass);
44 
45  // If the WrappedClass has a validity check defined, then call the validity check function.
46  // If it returns an error, then we won't instantiate the object.
47  if (wrappedMaxClass) {
48  if (wrappedMaxClass->validityCheck)
49  err = wrappedMaxClass->validityCheck(wrappedMaxClass->validityCheckArgument);
50  else
51  err = kTTErrNone;
52  }
53  else
54  err = kTTErrGeneric;
55 
56  if (!err)
57  self = (WrappedInstancePtr)object_alloc(wrappedMaxClass->maxClass);
58  if (self) {
59 
60  if (wrappedMaxClass->options && !wrappedMaxClass->options->lookup(TT("argumentDefinesNumInlets"), v)) {
61  int argumentOffsetToDefineTheNumberOfInlets = v;
62  if ((attrstart-argumentOffsetToDefineTheNumberOfInlets > 0) && argv+argumentOffsetToDefineTheNumberOfInlets)
63  numInputs = atom_getlong(argv+argumentOffsetToDefineTheNumberOfInlets);
64  }
65  for (TTUInt16 i=numInputs-1; i>0; i--)
66  self->inlets[i-1] = proxy_new(self, i, NULL);
67 
68  object_obex_store((void*)self, _sym_dumpout, (object*)outlet_new(self, NULL)); // dumpout
69  if (wrappedMaxClass->options && !wrappedMaxClass->options->lookup(TT("argumentDefinesNumOutlets"), v)) {
70  int argumentOffsetToDefineTheNumberOfOutlets = v;
71  if ((attrstart-argumentOffsetToDefineTheNumberOfOutlets > 0) && argv+argumentOffsetToDefineTheNumberOfOutlets)
72  numOutputs = atom_getlong(argv+argumentOffsetToDefineTheNumberOfOutlets);
73  }
74  for (TTInt16 i=numOutputs-1; i>=0; i--)
75  self->graphOutlets[i] = outlet_new(self, "audio.connect");
76 
77  self->wrappedClassDefinition = wrappedMaxClass;
78  v.resize(3);
79  v[0] = wrappedMaxClass->ttClassName;
80  v[1] = numInputs;
81  v[2] = numOutputs;
82  err = TTObjectBaseInstantiate(TT("audio.object"), (TTObjectBasePtr*)&self->graphObject, v);
83 
84  attr_args_process(self, argc, argv);
85  }
86  return (t_object*)self;
87 }
88 
89 
90 void wrappedClass_free(WrappedInstancePtr self)
91 {
92  if (self->graphObject)
93  TTObjectBaseRelease((TTObjectBasePtr*)&self->graphObject);
94 
95  for (int i=0; i<MAX_NUM_INLETS; i++) {
96  if (self->inlets[i])
97  object_free(self->inlets[i]);
98  }
99 }
100 
101 
102 
103 // METHODS SPECIFIC TO AUDIO GRAPH EXTERNALS
104 
105 
106 TTErr MaxGraphReset(t_object* x)
107 {
109  return self->graphObject->reset();
110 }
111 
112 
113 TTErr MaxGraphSetup(t_object* x)
114 {
116  t_atom a[2];
117  TTUInt16 i=0;
118 
119  atom_setobj(a+0, (t_object*)self->graphObject);
120  while (self->graphOutlets[i]) {
121  atom_setlong(a+1, i);
122  outlet_anything(self->graphOutlets[i], gensym("graph.connect"), 2, a);
123  i++;
124  }
125  return kTTErrNone;
126 }
127 
128 
129 TTErr MaxGraphConnect(t_object* x, TTGraphObjectBasePtr audioSourceObject, TTUInt16 sourceOutletNumber)
130 {
132  long inletNumber = proxy_getinlet(SELF);
133 
134  return self->graphObject->connect(audioSourceObject, sourceOutletNumber, inletNumber);
135 }
136 
137 
138 TTErr MaxGraphDrop(t_object* x, long inletNumber, t_object* sourceMaxObject, long sourceOutletNumber)
139 {
141  TTGraphObjectBasePtr sourceObject = NULL;
142  TTErr err;
143 
144  err = (TTErr)long(object_method(sourceMaxObject, gensym("graph.object"), &sourceObject));
145  if (self->graphObject && sourceObject && !err)
146  err = self->graphObject->drop(sourceObject, sourceOutletNumber, inletNumber);
147  return err;
148 }
149 
150 
151 TTErr MaxGraphObject(t_object* x, TTGraphObjectBasePtr* returnedGraphObject)
152 {
154 
155  *returnedGraphObject = self->graphObject;
156  return kTTErrNone;
157 }
158 
159 
160 t_max_err wrappedClass_attrGet(WrappedInstancePtr self, t_object* attr, long* argc, t_atom** argv)
161 {
162  t_symbol* attrName = (t_symbol*)object_method(attr, _sym_getname);
163  TTValue v;
164  long i;
165  TTPtr rawpointer;
166  t_max_err err;
167 
168  err = hashtab_lookup(self->wrappedClassDefinition->maxNamesToTTNames, attrName, (t_object**)&rawpointer);
169  if (err)
170  return err;
171 
172  TTSymbol ttAttrName(rawpointer);
173 
174  self->graphObject->mKernel.get(ttAttrName, v);
175 
176  *argc = v.size();
177  if (!(*argv)) // otherwise use memory passed in
178  *argv = (t_atom *)sysmem_newptr(sizeof(t_atom) * v.size());
179 
180  for (i=0; i<v.size(); i++) {
181  if (v[i].type() == kTypeFloat32 || v[i].type() == kTypeFloat64) {
182  TTFloat64 value = v[i];
183  atom_setfloat(*argv+i, value);
184  }
185  else if (v[i].type() == kTypeSymbol) {
186  TTSymbol value = v[i];
187  atom_setsym(*argv+i, gensym((char*)value.c_str()));
188  }
189  else { // assume int
190  TTInt32 value = v[i];
191  atom_setlong(*argv+i, value);
192  }
193  }
194  return MAX_ERR_NONE;
195 }
196 
197 
198 t_max_err wrappedClass_attrSet(WrappedInstancePtr self, t_object* attr, long argc, t_atom* argv)
199 {
200  if (argc && argv) {
201  t_symbol* attrName = (t_symbol*)object_method(attr, _sym_getname);
202  TTValue v;
203  long i;
204  t_max_err err;
205  TTPtr ptr;
206 
207  err = hashtab_lookup(self->wrappedClassDefinition->maxNamesToTTNames, attrName, (t_object**)&ptr);
208  if (err)
209  return err;
210 
211  TTSymbol ttAttrName(ptr);
212 
213  v.resize(argc);
214  for (i=0; i<argc; i++) {
215  if (atom_gettype(argv+i) == A_LONG)
216  v[i] = (TTInt32)atom_getlong(argv+i);
217  else if (atom_gettype(argv+i) == A_FLOAT)
218  v[i] = atom_getfloat(argv+i);
219  else if (atom_gettype(argv+i) == A_SYM)
220  v[i] = TT(atom_getsym(argv+i)->s_name);
221  else
222  object_error(SELF, "bad type for attribute setter");
223  }
224  self->graphObject->mKernel.set(ttAttrName, v);
225  return MAX_ERR_NONE;
226  }
227  return MAX_ERR_GENERIC;
228 }
229 
230 
231 void wrappedClass_anything(WrappedInstancePtr self, t_symbol* s, long argc, t_atom* argv)
232 {
233  TTValue v;
234  TTSymbol ttName;
235  t_max_err err;
236 
237  err = hashtab_lookup(self->wrappedClassDefinition->maxNamesToTTNames, s, (t_object**)&ttName);
238  if (err) {
239  object_post(SELF, "no method found for %s", s->s_name);
240  return;
241  }
242 
243  if (argc && argv) {
244  TTValue v;
245 
246  v.resize(argc);
247  for (long i=0; i<argc; i++) {
248  if (atom_gettype(argv+i) == A_LONG)
249  v[i] = (TTInt32)atom_getlong(argv+i);
250  else if (atom_gettype(argv+i) == A_FLOAT)
251  v[i] = atom_getfloat(argv+i);
252  else if (atom_gettype(argv+i) == A_SYM)
253  v[i] = TT(atom_getsym(argv+i)->s_name);
254  else
255  object_error(SELF, "bad type for message arg");
256  }
257  self->graphObject->mKernel.send(ttName, v, v); // FIXME: TEMPORARY HACK WHILE WE TRANSITION FROM 1-ARG MESSAGES to 2-ARG MESSAGES
258 
259  // process the returned value for the dumpout outlet
260  {
261  long ac = v.size();
262 
263  if (ac) {
264  t_atom* av = (t_atom*)malloc(sizeof(t_atom) * ac);
265 
266  for (long i=0; i<ac; i++) {
267  if (v[i].type() == kTypeSymbol) {
268  TTSymbol ttSym = v[i];
269  atom_setsym(av+i, gensym((char*)ttSym.c_str()));
270  }
271  else if (v[i].type() == kTypeFloat32 || v[i].type() == kTypeFloat64) {
272  TTFloat64 f = v[i];
273  atom_setfloat(av+i, f);
274  }
275  else {
276  TTInt32 l = v[i];
277  atom_setfloat(av+i, l);
278  }
279  }
280  object_obex_dumpout(self, s, ac, av);
281  free(av);
282  }
283  }
284  }
285  else
286  self->graphObject->mKernel.send(ttName);
287 }
288 
289 
290 // Method for Assistance Messages
291 void wrappedClass_assist(WrappedInstancePtr self, void *b, long msg, long arg, char *dst)
292 {
293  if (msg==1) // Inlets
294  strcpy(dst, "multichannel input and control messages");
295  else if (msg==2) { // Outlets
296  if (arg == 0)
297  strcpy(dst, "multichannel output");
298  else
299  strcpy(dst, "dumpout");
300  }
301 }
302 
303 
304 
305 
306 
307 
308 TTErr wrapAsMaxGraph(TTSymbol& ttClassName, char* maxClassName, WrappedClassPtr* c)
309 {
310  return wrapAsMaxGraph(ttClassName, maxClassName, c, (WrappedClassOptionsPtr)NULL);
311 }
312 
313 TTErr wrapAsMaxGraph(TTSymbol& ttClassName, char* maxClassName, WrappedClassPtr* c, WrappedClassOptionsPtr options)
314 {
315  TTObjectBasePtr o = NULL;
316  TTValue v;
317  TTUInt16 numChannels = 1;
318  WrappedClass* wrappedMaxClass = NULL;
319  TTSymbol name;
320  TTCString nameCString = NULL;
321  t_symbol* nameMaxSymbol = NULL;
322  TTUInt32 nameSize = 0;
323 
324  common_symbols_init();
325  TTGraphInit();
326 
327  if (!wrappedMaxClasses)
328  wrappedMaxClasses = hashtab_new(0);
329 
330  wrappedMaxClass = new WrappedClass;
331  wrappedMaxClass->maxClassName = gensym(maxClassName);
332  wrappedMaxClass->maxClass = class_new( maxClassName,
333  (method)wrappedClass_new,
334  (method)wrappedClass_free,
335  sizeof(WrappedInstance),
336  (method)0L,
337  A_GIMME,
338  0);
339  wrappedMaxClass->ttClassName = ttClassName;
340  wrappedMaxClass->validityCheck = NULL;
341  wrappedMaxClass->validityCheckArgument = NULL;
342  wrappedMaxClass->options = options;
343  wrappedMaxClass->maxNamesToTTNames = hashtab_new(0);
344 
345  // Create a temporary instance of the class so that we can query it.
346  TTObjectBaseInstantiate(ttClassName, &o, numChannels);
347 
348  o->getMessageNames(v);
349  for (TTUInt16 i=0; i<v.size(); i++) {
350  name = v[i];
351  nameSize = strlen(name.c_str());
352  nameCString = new char[nameSize+1];
353  strncpy_zero(nameCString, name.c_str(), nameSize+1);
354 
355  nameMaxSymbol = gensym(nameCString);
356  hashtab_store(wrappedMaxClass->maxNamesToTTNames, nameMaxSymbol, (t_object*)name.rawpointer());
357  class_addmethod(wrappedMaxClass->maxClass, (method)wrappedClass_anything, nameCString, A_GIMME, 0);
358 
359  delete nameCString;
360  nameCString = NULL;
361  }
362 
363  o->getAttributeNames(v);
364  for (TTUInt16 i=0; i<v.size(); i++) {
365  TTAttributePtr attr = NULL;
366  t_symbol* maxType = _sym_long;
367 
368  name = v[i];
369  nameSize = strlen(name.c_str());
370  nameCString = new char[nameSize+1];
371  strncpy_zero(nameCString, name.c_str(), nameSize+1);
372  nameMaxSymbol = gensym(nameCString);
373 
374  if (name == TT("MaxNumChannels"))
375  continue; // don't expose these attributes to Max users
376  if (name == TT("Bypass")) {
377  if (wrappedMaxClass->options && !wrappedMaxClass->options->lookup(TT("generator"), v))
378  continue; // generators don't have inputs, and so don't really provide a bypass
379  }
380 
381  o->findAttribute(name, &attr);
382 
383  if (attr->type == kTypeFloat32)
384  maxType = _sym_float32;
385  else if (attr->type == kTypeFloat64)
386  maxType = _sym_float64;
387  else if (attr->type == kTypeSymbol || attr->type == kTypeString)
388  maxType = _sym_symbol;
389 
390  hashtab_store(wrappedMaxClass->maxNamesToTTNames, nameMaxSymbol, (t_object*)name.rawpointer());
391  class_addattr(wrappedMaxClass->maxClass, attr_offset_new(nameCString, maxType, 0, (method)wrappedClass_attrGet, (method)wrappedClass_attrSet, 0));
392 
393  // Add display styles for the Max 5 inspector
394  if (attr->type == kTypeBoolean)
395  CLASS_ATTR_STYLE(wrappedMaxClass->maxClass, (char*)name.c_str(), 0, "onoff");
396  if (name == TT("fontFace"))
397  CLASS_ATTR_STYLE(wrappedMaxClass->maxClass, "fontFace", 0, "font");
398 
399  delete nameCString;
400  nameCString = NULL;
401  }
402 
404 
405  class_addmethod(wrappedMaxClass->maxClass, (method)MaxGraphReset, "graph.reset", A_CANT, 0);
406  class_addmethod(wrappedMaxClass->maxClass, (method)MaxGraphSetup, "graph.setup", A_CANT, 0);
407  class_addmethod(wrappedMaxClass->maxClass, (method)MaxGraphConnect, "graph.connect", A_OBJ, A_LONG, 0);
408  class_addmethod(wrappedMaxClass->maxClass, (method)object_obex_dumpout, "dumpout", A_CANT, 0);
409  class_addmethod(wrappedMaxClass->maxClass, (method)wrappedClass_assist, "assist", A_CANT, 0L);
410  class_addmethod(wrappedMaxClass->maxClass, (method)stdinletinfo, "inletinfo", A_CANT, 0);
411 
412  class_register(_sym_box, wrappedMaxClass->maxClass);
413  if (c)
414  *c = wrappedMaxClass;
415 
416  hashtab_store(wrappedMaxClasses, wrappedMaxClass->maxClassName, (t_object*)wrappedMaxClass);
417  return kTTErrNone;
418 }
419 
420 
421 TTErr wrapAsMaxGraph(TTSymbol& ttClassName, char* maxClassName, WrappedClassPtr* c, TTValidityCheckFunction validityCheck)
422 {
423  TTErr err = wrapAsMaxGraph(ttClassName, maxClassName, c);
424 
425  if (!err) {
426  (*c)->validityCheck = validityCheck;
427  (*c)->validityCheckArgument = (*c)->maxClass;
428  }
429  return err;
430 }
431 
432 TTErr wrapAsMaxGraph(TTSymbol& ttClassName, char* maxClassName, WrappedClassPtr* c, TTValidityCheckFunction validityCheck, WrappedClassOptionsPtr options)
433 {
434  TTErr err = wrapAsMaxGraph(ttClassName, maxClassName, c, options);
435 
436  if (!err) {
437  (*c)->validityCheck = validityCheck;
438  (*c)->validityCheckArgument = (*c)->maxClass;
439  }
440  return err;
441 }
442 
443 
444 TTErr wrapAsMaxGraph(TTSymbol& ttClassName, char* maxClassName, WrappedClassPtr* c, TTValidityCheckFunction validityCheck, TTPtr validityCheckArgument)
445 {
446  TTErr err = wrapAsMaxGraph(ttClassName, maxClassName, c);
447 
448  if (!err) {
449  (*c)->validityCheck = validityCheck;
450  (*c)->validityCheckArgument = validityCheckArgument;
451  }
452  return err;
453 }
454 
455 TTErr wrapAsMaxGraph(TTSymbol& ttClassName, char* maxClassName, WrappedClassPtr* c, TTValidityCheckFunction validityCheck, TTPtr validityCheckArgument, WrappedClassOptionsPtr options)
456 {
457  TTErr err = wrapAsMaxGraph(ttClassName, maxClassName, c, options);
458 
459  if (!err) {
460  (*c)->validityCheck = validityCheck;
461  (*c)->validityCheckArgument = validityCheckArgument;
462  }
463  return err;
464 }
465 
The TTGraphObjectBase wraps a TTDSP object such that it is possible to build a dynamic graph of audio...
std::uint16_t TTUInt16
16 bit unsigned integer
Definition: TTBase.h:176
TTErr TTObjectBaseRelease(TTObjectBasePtr *anObject)
DEPRECATED.
TTDataType type
The data type of the attribute value.
Definition: TTAttribute.h:84
size_type size() const noexcept
Return the number of elements.
This class represents a single attribute, as used by the TTObjectBase class.
Definition: TTAttribute.h:79
Base class for all first-class Jamoma objects.
Definition: TTObjectBase.h:109
Symbol type.
Definition: TTBase.h:282
double TTFloat64
64 bit floating point number
Definition: TTBase.h:188
#define TT
This macro is defined as a shortcut for doing a lookup in the symbol table.
Definition: TTSymbol.h:155
WrappedInstance * WrappedInstancePtr
Pointer to a wrapped instance of our object.
void * TTPtr
A generic pointer.
Definition: TTBase.h:248
std::int16_t TTInt16
16 bit signed integer
Definition: TTBase.h:175
TTPtr rawpointer() const
Get the value of the raw pointer into the symbol table.
Definition: TTSymbol.h:134
TTErr(* TTValidityCheckFunction)(const TTPtr data)
A type that can be used to store a pointer to a validity checking function.
Definition: MaxAudioGraph.h:36
64-bit floating point
Definition: TTBase.h:272
The TTSymbol class is used to represent a string and efficiently pass and compare that string...
Definition: TTSymbol.h:26
void getMessageNames(TTValue &messageNameList)
Return a list of names of the available messages.
TTErr TTObjectBaseInstantiate(const TTSymbol className, TTObjectBasePtr *returnedObjectPtr, const TTValue arguments)
DEPRECATED.
Boolean (1/0) or (true/false) flag.
Definition: TTBase.h:281
const char * c_str() const
Return a pointer to the internal string as a C-string.
Definition: TTSymbol.h:77
std::int32_t TTInt32
32 bit signed integer
Definition: TTBase.h:177
32-bit floating point
Definition: TTBase.h:271
Something went wrong, but what exactly is not known. Typically used for context-specific problems...
Definition: TTBase.h:344
TTErr
Jamoma Error Codes Enumeration of error codes that might be returned by any of the TTBlue functions a...
Definition: TTBase.h:342
std::uint32_t TTUInt32
32 bit unsigned integer
Definition: TTBase.h:178
No Error.
Definition: TTBase.h:343
TTErr drop(TTGraphObjectBasePtr anObject, TTUInt16 fromOutletNumber=0, TTUInt16 toInletNumber=0)
Drop a source from the list of objects from which to request audio.
String type.
Definition: TTBase.h:285
void resize(size_type n)
Change the number of elements.
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34
void getAttributeNames(TTValue &attributeNameList)
Return a list of names of the available attributes.
TTErr findAttribute(const TTSymbol name, TTAttribute **attr)
Find an attribute.
unsigned char TTUInt8
8 bit unsigned integer (char)
Definition: TTBase.h:174