From Fedora Project Wiki
(added most of the Method One how-to)
(partially-complete prose)
Line 5: Line 5:


== Requirements and Installation ==
== Requirements and Installation ==
(no algorithms)
=== Software and Hardware Requirements ===
 
* JACK
=== Requirements ===
* Planet CCRMA at Home
This will be a list of software and intellectual requirements for using SC.  Primarily it will be a list, but also I'll put in a brief description of each item.


* You need JACK, which is pulled in by suppercollider-sclang
* You need JACK, which is pulled in by suppercollider-sclang
**** BUG: supercollider-gedit requires the packages 'gnome-python2-gconf' but didn't pull it in for me
**** BUG: supercollider-gedit requires the packages 'gnome-python2-gconf' but didn't pull it in for me
=== Intellectual Requirements ===
<!-- Rarely is such a warning required, but I feel it is necessary for SuperCollider -->
SuperCollider is not easy to use(????better word????), and it is extremely unlikely that it could be learned by intuition and guessing alone.  The applications SuperCollider applications themselves are easy to use, and they generally work very well, but like LilyPond and Frescobaldi, they are merely tools to help you accomplish something useful.  SuperCollider is an extremely powerful and flexible programming language, tailored for audio processing.  As often happens with computers, however, added flexibility and power comes at the cost of added complexity - or at least greater understanding on the part of the user.
Your readiness for SuperCollider cannot be objectively judged beforehand, and any interested and sufficiently determined user will be able to understand and conquer the language's features.  Reading briefly about the following concepts before you start with SuperCollider may help you to more easily grasp the techniques in SuperCollider.  It is certainly not necessary to read all of these articles, but enough that you get a vague idea of the concepts.  It will be more useful for these to serve as a reference, should you need them.
* [http://en.wikipedia.org/wiki/Computer_programming Computer Programming]: You probably know what this is; it's what you'll be doing.
* [http://en.wikipedia.org/wiki/Programming_language Programming Language]: SuperCollider is a programming language.
* [http://en.wikipedia.org/wiki/Interpreter_%28computing%29 Interpreter]: This reads your code, and sends commands to the server, which cause it to produce sound.
* [http://en.wikipedia.org/wiki/Server_%28computing%29 Server]: SuperCollider has a 'server' component, which is operated by the interpreter.
* [http://en.wikipedia.org/wiki/Functional_programming Functional Programming]: SuperCollider can be treated as a "functional" language.
* [http://en.wikipedia.org/wiki/Imperative_programming Imperative Programming]: SuperCollider can be treated as an "imperative" language.
* [http://en.wikipedia.org/wiki/Object-oriented_programming Object-oriented Programming]: SuperCollider can be treated as an "object-oriented" language.
<!-- Sourced from http://en.wikipedia.org/wiki/List_of_basic_computer_programming_topics -->


=== Available SuperCollider Packages ===
=== Available SuperCollider Packages ===
# Use PackageKit, KPackageKit, or yum to list the available components of SC
The SuperCollider packages are all held in the Planet CCRMA at Home repository, and there are a lot of them.  Many of them have standard Fedora suffixes, but many are other kinds of optional components.  Most of the optional features add libraries to SuperCollider, allowing you to use them in your audio programs.  The specific features available in each additional package are not described here.
# Here's a list of what they do:
* supercollider-ambiem : Optional Library ("Ambisonics classes for SC").
#* supercollider-ambiem : "Ambisonics classes for SC"
* supercollider-debuginfo : Decodes the debugging information provided by SuperCollider.
#* supercollider-debuginfo :  
* supercollider-devel : Contains files needed for development with SuperCollider.
#* supercollider-devel :  
* supercollider-dewdrop : Optional Library ("DewDrop external collection for SC").
#* supercollider-dewdrop : "Dewdrop extrenal collection for SC"
* supercollider-emacs : Adds SuperCollider support to the "emacs" text editor.
#* supercollider-emacs :  
* supercollider-extras : Optional Library ("Extra plugins and classes for SC").
#* supercollider-extras : "Extra plugins and classes for SC"
* supercollider-gedit : Adds SuperCollider support to the "gedit" text editor.
#* supercollider-gedit :  
* supercollider-libscsynth : "SuperCollider synthesis library."
#* supercollider-libscsynth : "SuperCollider synthesis library"
* supercollider-quarks : Optional Library ("Local quarks repository for SuperCollider").
#* supercollider-quarks : "Local quarks repository for SuperCollider"
* supercollider-sclang : Help files, examples, the class library, and language interpreter.
#* supercollider-sclang : "SuperCollider language"
* supercollider-vim : Adds SuperCollider support to the "vim" text editor.
#* supercollider-vim :  
* supercollider : Installs the "minimum requirements" to run SuperCollider.
#* supercollider : (pulls in "minimum requirements" to run SC)
** supercollider-sclang
#** -sclang
** supercollider-libscsynth
#** -libscsynth
** fftw
#** libX11
** w3m-el
#** libgcc
** emacs
#** glibc
* supercollider-bbcut2 : Optional Library ("Beat tracking of audio streams").
#** fftw
* supercollider-bbcut2-debuginfo : Decodes the debugging information provided by bbcut2.
#** w3m-el
* supercollider-mathlib : Optional Library ("Useful classes for SC").
#** libsndfile
* supercollider-redclasses : Optional Library ("Frederik Olofsson Red SC classes").
#** emacs
* supercollider-redclasses-debuginfo : Decodes the debugging information provided by redclasses.
#** libstdc++
* supercollider-world : Installs most SuperCollider packages.
#* supercollider-bbcut2-debuginfo :  
** supercollider
#* supercollider-mathlib : "Useful classes for SC"
** abmiem
#* supercollider-redclasses : "Frederik Olofsson Red SC classes"
** -redclasses
#* suprcollider-redclasses-debuginfo :  
** -dewdrop
#* supercollider-world : (pulls in most SC packages)
** -emacs
#** supercollider
** -mathlib
#** abmiem
** -midifile
#** -redclasses
** -extras
#** -dewdrop
** -bbcut2
#** -emacs
** -reduniverse
#** -mathlib
* supercollider-midifile : Optional Library ("MIDI file reader for SuperCollider").
#** -midifile
* supercollider-reduniverse : Optional Library ("Sonification and visualization of dynamic systems").
#** -extras
 
#** -bbcut2
=== Recommended Installation ===
#** -reduniverse
If you have never used SuperCollider before, then I recommend installing the smallest number of packages possible.  This will allow you to start learning with the core classes, available on all SuperCollider installations.  Installing the bare minimum requirements will not prevent you from installing optional libraries in the future, of course - it just saves you disk space.
#* supercollider-midifile : "MIDI file reader for SuperCollider"
 
#* supercollider-reduniverse : "Sonification and visualization of dynamic systems"
The recommended installation also avoids installing the "emacs" or "vim" components, which - unless you are already a programmer - you probably don't know how to use.  The "emacs" and "vim" (based on "vi") text editors are extremely powerful and extensible, but they can be difficult to learn.  Furthermore, there's no reason to learn them just for SuperCollider, because the component for GEdit is more than sufficient.
#* supercollider-bbcut2 : "Beat tracking of audio streams"
 
# As a start, I recommend 'yum install supercollider supercollider-gedit'
Finally, SuperCollider is not yet fully 64-bit aware, so users of 64-bit systems may be required to install a relatively large number of 32-bit system libraries, just to use the core SuperCollider application.  SuperCollider is under constant development, and a fully 64-bit version may become available at any time.
  -avoids extensions that you might not use
  -avoids emacs
# On a 64-bit system, this will bring in many i686 dependencies


=== The Moduler Nature of SuperCollider ===
To install the minimum recommended installation for SuperCollider:
This will explain what the title says, as an explanation for why you can download different packages that do different things, but they aren't all required in order to use SuperColliderI'll also explain why I've chosen to avoid these extra packages in this tutorial.
# In a terminal, run <code>su -c 'yum install supercollider supercollider-gedit'</code>
# Review the proposed installation carefullyThe list may be quite long, and require a large download.


== Using GEdit to Write and Run Code/Programs/Music ==
== Using GEdit to Write and Run Music Porgrams ==
The 'supercollider-gedit' package installs an extension for GEdit that allows editing and running SuperCollider code from within the programAlso explain why I've chosen to avoid the emacs version, which seems to be more heavily suggested by the packaging method.
The 'supercollider-gedit' package installs an extension for GEdit which allows editing and running SuperCollider code from within GEdit.  There are also SuperCollider extensions for the "emacs" and "vim" text editorsThis tutorial uses the GEdit extension, because it is easier to learn how to use GEdit than "vim" or "emacs."


=== Enable and Configure SCEd in GEdit ===
=== Enable and Configure SCEd in GEdit ===
# open GEdit
These steps should be followed the first time that you use GEdit's SuperCollider extension.
# go to 'Edit > Preferences'
# Start "GEdit"
# 'Plugins' tab
# Open the Preferences window (from the menu, select 'Edit > Preferences')
# Scroll down to "Sced" and make sure that it's checked
# Choose the 'Plugins' tab, and scroll down to "Sced," then make sure that it is selected.
# Choose 'Configure Plugin' then select a "Runtime folder" where you'll store files created at runtimeI've got this set to a sub-folder of where I store my SuperCollider code.
# Click on the "Configure Plugin" button, then select a runtime folder where the SuperCollider server will store any synth sent to it during program executionThe safest place for this folder could be a sub-folder of the location where you will store your SuperCollider code.
# Close the Preferences dialogue


=== Put GEdit into SuperCollider Mode ===
=== Enable SuperCollider Mode and Start a Server ===
# Under the 'Tools' box, select 'SuperCollider Mode'
These steps should be followed every time you open GEdit, and wish to use the SuperCollider extension.
# Notice that a "SuperCollider" menu appears, and a window at the bottom, saying "SuperCollider output" In this window, it says "for help type ... crtl-U (sced/gedit)" meaning that you can press Ctrl + U at any time, and the help files will be opened in your default browser
# From the menu, select 'Tools > SuperCollider Mode'
## If you find code in the help files that you wish to execute, you'll have to copy and paste it into GEdit before using Ctrl + E
# A 'SuperCollider' menu should appear, and a window at the bottom which says, "SuperCollider output".
# When you save a file in GEdit with a *.sc extension, GEdit will automatically display that file in a specific way, coloured to more easily see the meaning of SuperCollider code
# If you cannot see the window at the bottom, then select 'View > Bottom Pane' from the menu, so that it shows up. It is sometimes important to see the information that SuperCollider provides in this window.
# In order to evaluate any code, an instance of the server will need to be runningYou can start the server with 'SuperCollider > Start Server'
# After enabling SuperCollider mode, the window should display a series of noticesNear the end should be something like this:
## Look through the "SuperCollider output" window (bottom pane; you may have closed it) for any warnings.
<pre>RESULT = 0
## The warning: "WARNING: GUI.fromID : The GUI scheme 'swing' is not installed" is okay, but it means that we will not be able to use any of the GUI features of SC, or the SwingOSC GUI server
Welcome to SuperCollider, for help type ctrl-c ctrl-h (Emacs) or :SChelp (vim) or ctrl-U (sced/gedit)</pre>
## You should see a message saying "Welcome to SuperCollider, for help... " again, followed by "RESULT = 0" meaning that it started successfully
If this window gives a non-zero value for "RESULT," then an error has probably occurred, and you should scroll up to see what it is, and try to fix it. If you receive the following warning: "The GUI scheme 'swing' is not installed" then you will not be able to run any SuperCollider prorams that use a GUI (graphical user interface).  The GUI components are not used anywhere in this Guide, and they are highly optional.
## You should also see messages from "JackDriver"; if JACK is not already started, then you'll see that happening. Either way you'll see messages similar to "JackDriver: connected SuperCollider:out_1 to system:playback_1"
# You will probably also want to start a server at this point, so from the menu select 'SuperCollider > Start Server'.
## One of the last lines will be "SuperCollider 3 server ready.." (yes, only 2 periods)
# After the server starts, you should see messages from "JackDriver".  If a JACK server is not already started, then SuperCollider will start one automatically.
 
# If the SuperCollider server started successfully, you should see a message similar to this:
N.B. Everything up to "Welcome to SuperCollider... " happens when SuperCollider mode is enabled.  The server's boot begins with "booting 57110" and messages from JACK; so, what I wrote above is wrong.
<pre>SuperCollider 3 server ready..
JackDriver: max output latency 46.4 ms
notification is on</pre>


=== Executing Code in GEdit ===
=== Executing Code in GEdit ===
# Select some code to execute (it must be a complete and playable portion), then press Ctrl + e
You can execute code directly from GEdit, without having to use "sclang" from the command-line.
# to stop the sound, press "esc"
# Ensure that SuperCollider mode is enabled, and that a server has been started.
# You may want to select the entire document, press Ctrl + a ("select all")before Ctrl + e
# Select the code that you wish to execute.  A single line of code may be executed simply by placing the text-input cursor on that line.
# If you want to hear only one line of code evaluated, it must still be complete and playable, but you need only put the cursor on that line, and there's no need to select the whole line
# Press 'Ctrl+E' on the keyboard, or from the menu select 'SuperCollider > Evaluate'
# If the server successfullly executes the code, then something will appear in the "SuperCollider output" pane; if it's a sound, then something like "Synth("temp_0":1000)" but perhaps "RESULT = 0"
# To stop all sound on the server, press 'Esc' on the keyboard, or from the menu select 'SuperCollider > Stop Sound'
# If the server successfully executes the code, then it will output something to the "SuperCollider output" pane.  The output will be different, depending on what SuperCollider asked the server to do, but will usually either look like this: <code>Synth("temp_0": 1000)</code>, or this: <code>RESULT = 0</code>.


=== Other Tips for Using GEdit with SuperCollider ===
=== Other Tips for Using GEdit with SuperCollider ===
* If you close GEdit while the SuperCollider server is running, then GEdit will automatically shut down the server.
* If you close GEdit while the SuperCollider server is running, then GEdit will automatically shut down the server.
* Any time that JACK is started by the SuperCollider server, it will be shut down when the SuperCollider server is shut down.
* If JACK is started by SuperCollider, then it will automatically terminate when the SuperCollider server terminates.
* It is necessary to go 'Tools > SuperCollider Mode' every time you start GEdit, but the plugin should remain enabled in 'Edit GEdit Preferences'
* SuperCollider will automatically attempt to connect its outputs to the system's outputs.  If your audio output doesn't work, then you should use "QjackCtl" to verify that it is correctly connected.
* Other Functions in the SuperCollider menu:
* Other Functions available in the SuperCollider menu include:
** Find Help (same as Ctrl + U; opens the help documents in the default browser)
** Find Help (Opens the SuperCollider help file for currently-selected object).
** Find Definition
** Find Definition (Opens the SuperCollider source file for the currently-selected object).
** Browse class
** Browse class (doesn't work for me?!?!?!?!)
** Inspect Object
** Inspect Object (doesn't work for me?!?!?!?!)
** Restart Interpreter
** Restart Interpreter (Restarts the SuperCollider interpreter; also closes running servers, but does not restart them).
** Clear output
** Clear output (Clears all output from the "SuperCollider output" pane).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


== The Different Parts of SuperCollider ==
== The Different Parts of SuperCollider ==
Line 137: Line 173:


=== Patterns ===
=== Patterns ===


== Composing with SuperCollider ==
== Composing with SuperCollider ==

Revision as of 06:02, 4 July 2010

What Is SuperCollider

(no algorithms)

!! Somewhere, I have to note that I break grammatical rules and use an apostrophe for pluralization when I'm talking about programming-language constructs !!

Requirements and Installation

Software and Hardware Requirements

  • JACK
  • Planet CCRMA at Home
  • You need JACK, which is pulled in by suppercollider-sclang
        • BUG: supercollider-gedit requires the packages 'gnome-python2-gconf' but didn't pull it in for me

Intellectual Requirements

SuperCollider is not easy to use(????better word????), and it is extremely unlikely that it could be learned by intuition and guessing alone. The applications SuperCollider applications themselves are easy to use, and they generally work very well, but like LilyPond and Frescobaldi, they are merely tools to help you accomplish something useful. SuperCollider is an extremely powerful and flexible programming language, tailored for audio processing. As often happens with computers, however, added flexibility and power comes at the cost of added complexity - or at least greater understanding on the part of the user.

Your readiness for SuperCollider cannot be objectively judged beforehand, and any interested and sufficiently determined user will be able to understand and conquer the language's features. Reading briefly about the following concepts before you start with SuperCollider may help you to more easily grasp the techniques in SuperCollider. It is certainly not necessary to read all of these articles, but enough that you get a vague idea of the concepts. It will be more useful for these to serve as a reference, should you need them.

Available SuperCollider Packages

The SuperCollider packages are all held in the Planet CCRMA at Home repository, and there are a lot of them. Many of them have standard Fedora suffixes, but many are other kinds of optional components. Most of the optional features add libraries to SuperCollider, allowing you to use them in your audio programs. The specific features available in each additional package are not described here.

  • supercollider-ambiem : Optional Library ("Ambisonics classes for SC").
  • supercollider-debuginfo : Decodes the debugging information provided by SuperCollider.
  • supercollider-devel : Contains files needed for development with SuperCollider.
  • supercollider-dewdrop : Optional Library ("DewDrop external collection for SC").
  • supercollider-emacs : Adds SuperCollider support to the "emacs" text editor.
  • supercollider-extras : Optional Library ("Extra plugins and classes for SC").
  • supercollider-gedit : Adds SuperCollider support to the "gedit" text editor.
  • supercollider-libscsynth : "SuperCollider synthesis library."
  • supercollider-quarks : Optional Library ("Local quarks repository for SuperCollider").
  • supercollider-sclang : Help files, examples, the class library, and language interpreter.
  • supercollider-vim : Adds SuperCollider support to the "vim" text editor.
  • supercollider : Installs the "minimum requirements" to run SuperCollider.
    • supercollider-sclang
    • supercollider-libscsynth
    • fftw
    • w3m-el
    • emacs
  • supercollider-bbcut2 : Optional Library ("Beat tracking of audio streams").
  • supercollider-bbcut2-debuginfo : Decodes the debugging information provided by bbcut2.
  • supercollider-mathlib : Optional Library ("Useful classes for SC").
  • supercollider-redclasses : Optional Library ("Frederik Olofsson Red SC classes").
  • supercollider-redclasses-debuginfo : Decodes the debugging information provided by redclasses.
  • supercollider-world : Installs most SuperCollider packages.
    • supercollider
    • abmiem
    • -redclasses
    • -dewdrop
    • -emacs
    • -mathlib
    • -midifile
    • -extras
    • -bbcut2
    • -reduniverse
  • supercollider-midifile : Optional Library ("MIDI file reader for SuperCollider").
  • supercollider-reduniverse : Optional Library ("Sonification and visualization of dynamic systems").

Recommended Installation

If you have never used SuperCollider before, then I recommend installing the smallest number of packages possible. This will allow you to start learning with the core classes, available on all SuperCollider installations. Installing the bare minimum requirements will not prevent you from installing optional libraries in the future, of course - it just saves you disk space.

The recommended installation also avoids installing the "emacs" or "vim" components, which - unless you are already a programmer - you probably don't know how to use. The "emacs" and "vim" (based on "vi") text editors are extremely powerful and extensible, but they can be difficult to learn. Furthermore, there's no reason to learn them just for SuperCollider, because the component for GEdit is more than sufficient.

Finally, SuperCollider is not yet fully 64-bit aware, so users of 64-bit systems may be required to install a relatively large number of 32-bit system libraries, just to use the core SuperCollider application. SuperCollider is under constant development, and a fully 64-bit version may become available at any time.

To install the minimum recommended installation for SuperCollider:

  1. In a terminal, run su -c 'yum install supercollider supercollider-gedit'
  2. Review the proposed installation carefully. The list may be quite long, and require a large download.

Using GEdit to Write and Run Music Porgrams

The 'supercollider-gedit' package installs an extension for GEdit which allows editing and running SuperCollider code from within GEdit. There are also SuperCollider extensions for the "emacs" and "vim" text editors. This tutorial uses the GEdit extension, because it is easier to learn how to use GEdit than "vim" or "emacs."

Enable and Configure SCEd in GEdit

These steps should be followed the first time that you use GEdit's SuperCollider extension.

  1. Start "GEdit"
  2. Open the Preferences window (from the menu, select 'Edit > Preferences')
  3. Choose the 'Plugins' tab, and scroll down to "Sced," then make sure that it is selected.
  4. Click on the "Configure Plugin" button, then select a runtime folder where the SuperCollider server will store any synth sent to it during program execution. The safest place for this folder could be a sub-folder of the location where you will store your SuperCollider code.

Enable SuperCollider Mode and Start a Server

These steps should be followed every time you open GEdit, and wish to use the SuperCollider extension.

  1. From the menu, select 'Tools > SuperCollider Mode'
  2. A 'SuperCollider' menu should appear, and a window at the bottom which says, "SuperCollider output".
  3. If you cannot see the window at the bottom, then select 'View > Bottom Pane' from the menu, so that it shows up. It is sometimes important to see the information that SuperCollider provides in this window.
  4. After enabling SuperCollider mode, the window should display a series of notices. Near the end should be something like this:
RESULT = 0
Welcome to SuperCollider, for help type ctrl-c ctrl-h (Emacs) or :SChelp (vim) or ctrl-U (sced/gedit)

If this window gives a non-zero value for "RESULT," then an error has probably occurred, and you should scroll up to see what it is, and try to fix it. If you receive the following warning: "The GUI scheme 'swing' is not installed" then you will not be able to run any SuperCollider prorams that use a GUI (graphical user interface). The GUI components are not used anywhere in this Guide, and they are highly optional.

  1. You will probably also want to start a server at this point, so from the menu select 'SuperCollider > Start Server'.
  2. After the server starts, you should see messages from "JackDriver". If a JACK server is not already started, then SuperCollider will start one automatically.
  3. If the SuperCollider server started successfully, you should see a message similar to this:
SuperCollider 3 server ready..
JackDriver: max output latency 46.4 ms
notification is on

Executing Code in GEdit

You can execute code directly from GEdit, without having to use "sclang" from the command-line.

  1. Ensure that SuperCollider mode is enabled, and that a server has been started.
  2. Select the code that you wish to execute. A single line of code may be executed simply by placing the text-input cursor on that line.
  3. Press 'Ctrl+E' on the keyboard, or from the menu select 'SuperCollider > Evaluate'
  4. To stop all sound on the server, press 'Esc' on the keyboard, or from the menu select 'SuperCollider > Stop Sound'
  5. If the server successfully executes the code, then it will output something to the "SuperCollider output" pane. The output will be different, depending on what SuperCollider asked the server to do, but will usually either look like this: Synth("temp_0": 1000), or this: RESULT = 0.

Other Tips for Using GEdit with SuperCollider

  • If you close GEdit while the SuperCollider server is running, then GEdit will automatically shut down the server.
  • If JACK is started by SuperCollider, then it will automatically terminate when the SuperCollider server terminates.
  • SuperCollider will automatically attempt to connect its outputs to the system's outputs. If your audio output doesn't work, then you should use "QjackCtl" to verify that it is correctly connected.
  • Other Functions available in the SuperCollider menu include:
    • Find Help (Opens the SuperCollider help file for currently-selected object).
    • Find Definition (Opens the SuperCollider source file for the currently-selected object).
    • Browse class (doesn't work for me?!?!?!?!)
    • Inspect Object (doesn't work for me?!?!?!?!)
    • Restart Interpreter (Restarts the SuperCollider interpreter; also closes running servers, but does not restart them).
    • Clear output (Clears all output from the "SuperCollider output" pane).













The Different Parts of SuperCollider

(no algorithms)

Basic Audio Programming with SuperCollider

This will be adapted from Scott Wilson and James Harkins' tutorial that is distributed with SuperCollider. It is distributed under the CC-BY-SA licence.

Functions, Variables, and Assignment

Sound-Making Functions

ArrayedCollections and Multichannel Audio

The Mixer??

How to Get Help

SynthDefs and Synths

Busses

Groups, Nodes, and Ordering

Buffers??

Scheduling and Clocks

Routines and Tasks

Patterns

Composing with SuperCollider

See Method One and Method One (Short).

What This Is

This section is an explanation of the creative thought-process that went into creating the SuperCollider composition that I've called "Method One," for which the source and exported audio files are available below in the "Included Files" section.

It is my hope that, in illustrating how I developed this composition from a single SinOsc command, the reader will not only learn about SuperCollider and its abilities, but learn about how to be creative with SuperCollider, and how a simple idea can turn into something of greater and greater complexity.

As musicians, our goal is to learn enough SuperCollider to make music; we don't want to have to memorize which parameters do what for which functions, and in which order to call them. We want to know what they do for us musically. Explicitly calling parameters, and making comments about what does what, so that we can return later and change musical things, are going to help our musical productivity, at the expense of slowing down our typing.

Included Files

FSC_method_1.sc : This is an extensively-commented version of the source code. The comments not only describe the way the code works, but pose some problems and questions that you may wish to work on, to increase your knowledge of SuperCollider. The problem with the verbosity of the comments is that it can be difficult to read the code itself, as it would be written in a real program.

FSC_method_1-short.sc : This is a less-commented version of the source code. I've also re-written part of the code, to make it more flexible for use in other programs. The differences between this, and code that I would have written for myself only, are trivial.

FSC_method_1.flac : This is a recording that I produced of the program, which I produced in Ardour.

Inspiration

The intention of this program is to represent one way to write a SuperCollider program. I decided to take one class, SinOsc, and use it for "everything." Here, "everything" means any function that returns a sound, or any function that directly controls a SinOsc.

In order to fill up time, I decided to employ a three-part "rounded binary" form: ABA' or "something, something new, then the first thing again."

Designing the First Part

  1. I started with something simple: a single SinOsc: { SinOsc.ar(); }.play;
  2. This is not exciting: it just stays the same forever, and it only uses one channel! So, I added another SinOsc to the right channel, using the [ , ] array notation. The result is { [ SinOsc.ar(), SinOsc.ar() ] }.play;
  3. Now it sounds balanced, at least, like it's coming from the middle. But it's still boring, so I added a frequency-changing SinOsc to the right channel, resulting in { [ SinOsc.ar(), SinOsc.ar(SinOsc.kr(1,50,300)) ] }.play;
  4. Since that's difficult to read, and since I know that I'm just going to keep adding things, I expand the code a little bit to make it more legible. This gives me
    {
       var left = SinOsc.ar();
       var right = SinOsc.ar( SinOsc.kr( 1, 50, 300 ) );
       
       [ left, right ]
       
    }.play;
    I define a variable holding everything I want in the left channel, then the same for the right. I still use the [ , ] array notation to create a stereo array. Remember that SuperCollider functions return the last value stated, so it might look like the stereo array is ignored, but because this array is what is returned by the function contained between { and }, it is this array that gets played by the following ".play;"
  5. I also added a frequency controller to the left SinOsc, and realized that it's getting a bit difficult to read again, especially if I wanted to add another parameter to the SinOsc.ar objects. So I placed the SinOsc.kr's into their own variables: frequencyL and frequencyR. This results in
    {
       var frequencyL = SinOsc.kr( freq:10, mul:200, add:400 );
       var frequencyR = SinOsc.kr( freq:1, mul:50, add:150 );
       
       var left = SinOsc.ar( frequencyL );
       var right = SinOsc.ar( frequencyR );
       
       [ left, right ]
       
    }.play;
  6. Now I can experiment with the frequency-changing SinOsc's, to make sure that I get things just right. When I realize what the parameters do, I make a note for myself (see "FSC-method-1-.sc"), so that it will be easy to adjust it later. I also explicitly call the parameters. This isn't necessary, but it also helps to avoid future confusion. Most programmers would not explicitly call the parameters, but we're musicians, not programmers.
  7. The left channel has something like a "melody," so I decided to add a drone-like SinOsc to it. This is easy, of course, because any SinOsc left alone is automatically a drone! But, where should it be added? Into the "left" variable, of course. We'll create an array using [ , ] array notation. There are two things that I would do at this point to help with future readability:
    1. Align all of the left-channel SinOsc's vertically (using tabs and spaces), so that each line is one sound-generating UGen.
    2. At the end of each line, write a small comment describing what the UGen on that line doesn.
  8. Now the volume is a problem. For most sound-producing UGen's, the "mul" argument controls the volume. For most of those, the default is "1.0," and anything greater will create distorted output. The physics and computer science factors that wind up creating distortion are rather complicated, and it isn't necessary to understand them. What we need to know is that, if the output of a UGen (or some UGen's) sounds distorted, then we should probably adjust the "mul" argument. Sometimes, of course, you may prefer that distorted output.
    • It seems that, when you're using multiple SinOsc's in one output channel, the "mul" of all of them must not add to more than 1.0
    • We're using two output channels (left and right). We'll leave the right channel alone for now, because it has only one output UGen.
    • So, I'll change add a "mul" argument to each of the left-channel UGen's, to 0.5
  9. Now we can't hear the left channel, because the right channel is too loud! Playing with volumes (sometimes called "adjusting levels" for computers) is a constant aesthetic concern for all musicians. Add a "mul" argument to the right channel, and set it to what seems an appropriate volume for the moment. It will probably change later, but that's okay.
  10. But let's add another dimension to this: there's no reason to keep the volume static, because we can use a SinOsc to change it periodically! I added a SinOsc variable called "volumeL," which I used as the argument to "mul" for the "frequencyL" SinOsc in the left channel.
  11. And now the sheer boredom of the drone in the left channel becomes obvious. I decide to make it more interesting by adding a series of overtones (an overtone is...). I decide to add six, then experiment with which frequencies to add. But, every time I adjust one frequency, I have to re-calculate and change all the others. So I decide to add a variable for the drone's frequency: "frequencyL_drone". This way, after finding the right intervals, I can easily adjust all of them just by changing the variable. I've decided on drone*1, 2, 5, 13, and 28. These are more or less arbitrary, and I arrived on them through experimentation. Of course, the drone will be way too loud.
  12. Having SinOsc.ar( [frequencyL_drone,2*frequencyL_drone,5*frequencyL_drone,13*frequencyL_drone,28*frequencyL_drone], mul:0.1 ) in your program is not easy to read, and actually it doesn't work out volume-balance-wise (for me, at least): the high frequencies are too loud, and the lower ones are not loud enough. In retrospect, I should have created a variable for the "mul" of these drones, so I could adjust them easily in proportion. But, I didn't.
  13. A constant drone isn't as much fun as one that slowly changes over time. So, I changed the "frequencyL_drone" value to a SinOsc.kr UGen. Because it's supposed to be a "drone," it should change only very gradually, so I used a very small freqeuncy argument. It still moves quite quickly, but people won't want to listen to this too long, anyway!
  14. I did something similar with the right channel, addding a slowly-changing drone and overtones above it.
  15. After some final volume adjustments, I feel that I have completed the first part. There is no way to know for sure that you've finished until it happens. Even then, you may want to change your program later.

Designing the Second Part

The next thing that I did was to design the second part. This will not join them together yet, and I'm going to focus on something completely different, so I decided to do this in a separate file.

My inspiration for this part came from experimenting with the drones of the first part. There are a virtually unlimited number of combinations of sets of overtones that could be created, and the combinations of discrete frequencies into complex sounds is something that has fascinated me for a long time. Moreover, when thousands of discrete frequencies combine in such a way as to create what we think of as "a violin playing one note," it seems like a magical moment.

I'm going to build up a set of pseudo-random tones, adding them one at a time, in set increments. As you will see, this introduces a number of problems, primarily because of the scheduling involved with the one-by-one introduction of tones, and keeping track of those tones.

The fact that there are ten tones also poses a problem, because it might require a lot of typing. We'll see solutions to that, which use SuperCollider's programming features to greatly increase the efficiency.

Although we've already solved the musical problems (that is, we know what we want this part to sound like), the computer science (programming) problems will have to be solved the old-fashioned way: start with something simple, and build it into a complex solution.

First I will develop the version used in FSC-method-1.sc, then the version used in FSC-method-1-short.sc

Creating Ten Pseudo-Random Tones

  1. We'll start again with something simple, that we know how to do.
    {
       SinOsc.ar();
    }.play;
  2. We already know that we want this to produce stereo output, and we already know that we're going to be using enough SinOsc's that we'll need to reduce "mul." Keeping in mind that there will be ten pitches, and two SinOsc's for each of them, set both of those things now, keeping just one pitch for now.
  3. The first challenge is to implement pseudo-randomness. We'll use the number.rand function to generate a pseudo-random number (integer, actually), but if run as 50.rand, we will get a result between 0 and 50. As a frequency, this is not useful: most audio equipment cannot produce pitches below 20 Hz, and many people have problems hearing very low frequencies. This means that we'll need to add a value to .rand's output (like 100 + 50.rand, which will yield an integer between 100 and 150). I decided to go with a value between 200 Hz and 800 Hz instead, largely because I felt like it. Try setting the freq with the .rand call.
  4. I hope you didn't end up with two different frequencies! If you did, you'll need to use a variable to temporarily store the pseduo-random frequency, so that both sides can use it.
  5. Now we need to make ten of these, so copy-and-paste until there are ten different stereo pitches at once.
    {
       var frequency = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency, mul:0.01 ), SinOsc.ar( freq:frequency, mul:0.01 ) ]
       var frequency = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency, mul:0.01 ), SinOsc.ar( freq:frequency, mul:0.01 ) ]
       var frequency = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency, mul:0.01 ), SinOsc.ar( freq:frequency, mul:0.01 ) ]
       var frequency = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency, mul:0.01 ), SinOsc.ar( freq:frequency, mul:0.01 ) ]
       var frequency = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency, mul:0.01 ), SinOsc.ar( freq:frequency, mul:0.01 ) ]
       var frequency = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency, mul:0.01 ), SinOsc.ar( freq:frequency, mul:0.01 ) ]
       var frequency = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency, mul:0.01 ), SinOsc.ar( freq:frequency, mul:0.01 ) ]
       var frequency = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency, mul:0.01 ), SinOsc.ar( freq:frequency, mul:0.01 ) ]
       var frequency = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency, mul:0.01 ), SinOsc.ar( freq:frequency, mul:0.01 ) ]
       var frequency = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency, mul:0.01 ), SinOsc.ar( freq:frequency, mul:0.01 ) ]
    }.play;
  6. It doesn't work: you'll also have to rename your frequency-setting variable each time.
    {
       var frequency1 = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency1, mul:0.01 ), SinOsc.ar( freq:frequency1, mul:0.01 ) ]
       var frequency2 = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency2, mul:0.01 ), SinOsc.ar( freq:frequency2, mul:0.01 ) ]
       var frequency3 = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency3, mul:0.01 ), SinOsc.ar( freq:frequency3, mul:0.01 ) ]
       var frequency4 = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency4, mul:0.01 ), SinOsc.ar( freq:frequency4, mul:0.01 ) ]
       var frequency5 = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency5, mul:0.01 ), SinOsc.ar( freq:frequency5, mul:0.01 ) ]
       var frequency6 = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency6, mul:0.01 ), SinOsc.ar( freq:frequency6, mul:0.01 ) ]
       var frequency7 = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency7, mul:0.01 ), SinOsc.ar( freq:frequency7, mul:0.01 ) ]
       var frequency8 = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency8, mul:0.01 ), SinOsc.ar( freq:frequency8, mul:0.01 ) ]
       var frequency9 = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency9, mul:0.01 ), SinOsc.ar( freq:frequency9, mul:0.01 ) ]
       var frequency0 = 200 + 600.rand;
       [ SinOsc.ar( freq:frequency0, mul:0.01 ), SinOsc.ar( freq:frequency0, mul:0.01 ) ]
    }.play;
  7. It still doesn't work! The error given in the "SuperCollider output" window is not easy to understand, but it means "You have to put all of your variable declarations before everything else."
    {
       var frequency1 = 200 + 600.rand;
       var frequency2 = 200 + 600.rand;
       var frequency3 = 200 + 600.rand;
       var frequency4 = 200 + 600.rand;
       var frequency5 = 200 + 600.rand;
       var frequency6 = 200 + 600.rand;
       var frequency7 = 200 + 600.rand;
       var frequency8 = 200 + 600.rand;
       var frequency9 = 200 + 600.rand;
       var frequency0 = 200 + 600.rand;
       
       [ SinOsc.ar( freq:frequency1, mul:0.01 ), SinOsc.ar( freq:frequency1, mul:0.01 ) ]
       [ SinOsc.ar( freq:frequency2, mul:0.01 ), SinOsc.ar( freq:frequency2, mul:0.01 ) ]
       [ SinOsc.ar( freq:frequency3, mul:0.01 ), SinOsc.ar( freq:frequency3, mul:0.01 ) ]
       [ SinOsc.ar( freq:frequency4, mul:0.01 ), SinOsc.ar( freq:frequency4, mul:0.01 ) ]
       [ SinOsc.ar( freq:frequency5, mul:0.01 ), SinOsc.ar( freq:frequency5, mul:0.01 ) ]
       [ SinOsc.ar( freq:frequency6, mul:0.01 ), SinOsc.ar( freq:frequency6, mul:0.01 ) ]
       [ SinOsc.ar( freq:frequency7, mul:0.01 ), SinOsc.ar( freq:frequency7, mul:0.01 ) ]
       [ SinOsc.ar( freq:frequency8, mul:0.01 ), SinOsc.ar( freq:frequency8, mul:0.01 ) ]
       [ SinOsc.ar( freq:frequency9, mul:0.01 ), SinOsc.ar( freq:frequency9, mul:0.01 ) ]
       [ SinOsc.ar( freq:frequency0, mul:0.01 ), SinOsc.ar( freq:frequency0, mul:0.01 ) ]
    }.play;
  8. It still doesn't work! SuperCollider is confused because I was been lazy and didn't include enough semicolons. The error we get is, "Index not an Integer," which is a clue as to what SuperCollider is trying to do (but it's irrelevant). The real problem is that SuperCollider interprets our ten stereo arrays as all being part of the same statement. We don't want them to be the same statement, however, because we want ten different stereo arrays to be played. Fix this problem by putting a semicolon at the end of each stereo array. You don't need to include one at the end of the last statement, because SuperCollider assumes the end of the statement when it encounters a } (end-of-function marker) after it. Since we're still building our code, we might move these around or add something aftwards, so it's better to include a semicolon at the end of each stereo array.
  9. Now the file plays successfully, but with a disappointing result. If you can't already see the problem, try to think of it before continuing to read.
  10. Only one SinOsc array gets played, and it's the last one. This is because the last statement is returned by the function that ends at } and it is that result which gets sent to the following .play
  11. To fix this, and ensure that all of the stereo arrays are played, you should remove the .play from the end of the function, and add a .play to each stereo array statement. You end up with
    {
       var frequency1 = 200 + 600.rand;
       var frequency2 = 200 + 600.rand;
       var frequency3 = 200 + 600.rand;
       var frequency4 = 200 + 600.rand;
       var frequency5 = 200 + 600.rand;
       var frequency6 = 200 + 600.rand;
       var frequency7 = 200 + 600.rand;
       var frequency8 = 200 + 600.rand;
       var frequency9 = 200 + 600.rand;
       var frequency0 = 200 + 600.rand;
       
       [ SinOsc.ar( freq:frequency1, mul:0.01 ), SinOsc.ar( freq:frequency1, mul:0.01 ) ].play;
       [ SinOsc.ar( freq:frequency2, mul:0.01 ), SinOsc.ar( freq:frequency2, mul:0.01 ) ].play;
       [ SinOsc.ar( freq:frequency3, mul:0.01 ), SinOsc.ar( freq:frequency3, mul:0.01 ) ].play;
       [ SinOsc.ar( freq:frequency4, mul:0.01 ), SinOsc.ar( freq:frequency4, mul:0.01 ) ].play;
       [ SinOsc.ar( freq:frequency5, mul:0.01 ), SinOsc.ar( freq:frequency5, mul:0.01 ) ].play;
       [ SinOsc.ar( freq:frequency6, mul:0.01 ), SinOsc.ar( freq:frequency6, mul:0.01 ) ].play;
       [ SinOsc.ar( freq:frequency7, mul:0.01 ), SinOsc.ar( freq:frequency7, mul:0.01 ) ].play;
       [ SinOsc.ar( freq:frequency8, mul:0.01 ), SinOsc.ar( freq:frequency8, mul:0.01 ) ].play;
       [ SinOsc.ar( freq:frequency9, mul:0.01 ), SinOsc.ar( freq:frequency9, mul:0.01 ) ].play;
       [ SinOsc.ar( freq:frequency0, mul:0.01 ), SinOsc.ar( freq:frequency0, mul:0.01 ) ].play;
    }
  12. When you execute this, no sound is produced, but SuperCollider outputs "a Function." Can you think of why this happens? It's because you wrote a function, but never told SuperCollider to evaluate it! At the end of execution, SuperCollider just throws away the function, because it's never used. This is the same thing that happened to the first nine stereo arrays - they were created, but you never said to do anything with them, so they were just thrown out. We need to execute the function. Because it doesn't produce a UGen, we can't use "play," so we have to use "value" instead. You can choose to do either of these:
    { ... }.value;
    or
    var myFunction = { ... }; myFunction.value;
  13. This gives us yet another error, as if we can't play the stereo arrays! In fact, we can't - and we didn't do it in the first part, either. We play'ed the result of returning a stereo array from a function. The subtle difference isn't important yet - we're just trying to make this work! Use { and } to build a function for .play to .play
  14. Now make the correction nine more times.
  15. When you play execute the resulting code, you probably get something that sounds quite "space-age." Execute it a few times, to see the kind of results you get.
  16. Scheduling the Tones

  17. The next step is to get these started consecutively, with 5-second pauses after each addition. For this we will use a TempoClock, and since this is the only thing that we're doing, we'll just use the default one called TempoClock.default. I don't feel like typing that, however, so we're going to define an alias variable: var t_c = TempoClock.default; You could put that in the main function, but I suggest putting it before the main function. This way, if we want to write another function later, then it can also access t_c.
  18. The default TempoClock has a default tempo of one beat per second (1 Hz). This will be good enough for us. If you wanted to change the tempo, remember that you can enter a metronome setting (which is "beats per minute") by dividing the metronome setting by 60. So a metronome's 120 beats per minute would be given to a new TempoClock as TempoClock.new( 120/60 ). Even though you could do that ahead of time and just write "2," inputting it as "120/60" makes it clearer what tempo you intend to set.
  19. You can schedule something on a TempoClock by using t_c.sched( x, f );, where "f" is a function to execute, and "x" is when it should be done, measured as the number of beats from now. So we can schedule our SinOsc like this:
    t_c.sched( 1, {{[ SinOsc.ar( freq:frequency1, mul:0.01 ), SinOsc.ar( freq:frequency1, mul:0.01 ) ]}.play;} );
  20. Schedule the rest, in intervals of five beats (which is five seconds). They will all be scheduled virtually instantaneously (that is, the computer will notice the slight delay between when each one is scheduled, but humans will not). I started at one beat from now, to insert a slight pause before the sound begins.
  21. If you've done this correctly, then we should get a build-up of ten pitches. But they never stop! This is going to take some more ingenuity to solve, because we can't just make a stereo array, play it, then throw it away. We need to hold onto the stereo array, so that we can stop it. The first step here is to store the stereo arrays in variables, and subsequently schedule them. You will end up with something like this:
    var sinosc1 = { [ SinOsc.ar( freq:frequency1, mul:0.01 ), SinOsc.ar( freq:frequency1, mul:0.01 ) ] };
    // the other nine...
       
       t_c.sched( 1, { sinosc1.play; } );
    // the other nine...
  22. It should still work, but we after all that cutting-and-pasting, we still haven't managed to turn off the SinOsc's. We need to "free" the object that was returned when we used the "play" function. We need to declare yet more variables: var so1, so2, so3, so4, so5, so6, so7, so8, so9, so0; should appear anywhere before the scheduler.
  23. Now adjust all the scheduling commands so they look like this: t_c.sched( 1, { so1 = sinosc1.play; } );
  24. Now you can add ten of these, after the existing scheduling commands: t_c.sched( 51, { so1.free; } );. Be sure to schedule each one for 51 beats, so that they all turn off simultaneously, 5 beats after the last pitch is added.
  25. It should work successfully. If it doesn't, then compare what you have to this, which does work:
    var t_c = TempoClock.default;
    
    {
       var frequency1 = 200 + 600.rand;
       var frequency2 = 200 + 600.rand;
       var frequency3 = 200 + 600.rand;
       var frequency4 = 200 + 600.rand;
       var frequency5 = 200 + 600.rand;
       var frequency6 = 200 + 600.rand;
       var frequency7 = 200 + 600.rand;
       var frequency8 = 200 + 600.rand;
       var frequency9 = 200 + 600.rand;
       var frequency0 = 200 + 600.rand;
       
       var sinosc1 = { [ SinOsc.ar( freq:frequency1, mul:0.01 ), SinOsc.ar( freq:frequency1, mul:0.01 ) ] };
       var sinosc2 = { [ SinOsc.ar( freq:frequency2, mul:0.01 ), SinOsc.ar( freq:frequency2, mul:0.01 ) ] };
       var sinosc3 = { [ SinOsc.ar( freq:frequency3, mul:0.01 ), SinOsc.ar( freq:frequency3, mul:0.01 ) ] };
       var sinosc4 = { [ SinOsc.ar( freq:frequency4, mul:0.01 ), SinOsc.ar( freq:frequency4, mul:0.01 ) ] };
       var sinosc5 = { [ SinOsc.ar( freq:frequency5, mul:0.01 ), SinOsc.ar( freq:frequency5, mul:0.01 ) ] };
       var sinosc6 = { [ SinOsc.ar( freq:frequency6, mul:0.01 ), SinOsc.ar( freq:frequency6, mul:0.01 ) ] };
       var sinosc7 = { [ SinOsc.ar( freq:frequency7, mul:0.01 ), SinOsc.ar( freq:frequency7, mul:0.01 ) ] };
       var sinosc8 = { [ SinOsc.ar( freq:frequency8, mul:0.01 ), SinOsc.ar( freq:frequency8, mul:0.01 ) ] };
       var sinosc9 = { [ SinOsc.ar( freq:frequency9, mul:0.01 ), SinOsc.ar( freq:frequency9, mul:0.01 ) ] };
       var sinosc0 = { [ SinOsc.ar( freq:frequency0, mul:0.01 ), SinOsc.ar( freq:frequency0, mul:0.01 ) ] };
       
       var so1, so2, so3, so4, so5, so6, so7, so8, so9, so0;
       
       t_c.sched( 1, { so1 = sinosc1.play; } );
       t_c.sched( 6, { so2 = sinosc2.play; } );
       t_c.sched( 11, { so3 = sinosc3.play; } );
       t_c.sched( 16, { so4 = sinosc4.play; } );
       t_c.sched( 21, { so5 = sinosc5.play; } );
       t_c.sched( 26, { so6 = sinosc6.play; } );
       t_c.sched( 31, { so7 = sinosc7.play; } );
       t_c.sched( 36, { so8 = sinosc8.play; } );
       t_c.sched( 41, { so9 = sinosc9.play; } );
       t_c.sched( 46, { so0 = sinosc0.play; } );
       
       t_c.sched( 51, { so1.free; } );
       t_c.sched( 51, { so2.free; } );
       t_c.sched( 51, { so3.free; } );
       t_c.sched( 51, { so4.free; } );
       t_c.sched( 51, { so5.free; } );
       t_c.sched( 51, { so6.free; } );
       t_c.sched( 51, { so7.free; } );
       t_c.sched( 51, { so8.free; } );
       t_c.sched( 51, { so9.free; } );
       t_c.sched( 51, { so0.free; } );
       
    }.value;
  26. <

Optimizing the Code

Hopefully, while working through the previous sections, you got an idea of how tedious, boring, difficult-to-read, and error-prone this sort of copy-and-paste programming can be. It's ridiculous, and it's poor programming:

  • We're using a lot of variables and variable names. They're all just used once or twice, too.
  • When you copy-and-paste code, but change it a little, you might make a mistake in that little change.
  • When you copy-and-paste code, when you make a mistake, you have to copy-and-paste to fix it everywhere.
  • Repetition is the enemy of high-quality code. It is much better to write something once and re-use that same code.

Thankfully, SuperCollider provides three things that will greatly help to solve these problems - at least for our current situation:

  • Arrays can be used to hold multiple instances of the same thing, all referred to with essentially the same name. We're already doing something similar, (sinosc1, sinosc2, etc.) but arrays are more flexible.
  • Functions can be written once, and executed as many times as desired.
  • Loops also provide a means to write code once, and execute it many times. As you will see, they are useful in situations different from functions.

It should be noted that, while it is good practise to program like this, it is also optional. You will probably find, though, that writing your programs well in the first place ends up saving huge headaches in the future.

  1. The first thing we'll do is write a function to deal with generating the stereo arrays of SinOsc's.
  2. Take the code required to generate one stereo array of SinOsc's with a pseudo-random frequency. Put it in a function, and declare a variable for it (I used the name "func").
  3. Now remove the frequency1 (etc.) variables, and change the sinosc1 (etc.) variables to use the new function. Make sure that the code still works in the same way. It's much easier to troubleshoot problems when you make only one change at a time!
  4. At this point, we've eliminated ten lines of code, and made ten more lines easier to read by eliminating the subtle copy-and-paste changes. If you can't manage to work it out, refer to the FSC_method_1.sc file for tips.
  5. We can eliminate ten more lines of code by using a loop with an array. Let's change only one thing at a time, to make it easier to find a problem, if it should arise. Start by commenting out the lines which declare and initialize sinosc1, sinosc2, and so on.
  6. Then declare a ten-element array in the same place: var sinosc = Array.new( 10 );
  7. The next part is to write code to get ten func.value's into the array. To add something to an array in SuperCollider, we use the "add" method: sinosc.add( thing_to_add ); There is a small wrinkle to this, described in the SuperCollider documentation. It's not important to understand (for musical reasons, that is - it is explained on this help page), but when you add an element to an array, you should re-assign the array to the variable-name: sinosc = sinosc.add( thing_to_add ) Basically it works out like this: if you don't re-assign, then there is a chance that the array name only includes the elements that were in the array before the "add" command was run.
  8. With this, we are able to eliminate a further level of redundancy in the code. Ten exact copies of sinosc = sinosc.add( { func.value; } ); Now, ten lines that look almost identical actually are identical. Furthermore, we don't have to worry about assigning unique names, or even about index numbers, as in other programming languages. SuperCollider does this for us!
  9. This still won't work, because we need to adjust the rest of the function to work with this array. The scheduling commands be changed to look something like this: t_c.sched( 1, { so1 = sinosc[0].play; } ); Since arrays are indexed from 0 to 9, those are the index numbers of the first ten objects in the array.
  10. Remember that you need to put all of your variable declarations before anything else.
  11. It should still work. Let's use a loop to get rid of the ten identical lines.
  12. In SuperCollider, x.do( f ); will send the "value" message to the function "f" "x" times. So, to do this ten times, we should write 10.do( { sinosc = sinosc.add( { func.value; } ); } ); and get rid of the other ones. This is very powerful for simple things that must be done multiple times, because you are definitely not going to make a copy-and-paste error, because it's easy to see what is being executed, and because it's easy to see how many times it is being executed.
  13. Now let's reduce the repetitiveness of the scheduling. First, replace so1, so2, etc. with a ten-element array. Test it to ensure that the code still works.
  14. Getting the next two loops working is a little bit more complicated. We know how to run the exact same code in a loop, but we don't know how to change it subtly (by supplying different index numbers for the array, for example). Thankfully, SuperCollider provides a way to keep track of how many times the function in a loop has already been run. The first argument given to a function in a loop is the number of times that the function has already been executed. The first time it is run, the function receives a 0; if we're using a 10.do( ... ); loop, then the last time the function is run, it receives a 9 because the function has already been executed 9 times. Since our ten-element array is indexed from 0 to 9, this works perfectly for us.
  15. The code to free is shorter: 10.do( { arg index; t_c.sched( 51, { so[index].free; } ); } ); This can look confusing, especially written in one line, like it is. If it helps, you might want to write it like this instead:
       10.do
       ({ arg index;
          t_c.sched( 51, { so[index].free; } );
        });
    Now it looks more like a typical function.
  16. The next step is to simplify the original scheduling calls in a similar way, but it's slightly more complicated because we have to schedule a different number of measures for each call. With a little math, this is also not a problem - it's just a simple linear equation:
    number_of_measures = 5 * array_index + 1
    Try to write this loop by yourself, before going to the next step.
  17. If you missed it, my solution is
    10.do( { arg index; t_c.sched( ((5*index)+1), { so = so.add( sinosc[index].play; ); } ); } );
    which includes some extra parentheses to ensure that the math is computed in the right order.
  18. The code is already much shorter, easier to understand, and easier to expand or change. There is one further optimzation that we can easily make: get rid of the sinosc array. This simply involves replacing sinosc[index] with what all of its elements are: {func.value;}
  19. The resulting program is a little different from what ended up in FSC_method_1.sc, but produces the same output. What I have is this:
    var t_c = TempoClock.default;
    
    {
       var sinosc = Array.new( 10 );
       var so = Array.new( 10 );
       
       var func = 
       {
          var frequency = 200 + 600.rand;
          [ SinOsc.ar( freq:frequency, mul:0.01 ), SinOsc.ar( freq:frequency, mul:0.01 ) ];
       };
       
       10.do( { arg index; t_c.sched( ((5*index)+1), { so = so.add( {func.value;}.play; ); } ); } );
       10.do( { arg index; t_c.sched( 51, { so[index].free; } ); } );
       
    }.value;

Making a Function out of the Second Part

This can be optional. It's more computer science than music, but this is SuperCollider...

Joining the Two Parts

Now it is time to join the two parts, and ensure a clean transition between them. My reasons for building the first part as a SynthDef, but the second part as a function are explained in the FSC_part_1.sc file. Additional reasons include my desire to illustrate the use of both possibilities, and because the second part stops itself (so it can be a function which is executed and forgotten), whereas the first part does not stop itself (so we'll need to hold onto the synth, so that we can stop it).

  1. I copy-and-pasted both parts into a new file, leaving the other original code in tact, in case I want to build on them in the future. Be sure to copy over the var t_c = TempoClock.default; definition from the second part.
  2. By default, the two parts would both start playing at the same time (give it a try!) This isn't what we want, however, so you'll need to erase the "play" command from both parts' functions. We'll also need some way to refer to them, so declare the second part as a variable (I've used the name, "secondPart,"), but don't worry about the first part yet. Don't forget the semicolon at the end of the function declaration!




















Exporting Sound Files

There are two methods:

  1. Use Ardour to record SuperCollider, which I will cover here.
  2. Use SuperCollider to export non-real-time audio, which I will not cover here.