Jamoma API  0.6.0.a19
TTSubscriber.cpp
Go to the documentation of this file.
1 /** @file
2  *
3  * @ingroup modularLibrary
4  *
5  * @brief A contextual subscriber to register TTObject as TTNode in a TTNodeDirectory
6  *
7  * @details TODO: Add more info
8  *
9  * @authors Théo de la Hogue
10  *
11  * @copyright © 2010, Théo de la Hogue @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 "TTSubscriber.h"
18 
19 #define thisTTClass TTSubscriber
20 #define thisTTClassName "Subscriber"
21 #define thisTTClassTags "node, subscriber"
22 
23 TT_MODULAR_CONSTRUCTOR,
24 mRelativeAddress(kTTAdrsEmpty),
25 mNodeAddress(kTTAdrsEmpty),
26 mContextAddress(kTTAdrsEmpty),
27 mNewInstanceCreated(NO),
28 mNewContextInstanceCreated(NO),
29 mSubscribed(NO),
30 mExposedMessages(NULL),
31 mExposedAttributes(NULL)
32 {
33  mObject = arguments[0];
34  mRelativeAddress = arguments[1];
35 
36  addAttribute(RelativeAddress, kTypeSymbol);
37  addAttribute(NodeAddress, kTypeSymbol);
38  addAttribute(ContextAddress, kTypeSymbol);
39  addAttribute(NewInstanceCreated, kTypeBoolean);
40  addAttribute(NewContextInstanceCreated, kTypeBoolean);
41  addAttribute(Subscribed, kTypeBoolean);
42 
43  addAttributeProperty(RelativeAddress, readOnly, YES);
44  addAttributeProperty(NodeAddress, readOnly, YES);
45  addAttributeProperty(ContextAddress, readOnly, YES);
46  addAttributeProperty(NewInstanceCreated, readOnly, YES);
47  addAttributeProperty(Subscribed, readOnly, YES);
48 
49  addMessageWithArguments(Subscribe);
50  addMessage(Unsubscribe);
51 
52  mExposedMessages = new TTHash();
53  mExposedAttributes = new TTHash();
54 }
55 
56 TTSubscriber::~TTSubscriber()
57 {
58  if (mSubscribed)
59  Unsubscribe();
60 
61  delete mExposedMessages;
62  delete mExposedAttributes;
63 }
64 
65 TTErr TTSubscriber::Subscribe(const TTValue& inputValue, TTValue& outputValue)
66 {
67  TTListPtr aContextList = NULL;
68  TTNodeDirectoryPtr aDirectory = accessApplicationLocalDirectory; // only subscribe into local directory
69  TTAddress contextAddress, absoluteAddress;
70  TTValue aTempValue, args;
71  TTPtr ourContext, hisContext;
72  TTList aNodeList;
73  TTNodePtr aNode, aContextNode;
74  TTObject hisObject;
75  TTErr err;
76 
77  mNewInstanceCreated = NO;
78  mNewContextInstanceCreated = NO;
79 
80  aContextList = TTListPtr((TTPtr)inputValue[0]);
81  TT_ASSERT("ContextList passed to TTSubscriber::Subscribe is not NULL", aContextList);
82 
83  if (aContextList) {
84 
85  // if there is no subcription, the local directory exists and the address is relative
86  if (!mSubscribed && aDirectory && mRelativeAddress.getType() == kAddressRelative) {
87 
88  // register each Context of the list as TTNode in the tree structure (if they don't exist yet)
89  aContextNode = this->registerContextList(aContextList);
90 
91  // Build the node at /contextAddress/parent/node
92  if (aContextNode) {
93 
94  // Get our Context
95  ourContext = aContextNode->getContext();
96 
97  // Make absolute address
98  aContextNode->getAddress(contextAddress);
99  absoluteAddress = contextAddress.appendAddress(this->mRelativeAddress);
100 
101  // Check if the node exists
102  err = aDirectory->Lookup(absoluteAddress, aNodeList, &aNode);
103 
104  // if the node doesn't exist, create it
105  if (err) {
106  if (mObject.valid())
107  aDirectory->TTNodeCreate(absoluteAddress, mObject, ourContext, &aNode, &this->mNewInstanceCreated);
108  else
109  return kTTErrGeneric;
110  }
111  // else the node already exists
112  else if (mObject.valid()) {
113 
114  // Get his refered object
115  hisObject = aNode->getObject();
116 
117  // if there is no refered object
118  if (!hisObject.valid()) {
119 
120  // set our object instead
121  aNode->setObject(mObject);
122 
123  // get his context
124  hisContext = aNode->getContext();
125 
126  // if there is no context
127  if (!hisContext) {
128 
129  // set our context instead
130  aTempValue.clear();
131  aTempValue.append((TTPtr)ourContext);
132  aNode->setContext(ourContext);
133  }
134 
135  // notify for the creation of the address when replacing the Object and Context
136  // !!! Maybe this could introduce confusion for namespace observer !!!
137  // introduce a new flag (kAddressObjectChanged) ?
138  aDirectory->notifyObservers(absoluteAddress, aNode, kAddressCreated);
139  }
140  // else there is already an object
141  else {
142 
143  // if it is the ContextNode, do nothing (our object can't be refered)
144  // else create another instance to refer our object
145  if (aNode != aContextNode)
146  aDirectory->TTNodeCreate(absoluteAddress, mObject, ourContext, &aNode, &this->mNewInstanceCreated);
147  }
148  }
149 
150  aNode->getAddress(this->mNodeAddress);
151  aContextNode->getAddress(this->mContextAddress);
152  aNode->getAddress(this->mRelativeAddress, this->mContextAddress);
153 
154  mSubscribed = YES;
155 
156  // return the node and the context node to work on them
157  outputValue = TTValue((TTPtr)aNode);
158  outputValue.append((TTPtr)aContextNode);
159 
160  return kTTErrNone;
161  }
162  }
163  }
164 
165  return kTTErrGeneric;
166 }
167 
168 TTErr TTSubscriber::Unsubscribe()
169 {
170  TTNodeDirectoryPtr aDirectory = accessApplicationLocalDirectory; // only subscribes into local directory
171  TTNodePtr aNode = NULL;
172  TTList childrenList;
173  TTValue aTempValue;
174  TTValue v, keys;
175  TTAddress objectAddress, nameToAddress;
176  TTSymbol k;
177  TTObject anObject;
178  TTUInt32 i;
179  TTErr err;
180 
181  if (mObject.valid()) {
182 
183  err = aDirectory->getTTNode(mNodeAddress, &aNode);
184  if (!err && aNode) {
185 
186  anObject = aNode->getObject();
187 
188  if (anObject == mObject) {
189 
190  // If node have no more child : destroy the node (except for root)
191  aNode->getChildren(S_WILDCARD, S_WILDCARD, childrenList);
192  if (childrenList.isEmpty() && aNode != aDirectory->getRoot())
193  aDirectory->TTNodeRemove(mNodeAddress);
194 
195  // else notify for the unregistration of the object
196  // !!! Maybe this could introduce confusion for namespace observer !!!
197  // introduce a new flag (kAddressObjectUnregistered) ?
198  else {
199 
200  // remove alias for TTContainer object before
201  if (anObject.name() == kTTSym_Container)
202  anObject.send("AliasRemove");
203 
204  // notify
205  aDirectory->notifyObservers(mNodeAddress, aNode, kAddressDestroyed);
206 
207  // set empty object
208  aNode->setObject();
209  }
210  }
211  }
212  }
213 
214  // Clear exposed Messages
215  err = mExposedMessages->getKeys(keys);
216  if (!err) {
217  for (i = 0; i < keys.size(); i++) {
218 
219  k = keys[i];
220  mExposedMessages->lookup(k, v);
221 
222  convertUpperCasedNameInAddress(k, nameToAddress);
223  objectAddress = mNodeAddress.appendAddress(nameToAddress);
224 
225  aDirectory->TTNodeRemove(objectAddress);
226 
227  mExposedMessages->remove(k);
228  }
229  }
230 
231  // Clear exposed Attributes
232  err = mExposedAttributes->getKeys(keys);
233  if (!err) {
234  for (i = 0; i < keys.size(); i++) {
235 
236  k = keys[i];
237  mExposedAttributes->lookup(k, v);
238 
239  convertUpperCasedNameInAddress(k, nameToAddress);
240  objectAddress = mNodeAddress.appendAddress(nameToAddress);
241 
242  aDirectory->TTNodeRemove(objectAddress);
243 
244  mExposedAttributes->remove(k);
245  }
246  }
247 
248  return kTTErrNone;
249 }
250 
251 TTNodePtr TTSubscriber::registerContextList(TTListPtr aContextList)
252 {
253  TTNodeDirectoryPtr aDirectory = accessApplicationLocalDirectory; // only subscribes into local directory
254  TTValue args;
255  TTSymbol formatedContextSymbol;
256  TTAddress relativeContextAddress, contextAddress, lowerContextAddress;
257  TTList contextNodeList, attributesAccess;
258  TTNodePtr contextNode, lowerContextNode;
259  TTPtr aContext, lowerContext;
260  TTObject empty;
261  TTBoolean found;
262 
263  // Build the /topContext/subContext/.../contextName/ structure
264  // Check each context instance looking at the patcher.
265 
266  // start by the root
267  contextNode = aDirectory->getRoot();
268 
269  // if there are contexts in the context list
270  if(!aContextList->isEmpty()){
271 
272  // for each context of the context list
273  for (aContextList->begin(); aContextList->end(); aContextList->next()){
274 
275  // get the context symbol as a relative context address
276  formatedContextSymbol = aContextList->current()[0];
277  relativeContextAddress = TTAddress(formatedContextSymbol);
278 
279  // get the context
280  aContext = aContextList->current()[1];
281 
282  // if one is missing stop the registration
283  if (relativeContextAddress == kTTAdrsEmpty || !aContext)
284  return NULL;
285 
286  // 1. Look for relativeContextName.* in order to find a child with the same context
287  contextNode->getChildren(relativeContextAddress.getName(), S_WILDCARD, contextNodeList);
288 
289  /* Former step 1.
290  This introduced a bug when a user edits /anAddress.I several times.
291  However this is faster than the new implementation because now we
292  always look for all children to find the context-like child...
293 
294  if (relativeContextAddress.getInstance() == NO_INSTANCE)
295  err = contextNode->getChildren(relativeContextAddress.getName(), S_WILDCARD, contextNodeList);
296  else
297  err = contextNode->getChildren(relativeContextAddress.getName(), relativeContextAddress.getInstance(), contextNodeList);
298  */
299 
300  // 3. For each node of the contextNodeList, check the context
301  // if one matches, keep it else we have to create the node
302  found = false;
303  lowerContextNode = NULL;
304  for (contextNodeList.begin(); contextNodeList.end(); contextNodeList.next()) {
305 
306  lowerContextNode = TTNodePtr((TTPtr)contextNodeList.current()[0]);
307 
308  // Check if objects are the same
309  lowerContext = lowerContextNode->getContext();
310 
311  if (aContext == lowerContext) {
312  found = true;
313  break;
314  }
315 
316  // else if the instance are the same
317  else if (relativeContextAddress.getInstance() == lowerContextNode->getInstance()) {
318 
319  // if there is no registered object
320  if (!lowerContextNode->getObject().valid()) {
321  found = true;
322  break;
323  }
324  }
325  }
326 
327  // if no node exists : create a new instance for this context
328  if (!found) {
329 
330  contextNode->getAddress(contextAddress);
331 
332  // don't create another root !
333  if (relativeContextAddress != kTTAdrsRoot) {
334 
335  lowerContextAddress = contextAddress.appendAddress(relativeContextAddress);
336 
337  // Make a TTNode with no object
338  aDirectory->TTNodeCreate(lowerContextAddress, empty, aContext, &contextNode, &this->mNewContextInstanceCreated);
339 
340  }
341  else {
342  contextNode = aDirectory->getRoot();
343 
344  // if the current context of the root is NULL : set our context
345  if (!contextNode->getContext())
346  contextNode->setContext(aContext);
347  }
348  }
349  else
350  contextNode = lowerContextNode;
351  } // end for
352  } // end if
353 
354  return contextNode;
355 }
356 
357 TTErr TTSubscriber::exposeMessage(TTObject anObject, TTSymbol messageName, TTObject& returnedData)
358 {
359  TTNodeDirectoryPtr aDirectory = accessApplicationLocalDirectory; // only subscribes into local directory
360  TTValue baton, v;
361  TTAddress nameToAddress, dataAddress;
362  TTNodePtr aNode;
363  TTBoolean nodeCreated;
364  TTPtr aContext;
365 
366  aDirectory->getTTNode(mNodeAddress, &aNode);
367  if (aNode) {
368 
369  // Create TTData
370  returnedData = TTObject(kTTSym_Data, kTTSym_message);
371 
372  baton = TTValue(TTObject(this), messageName);
373  returnedData.set(kTTSym_baton, baton);
374  returnedData.set(kTTSym_function, TTPtr(&TTSubscriberMessageReturnValueCallback));
375 
376  // register TTData into the local tree
377  convertUpperCasedNameInAddress(messageName, nameToAddress);
378  dataAddress = mNodeAddress.appendAddress(nameToAddress);
379  aContext = aNode->getContext();
380  accessApplicationLocalDirectory->TTNodeCreate(dataAddress, returnedData, aContext, &aNode, &nodeCreated);
381 
382  // store TTData and given object
383  v = TTValue(returnedData);
384  v.append(anObject);
385  mExposedMessages->append(messageName, v);
386 
387  return kTTErrNone;
388  }
389 
390  return kTTErrGeneric;
391 }
392 
393 TTErr TTSubscriber::exposeAttribute(TTObject anObject, TTSymbol attributeName, TTSymbol service, TTObject& returnedData)
394 {
395  TTNodeDirectoryPtr aDirectory = accessApplicationLocalDirectory; // only subscribes into local directory
396  TTValue args, baton, v, none;
397  TTObject observeValueCallback; // to set the data when an object attribute changed
398  TTAttributePtr anAttribute = NULL;
399  TTAddress nameToAddress, dataAddress;
400  TTNodePtr aNode;
401  TTBoolean nodeCreated;
402  TTPtr aContext;
403  TTErr err;
404 
405  aDirectory->getTTNode(mNodeAddress, &aNode);
406  if (aNode) {
407 
408  if (service == kTTSym_parameter || service == kTTSym_return) {
409 
410  // Create TTData
411  returnedData = TTObject(kTTSym_Data, service);
412 
413  baton = TTValue(TTObject(this), attributeName);
414  returnedData.set(kTTSym_baton, baton);
415  returnedData.set(kTTSym_function, TTPtr(&TTSubscriberMessageReturnValueCallback));
416 
417  // register TTData into the local tree
418  convertUpperCasedNameInAddress(attributeName, nameToAddress);
419  dataAddress = mNodeAddress.appendAddress(nameToAddress);
420  aContext = aNode->getContext();
421  accessApplicationLocalDirectory->TTNodeCreate(dataAddress, returnedData, aContext, &aNode, &nodeCreated);
422 
423  // observe the attribute of the object
424  err = anObject.instance()->findAttribute(attributeName, &anAttribute);
425  if (!err) {
426 
427  observeValueCallback = TTObject("callback");
428 
429  baton = TTValue(TTObject(this), attributeName);
430  observeValueCallback.set(kTTSym_baton, baton);
431  observeValueCallback.set(kTTSym_function, TTPtr(&TTSubscriberAttributeObserveValueCallback));
432 
433  anAttribute->registerObserverForNotifications(observeValueCallback);
434  }
435 
436  // store TTData and given object
437  v = TTValue(returnedData);
438  v.append(anObject);
439  mExposedAttributes->append(attributeName, v);
440 
441  return kTTErrNone;
442  }
443  }
444  return kTTErrGeneric;
445 }
446 
447 TTErr TTSubscriber::unexposeMessage(TTSymbol messageName)
448 {
449  TTNodeDirectoryPtr aDirectory = accessApplicationLocalDirectory; // only subscribes into local directory
450  TTValue storedObject;
451  TTAddress objectAddress, nameToAddress;
452 
453  if (!mExposedMessages->lookup(messageName, storedObject)) {
454 
455  convertUpperCasedNameInAddress(messageName, nameToAddress);
456  objectAddress = mNodeAddress.appendAddress(nameToAddress);
457 
458  aDirectory->TTNodeRemove(objectAddress);
459 
460  mExposedMessages->remove(messageName);
461 
462  return kTTErrNone;
463  }
464 
465  return kTTErrGeneric;
466 }
467 
468 TTErr TTSubscriber::unexposeAttribute(TTSymbol attributeName)
469 {
470  TTNodeDirectoryPtr aDirectory = accessApplicationLocalDirectory; // only subscribes into local directory
471  TTValue storedObject;
472  TTAddress objectAddress, nameToAddress;
473 
474  if (!mExposedAttributes->lookup(attributeName, storedObject)) {
475 
476  convertUpperCasedNameInAddress(attributeName, nameToAddress);
477  objectAddress = mNodeAddress.appendAddress(nameToAddress);
478 
479  aDirectory->TTNodeRemove(objectAddress);
480 
481  mExposedAttributes->remove(attributeName);
482 
483  return kTTErrNone;
484  }
485 
486  return kTTErrGeneric;
487 }
488 
489 #if 0
490 #pragma mark -
491 #pragma mark Some Methods
492 #endif
493 
494 TTErr TTSubscriberMessageReturnValueCallback(const TTValue& baton, const TTValue& data)
495 {
496  TTObject o;
497  TTSubscriberPtr aSubscriber;
498  TTSymbol messageName;
499  TTValue v, none;
500  TTErr err;
501 
502  // unpack baton (a #TTSubscriber)
503  o = baton[0];
504  aSubscriber = (TTSubscriberPtr)o.instance();
505  messageName = baton[1];
506 
507  // get the exposed TTObject
508  err = aSubscriber->mExposedMessages->lookup(messageName, v);
509 
510  if (!err) {
511  o = v[1];
512 
513  // protect data
514  v = data;
515 
516  // send data
517  o.send(messageName, data);
518 
519  return kTTErrNone;
520  }
521 
522  return kTTErrGeneric;
523 }
524 
525 TTErr TTSubscriberAttributeReturnValueCallback(const TTValue& baton, const TTValue& data)
526 {
527  TTObject o;
528  TTSubscriberPtr aSubscriber;
529  TTSymbol attributeName;
530  TTValue v;
531  TTErr err;
532 
533  // unpack baton (a #TTSubscriber)
534  o = baton[0];
535  aSubscriber = (TTSubscriberPtr)o.instance();
536  attributeName = baton[1];
537 
538  // get the exposed TTObject
539  err = aSubscriber->mExposedAttributes->lookup(attributeName, v);
540 
541  if (!err) {
542  o = v[1];
543 
544  // protect data
545  v = data;
546 
547  // send data
548  o.set(attributeName, data);
549 
550  return kTTErrNone;
551  }
552 
553  return kTTErrGeneric;
554 }
555 
556 TTErr TTSubscriberAttributeObserveValueCallback(const TTValue& baton, const TTValue& data)
557 {
558  TTObject o;
559  TTSubscriberPtr aSubscriber;
560  TTSymbol attributeName;
561  TTValue v;
562  TTErr err;
563 
564  // unpack baton (a #TTSubscriber)
565  o = baton[0];
566  aSubscriber = (TTSubscriberPtr)o.instance();
567  attributeName = baton[1];
568 
569  // get the TTData which expose the attribute
570  err = aSubscriber->mExposedAttributes->lookup(attributeName, v);
571 
572  if (!err) {
573  o = v[0];
574 
575  // protect data
576  v = data;
577 
578  // set data
579  o.set(kTTSym_value, data);
580 
581  return kTTErrNone;
582  }
583 
584  return kTTErrGeneric;
585 }
586 
TTAddress appendAddress(const TTAddress &toAppend)
Return a new TTAddress with the appended part.
Definition: TTAddress.h:167
TTErr setContext(TTPtr aContext)
Set the context of the node.
Definition: TTNode.cpp:277
bool TTBoolean
Boolean flag, same as Boolean on the Mac.
Definition: TTBase.h:167
TTErr registerObserverForNotifications(const TTObject &observingObject)
Register an observer.
this flag means that a TTNode have been destroyed in the tree structure
Definition: TTAddressBase.h:56
TTErr send(const TTSymbol aName)
Send a message to this object with no arguments.
Definition: TTObject.cpp:135
TTSymbol & getInstance()
Get the instance part.
Definition: TTAddress.h:124
TTErr TTNodeCreate(TTAddress &anAddress, TTObject &newObject, void *aContext, TTNodePtr *returnedTTNode, TTBoolean *nodeCreated)
Create a new TTNode, at the given location in the tree.
#define addAttribute(name, type)
A convenience macro to be used by subclasses for registering attributes with a custom getter...
Definition: TTAttribute.h:29
We build a directory of TTNodes, and you can request a pointer for any TTNode, or add an observer to ...
Definition: TTNode.h:59
TTErr setObject(TTObject anObject=TTObject())
Set the object of the node.
Definition: TTNode.cpp:271
TTPtr getContext()
Get a pointer to the context of this node.
Definition: TTNode.cpp:473
TTNodePtr getRoot()
Get the root of the TTNodeDirectory.
The TTAddress class is used to represent a string and efficiently pass and compare that string...
Definition: TTAddress.h:29
Create and use Jamoma object instances.
Definition: TTObject.h:29
TTObject & getObject()
Get the object binded by this node.
Definition: TTNode.cpp:468
size_type size() const noexcept
Return the number of elements.
this flag means that an address have no leading slash
Definition: TTAddressBase.h:64
This class represents a single attribute, as used by the TTObjectBase class.
Definition: TTAttribute.h:79
TTErr notifyObservers(TTAddress anAddress, TTNodePtr aNode, TTAddressNotificationFlag flag)
Notify life cycle observers that something appends below this TTNode.
TTErr TTNodeRemove(TTAddress &anAddress)
Remove a TTNodefrom the directory.
Maintain a collection of TTValue objects indexed by TTSymbol pointers.
Definition: TTHash.h:36
Symbol type.
Definition: TTBase.h:282
TTErr Lookup(TTAddress anAddress, TTList &returnedTTNodes, TTNodePtr *firstReturnedTTNode)
Find TTNodes by address.
void append(const T &anElementValueToAppend)
Insert a single TTElement at the end.
Definition: TTValue.h:243
void * TTPtr
A generic pointer.
Definition: TTBase.h:248
TTSymbol name() const
Return the name of this class.
Definition: TTObject.cpp:129
TTSymbol & getInstance()
Get the instance of the node.
Definition: TTNode.cpp:291
#define addAttributeProperty(attributeName, propertyName, initialValue)
A convenience macro to be used for registering properties of attributes.
Definition: TTAttribute.h:68
TTErr set(const TTSymbol aName, T aValue)
Set an attribute value for an object.
The TTSymbol class is used to represent a string and efficiently pass and compare that string...
Definition: TTSymbol.h:26
Boolean (1/0) or (true/false) flag.
Definition: TTBase.h:281
#define addMessageWithArguments(name)
A convenience macro to be used by subclasses for registering messages.
Definition: TTMessage.h:27
void clear()
Clear all values from the vector, leaving with size of 0.
Definition: TTValue.h:131
TTErr getAddress(TTAddress &returnedAddress, TTAddress from=kTTAdrsEmpty)
Get the address of the node.
Definition: TTNode.cpp:478
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
this flag means that a TTNode have been created in the tree structure
Definition: TTAddressBase.h:57
std::uint32_t TTUInt32
32 bit unsigned integer
Definition: TTBase.h:178
#define addMessage(name)
A convenience macro to be used by subclasses for registering messages.
Definition: TTMessage.h:19
TTErr getChildren(TTSymbol &name, TTSymbol &instance, TTList &returnedChildren)
Get a linklist of children of the node : select them by name and instance (use wilcards to select the...
Definition: TTNode.cpp:301
TTObjectBase * instance() const
Return a direct pointer to the internal instance.
Definition: TTObject.cpp:105
No Error.
Definition: TTBase.h:343
A contextual subscriber to register TTObject as TTNode in a TTNodeDirectory.
TTBoolean valid() const
Determine if the object contained by this TTObject is truly ready for use.
Definition: TTObject.cpp:179
TTSymbol & getName()
Get the name part.
Definition: TTAddress.h:118
We build a tree of TTNodes, and you can request a pointer for any TTNode, or add an observer to any T...
#define accessApplicationLocalDirectory
Access to the local application directory.
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34
TTErr getTTNode(const char *anAddress, TTNodePtr *returnedTTNode)
Given an address, return a pointer to a TTNode.
TTErr findAttribute(const TTSymbol name, TTAttribute **attr)
Find an attribute.