Super Saw Waves are Fun

I recently had a student talk to me about all the different uses of various types of oscillators.  At the end of this conversation he asked how come you didn’t mention the super saw.  To be honest I had no clue what he was taking about, so I did some research.  All a super saw is, is multiple sawtooth oscillators out of tune with one another.  Something I have done a million times but never knew there was a name for it.  After this conversation I programmed a basic example with a GUI in Supercollider to demonstrate to the class.  Super Saws are fun for many reasons.  Personally I like them because the phasing creates interesting polyrhythms.  In the code below I made the frequencies detunable by +/- 10Hz so the maximum difference between oscillators is 20Hz.

SynthDef(“supersaw”, { arg freq, d1, d2, d3, d4, d5, d6, d7, d8, vol = 0, rl = 5, gate = 1;

var mix, env;

env = Env.cutoff(rl);
mix = Mix.new([Saw.ar(freq, 0.4), Saw.ar(freq + d1, 0.4), Saw.ar(freq – d2, 0.4), Saw.ar(freq + d3, 0.4), Saw.ar(freq – d4, 0.4), Saw.ar(freq + d5, 0.4), Saw.ar(freq + d6, 0.4), Saw.ar(freq + d7, 0.4), Saw.ar(freq – d8, 0.4),]);

Out.ar([0, 1], (mix * vol) * EnvGen.kr(env, gate, doneAction: Done.freeSefl));
}).send(s);

 

To break down this code:  I am using the freq argument to set the main frequency and the dn arguments to control the amount of detune in Hz.  The Mix.new is very important for this.  The mix sums all of the saws together which virtually allows the phases to do fun things.  I am also using a generator to produce a fade out for demonstration purposes for class.  The little SynthDef is nothing fancy but it is fun.  I am copying the GUI code below.  it create sliders to control the amount of detuning for each oscillator.  The GUI isn’t super pretty, but gets the job done for quick demonstration purposes.

 

(

var sl1, sl2, sl3, sl4, sl5, sl6, sl7, sl8, nb, nb2, bu, reset;
w = Window.new(“Super Saw”, Rect(128, 64, 360, 360));
y = FreqScopeView(w,Rect(240, 10, 100, 50) );
y.active_(true);
y.freqMode_(1);
w.onClose_({ y.kill });
t = ControlSpec(0.0, 1, \lin, 0.01);
b = ControlSpec(-10, 10, \lin, 0.01);
c = ControlSpec(-10, 10, \lin, 0.01);
d = ControlSpec(-10, 10, \lin, 0.01);
e = ControlSpec(-10, 10, \lin, 0.01);
f = ControlSpec(-10, 10, \lin, 0.01);
g = ControlSpec(-10, 10, \lin, 0.01);
h = ControlSpec(-10, 10, \lin, 0.01);

nb = NumberBox(w, Rect(20, 20, 150, 20))
.action_({ z.set(\freq, nb.value);
});

nb2 = NumberBox(w, Rect(180, 20, 50, 30))
.value_(0)

.action_({
u = (t.map(nb2.value));
z.set(\vol, u);

});
nb2.scroll_step = 0.01;

bu = Button(w, Rect(180, 50, 50, 30))
.states_([
[“Off”, Color.black, Color.red],
[“On”, Color.white, Color.black]])
.action_({
if (bu.value == 1 , {z = Synth(“supersaw”);}, {z.free})
});

sl1 = Slider(w, Rect(20, 50, 150, 20))
.value_(0.5)
.action_({
a = (b.map(sl1.value));
[“Slider1”,a.value].postln;
z.set(\d1, a);
});

sl2 = Slider(w, Rect(20, 80, 150, 20))
.value_(0.5)
.action_({
i = (c.map(sl2.value));
[“Slider2”,i.value].postln;
z.set(\d2, i);
});

sl3 = Slider(w, Rect(20, 110, 150, 20))
.value_(0.5)
.action_({
j = (d.map(sl3.value));
[“Slider3”,j.value].postln;
z.set(\d3, j);
});

sl4 = Slider(w, Rect(20, 140, 150, 20))
.value_(0.5)
.action_({
k= (e.map(sl4.value));
[“Slider4”,k.value].postln;
z.set(\d4, k);
});

sl5 = Slider(w, Rect(20, 170, 150, 20))
.value_(0.5)
.action_({
l= (f.map(sl5.value));
[“Slider5”,l.value].postln;
z.set(\d5, l);
});

sl6 = Slider(w, Rect(20, 200, 150, 20))
.value_(0.5)
.action_({
m= (g.map(sl6.value));
[“Slider6”,m.value].postln;
z.set(\d6, m);
});

sl7 = Slider(w, Rect(20, 230, 150, 20))
.value_(0.5)
.action_({
n= (h.map(sl7.value));
[“Slider7”,n.value].postln;
z.set(\d7, n);
});

sl8 = Slider(w, Rect(20, 260, 150, 20))
.value_(0.5)
.action_({
o= (b.map(sl8.value));
[“Slider8”,o.value].postln;
z.set(\d7, o);
});

ServerMeterView.new(s, w, 170@130, 2, 4);
CmdPeriod.doOnce({w.close});

reset = Button.new(w, Rect(240, 90, 100, 40) )
.states_([
[“Reset”, Color.white, Color.black]])
.action_({
reset.value.postln;
if( reset.value == 0) {sl1.valueAction = 0.5; sl2.valueAction = 0.5; sl3.valueAction = 0.5; sl4.valueAction = 0.5; sl5.valueAction = 0.5;sl6.valueAction = 0.5; sl7.valueAction = 0.5; sl8.valueAction = 0.5};
});
w.front
)

Using Demand in Supercollider

It wasn’t until recently that I explored the demand rate objects in Supercollider.  They always kind of confused me.  Learning sound synthesis in Csound there was AudioRate and ControlRate.  Now in Supercollider this demand rate stuff seemed odd.  Now after exploring the objects for the past year I have learned to love them.  While I still don’t know how to explain them in terms of AudioRate and ControlRate I use them regularly to trigger patterns and change arguments.

There are several objects similar to the P objects i.e. Pseq, Pbind etc. but D objects can run in SynthDefs which is pretty cool.

This first example shows triggering frequency changes using Duty

trigger = {Duty.ar(Dxrand([0.1, 0.2, 0.3, 0.4, 0.5], inf), 0, Dwhite(100, 1000, inf))};

In this code the first argument “Dxrand([0.1, 0.2, 0.3, 0.4, 0.5], inf)” creates triggered rhythm with Dxrand selecting a value randomly and never repeating the same one in a row.  The “0” after the Dxrand is reset and for this example we don’t need to reset anything.  The TDuty will continue to select time values from the Dxrand.  The last part, Dwhite, is a random white noise generator with values between 100 and 1000.  The value produced by Dwhite is what gets sent as an argument to the object that Duty is controlling.  For Example:

(
SynthDef(“example”, {
var in, trig;
trig = Duty.ar(Dxrand([0.1, 0.2, 0.3, 0.4, 0.5], inf), 0, Dwhite(100, 1000, inf)).poll;
in = SinOsc.ar(trig, 0, 0.4) ;
Out.ar(0, in);
}).add
)
a = Synth(“example”)

While you can also do this with “P” objects there are some fun uses using the “D” objects in ways that work well for interaction.

(
SynthDef(“delay”, { arg att = 0.01, rel = 1, test = 0.01;
var in, delay, thresh, al, de, b, list;
in = AudioIn.ar(1);
thresh = Amplitude.kr(in, att, rel, 1);
al = (thresh > test);
list = [some kind of list of values for delay time];
b = Dxrand(list, inf) ;
de = Demand.kr(al, 0, b);
delay = DelayN.ar(in, 1, de, 0.5);
Out.ar(0, [in, delay]);
}).add
)

In this example an audio input’s amplitude is being tracked and when over a certain threshold triggering the Demand.kr object to release a number to set the Delay amount.  In this case the number is randomly selected using the Dxrand but an object like Dseq could also be used to sequence a series of delays in order.

Demand is a neat object because unlike Duty or the TDuty object Demand waits for a trigger to release a value.  This makes it very powerful for interactivity.

(

SynthDef(“example2”, { arg fq = 10;
var trig, seq, freq;
trig = Impulse.kr(fq);
seq = Drand([Dseq((1..5), 1), Drand((4..10), 8)], 2000);
freq = Demand.kr(trig, 0, seq * 50).poll;
SinOsc.ar(freq, 0, 0.3);
}).add;

)

In this final example we are controlling the frequency of a SinOsc using a semi-random pattern.  Drand selects one of the sequences and the value in the sequence is output to the Demand object.  The demand object is triggered using an Impulse and the value selected by the Drand is multiplied by 50 which serves as the frequency we hear.  Something I like doing with D objects is making their values arguments so I can control them dynamically when I am live coding.

 

I hope this small demand explanation is helpful.  I am always looking to write about supercollider and Max/MSP stuff.  A lot of times I write about questions or comments that my students bring up in class.  Feel free to contact me if you have a question or want something demonstrated.  I will do my best to make it useful.

 

Deterministic Soundscape

I was recently working with a student on a composition that involved subtle, controllable sample playback.  We were doing this in Supercollider because it is free and coming up with ways of randomly triggering city sounds throughout the composition is easy to program and a great learning opportunity.  The little bit of code below is easy and fun way to randomly trigger sounds.

 

b = SoundFile.collectIntoBuffers(“path/*”,s) // path/* will load a folder with all the sound int he folder.  The * is kind of like a place holder for all the file names in the folder

SynthDef(“playback”, {arg bufnum, rep = 0, speed = 1;

var play;
play = PlayBuf.ar(1, bufnum, rate: speed, loop: rep, doneAction:2);

Out.ar(0, play);

}).send(s)

b = Synth(“playback”, [\bufnum, rrand(0,5)] )

c = Synth(“playback”, [\bufnum, rrand(0,5)] )

etc…..

 

What made this a good little lesson for class was the use of rrand vs. Rand, NRand, TRand, etc.

We could use Rand or some variation but if we want to change a range of numbers halfway through the piece we are unable to because Rand() is a Ugen that can’t be used in a control (Synth.new).  rrand(), however, can be used in a control and allows the user to set a range.  This is particularly use when trigger back different samples from a range of samples or different frequencies from a range.

With any random function in supercollider it is a range of values from either 0 – inf or x, y, but we can select from a particular set of values by using an array and .choose.

[1, 5, 4, 8, 6, 9, 7].choose // this makes every number have equal probability of occurring.

We can use wchoose([]) to have weights. wchoose has to have weightd that add up to 1

[4, 7, 1, 3, 456, 8, 9].wchoose([0.7, 0.1, 0.05, 0.04, 0.01, 0.05, 0.05])

Finally in the above code we use doneAction 2 in the SynthDef so when the sample is done playing it frees from the node.  This allows us to layer many samples and not use to much processing power.

 

As with my other posts.  If you enjoy them, find anything useful or have used my software please consider donating to my hurdy gurdy fund on my home page.

Hurdy Gurdy

I have final ordered a Hurdy Gurdy.  My gofund page is still active because any money will help pay for it.  I order the gurdy from Altarwind.com.  It has 2 melody string, and 3 drones along with the buzzing bridge.  In addition I put upgrades into it so each drone has a capo on it, it has buttons to turn strings on and off, sypathetic strings that work almost like natural reverb (they vibrate wth the instrument) and am getting it color treated to be a dark orange with black highlights.  I am also going to experiment with acoustic guitar pickups or making my own contact mic.  I can not wait to get it and learn of to play.  My goal is to make this a traveling instrument and be able to write and perform at music conferences and in the streets.  Hopeful I can get good enough to put a call for scores out for composers or have friends write music for me to play and record.

Set Theory in Max/MSP

I started a new job teaching and part of my teaching load is post tonal theory.  As I was teaching a course I thought to myself, there is no program that I could find that gives the prime form, subsets, transpositions, pitch centricity, ICV, complimentary set and a summation square (for invariant tones under transposition).  So I got the idea to use the Bach library in Max/MSP and build a patch that gives all of this information in an easy to understand and manipulate fashion.  Here is a screen shot

Screen Shot 2017-10-11 at 2.13.24 PM.png

 

The top left box shows the main interface where you can enter sets in different ways, transpose the information, a histogram for note occurrences, various boxes for different transpositions and buttons to bring up the other screens.  The top right screen gives you a summation square for invariant tones under transposition, bottom right is all the possible subsets and the bottom left is a pitch lattice that shows the set in another way.

My future plan is to take all the subsets and add an abstract subset function.  I have been using this for my own personal compositions and analysis but also as a way to think of other tools that are useful for understanding music.  it is my hopes that other types of graphs and functions will be added so the patch turn into a visual analysis tool for various types of music.  Right now I am not openly distributing the patch, but if you are interest in it, email me and I will send it to you.  All you need is Max/MSP and the Bach and Cage packages.

 

If you enjoy reading my posts, find my posts useful, read my research, look and listen to my music and photography please consider donating to my gofundme on my main bio page.

TidalCycles

I am always looking for new ways of producing sound and music. As part of my recent search I stumbled upon a fun and powerful program called TidalCycles. It is a text based program that runs alongside a Supercollider quark called SuperDirt an is made for live coding. This means that the sounds and effects are processed with SCsynth but the programming is done elsewhere. TidalCycles uses Haskell as its main language, however, it is very easy to pick up the basics and get started.

TidalCycles works via loops. This can be thought of as 1 cycle per second is one loop per second. The loop is always the same duration so as more sounds get put in the sequence they happen faster. Here is a little code that shows the basics of making a sound.

d1 sound “bd”

This code plays a bass drum sound once per cycle on d1 (dirt 1). d1 is kind of In TidalCycles the d stands for dirt but it is really like saying play a sound on voice 1. There are 9 possible voices.

To double the beat the code would change to

d1 sound “bd*2”

and says play the bass drum sample twice per cycle.

d1 sound “bd/2”

will play the bass drum sound once every 2 cycles or twice as slow. This is all very easy little 1 liners. The following example shows something a little more complex.

d1 $ every (irand 4) (fast 10) $ every (irand 10) (fast (irand 2)) $ sound
“[808bd*2 bd*9 [808bd*10 bd*17 ]] [808bd*5 [sn sn*6 [bd*3 bd*6] sn*134 ] sn cp*9]”

To break down this example

Every random amount of cycles will trigger the rhythm to be played 10 times faster. In this case it will randomly select between 1 cycle and 4 cycles. Every random 10 times the rhythm will be played either at normal speed or twice as fast.

For this sequence there are 2 steps. The first one is [808bd*2 bd*9 [808bd*10 bd*17 ]] while the second is [808bd*5 [sn sn*6 [bd*3 bd*6] sn*134 ] sn cp*9].

The first step can be broken down into 3 smaller steps and on the third step there are 2 smaller steps because of the []. If we disregard the *n part of the sounds and turn it into [808bd bd [808bd bd]] The rhythm is kind of like a triplet with [8th 8th [16th 16th]].

In the second main step “[808bd*5 [sn sn*6 [bd*3 bd*6] sn*134 ] sn cp*9]” disregarding the *n again there are 4 steps with the second step having a nested rhythm. In terms of rhythmical notation this collection would be kind of like 16th [32nd 32nd [64th 64th] 32nd] 16th 16th.

When we add the *n after the sample the rhythm technical remains the same in terms of sound groupings, however, because of the fast repetition of the samples, the timbre changes. On top of that the randomized speed quickly changes the overall speed of the cycle adding more unpredictability.

Note: While I tried to write what the rhythmical duration is, the way that TidalCycles makes all the samples fit into one cycle makes the rhythm when trying to dictate a little wobbly. The main point is that TidalCycle scales the sounds faster and slower to make everything fit. As someone who likes finding odd ways of generating rhythm TidalCycles approach to rhythm opens up some possibilities that I have not thought about before.

If you want me to write about something involving TidalCycles please email me and I will try my best. Also, if you enjoy reading my posts, reading my research, looking and listening to my music and photography please consider donating to my gofundme on my main bio page.

Loading Multiple Files Into Buffers

The other day I was helping  friend with some Supercollider code involving buffers.  Normally in Supercollider you can load a buffer with code that looks like the following

b = Buffer.read(s, “file extension”);

This simply loads a buffer onto the server and assigns it to b.  Then we can use PlayBuf to play it back

SynthDef(“buffer”, (arg bufnum, numchannel = 1;

var in;

a = PlayBuf.ar(numchanel, bufnum, rate, trigger, startpos, loop, donAction:2);

Out.ar(0, a);

}).send(s, \bufnum, b);

In the previous code bufnum is the buffer number assigned to the file.  At the send of the synthdef we use \bufnum, b to assign the buffer number to argument bufnum. doneAction is an argument that controls when the synth gets freed from the server or not.  Look at the doneAction help file to read about them.  There are 14 of them.

 

If only 1 sound file is being used loading the buffer is very easy.  If we have multiple files then this method is not going to work.  To load a file full of sounds the object Buffer is not even used.  Instead “SoundFile” is used which is a little counter intuitive.  The following code shows how to load many sounds from a file into individual buffers.

b = SoundFile.collect(“filepath/*”, s)

This in turn will load every sound in the file into a separate buffer and give it a number value.  It is important to add the * at the end of the path otherwise Supercollider will load a blank array.  Then we can say

b.inspect;

to load a window with each buffers information.

Using the inspect window we can then get all of the buffer numbers to be used in the PlayBuf code.

s.boot

b = SoundFile.collect(“filepath/*”, s)

SynthDef(“buffer”, (arg bufnum, numchannel = 1, rate = 1, ;

var in;

a = PlayBuf.ar(numchanel, bufnum, rate, loop: 1, donAction:2);

Out.ar(0, a);

}).send(s)

a = Synth(“buffer”, [\bufnum, 1, \rate, 1.3]);

a.free;

 

A Fun Little Supercollider Patch

The other day I was playing around with Sine tones and arrays in Supercollider and came up with this little program that creates partials from a fundamental frequency. Nothing special really, but when the volume of the harmonics is controlled by a max statement and sine tone things get interesting.  Here is what the code looks like

SynthDef(“sinetone”, {arg fund = 100, osc = 100;
var sound;
sound = Mix.ar(
Array.fill(12, {
arg count;
var harm;
harm = count +1*fund;
SinOsc.ar(harm, mul: max([0,0], SinOsc.kr(osc)))
})
)*0.7;
Out.ar(0, sound);
}).send(s)

a = Synth(“sinetone”)

The code gives you control of the fundamental frequency with the argument fund and the volume frequency with the argument osc.  To make the sound more interesting SinOsc.ar can be replaced with any kind of generator.  Sine tones give a more “harmonious” sound while Ugens like LFNoise0.ar give a more helicopter sound.

 

 

 

Creating L-Systems in Supercollider

L-systems also known as Lindenmayer Systems is a rule based parallel rewriting system.  Every L-system has an axiom (starting point) and series of rules.  The rules get replaced into one another to grow the L-system or in other words works with the ideas of formal grammars.  L-systems were first introduced by Aristid Lindenmayer to describe growth models in plants.  Since then they have evolved further into fractal like approaches, Turtle Graphics, arts and music.

I first discovered musical aspects of L-systems when I was getting my masters degree and ended up writing my thesis about ways of interpreting the resultant string of characters as well as using the image as guides for music composition.  When I was researching this, I found it very difficult to produce a complex L-system whose output string I could use directly in a synthesis environment.  Max/MSP has certain capabilities but also  can only hold a maximum number of values in a table or collection and they all have to be indexed which is not efficient if the string is 4000 characters long.  Supercollider though has the abilities to generate the string as well as refer back to it for controlling parameters or musical rules.

The following code shows how I put together an L-system string calculator. For this code to work the wslib Quark has to be installed. To do this type Quarks.gui and select the wslib.  I have also used this L-system sting calculator in 2 sound installations titled Reflective Tunnel and Reflective Catacombs.  Reflective Tunnel was set in the tunnels at Wesleyan University in Connecticut and used the harmonics of a cello split to different rules in the L-system.  Then the L-system was played back in the tunnel with pseudo-random envelopes on all the frequencies to create layers of sound that phased and reflected off the concrete walls. In Reflective Catacombs the space was sonically mapped to obtain the resonant frequency and assigned to rules in an L-system and pseudo-random envelopes to play back in the space.  Both installations created shifting atmospheres and interesting beating patterns.

UPDATE:  The code that was previously posted did not work with the current version of Supercollider.  I updated it and it seems to work now.  If it doesn’t work for you I would love to hear the error you received so I can update it again.

((
// system to substitute rules

~substitute = { |string, dict|

var str=””;

string.do { | c | var val = dict[c.asSymbol] ?? c ; str = str ++ val;
};
str.post;

~route = str.asStream;};
// fill a dictionary with entries

~myDictionary = Environment.new;

~myDictionary[\A] = “B–CG”;

~myDictionary[\B] = “A*F-D”;

~myDictionary[\C] = “E/G+F”;

~myDictionary[\D] = “G–BC”;

~myDictionary[\E] = “G/E–BC”;

~myDictionary[\F]= “A-BC/E”;

~myDictionary[\G] = “A-DF/G”;
)
//axiom change the letter in “” based on the rule you want to start with

a = “A”
//The number before collect is the number of iterations
~history = 7.collect {  a = ~substitute.value(a, ~myDictionary)};

Once this code is run the stream gets assigned to ~history that can be assigned to a variable and put into a routine  or printed as a long list.

k = ~history

t = Routine({

k.do { | c| c ;

0.35.wait;
if(c.asString == “F”, {Synth.new(“FM”, [\carrierfreq, [34, 100, 673, 589, 10, 2, 7, 45, 234, 45, 4357, 1239, 3240].choose.postln])});

 

This routine reads one character in the L-system every 0.35 seconds and looks to see if it is an F.  If it is it selects a frequency at equal probability from the list and assigns it to the carrier frequency of the FM synth definition.

 

For interest in my L-system interpretations and mappings to compositions my thesis can be found at https://www.academia.edu/24235602/THE_MUSICAL_MAPPINGS_OF_L-SYSTEMS