Jamoma API  0.6.0.a19
j.plug.out.cpp
1 /*
2  * j.plug.out=
3  * Plugtastic Output External
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 "j.plug.out.h"
11 
12 
13 // Prototypes for methods
14 PlugOutPtr PlugOutNew(SymbolPtr msg, AtomCount argc, AtomPtr argv);
15 void PlugOutFree(PlugOutPtr self);
16 MaxErr PlugOutNotify(PlugOutPtr self, SymbolPtr s, SymbolPtr msg, ObjectPtr sender, TTPtr data);
17 void PlugOutQFn(PlugOutPtr self);
18 void PlugOutAttachToPatchlinesForPatcher(PlugOutPtr self, ObjectPtr patcher);
19 void PlugOutAssist(PlugOutPtr self, void* b, long msg, long arg, char* dst);
20 TTErr PlugOutSetup(PlugOutPtr self);
21 void PlugOutIterateResetCallback(PlugOutPtr self, ObjectPtr obj);
22 void PlugOutIterateSetupCallback(PlugOutPtr self, ObjectPtr obj);
23 MaxErr PlugOutBuildAudioUnit(PlugOutPtr self, SymbolPtr s, AtomCount argc, AtomPtr argv);
24 MaxErr PlugOutSetVersion(PlugOutPtr self, void* attr, AtomCount argc, AtomPtr argv);
25 
26 
27 // Globals
28 static ClassPtr sPlugOutClass;
29 
30 
31 /************************************************************************************/
32 // Main() Function
33 
34 int TTCLASSWRAPPERMAX_EXPORT main(void)
35 {
36  ClassPtr c;
37 
38  TTAudioGraphInit();
39  common_symbols_init();
40 
41  c = class_new((char*)"j.plug.out=", (method)PlugOutNew, (method)PlugOutFree, sizeof(PlugOut), (method)0L, A_GIMME, 0);
42 
43  class_addmethod(c, (method)PlugOutNotify, "notify", A_CANT, 0);
44  class_addmethod(c, (method)MaxAudioGraphReset, "audio.reset", A_CANT, 0);
45  class_addmethod(c, (method)PlugOutSetup, "audio.setup", A_CANT, 0);
46  class_addmethod(c, (method)MaxAudioGraphConnect, "audio.connect", A_OBJ, A_LONG, 0);
47  class_addmethod(c, (method)MaxAudioGraphDrop, "audio.drop", A_CANT, 0);
48  class_addmethod(c, (method)MaxAudioGraphObject, "audio.object", A_CANT, 0);
49  class_addmethod(c, (method)PlugOutBuildAudioUnit, "build.au", A_GIMME, 0);
50  class_addmethod(c, (method)PlugOutAssist, "assist", A_CANT, 0);
51  class_addmethod(c, (method)object_obex_dumpout, "dumpout", A_CANT, 0);
52 
53  CLASS_ATTR_SYM(c, "manufacturer", 0, PlugOut, pluginManufacturer);
54  CLASS_ATTR_SYM(c, "manufacturerCode", 0, PlugOut, pluginManufacturerCode);
55  CLASS_ATTR_SYM(c, "pluginIdCode", 0, PlugOut, pluginID);
56  CLASS_ATTR_SYM(c, "pluginName", 0, PlugOut, pluginName);
57  CLASS_ATTR_SYM(c, "pluginVersion", 0, PlugOut, pluginVersion);
58  CLASS_ATTR_ACCESSORS(c, "pluginVersion", NULL, PlugOutSetVersion);
59 
60  class_register(_sym_box, c);
61  sPlugOutClass = c;
62  return 0;
63 }
64 
65 
66 /************************************************************************************/
67 // Life Cycle
68 
69 PlugOutPtr PlugOutNew(SymbolPtr msg, AtomCount argc, AtomPtr argv)
70 {
71  PlugOutPtr self = PlugOutPtr(object_alloc(sPlugOutClass));
72  TTValue v;
73  TTErr err;
74 
75  if (self) {
76  v.setSize(2);
77  v.set(0, TT("plugtastic.output"));
78  v.set(1, 2);
79  err = TTObjectBaseInstantiate(TT("audio.object"), (TTObjectBasePtr*)&self->audioGraphObject, v);
80 
81  v = TTPtr(self->audioGraphObject);
82 
83  object_obex_store((void*)self, _sym_dumpout, (object*)outlet_new(self, NULL));
84  self->audioGraphOutlet = outlet_new(self, "audio.connect");
85  self->qelem = qelem_new(self, (method)PlugOutQFn);
86 
87  object_obex_lookup(self, GENSYM("#P"), &self->patcher);
88  self->pluginName = object_attr_getsym(self->patcher, _sym_name);
89  self->pluginVersion = GENSYM("1.0");
90  self->pluginVersionHex = GENSYM("0x00010000");
91  self->pluginManufacturer = GENSYM("Plugtastic");
92  self->pluginManufacturerCode = GENSYM("74Ob");
93  self->pluginID = GENSYM("ftmp");
94 
95  attr_args_process(self, argc, argv);
96  }
97  return self;
98 }
99 
100 
101 void PlugOutFree(PlugOutPtr self)
102 {
103  if (self->patcherview) {
104  object_detach_byptr(self, self->patcherview);
105  self->patcherview = NULL;
106  }
107  TTObjectBaseRelease((TTObjectBasePtr*)&self->audioGraphObject);
108  qelem_free(self->qelem);
109  delete self->buildThread;
110 }
111 
112 
113 /************************************************************************************/
114 // Methods bound to input/inlets
115 
116 MaxErr PlugOutNotify(PlugOutPtr self, SymbolPtr s, SymbolPtr msg, ObjectPtr sender, TTPtr data)
117 {
118  if (sender == self->patcherview) {
119  if (msg == _sym_attr_modified) {
120  SymbolPtr name = (SymbolPtr)object_method((ObjectPtr)data, _sym_getname);
121  if (name == _sym_dirty) {
122  qelem_set(self->qelem);
123  }
124  }
125  else if (msg == _sym_free)
126  self->patcherview = NULL;
127  }
128  else {
129  if (msg == _sym_free) {
130  ObjectPtr sourceBox;
131  ObjectPtr sourceObject;
132  long sourceOutlet;
133  ObjectPtr destBox;
134  ObjectPtr destObject;
135  long destInlet;
136 
137  if (self->patcherview)
138  goto out; // if there is no patcherview, then we are freeing the whole thing and can skip this
139 
140  #ifdef DEBUG_NOTIFICATIONS
141  object_post(SELF, "patch line deleted");
142  #endif // DEBUG_NOTIFICATIONS
143 
144  // get boxes and inlets
145  sourceBox = jpatchline_get_box1(sender);
146  if (!sourceBox)
147  goto out;
148  sourceObject = jbox_get_object(sourceBox);
149  sourceOutlet = jpatchline_get_outletnum(sender);
150  destBox = jpatchline_get_box2(sender);
151  if (!destBox)
152  goto out;
153  destObject = jbox_get_object(destBox);
154  destInlet = jpatchline_get_inletnum(sender);
155 
156  // if both boxes are audio graph objects
157  if ( zgetfn(sourceObject, GENSYM("audio.object")) && zgetfn(destObject, GENSYM("audio.object")) ) {
158  #ifdef DEBUG_NOTIFICATIONS
159  object_post(SELF, "deleting audio graph patchline!");
160  #endif // DEBUG_NOTIFICATIONS
161 
162  object_method(destObject, GENSYM("audio.drop"), destInlet, sourceObject, sourceOutlet);
163  }
164  out:
165  ;
166  }
167  }
168  return MAX_ERR_NONE;
169 }
170 
171 
172 // Qelem function, which clumps together dirty notifications before making the new connections
173 void PlugOutQFn(PlugOutPtr self)
174 {
175  t_atom result;
176 
177  #ifdef DEBUG_NOTIFICATIONS
178  object_post(SELF, "patcher dirtied");
179  #endif // DEBUG_NOTIFICATIONS
180 
181  object_method(self->patcher, GENSYM("iterate"), (method)PlugOutIterateSetupCallback, self, PI_DEEP, &result);
182 
183  // attach to all of the patch cords so we will know if one is deleted
184  // we are not trying to detach first -- hopefully this is okay and multiple attachments will be filtered (?)
185  PlugOutAttachToPatchlinesForPatcher(self, self->patcher);
186 }
187 
188 
189 void PlugOutAttachToPatchlinesForPatcher(PlugOutPtr self, ObjectPtr patcher)
190 {
191  ObjectPtr patchline = object_attr_getobj(patcher, _sym_firstline);
192  ObjectPtr box = jpatcher_get_firstobject(patcher);
193 
194  while (patchline) {
195  object_attach_byptr_register(self, patchline, _sym_nobox);
196  patchline = object_attr_getobj(patchline, _sym_nextline);
197  }
198 
199  while (box) {
200  SymbolPtr classname = jbox_get_maxclass(box);
201 
202  if (classname == _sym_jpatcher) {
203  ObjectPtr subpatcher = jbox_get_object(box);
204 
205  PlugOutAttachToPatchlinesForPatcher(self, subpatcher);
206  }
207  box = jbox_get_nextobject(box);
208  }
209 }
210 
211 
212 // Method for Assistance Messages
213 void PlugOutAssist(PlugOutPtr self, void* b, long msg, long arg, char* dst)
214 {
215  if (msg==1) // Inlets
216  strcpy(dst, "multichannel audio connection and control messages");
217  else if (msg==2){ // Outlets
218  if(arg == 0)
219  strcpy(dst, "stats on DSP graph");
220  else
221  strcpy(dst, "dumpout");
222 
223  }
224 }
225 
226 
227 TTErr PlugOutSetup(PlugOutPtr self)
228 {
229  Atom a[2];
230 
231  atom_setobj(a+0, ObjectPtr(self->audioGraphObject));
232  atom_setlong(a+1, 0);
233  outlet_anything(self->audioGraphOutlet, GENSYM("audio.connect"), 2, a);
234  return kTTErrNone;
235 }
236 
237 
238 void PlugOutIterateResetCallback(PlugOutPtr self, ObjectPtr obj)
239 {
240  TTUInt32 vectorSize;
241  method audioResetMethod = zgetfn(obj, GENSYM("audio.reset"));
242 
243  if (audioResetMethod) {
244  self->audioGraphObject->getUnitGenerator()->getAttributeValue(TT("vectorSize"), vectorSize);
245  audioResetMethod(obj, vectorSize);
246  }
247 }
248 
249 
250 void PlugOutIterateSetupCallback(PlugOutPtr self, ObjectPtr obj)
251 {
252  method audioSetupMethod = zgetfn(obj, GENSYM("audio.setup"));
253 
254  if (audioSetupMethod)
255  audioSetupMethod(obj);
256 }
257 
258 
259 TTErr PlugOutBuildGraph(PlugOutPtr self)
260 {
261  MaxErr err;
262  ObjectPtr patcher = NULL;
263  ObjectPtr parent = NULL;
264  long result = 0;
265 
266  err = object_obex_lookup(self, GENSYM("#P"), &patcher);
267 
268  // first find the top-level patcher
269  err = object_obex_lookup(self, GENSYM("#P"), &patcher);
270  parent = patcher;
271  while (parent) {
272  patcher = parent;
273  parent = object_attr_getobj(patcher, _sym_parentpatcher);
274  }
275 
276  //object_method(patcher, gensym("iterate"), (method)PlugOutIterateResetCallback, self, PI_DEEP, &result);
277  object_method(patcher, GENSYM("iterate"), (method)PlugOutIterateSetupCallback, self, PI_DEEP, &result);
278 
279  return kTTErrNone;
280 }
281 
282 
283 MaxErr PlugOutDoBuildAudioUnit(PlugOutPtr self, SymbolPtr s, AtomCount argc, AtomPtr argv)
284 {
285  if (self->progressWindow) {
286  object_error(SELF, "Already building a plug-in. Please wait until the process is complete and try again.");
287  return MAX_ERR_GENERIC;
288  }
289 
290  open_progress_window(self);
291  PlugOutBuildGraph(self);
292  self->buildThread = new TTThread((TTThreadCallbackType)PlugOutDoBuildAudioUnit_Export, self);
293  return MAX_ERR_NONE;
294 }
295 
296 
297 MaxErr PlugOutBuildAudioUnit(PlugOutPtr self, SymbolPtr s, AtomCount argc, AtomPtr argv)
298 {
299  defer(self, (method)PlugOutDoBuildAudioUnit, s, argc, argv);
300  return MAX_ERR_NONE;
301 }
302 
303 
304 MaxErr PlugOutSetVersion(PlugOutPtr self, void* attr, AtomCount argc, AtomPtr argv)
305 {
306  if (argc) {
307  char str[16];
308  int major = 0;
309  int minor = 0;
310  int revision = 0;
311 
312  self->pluginVersion = atom_getsym(argv);
313  sscanf(self->pluginVersion->s_name, "%i.%i.%i", &major, &minor, &revision);
314  snprintf(str, 16, "0x00%02i%02i%02i", major, minor, revision);
315  self->pluginVersionHex = GENSYM(str);
316  }
317  return MAX_ERR_NONE;
318 }
TTErr TTObjectBaseRelease(TTObjectBasePtr *anObject)
DEPRECATED.
TTErr MaxAudioGraphReset(t_object *x, long vectorSize)
Clear the list of source objects from which this object will try to pull audio.
Base class for all first-class Jamoma objects.
Definition: TTObjectBase.h:109
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
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 MaxAudioGraphObject(t_object *x, TTAudioGraphObjectBasePtr *returnedAudioGraphObject)
Returns a pointer to the Jamoma Audio Graph object that is wrapped by this Max object.
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
std::uint32_t TTUInt32
32 bit unsigned integer
Definition: TTBase.h:178
TTErr MaxAudioGraphDrop(t_object *x, long inletNumber, t_object *sourceMaxObject, long sourceOutletNumber)
Method called when a connection from an upstream node is dropped.
No Error.
Definition: TTBase.h:343
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34