Getting Starting with SuperCollider3 Source Code, Plugins & Debugging

Last Updated 16-Dec 2004

.: Notes, Warnings, etc :.
.: Downloading and Compiling the Required, Non-SC3 Source Code and Apps :.
.: Downloading the SC3 Source Code with CVS :.
.: Compiling the SC3 Source Code :.
.: Compiling Your First Test Plugin :.
.: Debugging :.

I first compiled these notes on March 6, 2004 on a lonely Friday night in southern California... Check the top of the page to see when this page was last updated... I plan to update this information when needed, as well as add extra info here and there when i discover it. My purpose for providing this page is to gather several resources, links and bits of advice into one place. In my experience it seems to be case in the programming world that other programmers and program developers sort of assume that everyone else innately knows what to do, what to download, how to do this and that, which makes getting started more of a challenge than it needs to be. I don't think you need to have ANY programming experience to download and compile this source code as long as you can follow directions and have access to the right software. If you haven't programmed before but are interested, this process may prove as exceedingly enlightening to you as it did to me, so dive right in.

Someday I'll php-ify this page so that folks can add comments to the actual site, but until then please do send any commments or corrections to a[at]plus1plus1plus[dot]org

Special thanks to Aaron Spafford for additional suggestions and contributions, and of course to anyone who has contributed to the SuperCollider Swiki

My original "Program Notes":
i apologize in advance if there are any errors below - i'm pretty sure i got it all right, but it's late and i just figured most of this out in the past 5 hours. hopefully this will make things much faster for the uninitiated - i wish there had been a tutorial on all of this for me. i must acknowledge the awesome help that ccos and the swiki site have provided in figuring this stuff out. here, however, is all the info you will need to get started, all in one place.

Warning: Please note that I use OSX, so some of this info may be different, unusable, or confusing to Linux or Windows users. Or not - I think most of it generally the same... Just be forewarned...

 

top

................................
................................

Getting Started: Downloading and Compiling Other Required Software

Before you get started with building SC3 plugins, you'll need to get the SC3 source code PLUS a couple other programs and libraries, some of which also have to be compiled. You will need: Apple's Developer Tools which will provide you with various Unix and compilation programs like CVS and Xcode, and libsndfile.
much of the following info was gathered from the SuperCollider Swiki website which has a good deal of information related to SC3 stuff...

.................................

1st:

make sure you have Developer Tools installed on your computer, specifically Xcode if you're running OS 10.3 or higher, or Project Builder if you're running OS 10.2. if you don't have either of these, you can get the one you need for FREE from Apple's website after filling out a few forms and dodging their requests for money: http://developer.apple.com/tools/download/
It's a huge download however so be prepared...

...................................

2nd:

make sure you have libsndfile downloaded and installed. you can download it here: http://www.mega-nerd.com/libsndfile/

as of March 6, 2004, the latest version is 1.0.7. beware that v.1.0.6 has major bugs that will prevent SC3 from compiling, so make sure you get the right, most up-to-date version, or else you'll spend a couple sleepless nights trying to figure out what you're doing wrong, like i did last week...

..................................

3rd:

install libsndfile using Terminal (it's typically located on your mac in the Utilities folder)
this may seem a little more daunting than expected if you've never used Terminal, but the process is actually extremely simple, so don't be scared. you may first want to check out and try your hand at some unix basics

you may want to save your libsndfile folder that you just downloaded in your Developer Tools folder so that you keep everything nice and organized. you're going to need to know where to find this file in just a second.

For your reference, the instructions for installing libsndfile can be found in the INSTALL file in the folder, but i will guide you through the process here:

1) Open Terminal and type the following:

cd

2) drag-and-drop the actual libsndfile folder to the Terminal window - the pathname will magically appear and should look something like this, although it may vary depending on where your file is located:

cd /Developer/dev_libsndfile/libsndfile-1.0.7

then hit enter

3) type in the following commands in order, making sure to wait until each one is done executing; they all take awhile...

./configure
(hit enter and wait for it to finish)

make
(hit enter and wait for it to finish)

then you'll need to log in as the 'root user' by typing

su
(hit enter)

type in your 'root user' password where it tells you to. note that you you won't be able to see your password as you type it, in fact the cursor won't even move forward. just hit enter once you're done typing. you may have set this up when you first got your computer. if not, you might be screwed for the moment. go here and see if this works:
http://swiki.hfbk-hamburg.de:8888/MusicTechnology/423

aaron spafford has recommended the following for procedure for setting up your root password if you haven't done it already:

###################################################
If you know your admin (login) password, but havn't yet set up a root password,
just type this into the terminal:

sudo sh

at prompt enter your admin password

passwd root

Now, at the password prompt, type in your desired root password, hit enter and type

exit

Now you're all set!
#################################

after you're logged in, your user name should be the same, except now it will have a # sign after it instead of a $ sign, for instance mine is aoverton$ when i'm in normal mode, and aoverton# when i've logged into root-user mode.
now type in the following:

make install
(hit enter and wait ahwile)

if everything goes alright and finishes correctly, type

exit

to log out of the root user mode. (it is dangerous to stay logged into root user mode.)

great - libsndfile should now be installed on your machine and you are ready to proceed...

 

top

................................
................................

Downloading the SC3 source code with CVS

Download the SuperCollider3 source code using CVS, which is accessed by once again by using the Terminal app. CVS should already installed on your mac if you have Developer Tools installed.

figure out which folder on your computer you want to put the SC source code folder in, and then go there in terminal, for instance:

cd /Applications
(hit enter)

(once again, you could've just typed cd and then dragged-and-dropped your desired destination folder to terminal to get its pathname to magically appear.

[the following are instructions from the swiki site]:

go to the directory where you want to put it, it will create a SuperCollider3 directory there. eg:
cd ~/dev

Then type the following command:
cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/supercollider login

When it asks for the password, just press enter and wait for the prompt to return.
Now type the following command. This will take some time as it checks out all the files for the project.
cvs -z3 -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/supercollider co SuperCollider3

You should see lines like the following:
cvs server: Updating SuperCollider3
U SuperCollider3/.DS_Store
U SuperCollider3/COPYING

etc...

If you get "connection reset by peer", just try again. This is probably server traffic problems. You might also have to try the login again.
After this you will have the project in a folder named SuperCollider3.

to update, from the same location, do
cvs -z3 update -dP

 

top

................................
................................

Compiling the SC3 Source Code

there are a handful of ways you can do this, but it's probably easiest to do it from the Terminal. if you want to know the other ways, go to the web for instructions (http://swiki.hfbk-hamburg.de:8888/MusicTechnology/554) and scroll down.
the following instructions can be found listed in the file 'compile-xcode.sh' in the SuperCollider3 folder you just downloaded, and will compile the source code into the actual SC3 app you've come know and love:

copy and paste the following text all at once into Terminal; this might take a while to finish executing. go grab a glass of wine or juice:

echo "building scsynth"; xcodebuild -project xSC3synth.pbproj -target "All" -buildstyle "Deployment" build;

echo "building plugins"; xcodebuild -project xSC3plugins.pbproj -target "All" -buildstyle "Deployment" build;

echo "building sclang"; xcodebuild -project xSC3lang.pbproj -target "All" -buildstyle "Deployment" build;

 

top

................................
................................

How To Compile A Test Plugin in SC3

guess what? every single unit generator in SuperCollider3 is a plugin. what does that mean? well, it means that in order to learn how to make your own, you can use the preexisting source code of your favorite Ugen as a starting point by simply renaming it and then modifying it. below are instructions on how to get started and by compiling your first example plugin. before you do this, though, you must download the SuperCollider source code.

NOTE: These instructions might be outdated now. James McCartney has since placed a plugin-writing help-file in the SC3 distribution - you will want to look at that first before going on

here are some good links:
http://swiki.hfbk-hamburg.de:8888/MusicTechnology/492

Part I -- Hacking the Source Code

let's make a test-run plugin by grabbing the source code of Osc (a table lookup oscillator), and turning it into TestOsc - note that this is only a test to make sure that we know the procedure for quickly getting started on a plugin. in the end, this example will just produce an Osc.ar when you type TestOsc.ar in SC. the point, though, is to demonstrate how easy it might be to get started on a plugin.

alrighty. here's how:

go here: SuperCollider3/source/plugins/ and get the OscUGens.cpp file. duplicate it (command-D) and rename it TestOsc.cpp, and then open it with a good text editor like BBEdit (you won't want to use TextEdit or anything that will save it as an rtf)...

find the code that you will need to compile in order to make an Osc only and delete all the rest. Notice that alllllll of the Oscillator ugens can be found here snuggled together. be careful in your editing - you'll need a handful of stuff. after the code is extracted, change every name with 'Osc' in it to 'TestOsc'. save your changes. here's what i came up with:

/////////////// C++ code in a file named TestOsc.cpp ///////////////////

#include "SC_PlugIn.h"
#include <limits.h>

static InterfaceTable *ft;

struct BufUnit : public Unit
{
SndBuf *m_buf;
float m_fbufnum;
};

struct TableLookup : public BufUnit
{
double m_cpstoinc, m_radtoinc;
int32 mTableSize;
int32 m_lomask;
};

struct TestOsc : public TableLookup
{
int32 m_phase, m_phaseoffset;
float m_phasein;
};

//////////////////////////////////////////////////////////////////////////////////////////////////

extern "C"
{
void load(InterfaceTable *inTable);

void TestOsc_Ctor(TestOsc *unit);
void TestOsc_next_ikk(TestOsc *unit, int inNumSamples);
void TestOsc_next_ika(TestOsc *unit, int inNumSamples);
void TestOsc_next_iak(TestOsc *unit, int inNumSamples);
void TestOsc_next_iaa(TestOsc *unit, int inNumSamples);
}

//////////////////////////////////////////////////////////////////////////////////////////////////

#define GET_TABLE \
float fbufnum = ZIN0(0); \
if (fbufnum != unit->m_fbufnum) { \
uint32 bufnum = (uint32)fbufnum; \
World *world = unit->mWorld; \
if (bufnum >= world->mNumSndBufs) bufnum = 0; \
unit->m_buf = world->mSndBufs + bufnum; \
} \
SndBuf *buf = unit->m_buf; \
if(!buf) { \
ClearUnitOutputs(unit, inNumSamples); \
return; \
} \
float *bufData __attribute__((__unused__)) = buf->data; \
if (!bufData) { \
ClearUnitOutputs(unit, inNumSamples); \
return; \
} \
int tableSize = buf->samples;

////////////////////////////////////////////////////////////////////////////////////////////////////////

void TestOsc_Ctor(TestOsc *unit)
{
unit->m_fbufnum = -1e9f;
if (INRATE(1) == calc_FullRate) {
if (INRATE(2) == calc_FullRate) {
//Print("next_iaa\n");
SETCALC(TestOsc_next_iaa);
} else {
//Print("next_iak\n");
SETCALC(TestOsc_next_iak);
}
} else {
if (INRATE(2) == calc_FullRate) {
//Print("next_ika\n");
SETCALC(TestOsc_next_ika);
} else {
//Print("next_ikk\n");
SETCALC(TestOsc_next_ikk);
}
}
unit->m_buf = unit->mWorld->mSndBufs;
unit->mTableSize = -1;
unit->m_phasein = ZIN0(2);
unit->m_phaseoffset = (int32)(unit->m_phasein * unit->m_radtoinc);
if (INRATE(2) == calc_FullRate) {
unit->m_phase = 0;
} else {
unit->m_phase = unit->m_phaseoffset;
}
TestOsc_next_ikk(unit, 1);
}

//////////////!!!

void TestOsc_next_ikk(TestOsc *unit, int inNumSamples)
{
// get table
GET_TABLE

float *table0 = bufData;
float *table1 = table0 + 1;
if (tableSize != unit->mTableSize) {
unit->mTableSize = tableSize;
int tableSize2 = tableSize >> 1;
unit->m_lomask = (tableSize2 - 1) << 3; // Osc, OscN, COsc, COsc, COsc2, OscX4, OscX2
unit->m_radtoinc = tableSize2 * (rtwopi * 65536.); // Osc, OscN, PMOsc
// SigOsc, Osc, OscN, PMOsc, COsc, COsc2, OscX4, OscX2
unit->m_cpstoinc = tableSize2 * SAMPLEDUR * 65536.;
}

float *out = ZOUT(0);
float freqin = ZIN0(1);
float phasein = ZIN0(2);

int32 phase = unit->m_phase;
int32 lomask = unit->m_lomask;

int32 freq = (int32)(unit->m_cpstoinc * freqin);
int32 phaseinc = freq + (int32)(CALCSLOPE(phasein, unit->m_phasein) * unit->m_radtoinc);
unit->m_phasein = phasein;

LOOP(inNumSamples,
ZXP(out) = lookupi1(table0, table1, phase, lomask);
phase += phaseinc;
);
unit->m_phase = phase;

}

void TestOsc_next_ika(TestOsc *unit, int inNumSamples)
{
// get table
GET_TABLE
float *table0 = bufData;
float *table1 = table0 + 1;
if (tableSize != unit->mTableSize) {
unit->mTableSize = tableSize;
int tableSize2 = tableSize >> 1;
unit->m_lomask = (tableSize2 - 1) << 3; // Osc, OscN, COsc, COsc, COsc2, OscX4, OscX2
unit->m_radtoinc = tableSize2 * (rtwopi * 65536.); // Osc, OscN, PMOsc
// SigOsc, Osc, OscN, PMOsc, COsc, COsc2, OscX4, OscX2
unit->m_cpstoinc = tableSize2 * SAMPLEDUR * 65536.;
}

float *out = ZOUT(0);
float freqin = ZIN0(1);
float *phasein = ZIN(2);

int32 phase = unit->m_phase;
int32 lomask = unit->m_lomask;

int32 freq = (int32)(unit->m_cpstoinc * freqin);
float radtoinc = unit->m_radtoinc;
//Print("Osc_next_ika %d %g %d\n", inNumSamples, radtoinc, phase);
LOOP(inNumSamples,
int32 phaseoffset = phase + (int32)(radtoinc * ZXP(phasein));
ZXP(out) = lookupi1(table0, table1, phaseoffset, lomask);
phase += freq;
);
unit->m_phase = phase;
//unit->m_phasein = phasein;

}

void TestOsc_next_iaa(TestOsc *unit, int inNumSamples)
{
// get table
GET_TABLE
float *table0 = bufData;
float *table1 = table0 + 1;
if (tableSize != unit->mTableSize) {
unit->mTableSize = tableSize;
int tableSize2 = tableSize >> 1;
unit->m_lomask = (tableSize2 - 1) << 3; // Osc, OscN, COsc, COsc, COsc2, OscX4, OscX2
unit->m_radtoinc = tableSize2 * (rtwopi * 65536.); // Osc, OscN, PMOsc
// SigOsc, Osc, OscN, PMOsc, COsc, COsc2, OscX4, OscX2
unit->m_cpstoinc = tableSize2 * SAMPLEDUR * 65536.;
}

float *out = ZOUT(0);
float *freqin = ZIN(1);
float *phasein = ZIN(2);

int32 phase = unit->m_phase;
int32 lomask = unit->m_lomask;

float cpstoinc = unit->m_cpstoinc;
float radtoinc = unit->m_radtoinc;
//Print("Osc_next_iaa %d %g %g %d\n", inNumSamples, cpstoinc, radtoinc, phase);
LOOP(inNumSamples,
int32 phaseoffset = phase + (int32)(radtoinc * ZXP(phasein));
float z = lookupi1(table0, table1, phaseoffset, lomask);
phase += (int32)(cpstoinc * ZXP(freqin));
ZXP(out) = z;
);
unit->m_phase = phase;
//unit->m_phasein = ZX(phasein);

}

void TestOsc_next_iak(TestOsc *unit, int inNumSamples)
{
// get table
GET_TABLE
float *table0 = bufData;
float *table1 = table0 + 1;
if (tableSize != unit->mTableSize) {
unit->mTableSize = tableSize;
int tableSize2 = tableSize >> 1;
unit->m_lomask = (tableSize2 - 1) << 3; // Osc, OscN, COsc, COsc, COsc2, OscX4, OscX2
unit->m_radtoinc = tableSize2 * (rtwopi * 65536.); // Osc, OscN, PMOsc
// SigOsc, Osc, OscN, PMOsc, COsc, COsc2, OscX4, OscX2
unit->m_cpstoinc = tableSize2 * SAMPLEDUR * 65536.;
}

float *out = ZOUT(0);
float *freqin = ZIN(1);
float phasein = ZIN0(2);

int32 phase = unit->m_phase;
int32 lomask = unit->m_lomask;

float cpstoinc = unit->m_cpstoinc;
int32 phaseinc = (int32)(CALCSLOPE(phasein, unit->m_phasein) * unit->m_radtoinc);
int32 phaseoffset = unit->m_phaseoffset;

LOOP(inNumSamples,
float z = lookupi1(table0, table1, phase + phaseoffset, lomask);
phaseoffset += phaseinc;
phase += (int32)(cpstoinc * ZXP(freqin));
ZXP(out) = z;
);
unit->m_phase = phase;
unit->m_phasein = phasein;
unit->m_phaseoffset = phaseoffset;

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////

void load(InterfaceTable *inTable)
{
ft = inTable;

DefineSimpleUnit(TestOsc);
}

//////////////////////////////////////////////////////////////////////////////////////////////////

.............................................

Part II -- Compiling Your SC3 Plugin...

1) you won't be needing Terminal anymore at this point, so quit it...
2) go to the folder SuperCollider3 and duplicate (command-D) the file 'xSC3plugins.pbproj'
NOTE THAT THIS IS A DIFFERENT FILE THAN 'SC3pluginsTEST.pbproj' WHICH WILL NOT WORK!!

rename the new file something like 'xSC3pluginsTEST.pbproj'

3) now open that file in Xcode. go to 'Project' on the menu bar, select 'Add Files...', and select the 'TestOsc.cpp' file you just created. hit OK. 'TestOsc.cpp' will appear up at the top of the 'Groups & Files' column. we will come back to this file in a moment...

4) toggle the 'Targets' arrow so that you can see all of the targets. option-click on the Target called 'OscUGens' and select 'duplicate'. down at the bottom of the target list a duplicate file with the same name will appear. option-click the duplicate and select 'rename'. rename it 'TestOsc'.

5) drag the the 'TestOsc.cpp' file to the 'Sources' icon under the Target 'TestOsc'.

6) now we have to do some searching to modify a handful of things. first make sure that the editor frame is open by clicking the editor window button in the toolbar. then click on the new Target, 'TestOsc' - information should appear in the editor. do the following:
- click 'Summary' and change the Base Product Name to TestOsc.scx
- click 'Settings' and make sure the Product Name has also been changed to TestOsc.scx
- click on 'Build Phases'; under Sources, select and delete the file OscUgens.cpp - make sure to leave the TestOsc.cpp file in its place...
- then scroll to the bottom of the 'Build Phases' frame; go to 'Shell Script Files' and correct the 2nd line so that it now says: cp build/TestOsc.scx build/plugins

7) go back up and double-click the Target 'All Plugins' so that it's info panel pops up. click the + button at the bottom and add the 'TestOsc' Target to the list.

8) close the window and hit the Build icon. if you've done everything correctly, you should in a few minutes have a nice TestOsc.scx plugin sitting in the 'SuperCollider3/build/plugins' folder.

9) now open SuperCollider but don't boot it up yet. find the Osc.sc file by 1) typing 'Osc', 2) highlighting it, and 3) then hitting command-J. find the class definition for Osc and copy it into another file - name that file TestOsc.sc and change the mention of Osc in it to 'TestOsc'. your file should end up looking like this:

TestOsc : UGen {
*ar {
arg bufnum, freq=440.0, phase=0.0, mul=1.0, add=0.0;
^this.multiNew('audio', bufnum, freq, phase).madd(mul, add)
}
*kr {
arg bufnum, freq=440.0, phase=0.0, mul=1.0, add=0.0;
^this.multiNew('control', bufnum, freq, phase).madd(mul, add)
}
}

save that file somewhere into your SCClassLibrary folder.

10) now boot up your SC Server. if you get any error messages, retrace your steps...

11) open the Osc helpfile and change the SynthDef contents so that it says TestOsc.ar instead of Osc.ar.

12) compile the code and listen to your new plugin that you just created by modifying some pre-existing SC3 code. if you're getting an error message, retrace your steps...

congrats. that's all there is to getting started with Plugins in SC3. after a few times through the process, you'll find it to be a pretty simple procedure. the hardest parts really have less to do with SuperCollider and more to do with the clunkiness of the Xcode interface.

as for dsp code, all of SuperCollider is laying there before you. chop away. i've also heard that http://www.musicdsp.org has a good deal of code online in its archive...

good luck. contact me if i've missed something and i'll update this...
adam overton | a[at]plus1plus1plus[dot]org

 

top

................................
................................

Debugging your SC3 Plugin

I can't believe it took me sooooo long (almost a year) to figure out that I could use the Xcode debugger in SuperCollider. Part of the reason it took so long for me to figure this out is that a teacher of mine was of the belief that it wasn't possible because of such and such. I sadly took his advice... But recently I went online and did a search and found the information that I was looking for right here: Nick Collin's Plugin Tutorial. The only reason I will reproduce the steps down here is because I found his description a little vague and confusing at first, AND he failed to mention one very important thing: YOU CAN ONLY DEBUG WHILE USING THE INTERNAL SERVER!! This took me quite awhile to figure out. Anyways, here are the steps that need to be taken:

1. Make Sure You're in Development Mode. In your Xcode project, either double-click on the plugin project icon or highlight it and Get Info (Cmd-I) - this will bring up the Project Info Pane. Click over to the 'Styles' menu and make sure the 'Active Build Style' is set to "Development". Also make sure in the scroll-down list that 'Generate Debug Symbols' is enabled.

2. Create a Custom Executable. Now ctrl-click your plugins project icon to bring up a pop-up menu. Go to the 'Add' sub-menu and select 'New Custom Executable'. Name this whatever you want, choose the path to the SuperCollider3 application as your executable path, and add this to your existing project by clicking 'Finish'.

3. Add Some Stuff to the Custom Executable. Highlight the new custom executable that you have created and either double-click or Get Info. Go to the 'Arguments' menu. Add the following Argument:

-u 57110

Leave the variables area alone and close the window.

4. Add Some Debugging Breakpoints. Click around in the grey left-hand column of your code document in Xcode - you'll notice little pointers will appear and disappear - they are your break-points. When debugging, the compiler will freeze the action of the program wherever you place these breakpoints so that you can then jump around and look at what values your variables are holding, and also to see if your algorithms are executing correctly.

5. Attempt to Debug. Make sure your SuperCollider app is not already open - if it is open I've found that Xcode will open multiple copies of it which can become confusing. Click either 'Compile and Debug' (or just 'Debug' if you haven't made any changes to your code). It may take a few seconds, but after awhile SC3 will automatically open. You should boot the INTERNAL SERVER (THIS WILL NOT WORK WITH THE LOCAL SERVER!!!), open the SuperCollider code that uses your new plugin, and execute it as you normally would. If SC ends up cycling past one of your breakpoints then it will freeze and the Debugger window in Xcode will appear, showing you the current position in the plugin code and all the various values being stored in memory. Explore by clicking all of the various navigation buttons. The 'continue' button takes you from breakpoint to breakpoint; the 'step into' button advances forward line-by-line -- if you reach a function, however, it will jump to the location in your document where the function's innards exist before proceeding; the 'step over' button does basically the same line-by-line action except it jumps over functions and only shows their results. Explore explore explore.

It's it important to remember two things regarding debugging with the Internal or Local Servers: as I said earlier, the debugger only works (at least with the debug argument that we're using) with the Internal Server. On the other hand, another way to debug is to add printf() statements - however, these seem to only print out when you're using the Localhost Server(!?). So just remain aware, or else you risk wasting a lot of time trying to figure out why your code isn't working/debugging properly.

 

top