To start sound in SuperCollider, you need to boot the audio server from sclang. This starts up the server and establishes a network connection between sclang and the server. The quickest way to start the server is using s.boot
. By default, sound will be outputted through your default settings.
s.boot
A sine wave intended as audio can be created by using the class SinOsc.ar
. SinOsc.ar
can take several arguments.
The documentation for SinOsc
can be found here. If the intent is to playback the sine wave as audio, one should always use the class method .ar
. When we use sine waves to modulate other waves in later classes, it can be more efficient to use .kr
.
To actually play the sine wave, we wrap the wave in a function and use the .play
method.
{SinOsc.ar(440, 0, 0.5)}.play
To stop the sound, run the command below. Equivalently, if you are in the SuperCollider IDE, hit CMD+PERIOD if on a Mac or Ctrl+PERIOD on a PC.
CmdPeriod.run // stop the sound
If you noticed, the above code only plays out of the left speaker. If we want to hear audio out of both speakers, we need to create an array of sine waves. Python users will recognize the array notation in SuperCollider as similar to the list notation of Python.
[2, 3] // an array of two elements
[2, 3].class
Below is an array of two sine waves so we can here audio out of both speakers.
{[SinOsc.ar(400, 0, 0.5), SinOsc.ar(400, 0, 0.5)]}.play
SuperCollider offers a shorthand to create arrays for stereo sound called multichannel expansion. If you pass an array into the input, the signal will be converted into an array of audio signals that can be played out of different speakers.
{SinOsc.ar([400, 400], 0, 0.5)}.play // Equivalent to the code above
{SinOsc.ar(400, 0, [0.5, 0.5])}.play // Equivalent to the code above
CmdPeriod.run // stop the sound
Play a sine wave of frequency 430Hz out of the left speaker and 440 out of the right speaker.
{SinOsc.ar([430, 440], 0, 0.5)}.play
CmdPeriod.run // stop the sound
As an experiment, run the code below with different frequencies. The frequency range of human hearing is from approximately 20-20000Hz. A pulse wave is simply a blip of sound at regular intervals.
var freq = 2;
{Pulse.ar([freq, freq], mul: 0.2)}.play;
CmdPeriod.run // stop the sound
Execute the cell below to sweep through the audible frequency range of human hearing. This function uses a new UGen called XLine. In this example, XLine
ramps from 10 to 20000 exponentially over the course of 6 seconds. The XLine
is passed to the frequency argument of Pulse
to modulated the frequency of the pulse wave.
{
var expPitch = XLine.kr(10, 20000, 6);
Pulse.ar([expPitch, expPitch], 0.1, 0.2) ;
}.play; // This is actually still playing 20000Hz! Try with 10000Hz.
CmdPeriod.run // stop the sound
printHarmonics
¶Below write a function called ~printHarmonics
that accepts two arguments: a fundamental frequency called fundFreq
and a number of harmonics called numHarmonics
. ~printHarmonics
should print out numHarmonics
from the harmonic series of the given fundamental fundFreq
. Assume that the fundamental frequency has a default value of 40 and numHarmonics
has a default value of 20.
// Your code here
~printHarmonics = {
arg fundFreq = 40, numHarmonics = 20;
var harmNum = 1, freq = fundFreq;
while({harmNum <= numHarmonics}, {
freq.postln;
freq = freq + fundFreq;
harmNum = harmNum + 1;
});
}
~printHarmonics.value(30, 10) // should print 30, 60, 90, 120, ...
~printHarmonics.value // should print 40, 80, 120
The below code plays 20 harmonics from the harmonic series of the fundamental fundFreq
. Listen to how the "distance" of the notes gets shorter and shorter.
var fundFreq = 32.70; // C1
var numHarms = 20;
r = Routine({
var harmNum = 1;
var freq = fundFreq;
while({harmNum < numHarms}, {
var sig;
("Freq is " ++ freq ++ " | Harmonic is " ++ harmNum).postln;
sig = {SinOsc.ar([freq, freq], 0, 0.3)}.play(fadeTime: 0.2);
harmNum = harmNum + 1;
freq = freq + fundFreq;
0.7.wait;
sig.release();
0.2.wait;
});
}).play;
CmdPeriod.run
Listen carefully and think about whether you hear the sound as one single unit or twenty distinct harmonics.
var fundFreq = 300;
for(1, 30, {
arg n;
var harmFreq = fundFreq * n;
{SinOsc.ar(harmFreq ! 2, 0, 0.04)}.play;
});
CmdPeriod.run // Stop sound
!
operator¶As a side note, the !
operator in sclang takes a value on the left and duplicates it by the number of times of the integer on the right, storing those values in an array.
3 ! 2 // produces [3, 3]
2 ! 4 // produces [2, 2, 2, 2]
"music" ! 2 // produces ["music", "music"]
Therefore, writing SinOsc.ar(harmFreq ! 2, 0, 0.04)
is equivalent to SinOsc.ar([harmFreq, harmFreq], 0, 0.04)
. Recall that an array inside a UGen like SinOsc
will be expanded through multichannel expansion to an array of two sine waves, allowing us to hear the waves out of both the left and right speakers.
Listen carefully and think about whether you hear the sound as one single unit or twenty distinct harmonics. How did reducing the amplitude of the harmonics change the perception of the sound?
var fundFreq = 300;
for(1, 30, {
arg n;
var harmFreq = fundFreq * n;
{SinOsc.ar(harmFreq ! 2, 0, 0.08/n)}.play;
});
CmdPeriod.run // Stop sound
The previous two experiments showed that our brains attempt to fuse harmonics from the harmonic series into a single sound with the fundamental as the pitch we perceive. Let's try and "break" our perception by strongly accentuating two partials to see if we can hear them as distinct sounds. Here we will make the amplitude of the two partials much larger than the rest. Play around with the numbers and see what you can determine.
var fundFreq = 300;
var harmonicNumA = 1;
var harmonicNumB = 12; // Try with a higher number
var largeAmp = 0.2; // Try with a lower number
var smallAmp = 0.05; // Try with a smaller number like 0.005
for(1, 30, {
arg n;
var harmFreq = fundFreq * n;
var amp;
if((n == harmonicNumA) || (n == harmonicNumB),
{amp = largeAmp},
{amp = smallAmp}
);
{SinOsc.ar(harmFreq ! 2, 0, amp)}.play;
});
CmdPeriod.run // Stop sound
This experiment is exactly the same as experiment 2 except now the fundamental is not included. Do we still perceive the fundamental as the pitch of the note or not? Compare to experiment 2. What sounds the same? What sounds different?
var fundFreq = 300;
for(2, 30, { // Note how we start with the second harmonic
arg n;
var harmFreq = fundFreq * n;
{SinOsc.ar(harmFreq ! 2, 0, 0.08/n)}.play;
});
CmdPeriod.run // Stop sound
Below are two sine waves that are out of phase by $\pi/2$. Do we perceive a difference between each sine wave?
{SinOsc.ar([440, 440], 0, 0.6)}.play;
{SinOsc.ar([440, 440], -pi/2, 0.6)}.play;
CmdPeriod.run // Stop sound
Compare the volume of two sine waves in phase vs. a single sine wave. Which sounds louder?
{SinOsc.ar([440, 440], 0, 0.5)}.play;
{SinOsc.ar([440, 440], 0, 0.5) + SinOsc.ar([440, 440], 0, 0.5)}.play; // Two sine waves in phase added
CmdPeriod.run // Stop sound
Compare the volume of two sine waves perfectly out of phase vs. a single sine wave. Which sounds louder?
{SinOsc.ar([440, 440], 0, 0.5)}.play;
{SinOsc.ar([440, 440], 0, 0.5) + SinOsc.ar([440, 440], -pi, 0.5)}.play;
CmdPeriod.run // Stop sound
Below is a snippet of code to play some frequency and a frequence numOctaves
above it. Change the value of freq
or numOctaves
to hear the differences.
var freq = 440, numOctaves = 1;
var octaveFreq = {
arg fundFreq, numOctaves;
fundFreq * (2 ** numOctaves) // Implied return value
};
{SinOsc.ar(freq ! 2, mul: 0.5) + SinOsc.ar(octaveFreq.value(freq, n) ! 2, mul: 0.5)}.play;
CmdPeriod.run // Stop sound