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