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