The tutorials in the SC distro are not really great on how to do plugins, so here’s my version. After you get the SC source code (an exercize left to the reader), you’re going to want to build your own copy of SC. This should not be the same copy that you use for doing your music, because development tends to break things and you don’t want to break your instrument. First build the Server, then the Plugins, then the Lang.
Once you’ve done that, open the plugin project with Xcode. In the Project Window, pick a target and ctrl-click on it to duplicate it. Then option click the new target to rename it to [whatever]. Double click on it to bring up the target inspector. In the summary, rename Base Product Name to [whatever].scx . Then click on “Settings” in the list on the left. Change Product Name to [whatever].scx . Close the target inspector menu and go back to the project menu. Drag your new target into the list for All, so it gets built when you build all.
Ctrl-click on your new target again to Add. Add a new C++ file. Don’t generate a header file for it. This is my example, a biquad filter:
/* * LesUGens.cpp * xSC3plugins * * Created by Celeste Hutchins on 16/10/06. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // all plugins should include SC_Plugin.h #include "SC_PlugIn.h" // and this line static InterfaceTable *ft; // here you define the data that your plugin will need to keep around // biquads have two delayed samples, so that's what we save struct Biquad : public Unit { // delayed samples float m_sa1; float m_sa2; }; // declare the functions that your UGen will need extern "C" { // this line is required void load(InterfaceTable *inTable); // calculate the next batch of samples void Biquad_next(Biquad *unit, int numsamples); // constructor void Biquad_Ctor(Biquad* unit); } ////////////////////////////////////////////////////////////////////////////////////////////////// // Calculation function for the UGen. This gets called once per x samples (usually 66) void Biquad_next(Biquad *unit, int numsamples) { // pointers to in and out float *out = ZOUT(0); float *in = ZIN(0); // load delayed samples from our struct float delay1 = unit->m_sa1; float delay2 = unit->m_sa2; // the filter co-efficients are passed in. These might change at the control rate, // so we re-read them every time. // the optimizer will stick these in registers float amp0 = ZIN0(1); float amp1 = ZIN0(2); float amp2 = ZIN0(3); float amp3 = ZIN0(4); float amp4 = ZIN0(5); float next_delay; // This loop actually does the calculation LOOP(numsamples, // read in the next sample float samp = ZXP(in); // calculate next_delay = (amp0 * samp) + (amp1 * delay1) + (amp2 * delay2); //write out result ZXP(out) = next_delay - (amp3 *delay1) - (amp4 * delay2); // keep track of data delay2 = delay1; delay1 = next_delay; ); // write data back into the struct for the next time unit->m_sa1 = delay1; unit->m_sa2 = delay2; } // The constructor function // This only runs once // It initializes the struct // Sets the calculation function // And, for reasons I don't understand, calculates one sample of output void Biquad_Ctor(Biquad *unit) { // set the calculation function SETCALC(Biquad_next); // initialize data unit->m_sa1 = 0.f; unit->m_sa2 = 0.f; // 1 sample of output Biquad_next(unit, 1); } // This function gets called when the plugin is loaded void load(InterfaceTable *inTable) { // don't forget this line ft = inTable; // Nor this line DefineSimpleUnit(Biquad); }
Ok, when you build this, it will get copied into the plugin directory. But that’s not enough. The SCLang also needs to know about your new UGen. Create a new class file called [whatever].sc .  You can stick this in your ~/Library, it won’t mess up your other copies of SuperCollider. This is my file:
Biquad : UGen { *ar { arg in, a0 = 1, a1 = 0, a2 =0, a3 =0, a4 =0, mul = 1.0, add = 0.0; ^this.multiNew('audio', in, a0, a1, a2, a3, a4).madd(mul, add) } *kr { arg in, a0 = 1, a1 = 0, a2 =0, a3 =0, a4 =0, mul = 1.0, add = 0.0; ^this.multiNew('control', in, a0, a1, a2, a3, a4).madd(mul, add) } }
The multiNew part handles multiple channel expansion for you. The .madd ads the convenience variables mul and add. Your users like to have those.
I don’t know if a biquad filter comes with SC or not. I couldn’t find one. They’re useful for Karplus-Strong and a few other things. For more information, check out the wikipedia article on Filter Design
Tags: SuperCollider , Celesteh