Jamoma API  0.6.0.a19
j.plug.parameter.cpp
1 /*
2  * j.plug.parameter!
3  * Plugtastic parameter definition
4  * Copyright © 2010 by Timothy Place
5  *
6  * License: This code is licensed under the terms of the "New BSD License"
7  * http://creativecommons.org/licenses/BSD/
8  */
9 
10 #include "MaxGraph.h"
11 #include "TTGraphInput.h"
12 #include "TTAudioGraphAPI.h"
13 
14 
15 // Data Structure for this object
16 typedef struct PlugParameter {
17  Object obj;
18  TTGraphObjectBasePtr graphObject; // this _must_ be second
19  TTPtr graphOutlets[16]; // this _must_ be third (for the setup call)
20  TTDictionaryPtr graphDictionary;
21  ObjectPtr patcher;
22  ObjectPtr patcherview;
23  TTPtr qelem; // for clumping dirty events together
24 
25  SymbolPtr name;
26  double range[2];
27  SymbolPtr style;
28  double defaultValue;
29 };
30 typedef PlugParameter* PlugParameterPtr;
31 
32 
33 // Prototypes for methods
34 PlugParameterPtr PlugParameterNew (SymbolPtr msg, AtomCount argc, AtomPtr argv);
35 void PlugParameterFree (PlugParameterPtr self);
36 void PlugParameterStartTracking (PlugParameterPtr self);
37 MaxErr PlugParameterNotify (PlugParameterPtr self, SymbolPtr s, SymbolPtr msg, ObjectPtr sender, TTPtr data);
38 void PlugParameterQFn (PlugParameterPtr self);
39 void PlugParameterAssist (PlugParameterPtr self, void* b, long msg, long arg, char* dst);
40 void PlugParameterInt (PlugParameterPtr self, long value);
41 void PlugParameterFloat (PlugParameterPtr self, double value);
42 MaxErr PlugParameterSetName(PlugParameterPtr self, void* attr, AtomCount argc, AtomPtr argv);
43 MaxErr PlugParameterSetRange(PlugParameterPtr self, void* attr, AtomCount argc, AtomPtr argv);
44 MaxErr PlugParameterSetStyle(PlugParameterPtr self, void* attr, AtomCount argc, AtomPtr argv);
45 MaxErr PlugParameterSetDefault(PlugParameterPtr self, void* attr, AtomCount argc, AtomPtr argv);
46 
47 
48 // Globals
49 static ClassPtr sPlugParameterClass;
50 
51 
52 /************************************************************************************/
53 // Main() Function
54 
55 int TTCLASSWRAPPERMAX_EXPORT main(void)
56 {
57  ClassPtr c;
58 
59  TTAudioGraphInit();
60  common_symbols_init();
61 
62  c = class_new((char*)"j.plug.parameter-", (method)PlugParameterNew, (method)PlugParameterFree, sizeof(PlugParameter), (method)0L, A_GIMME, 0);
63 
64  class_addmethod(c, (method)PlugParameterInt, "int", A_LONG, 0);
65  class_addmethod(c, (method)PlugParameterFloat, "float", A_LONG, 0);
66  //class_addmethod(c, (method)PlugParameterList, "list", A_GIMME, 0);
67  //class_addmethod(c, (method)PlugParameterAnything, "anything", A_GIMME, 0);
68 
69  class_addmethod(c, (method)MaxGraphReset, "graph.reset", A_CANT, 0);
70  class_addmethod(c, (method)MaxGraphSetup, "graph.setup", A_CANT, 0);
71  class_addmethod(c, (method)MaxGraphDrop, "graph.drop", A_CANT, 0);
72  class_addmethod(c, (method)MaxGraphObject, "graph.object", A_CANT, 0);
73 
74  class_addmethod(c, (method)PlugParameterNotify, "notify", A_CANT, 0);
75  class_addmethod(c, (method)PlugParameterAssist, "assist", A_CANT, 0);
76  class_addmethod(c, (method)object_obex_dumpout, "dumpout", A_CANT, 0);
77 
78  CLASS_ATTR_SYM(c, "name", 0, PlugParameter, name);
79  CLASS_ATTR_ACCESSORS(c, "name", NULL, PlugParameterSetName);
80 
81  CLASS_ATTR_DOUBLE_ARRAY(c, "range", 0, PlugParameter, range, 2);
82  CLASS_ATTR_ACCESSORS(c, "range", NULL, PlugParameterSetRange);
83 
84  CLASS_ATTR_SYM(c, "style", 0, PlugParameter, style);
85  CLASS_ATTR_ACCESSORS(c, "style", NULL, PlugParameterSetStyle);
86 
87  CLASS_ATTR_DOUBLE(c, "default", 0, PlugParameter, defaultValue);
88  CLASS_ATTR_ACCESSORS(c, "default", NULL, PlugParameterSetDefault);
89 
90  class_register(_sym_box, c);
91  sPlugParameterClass = c;
92  return 0;
93 }
94 
95 
96 /************************************************************************************/
97 // Object Creation Method
98 
99 PlugParameterPtr PlugParameterNew(SymbolPtr msg, AtomCount argc, AtomPtr argv)
100 {
101  PlugParameterPtr self = PlugParameterPtr(object_alloc(sPlugParameterClass));
102  TTValue v;
103  TTErr err;
104  long attrStart = attr_args_offset(argc, argv);
105 
106  if (self) {
107  object_obex_store((void*)self, _sym_dumpout, (ObjectPtr)outlet_new(self, NULL));
108  self->graphOutlets[0] = outlet_new(self, "graph.connect");
109 
110  v.setSize(2);
111  v.set(0, TT("parameter"));
112  v.set(1, TTUInt32(1));
113  err = TTObjectBaseInstantiate(TT("graph.object"), (TTObjectBasePtr*)&self->graphObject, v);
114  ((TTGraphInput*)self->graphObject->mKernel)->setOwner(self->graphObject);
115 
116  if (!self->graphObject->mKernel) {
117  object_error(SELF, "cannot load Jamoma object");
118  return NULL;
119  }
120 
121  self->graphDictionary = new TTDictionary;
122  self->graphDictionary->setSchema(TT("none"));
123  self->graphDictionary->append(TT("outlet"), 0);
124 
125  self->range[0] = 0.0;
126  self->range[1] = 1.0;
127  self->style = GENSYM("generic");
128  attr_args_process(self, argc, argv);
129 
130  if (!self->name) {
131  if (attrStart)
132  object_attr_setsym(self, _sym_name, atom_getsym(argv));
133  else
134  object_attr_setsym(self, _sym_name, GENSYM("A parameter"));
135  }
136 
137  self->qelem = qelem_new(self, (method)PlugParameterQFn);
138 
139  // PackStartTracking(self);
140  defer_low(self, (method)PlugParameterStartTracking, NULL, 0, NULL);
141  }
142  return self;
143 }
144 
145 // Memory Deallocation
146 void PlugParameterFree(PlugParameterPtr self)
147 {
148  TTObjectBaseRelease((TTObjectBasePtr*)&self->graphObject);
149  qelem_free(self->qelem);
150 }
151 
152 
153 /************************************************************************************/
154 
155 MaxErr PlugParameterNotify(PlugParameterPtr self, SymbolPtr s, SymbolPtr msg, ObjectPtr sender, TTPtr data)
156 {
157  if (sender == self->patcherview) {
158  if (msg == _sym_attr_modified) {
159  SymbolPtr name = (SymbolPtr)object_method((ObjectPtr)data, _sym_getname);
160  if (name == _sym_dirty) {
161  qelem_set(self->qelem);
162  }
163  }
164  else if (msg == _sym_free)
165  self->patcherview = NULL;
166  }
167  else {
168  if (msg == _sym_free) {
169  ObjectPtr sourceBox;
170  ObjectPtr sourceObject;
171  long sourceOutlet;
172  ObjectPtr destBox;
173  ObjectPtr destObject;
174  long destInlet;
175 
176 #ifdef DEBUG_NOTIFICATIONS
177  object_post(SELF, "patch line deleted");
178 #endif // DEBUG_NOTIFICATIONS
179 
180  // get boxes and inlets
181  sourceBox = jpatchline_get_box1(sender);
182  if (!sourceBox)
183  goto out;
184 
185  sourceObject = jbox_get_object(sourceBox);
186  sourceOutlet = jpatchline_get_outletnum(sender);
187  destBox = jpatchline_get_box2(sender);
188  if (!destBox)
189  goto out;
190  destObject = jbox_get_object(destBox);
191  destInlet = jpatchline_get_inletnum(sender);
192 
193  // if both boxes are graph objects
194  if ( zgetfn(sourceObject, GENSYM("graph.object")) && zgetfn(destObject, GENSYM("graph.object")) ) {
195 #ifdef DEBUG_NOTIFICATIONS
196  object_post(SELF, "deleting graph patchline!");
197 #endif // DEBUG_NOTIFICATIONS
198 
199  object_method(destObject, GENSYM("graph.drop"), destInlet, sourceObject, sourceOutlet);
200  }
201  out:
202  ;
203  }
204  }
205  return MAX_ERR_NONE;
206 }
207 
208 
209 void PlugParameterIterateResetCallback(PlugParameterPtr self, ObjectPtr obj)
210 {
211  MaxErr err = MAX_ERR_NONE;
212  method graphResetMethod = zgetfn(obj, GENSYM("graph.reset"));
213 
214  if (graphResetMethod)
215  err = (MaxErr)graphResetMethod(obj);
216 }
217 
218 
219 void PlugParameterIterateSetupCallback(PlugParameterPtr self, ObjectPtr obj)
220 {
221  MaxErr err = MAX_ERR_NONE;
222  method graphSetupMethod = zgetfn(obj, GENSYM("graph.setup"));
223 
224  if (graphSetupMethod)
225  err = (MaxErr)graphSetupMethod(obj);
226 }
227 
228 
229 void PlugParameterAttachToPatchlinesForPatcher(PlugParameterPtr self, ObjectPtr patcher)
230 {
231  ObjectPtr patchline = object_attr_getobj(patcher, _sym_firstline);
232  ObjectPtr box = jpatcher_get_firstobject(patcher);
233 
234  while (patchline) {
235  object_attach_byptr_register(self, patchline, _sym_nobox);
236  patchline = object_attr_getobj(patchline, _sym_nextline);
237  }
238 
239  while (box) {
240  SymbolPtr classname = jbox_get_maxclass(box);
241 
242  if (classname == _sym_jpatcher) {
243  ObjectPtr subpatcher = jbox_get_object(box);
244 
245  PlugParameterAttachToPatchlinesForPatcher(self, subpatcher);
246  }
247  box = jbox_get_nextobject(box);
248  }
249 }
250 
251 
252 // Qelem function, which clumps together dirty notifications before making the new connections
253 void PlugParameterQFn(PlugParameterPtr self)
254 {
255  t_atom result;
256 
257 #ifdef DEBUG_NOTIFICATIONS
258  object_post(SELF, "patcher dirtied");
259 #endif // DEBUG_NOTIFICATIONS
260 
261  object_method(self->patcher, GENSYM("iterate"), (method)PlugParameterIterateSetupCallback, self, PI_DEEP, &result);
262 
263  // attach to all of the patch cords so we will know if one is deleted
264  // we are not trying to detach first -- hopefully this is okay and multiple attachments will be filtered (?)
265  PlugParameterAttachToPatchlinesForPatcher(self, self->patcher);
266 }
267 
268 
269 // Start keeping track of edits and connections in the patcher
270 void PlugParameterStartTracking(PlugParameterPtr self)
271 {
272  ObjectPtr patcher = NULL;
273  ObjectPtr parent = NULL;
274  ObjectPtr patcherview = NULL;
275  MaxErr err;
276  Atom result;
277 
278  // first find the top-level patcher
279  err = object_obex_lookup(self, GENSYM("#P"), &patcher);
280  parent = patcher;
281  while (parent) {
282  patcher = parent;
283  parent = object_attr_getobj(patcher, _sym_parentpatcher);
284  }
285 
286  // now iterate recursively from the top-level patcher down through all of the subpatchers
287  object_method(patcher, GENSYM("iterate"), (method)PlugParameterIterateResetCallback, self, PI_DEEP, &result);
288  object_method(patcher, GENSYM("iterate"), (method)PlugParameterIterateSetupCallback, self, PI_DEEP, &result);
289 
290 
291  // now let's attach to the patcherview to get notifications about any further changes to the patch cords
292  // the patcher 'dirty' attribute is not modified for each change, but the patcherview 'dirty' attribute is
293  if (!self->patcherview) {
294  patcherview = jpatcher_get_firstview(patcher);
295  self->patcherview = patcherview;
296  self->patcher = patcher;
297  object_attach_byptr_register(self, patcherview, _sym_nobox);
298  }
299 
300  // now we want to go a step further and attach to all of the patch cords
301  // this is how we will know if one is deleted
302  PlugParameterAttachToPatchlinesForPatcher(self, self->patcher);
303 }
304 
305 
306 /************************************************************************************/
307 // Methods bound to input/inlets
308 
309 // Method for Assistance Messages
310 void PlugParameterAssist(PlugParameterPtr self, void* b, long msg, long arg, char* dst)
311 {
312  if (msg==1) // Inlets
313  strcpy(dst, "multichannel audio connection and control messages");
314  else if (msg==2) { // Outlets
315  if (arg == 0)
316  strcpy(dst, "multichannel audio connection");
317  else
318  strcpy(dst, "dumpout");
319  }
320 }
321 
322 
323 void PlugParameterInt(PlugParameterPtr self, long value)
324 {
325  TTValue v = int(value);
326 
327  // self->graphDictionary->setSchema(TT("number"));
328  // TODO: maybe the attribute dictionary should contain a name and the value should then
329  // be a dictionary containing the number/array/etc...
330  // YES!
331 
332  self->graphDictionary->setSchema(TT("attribute"));
333  self->graphDictionary->append(TT("name"), TT(self->name->s_name));
334  self->graphDictionary->setValue(v);
335  ((TTGraphInput*)self->graphObject->mKernel)->push(*self->graphDictionary);
336 }
337 
338 
339 void PlugParameterFloat(PlugParameterPtr self, double value)
340 {
341  TTValue v = value;
342 
343  self->graphDictionary->setSchema(TT("attribute"));
344  self->graphDictionary->append(TT("name"), TT(self->name->s_name));
345  self->graphDictionary->setValue(v);
346  ((TTGraphInput*)self->graphObject->mKernel)->push(*self->graphDictionary);
347 }
348 
349 
350 MaxErr PlugParameterSetName(PlugParameterPtr self, void* attr, AtomCount argc, AtomPtr argv)
351 {
352  if (argc) {
353  self->name = atom_getsym(argv);
354  self->graphObject->mKernel->setAttributeValue(TT("name"), TT(self->name->s_name));
355  }
356  return MAX_ERR_NONE;
357 }
358 
359 
360 MaxErr PlugParameterSetRange(PlugParameterPtr self, void* attr, AtomCount argc, AtomPtr argv)
361 {
362  if (argc == 2) {
363  self->range[0] = atom_getfloat(argv+0);
364  self->range[1] = atom_getfloat(argv+1);
365  self->graphObject->mKernel->setAttributeValue(TT("rangeBottom"), self->range[0]);
366  self->graphObject->mKernel->setAttributeValue(TT("rangeTop"), self->range[1]);
367  }
368  return MAX_ERR_NONE;
369 }
370 
371 
372 MaxErr PlugParameterSetStyle(PlugParameterPtr self, void* attr, AtomCount argc, AtomPtr argv)
373 {
374  if (argc) {
375  self->style = atom_getsym(argv);
376  self->graphObject->mKernel->setAttributeValue(TT("style"), TT(self->style->s_name));
377  }
378  return MAX_ERR_NONE;
379 }
380 
381 
382 MaxErr PlugParameterSetDefault(PlugParameterPtr self, void* attr, AtomCount argc, AtomPtr argv)
383 {
384  if (argc) {
385  self->defaultValue = atom_getfloat(argv);
386  self->graphObject->mKernel->setAttributeValue(TT("default"), self->defaultValue);
387  }
388  return MAX_ERR_NONE;
389 }
390 
391 
The TTGraphObjectBase wraps a TTDSP object such that it is possible to build a dynamic graph of audio...
TTErr TTObjectBaseRelease(TTObjectBasePtr *anObject)
DEPRECATED.
Provides all necessary definitions for AudioGraph API.
Base class for all first-class Jamoma objects.
Definition: TTObjectBase.h:109
#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
void * TTPtr
A generic pointer.
Definition: TTBase.h:248
void setSize(const TTUInt16 arg)
DEPRECATED.
Definition: TTValue.h:528
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
void set(const TTUInt16 index, const T &anElementValue)
DEPRECATED.
Definition: TTValue.h:569
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
std::uint32_t TTUInt32
32 bit unsigned integer
Definition: TTBase.h:178
Jamoma Asynchronous Object Graph Layer.
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34