t_plugtastic* plugtastic_new() { t_plugtastic* self = (t_plugtastic*)(object_alloc(sPlugtasticClass)); t_atom a; t_class* c; t_object* p; c = class_findbyname(_sym_box, _sym_forward); if (!c) { p = (t_object*)newinstance(_sym_forward, 0, NULL); if (p) { c = class_findbyname(_sym_box, _sym_forward); freeobject(p); p = NULL; } } atom_setsym(&a, GENSYM("plugtastic_extra_toggle")); self->forward = (t_object*)object_new_typed(CLASS_BOX, _sym_forward, 1, &a); self->openSplash = &sPlugtasticSplash; a.a_type = 0; preferences_getatomforkey(GENSYM("plugtastic_splash"), &a); if (a.a_type) *self->openSplash = atom_getlong(&a); return self; }
MaxErr PlugOutNotify(PlugOutPtr self, SymbolPtr s, SymbolPtr msg, ObjectPtr sender, TTPtr data) { if (sender == self->patcherview) { if (msg == _sym_attr_modified) { SymbolPtr name = (SymbolPtr)object_method((ObjectPtr)data, _sym_getname); if (name == _sym_dirty) { qelem_set(self->qelem); } } else if (msg == _sym_free) self->patcherview = NULL; } else { if (msg == _sym_free) { ObjectPtr sourceBox; ObjectPtr sourceObject; long sourceOutlet; ObjectPtr destBox; ObjectPtr destObject; long destInlet; if (self->patcherview) goto out; // if there is no patcherview, then we are freeing the whole thing and can skip this #ifdef DEBUG_NOTIFICATIONS object_post(SELF, "patch line deleted"); #endif // DEBUG_NOTIFICATIONS // get boxes and inlets sourceBox = jpatchline_get_box1(sender); if (!sourceBox) goto out; sourceObject = jbox_get_object(sourceBox); sourceOutlet = jpatchline_get_outletnum(sender); destBox = jpatchline_get_box2(sender); if (!destBox) goto out; destObject = jbox_get_object(destBox); destInlet = jpatchline_get_inletnum(sender); // if both boxes are audio graph objects if ( zgetfn(sourceObject, GENSYM("audio.object")) && zgetfn(destObject, GENSYM("audio.object")) ) { #ifdef DEBUG_NOTIFICATIONS object_post(SELF, "deleting audio graph patchline!"); #endif // DEBUG_NOTIFICATIONS object_method(destObject, GENSYM("audio.drop"), destInlet, sourceObject, sourceOutlet); } out: ; } } return MAX_ERR_NONE; }
void PlugOutIterateSetupCallback(PlugOutPtr self, ObjectPtr obj) { method audioSetupMethod = zgetfn(obj, GENSYM("audio.setup")); if (audioSetupMethod) audioSetupMethod(obj); }
TTErr PlugOutSetup(PlugOutPtr self) { Atom a[2]; atom_setobj(a+0, ObjectPtr(self->audioGraphObject)); atom_setlong(a+1, 0); outlet_anything(self->audioGraphOutlet, GENSYM("audio.connect"), 2, a); return kTTErrNone; }
void PlugOutIterateResetCallback(PlugOutPtr self, ObjectPtr obj) { TTUInt32 vectorSize; method audioResetMethod = zgetfn(obj, GENSYM("audio.reset")); if (audioResetMethod) { self->audioGraphObject->getUnitGenerator()->getAttributeValue(TT("vectorSize"), vectorSize); audioResetMethod(obj, vectorSize); } }
TTErr OpDropAudio(OpPtr self, long inletNumber, ObjectPtr sourceMaxObject, long sourceOutletNumber) { TTAudioGraphObjectPtr sourceObject = NULL; TTErr err; if (inletNumber == 1) self->audioGraphObject->setAttributeValue(TT("numAudioInlets"), 1); err = (TTErr)int(object_method(sourceMaxObject, GENSYM("audio.object"), &sourceObject)); if (self->audioGraphObject && sourceObject && !err) err = self->audioGraphObject->dropAudio(sourceObject, sourceOutletNumber, inletNumber); return err; }
PlugOutPtr PlugOutNew(SymbolPtr msg, AtomCount argc, AtomPtr argv) { PlugOutPtr self = PlugOutPtr(object_alloc(sPlugOutClass)); TTValue v; TTErr err; if (self) { v.setSize(2); v.set(0, TT("plugtastic.output")); v.set(1, 2); err = TTObjectBaseInstantiate(TT("audio.object"), (TTObjectBasePtr*)&self->audioGraphObject, v); v = TTPtr(self->audioGraphObject); object_obex_store((void*)self, _sym_dumpout, (object*)outlet_new(self, NULL)); self->audioGraphOutlet = outlet_new(self, "audio.connect"); self->qelem = qelem_new(self, (method)PlugOutQFn); object_obex_lookup(self, GENSYM("#P"), &self->patcher); self->pluginName = object_attr_getsym(self->patcher, _sym_name); self->pluginVersion = GENSYM("1.0"); self->pluginVersionHex = GENSYM("0x00010000"); self->pluginManufacturer = GENSYM("Plugtastic"); self->pluginManufacturerCode = GENSYM("74Ob"); self->pluginID = GENSYM("ftmp"); attr_args_process(self, argc, argv); } return self; }
TTErr PlugOutBuildGraph(PlugOutPtr self) { MaxErr err; ObjectPtr patcher = NULL; ObjectPtr parent = NULL; long result = 0; err = object_obex_lookup(self, GENSYM("#P"), &patcher); // first find the top-level patcher err = object_obex_lookup(self, GENSYM("#P"), &patcher); parent = patcher; while (parent) { patcher = parent; parent = object_attr_getobj(patcher, _sym_parentpatcher); } //object_method(patcher, gensym("iterate"), (method)PlugOutIterateResetCallback, self, PI_DEEP, &result); object_method(patcher, GENSYM("iterate"), (method)PlugOutIterateSetupCallback, self, PI_DEEP, &result); return kTTErrNone; }
// Qelem function, which clumps together dirty notifications before making the new connections void PlugOutQFn(PlugOutPtr self) { t_atom result; #ifdef DEBUG_NOTIFICATIONS object_post(SELF, "patcher dirtied"); #endif // DEBUG_NOTIFICATIONS object_method(self->patcher, GENSYM("iterate"), (method)PlugOutIterateSetupCallback, self, PI_DEEP, &result); // attach to all of the patch cords so we will know if one is deleted // we are not trying to detach first -- hopefully this is okay and multiple attachments will be filtered (?) PlugOutAttachToPatchlinesForPatcher(self, self->patcher); }
MaxErr PlugOutSetVersion(PlugOutPtr self, void* attr, AtomCount argc, AtomPtr argv) { if (argc) { char str[16]; int major = 0; int minor = 0; int revision = 0; self->pluginVersion = atom_getsym(argv); sscanf(self->pluginVersion->s_name, "%i.%i.%i", &major, &minor, &revision); snprintf(str, 16, "0x00%02i%02i%02i", major, minor, revision); self->pluginVersionHex = GENSYM(str); } return MAX_ERR_NONE; }
void plug_setup_db(void) { t_atom a[4]; // This ugly hack not needed as of Max 5.1.7 // if (sDeferCount) { // sDeferCount--; // defer_low(sMaxObject, (method)plug_setup_db, NULL, 0, NULL); // return; // } // OBJECTS plug_alias_register("adc≈", "jcom.adc≈", "Audio", "Audio input"); plug_alias_register("adsr≈", "jcom.adsr≈", "Audio", "ADSR Envelope Generator"); plug_alias_register("audiounit≈", "jcom.audiounit≈", "Audio", "Host an AudioUnit plug-in"); plug_alias_register("dac≈", "jcom.dac≈", "Audio", "Audio output"); plug_alias_register("dcblocker≈", "jcom.dcblocker≈", "Audio", "Eliminate DC offsets"); plug_alias_register("degrade≈", "jcom.degrade≈", "Audio", "Distortion effect"); plug_alias_register("delay≈", "jcom.delay≈", "Audio", "Delay audio by a specified time"); plug_alias_register("fft≈", "jcom.fft≈", "Audio", "Convert a signal into the frequency domain"); plug_alias_register("filter≈", "jcom.filter≈", "Audio", "Swiss-Army knife of audio filters"); plug_alias_register("gain≈", "jcom.gain≈", "Audio", "Amplify or attenuate audio"); plug_alias_register("hilbert≈", "jcom.hilbert≈", "Audio", "Phase quadrature filter"); plug_alias_register("info≈", "jcom.info≈", "Audio", "Get properties of an audio signal"); plug_alias_register("join≈", "jcom.join≈", "Audio", "Join multiple signals together into a single signal"); plug_alias_register("limiter≈", "jcom.limiter≈", "Audio", "Lookahead dynamics processor"); plug_alias_register("matrix≈", "jcom.matrix≈", "Audio", "Mix and route channels within a signal"); plug_alias_register("matrixmixer≈", "jcom.matrixmixer≈", "Audio", "Mix and route multiple channels with each other"); plug_alias_register("noise≈", "jcom.noise≈", "Audio", "Generate various kinds of noise"); plug_alias_register("op≈", "jcom.op≈", "Audio", "Perform mathematical operations"); plug_alias_register("overdrive≈", "jcom.overdrive≈", "Audio", "Saturation effect"); plug_alias_register("pack≈", "jcom.pack≈", "Audio", "Bridge from MSP audio signals to Plugtastic"); plug_alias_register("phasor≈", "jcom.phasor≈", "Audio", "Oscillator ramping linearly from 0.0 to 1.0"); plug_alias_register("pulsesub≈", "jcom.pulsesub≈", "Audio", "Apply a cyclic ADSR envelope onto an input signal"); plug_alias_register("resample≈", "jcom.resample≈", "Audio", "Up/Downsample an audio signal"); plug_alias_register("sig≈", "jcom.sig≈", "Audio", "Create a signal from a constant value"); plug_alias_register("soundfile.player≈", "jcom.soundfile.player≈", "Audio", "Play a Soundfile"); plug_alias_register("soundfile.recorder≈", "jcom.soundfile.recorder≈", "Audio", "Record a Soundfile"); plug_alias_register("split≈", "jcom.split≈", "Audio", "Break a multichannel audio signal into smaller signals"); plug_alias_register("unpack≈", "jcom.unpack≈", "Audio", "Bridge from Plugtastic to MSP audio signals"); plug_alias_register("wavetable≈", "jcom.wavetable≈", "Audio", "Wavetable oscillator with several waveform options"); plug_alias_register("window≈", "jcom.window≈", "Audio", "Generate/Apply a window function for signal vector"); plug_alias_register("in≈", "plug.in≈", "Environment", "Audio input from the plug-in host environment"); plug_alias_register("out≈", "plug.out≈", "Environment", "Audio output to the plug-in host environment"); plug_alias_register("parameter#", "plug.parameter#", "Environment", "Define a parameter to be controlled in the host environment"); plug_alias_register("append#", "jcom.append#", "Control", "Add/Replace named data in a dictionary"); plug_alias_register("dataspace#", "jcom.dataspace#", "Control", "Convert values expressed in one unit into another unit."); plug_alias_register("iter#", "jcom.iter#", "Control", "Output all key/value sets from a dictionary to Max messages."); plug_alias_register("log#", "jcom.log#", "Control", "Print input to the Max window."); plug_alias_register("midi.in#", "jcom.midi.in#", "Control", "MIDI Input from a device, or from the plug-in host environment"); plug_alias_register("midi.out#", "jcom.midi.out#", "Control", "MIDI Output to a device"); plug_alias_register("midi.filter#", "jcom.midi.filter#", "Control", "Parse raw MIDI events"); plug_alias_register("midi.format#", "jcom.midi.format#", "Control", "Format dictionaries into raw MIDI events"); plug_alias_register("op#", "jcom.op#", "Control", "Perform mathematical operations on a dictionary"); plug_alias_register("pack#", "jcom.pack#", "Control", "Convert native Max data into a Plugtastic dictionary"); plug_alias_register("unpack#", "jcom.unpack#", "Control", "Convert a Plugtastic dictionary into native Max data"); // backwards compatibility //plug_alias_register("parameter!", "plug.parameter#", "Environment", "Define a parameter to be controlled in the host environment"); //plug_alias_register("append!", "jcom.append#", "Control", "Add/Replace named data in a dictionary"); //plug_alias_register("op!", "jcom.op#", "Control", "Perform mathematical operations on a dictionary"); //plug_alias_register("pack!", "jcom.pack#", "Control", "Convert native Max data into a Plugtastic dictionary"); //plug_alias_register("unpack!", "jcom.unpack#", "Control", "Convert a Plugtastic dictionary into native Max data"); // CLIPPINGS atom_setsym(a+1, _sym_clipping); atom_setsym(a+2, _sym_tag); atom_setsym(a+3, GENSYM("Plugtastic")); atom_setsym(a+0, GENSYM("plug.input")); object_method_typed(sMaxObject, ps_db_addmetadata, 4, a, NULL); atom_setsym(a+0, GENSYM("plug.output")); object_method_typed(sMaxObject, ps_db_addmetadata, 4, a, NULL); atom_setsym(a+0, GENSYM("plug.param")); object_method_typed(sMaxObject, ps_db_addmetadata, 4, a, NULL); atom_setsym(a+3, GENSYM("Environment")); atom_setsym(a+0, GENSYM("plug.input")); object_method_typed(sMaxObject, ps_db_addmetadata, 4, a, NULL); atom_setsym(a+0, GENSYM("plug.output")); object_method_typed(sMaxObject, ps_db_addmetadata, 4, a, NULL); atom_setsym(a+0, GENSYM("plug.param")); object_method_typed(sMaxObject, ps_db_addmetadata, 4, a, NULL); atom_setsym(a+2, _sym_description); atom_setsym(a+0, GENSYM("plug.input")); atom_setsym(a+3, GENSYM("Input stage for developing a plug-in")); object_method_typed(sMaxObject, ps_db_addmetadata, 4, a, NULL); atom_setsym(a+0, GENSYM("plug.output")); atom_setsym(a+3, GENSYM("Output stage for developing a plug-in")); object_method_typed(sMaxObject, ps_db_addmetadata, 4, a, NULL); atom_setsym(a+0, GENSYM("plug.param")); atom_setsym(a+3, GENSYM("Expanded parameter with a number box")); object_method_typed(sMaxObject, ps_db_addmetadata, 4, a, NULL); // EXAMPLES atom_setsym(a+1, _sym_patcher); atom_setsym(a+2, _sym_tag); atom_setsym(a+3, GENSYM("Plugtastic")); atom_setsym(a+0, GENSYM("My Plugtastic Adventure")); object_method_typed(sMaxObject, ps_db_addmetadata, 4, a, NULL); atom_setsym(a+3, GENSYM("Example")); atom_setsym(a+0, GENSYM("My Plugtastic Adventure")); object_method_typed(sMaxObject, ps_db_addmetadata, 4, a, NULL); }
void plug_alias_register(const char* plugtastic_name, const char* original_name, const char* category, const char* description) { t_atom a[4]; TTString str = "Plugtastic "; // create alias object_method(sMaxObject, ps_objectfile, GENSYM(plugtastic_name), GENSYM(original_name), GENSYM(original_name)); // add to autocompletion atom_setsym(a, GENSYM(plugtastic_name)); object_method_typed(sMaxObject, ps_db_object_addinternal, 1, a, NULL); // add to object list str += category; object_method(sMaxObject, ps_oblist, GENSYM(str.c_str()), GENSYM(plugtastic_name)); atom_setsym(a+0, GENSYM(plugtastic_name)); atom_setsym(a+1, _sym_object); atom_setsym(a+2, _sym_tag); atom_setsym(a+3, GENSYM("Plugtastic")); object_method_typed(sMaxObject, ps_db_addmetadata, 4, a, NULL); atom_setsym(a+0, GENSYM(plugtastic_name)); atom_setsym(a+1, _sym_object); atom_setsym(a+2, _sym_tag); atom_setsym(a+3, GENSYM(category)); object_method_typed(sMaxObject, ps_db_addmetadata, 4, a, NULL); atom_setsym(a+0, GENSYM(plugtastic_name)); atom_setsym(a+1, _sym_object); atom_setsym(a+2, _sym_description); atom_setsym(a+3, GENSYM(description)); object_method_typed(sMaxObject, ps_db_addmetadata, 4, a, NULL); }
int main(void) { common_symbols_init(); PlugtasticInit(); plugtastic_classinit(); sPlugtasticObject = (t_object*)plugtastic_new(); ps_plugtastic = GENSYM("plugtastic"); ps_plugtastic->s_thing = sPlugtasticObject; sMaxObject = _sym_max->s_thing; ps_objectfile = GENSYM("objectfile"); ps_db_object_addinternal = GENSYM("db.object_addinternal"); ps_oblist = GENSYM("oblist"); ps_db_addmetadata = GENSYM("db.addmetadata"); //defer_low(sMaxObject, (method)plug_setup_db, NULL, 0, NULL); plug_setup_db(); post("Plugtastic Version %s | 74Objects.com", PLUGTASTIC_VERSION); // This tells Max 5.0.6 and higher that we want the patcher files to be saved such that they are sorted. // Having the saved this way makes our SVN diffs much more meaningful. object_method_long(sMaxObject, GENSYM("sortpatcherdictonsave"), 1, NULL); // This tells Max 4.5.7 and higher to take any posts to the Max window and also make the // post to the system console, which greatly aids in debugging problems and crashes object_method_long(sMaxObject, GENSYM("setmirrortoconsole"), 1, NULL); // OPEN THE SPLASH if (sPlugtasticSplash) { char name[MAX_FILENAME_CHARS]; short path = 0; long type = 0; long typelist[2] = {'JSON', 'TEXT'}; short err; t_dictionary* d; t_object* p; t_atom a[2]; strncpy_zero(name, "Plugtastic.maxpat", MAX_FILENAME_CHARS); err = locatefile_extended(name, &path, &type, typelist, 2); dictionary_read(name, path, &d); atom_setobj(a, d); p = (t_object*)object_new_typed(_sym_nobox, _sym_jpatcher, 1, a); object_attr_setlong(p, _sym_locked, 1); // start out locked object_attr_setchar(p, _sym_enablehscroll, 0); // turn off scroll bars object_attr_setchar(p, _sym_enablevscroll, 0); object_attr_setchar(p, _sym_toolbarvisible, 0); object_attr_setsym(p, _sym_title, gensym("Welcome to Plugtastic")); object_attr_setparse(p, _sym_rect, "271 170 799 489"); object_attr_setparse(p, _sym_defrect, "271 170 799 489"); object_method(p, _sym_vis); // "vis" happens immediately, "front" is defer_lowed object_method(p, _sym_loadbang); // object_method_parse(p, _sym_window, "constrain 799 489 799 489", NULL); object_method_parse(p, _sym_window, "flags nozoom", NULL); object_method_parse(p, _sym_window, "flags nogrow", NULL); object_method_parse(p, _sym_window, "exec", NULL); } return 0; }
int script_lua_atoms_from_args(lua_State *L, int index_start, t_atom **args) { int i, n; int num_args = 0; n = lua_gettop(L); //count the number of arguments for(i=index_start; i <= n; i++) { int type = lua_type(L, i); switch(type) { case LUA_TTABLE: num_args += luaL_getn(L, i); break; case LUA_TSTRING: case LUA_TNUMBER: case LUA_TUSERDATA: num_args++; break; default: break; } } *args = (t_atom *)jit_getbytes(num_args*sizeof(t_atom)); { int arg_index=0; //index of current argument //iterate over the number of stack elements for(i=index_start; i <= n; i++) { int type = lua_type(L, i); switch(type) { case LUA_TSTRING: jit_atom_setsym(*args+arg_index, GENSYM(lua_tostring(L, i))); arg_index++; break; case LUA_TNUMBER: jit_atom_setfloat(*args+arg_index, lua_tonumber(L, i)); arg_index++; break; case LUA_TUSERDATA: (*args+arg_index)->a_w.w_obj = toJitobj(L, i); (*args+arg_index)->a_type = A_OBJ; arg_index++; break; case LUA_TTABLE: { int j; int table_type; //doesn't account for tables of userdata yet for(j=1; j <= luaL_getn(L, i); j++) { lua_rawgeti(L, i, j); table_type = lua_type(L, -1); switch(table_type) { case LUA_TSTRING: jit_atom_setsym(*args+arg_index, GENSYM(lua_tostring(L, -1))); arg_index++; break; case LUA_TNUMBER: jit_atom_setfloat(*args+arg_index, lua_tonumber(L, -1)); arg_index++; break; case LUA_TUSERDATA: (*args+arg_index)->a_w.w_obj = toJitobj(L, -1); (*args+arg_index)->a_type = A_OBJ; arg_index++; break; default: break; } } } break; default: break; } } } return num_args; }