Jamoma API  0.6.0.a19
PureAudioGraph.cpp
1 /*
2  * MaxAudioGraph
3  * A thin wrapper of the Lydbaer audio system for use in the Cycling '74 Max/MSP environment.
4  * Includes an automated class wrapper to make Jamoma object's available as objects for Max/MSP.
5  * Copyright © 2008 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 "maxAudioGraph.h"
12 #include "ext_hashtab.h"
13 
14 
15 // Data Structure for this object
16 typedef struct _wrappedInstance {
17  t_object obj; ///< Max audio object header
18  WrappedClassPtr wrappedClassDefinition; ///< A pointer to the class definition
19  TTAudioGraphObjectPtr audioGraphObject; ///< The instance of the Jamoma object we are wrapping
20  TTPtr audioGraphOutlets[16]; ///< Array of outlets, may eventually want this to be more dynamic
21  TTPtr inlets[16]; ///< Array of proxy inlets beyond the first inlet
22 } WrappedInstance;
23 
24 typedef WrappedInstance* WrappedInstancePtr; ///< Pointer to a wrapped instance of our object.
25 
26 
27 /** A hash of all wrapped clases, keyed on the Max class name. */
28 static t_hashtab* wrappedMaxClasses = NULL;
29 
30 
31 ObjectPtr wrappedClass_new(SymbolPtr name, AtomCount argc, AtomPtr argv)
32 {
33  WrappedClass* wrappedMaxClass = NULL;
34  WrappedInstancePtr self = NULL;
35  TTValue v;
36  TTErr err = kTTErrNone;
37  TTUInt8 numInputs = 1;
38  TTUInt8 numOutputs = 1;
39  long attrstart = attr_args_offset(argc, argv); // support normal arguments
40 
41  // Find the WrappedClass
42  hashtab_lookup(wrappedMaxClasses, name, (ObjectPtr*)&wrappedMaxClass);
43 
44  // If the WrappedClass has a validity check defined, then call the validity check function.
45  // If it returns an error, then we won't instantiate the object.
46  if (wrappedMaxClass) {
47  if (wrappedMaxClass->validityCheck)
48  err = wrappedMaxClass->validityCheck(wrappedMaxClass->validityCheckArgument);
49  else
50  err = kTTErrNone;
51  }
52  else
53  err = kTTErrGeneric;
54 
55  if (!err)
56  self = (WrappedInstancePtr)object_alloc(wrappedMaxClass->maxClass);
57  if (self){
58 
59  if (wrappedMaxClass->options && !wrappedMaxClass->options->lookup(TT("argumentDefinesNumInlets"), v)) {
60  long argumentOffsetToDefineTheNumberOfInlets = v;
61  if ((attrstart-argumentOffsetToDefineTheNumberOfInlets > 0) && argv+argumentOffsetToDefineTheNumberOfInlets)
62  numInputs = atom_getlong(argv+argumentOffsetToDefineTheNumberOfInlets);
63  }
64  for (TTUInt16 i=numInputs-1; i>0; i--)
65  self->inlets[i-1] = proxy_new(self, i, NULL);
66 
67  object_obex_store((void*)self, _sym_dumpout, (object*)outlet_new(self, NULL)); // dumpout
68  if (wrappedMaxClass->options && !wrappedMaxClass->options->lookup(TT("argumentDefinesNumOutlets"), v)) {
69  long argumentOffsetToDefineTheNumberOfOutlets = v;
70  if ((attrstart-argumentOffsetToDefineTheNumberOfOutlets > 0) && argv+argumentOffsetToDefineTheNumberOfOutlets)
71  numOutputs = atom_getlong(argv+argumentOffsetToDefineTheNumberOfOutlets);
72  }
73  for (TTInt16 i=numOutputs-1; i>=0; i--)
74  self->audioGraphOutlets[i] = outlet_new(self, "audio.connect");
75 
76  self->wrappedClassDefinition = wrappedMaxClass;
77  v.setSize(3);
78  v.set(0, wrappedMaxClass->ttClassName);
79  v.set(1, numInputs);
80  v.set(2, numOutputs);
81  err = TTObjectInstantiate(TT("audio.object"), (TTObjectPtr*)&self->audioGraphObject, v);
82 
83  attr_args_process(self, argc, argv);
84  }
85  return ObjectPtr(self);
86 }
87 
88 
89 void wrappedClass_free(WrappedInstancePtr self)
90 {
91  if (self->audioGraphObject)
92  TTObjectRelease((TTObjectPtr*)&self->audioGraphObject);
93  // FIXME: leaking proxy inlets!
94 }
95 
96 
97 
98 // METHODS SPECIFIC TO AUDIO GRAPH EXTERNALS
99 
100 TTErr MaxAudioGraphReset(WrappedInstancePtr self, long vectorSize)
101 {
102  return self->audioGraphObject->reset();
103 }
104 
105 
107 {
108  Atom a[2];
109  TTUInt16 i=0;
110 
111  atom_setobj(a+0, ObjectPtr(self->audioGraphObject));
112  while (self->audioGraphOutlets[i]) {
113  atom_setlong(a+1, i);
114  outlet_anything(self->audioGraphOutlets[i], gensym("audio.connect"), 2, a);
115  i++;
116  }
117  return kTTErrNone;
118 }
119 
120 
121 TTErr MaxAudioGraphConnect(WrappedInstancePtr self, TTAudioGraphObjectPtr audioSourceObject, TTUInt16 sourceOutletNumber)
122 {
123  long inletNumber = proxy_getinlet(SELF);
124  return self->audioGraphObject->connect(audioSourceObject, sourceOutletNumber, inletNumber);
125 }
126 
127 
128 t_max_err wrappedClass_attrGet(WrappedInstancePtr self, ObjectPtr attr, AtomCount* argc, AtomPtr* argv)
129 {
130  SymbolPtr attrName = (SymbolPtr)object_method(attr, _sym_getname);
131  TTValue v;
132  AtomCount i;
133  TTSymbolPtr ttAttrName = NULL;
134  MaxErr err;
135 
136  err = hashtab_lookup(self->wrappedClassDefinition->maxAttrNamesToTTAttrNames, attrName, (ObjectPtr*)&ttAttrName);
137  if (err)
138  return err;
139 
140  self->audioGraphObject->mUnitGenerator->getAttributeValue(ttAttrName, v);
141 
142  *argc = v.getSize();
143  if (!(*argv)) // otherwise use memory passed in
144  *argv = (t_atom *)sysmem_newptr(sizeof(t_atom) * v.getSize());
145 
146  for (i=0; i<v.getSize(); i++) {
147  if(v.getType(i) == kTypeFloat32 || v.getType(i) == kTypeFloat64){
148  TTFloat64 value;
149  v.get(i, value);
150  atom_setfloat(*argv+i, value);
151  }
152  else if(v.getType(i) == kTypeSymbol){
153  TTSymbolPtr value = NULL;
154  v.get(i, &value);
155  atom_setsym(*argv+i, gensym((char*)value->getCString()));
156  }
157  else{ // assume int
158  TTInt32 value;
159  v.get(i, value);
160  atom_setlong(*argv+i, value);
161  }
162  }
163  return MAX_ERR_NONE;
164 }
165 
166 
167 t_max_err wrappedClass_attrSet(WrappedInstancePtr self, ObjectPtr attr, AtomCount argc, AtomPtr argv)
168 {
169  if (argc && argv) {
170  SymbolPtr attrName = (SymbolPtr)object_method(attr, _sym_getname);
171  TTValue v;
172  AtomCount i;
173  TTSymbolPtr ttAttrName = NULL;
174  MaxErr err;
175 
176  err = hashtab_lookup(self->wrappedClassDefinition->maxAttrNamesToTTAttrNames, attrName, (ObjectPtr*)&ttAttrName);
177  if (err)
178  return err;
179 
180  v.setSize(argc);
181  for (i=0; i<argc; i++) {
182  if(atom_gettype(argv+i) == A_LONG)
183  v.set(i, AtomGetInt(argv+i));
184  else if(atom_gettype(argv+i) == A_FLOAT)
185  v.set(i, atom_getfloat(argv+i));
186  else if(atom_gettype(argv+i) == A_SYM)
187  v.set(i, TT(atom_getsym(argv+i)->s_name));
188  else
189  object_error(SELF, "bad type for attribute setter");
190  }
191  self->audioGraphObject->mUnitGenerator->setAttributeValue(ttAttrName, v);
192  return MAX_ERR_NONE;
193  }
194  return MAX_ERR_GENERIC;
195 }
196 
197 
198 void wrappedClass_anything(WrappedInstancePtr self, SymbolPtr s, AtomCount argc, AtomPtr argv)
199 {
200  if (argc && argv) {
201  TTValue v;
202 
203  v.setSize(argc);
204  for (AtomCount i=0; i<argc; i++) {
205  if (atom_gettype(argv+i) == A_LONG)
206  v.set(i, AtomGetInt(argv+i));
207  else if (atom_gettype(argv+i) == A_FLOAT)
208  v.set(i, atom_getfloat(argv+i));
209  else if (atom_gettype(argv+i) == A_SYM)
210  v.set(i, TT(atom_getsym(argv+i)->s_name));
211  else
212  object_error(SELF, "bad type for message arg");
213  }
214  self->audioGraphObject->mUnitGenerator->sendMessage(TT(s->s_name), v);
215 
216  // process the returned value for the dumpout outlet
217  {
218  AtomCount ac = v.getSize();
219 
220  if (ac) {
221  AtomPtr av = (AtomPtr)malloc(sizeof(Atom) * ac);
222 
223  for (AtomCount i=0; i<ac; i++) {
224  if (v.getType() == kTypeSymbol){
225  TTSymbolPtr ttSym = NULL;
226  v.get(i, &ttSym);
227  atom_setsym(av+i, gensym((char*)ttSym->getCString()));
228  }
229  else if (v.getType() == kTypeFloat32 || v.getType() == kTypeFloat64) {
230  TTFloat64 f = 0.0;
231  v.get(i, f);
232  atom_setfloat(av+i, f);
233  }
234  else {
235  TTInt32 l = 0;
236  v.get(i, l);
237  atom_setfloat(av+i, l);
238  }
239  }
240  object_obex_dumpout(self, s, ac, av);
241  }
242  }
243  }
244  else
245  self->audioGraphObject->mUnitGenerator->sendMessage(TT(s->s_name));
246 }
247 
248 
249 // Method for Assistance Messages
250 void wrappedClass_assist(WrappedInstancePtr self, void *b, long msg, long arg, char *dst)
251 {
252  if (msg==1) // Inlets
253  strcpy(dst, "multichannel input and control messages");
254  else if (msg==2) { // Outlets
255  if (arg == 0)
256  strcpy(dst, "multichannel output");
257  else
258  strcpy(dst, "dumpout");
259  }
260 }
261 
262 
263 
264 
265 
266 
267 TTErr wrapAsMaxAudioGraph(TTSymbolPtr ttClassName, char* maxClassName, WrappedClassPtr* c)
268 {
269  return wrapAsMaxAudioGraph(ttClassName, maxClassName, c, (WrappedClassOptionsPtr)NULL);
270 }
271 
272 TTErr wrapAsMaxAudioGraph(TTSymbolPtr ttClassName, char* maxClassName, WrappedClassPtr* c, WrappedClassOptionsPtr options)
273 {
274  TTObject* o = NULL;
275  TTValue v;
276  TTUInt16 numChannels = 1;
277  WrappedClass* wrappedMaxClass = NULL;
278 
279  common_symbols_init();
280  TTAudioGraphInit();
281 
282  if(!wrappedMaxClasses)
283  wrappedMaxClasses = hashtab_new(0);
284 
285  wrappedMaxClass = new WrappedClass;
286  wrappedMaxClass->maxClassName = gensym(maxClassName);
287  wrappedMaxClass->maxClass = class_new( maxClassName,
288  (method)wrappedClass_new,
289  (method)wrappedClass_free,
290  sizeof(WrappedInstance),
291  (method)0L,
292  A_GIMME,
293  0);
294  wrappedMaxClass->ttClassName = ttClassName;
295  wrappedMaxClass->validityCheck = NULL;
296  wrappedMaxClass->validityCheckArgument = NULL;
297  wrappedMaxClass->options = options;
298  wrappedMaxClass->maxAttrNamesToTTAttrNames = hashtab_new(0);
299 
300  // Create a temporary instance of the class so that we can query it.
301  TTObjectInstantiate(ttClassName, &o, numChannels);
302 
303  o->getMessageNames(v);
304  for(TTUInt16 i=0; i<v.getSize(); i++){
305  TTSymbolPtr name = NULL;
306 
307  v.get(i, &name);
308  if(name == TT("updateMaxNumChannels") || name == TT("updateSr"))
309  continue; // don't expose these attributes to Max users
310 
311  class_addmethod(wrappedMaxClass->maxClass, (method)wrappedClass_anything, (char*)name->getCString(), A_GIMME, 0);
312  }
313 
314  o->getAttributeNames(v);
315  for (TTUInt16 i=0; i<v.getSize(); i++) {
316  TTSymbolPtr name = NULL;
317  TTAttributePtr attr = NULL;
318  SymbolPtr maxType = _sym_long;
319  TTCString nameCString = NULL;
320  SymbolPtr nameMaxSymbol = NULL;
321  TTUInt32 nameSize = 0;
322 
323  v.get(i, &name);
324  if(name == TT("maxNumChannels") || name == TT("processInPlace"))
325  continue; // don't expose these attributes to Max users
326 
327  o->findAttribute(name, &attr);
328 
329  if (attr->type == kTypeFloat32)
330  maxType = _sym_float32;
331  else if (attr->type == kTypeFloat64)
332  maxType = _sym_float64;
333  else if (attr->type == kTypeSymbol || attr->type == kTypeString)
334  maxType = _sym_symbol;
335 
336  // convert first letter to lower-case if it isn't already
337  nameSize = name->getString().length();
338  nameCString = new char[nameSize+1];
339  strncpy_zero(nameCString, name->getCString(), nameSize+1);
340  if (nameCString[0]>64 && nameCString[0]<91)
341  nameCString[0] += 32;
342  nameMaxSymbol = gensym(nameCString);
343 
344  hashtab_store(wrappedMaxClass->maxAttrNamesToTTAttrNames, nameMaxSymbol, ObjectPtr(name));
345  class_addattr(wrappedMaxClass->maxClass, attr_offset_new(nameCString, maxType, 0, (method)wrappedClass_attrGet, (method)wrappedClass_attrSet, NULL));
346 
347  // Add display styles for the Max 5 inspector
348  if (attr->type == kTypeBoolean)
349  CLASS_ATTR_STYLE(wrappedMaxClass->maxClass, (char*)name->getCString(), 0, "onoff");
350  if (name == TT("fontFace"))
351  CLASS_ATTR_STYLE(wrappedMaxClass->maxClass, "fontFace", 0, "font");
352  }
353 
354  TTObjectRelease(&o);
355 
356  class_addmethod(wrappedMaxClass->maxClass, (method)MaxAudioGraphReset, "audio.reset", A_CANT, 0);
357  class_addmethod(wrappedMaxClass->maxClass, (method)MaxAudioGraphSetup, "audio.setup", A_CANT, 0);
358  class_addmethod(wrappedMaxClass->maxClass, (method)MaxAudioGraphConnect, "audio.connect", A_OBJ, A_LONG, 0);
359  class_addmethod(wrappedMaxClass->maxClass, (method)object_obex_dumpout, "dumpout", A_CANT, 0);
360  class_addmethod(wrappedMaxClass->maxClass, (method)wrappedClass_assist, "assist", A_CANT, 0L);
361  class_addmethod(wrappedMaxClass->maxClass, (method)stdinletinfo, "inletinfo", A_CANT, 0);
362 
363  class_register(_sym_box, wrappedMaxClass->maxClass);
364  if (c)
365  *c = wrappedMaxClass;
366 
367  hashtab_store(wrappedMaxClasses, wrappedMaxClass->maxClassName, ObjectPtr(wrappedMaxClass));
368  return kTTErrNone;
369 }
370 
371 
372 TTErr wrapAsMaxAudioGraph(TTSymbolPtr ttClassName, char* maxClassName, WrappedClassPtr* c, TTValidityCheckFunction validityCheck)
373 {
374  TTErr err = wrapAsMaxAudioGraph(ttClassName, maxClassName, c);
375 
376  if (!err) {
377  (*c)->validityCheck = validityCheck;
378  (*c)->validityCheckArgument = (*c)->maxClass;
379  }
380  return err;
381 }
382 
383 TTErr wrapAsMaxAudioGraph(TTSymbolPtr ttClassName, char* maxClassName, WrappedClassPtr* c, TTValidityCheckFunction validityCheck, WrappedClassOptionsPtr options)
384 {
385  TTErr err = wrapAsMaxAudioGraph(ttClassName, maxClassName, c, options);
386 
387  if (!err) {
388  (*c)->validityCheck = validityCheck;
389  (*c)->validityCheckArgument = (*c)->maxClass;
390  }
391  return err;
392 }
393 
394 
395 TTErr wrapAsMaxAudioGraph(TTSymbolPtr ttClassName, char* maxClassName, WrappedClassPtr* c, TTValidityCheckFunction validityCheck, TTPtr validityCheckArgument)
396 {
397  TTErr err = wrapAsMaxAudioGraph(ttClassName, maxClassName, c);
398 
399  if (!err) {
400  (*c)->validityCheck = validityCheck;
401  (*c)->validityCheckArgument = validityCheckArgument;
402  }
403  return err;
404 }
405 
406 TTErr wrapAsMaxAudioGraph(TTSymbolPtr ttClassName, char* maxClassName, WrappedClassPtr* c, TTValidityCheckFunction validityCheck, TTPtr validityCheckArgument, WrappedClassOptionsPtr options)
407 {
408  TTErr err = wrapAsMaxAudioGraph(ttClassName, maxClassName, c, options);
409 
410  if (!err) {
411  (*c)->validityCheck = validityCheck;
412  (*c)->validityCheckArgument = validityCheckArgument;
413  }
414  return err;
415 }
416 
417 
418 // NOTE: DUPLICATIONS FROM THE MSP WRAPPER
419 
420 #ifdef __LP64__
421 TTInt64 AtomGetInt(AtomPtr a)
422 {
423  return (TTInt64)atom_getlong(a);
424 }
425 #else
426 int AtomGetInt(AtomPtr a)
427 {
428  return (int)atom_getlong(a);
429 }
430 #endif
std::uint16_t TTUInt16
16 bit unsigned integer
Definition: TTBase.h:176
TTUInt16 getSize() const
DEPRECATED.
Definition: TTValue.h:521
TTDataType type
The data type of the attribute value.
Definition: TTAttribute.h:84
TTErr MaxAudioGraphSetup(t_object *x)
Set up fresh connections from this object to nodes that are connected downstream. ...
TTErr wrapAsMaxAudioGraph(TTSymbol ttClassName, char *maxClassName, MaxAudioGraphWrappedClassPtr *c)
Wrap an AudioGraph class as a Max class.
TTErr MaxAudioGraphReset(t_object *x, long vectorSize)
Clear the list of source objects from which this object will try to pull audio.
Create and use Jamoma object instances.
Definition: TTObject.h:29
TTDataType getType(const TTUInt16 index=0) const
DEPRECATED.
Definition: TTValue.h:548
This class represents a single attribute, as used by the TTObjectBase class.
Definition: TTAttribute.h:79
Symbol type.
Definition: TTBase.h:282
double TTFloat64
64 bit floating point number
Definition: TTBase.h:188
TTErr MaxAudioGraphConnect(t_object *x, TTAudioGraphObjectBasePtr audioSourceObject, TTUInt16 sourceOutletNumber)
Method called when an upstream node is connected to this node.
#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::int64_t TTInt64
64 bit signed integer
Definition: TTBase.h:179
void setSize(const TTUInt16 arg)
DEPRECATED.
Definition: TTValue.h:528
std::int16_t TTInt16
16 bit signed integer
Definition: TTBase.h:175
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
void get(const TTUInt16 index, T &returnedElementValue) const
DEPRECATED.
Definition: TTValue.h:591
Boolean (1/0) or (true/false) flag.
Definition: TTBase.h:281
std::int32_t TTInt32
32 bit signed integer
Definition: TTBase.h:177
A thin wrapper of Jamoma AudioGraph for use in the Cycling '74 Max/MSP environment.
32-bit floating point
Definition: TTBase.h:271
void set(const TTUInt16 index, const T &anElementValue)
DEPRECATED.
Definition: TTValue.h:569
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
String type.
Definition: TTBase.h:285
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34
unsigned char TTUInt8
8 bit unsigned integer (char)
Definition: TTBase.h:174