Player Attributes

Introduction

FoxDot player objects are assigned SynthDefs take various keyword arguments in a set of brackets to manipulate the sequences being played. You’ve probably already seen dur and pan but what else can we use? There are lots of options, which are broadly split into two groups: attributes and effects. Attributes are things that affect what note is played when and effects are things that change how the sound, well, sounds! You can see a list of all of the possible keyword terms by evaluating the code

>>> print(Player.get_attributes()
('degree', 'oct', 'dur', 'delay', 'blur', 'amplify', 'scale', 'bpm', 'sample', 'sus', 'fmod', 'pan', 'rate', 'amp', 'vib', 'vibdepth', 'slide', 'sus', 'slidedelay', 'slidefrom', 'bend', 'benddelay', 'coarse', 'striate', 'pshift', 'hpf', 'hpr', 'lpf', 'lpr', 'swell', 'bpf', 'bpr', 'bits', 'amp', 'crush', 'dist', 'chop', 'tremolo', 'echo', 'decay', 'spin', 'cut', 'room', 'mix', 'formant', 'shape')

Bear in mind that a SuperCollider SynthDef might take specific keyword arguments that aren’t listed above. You can set the values for a player’s attribute or effect by specifying them as a keyword argument inside of a SynthDef call like so:

p1 >> pluck([0, 1, 2, 3], dur=1/2, sus=2)

Or you can set the value directly to the player object attributes:

p1 >> pluck()
p1.degree = [0, 1, 2, 3]
p1.dur = 1/2
p1.sus = 2

This section will take you through the different attribute keywords with some example code. If you want to know more about effects, you can find more information in the next section.

Attributes

These are the keyword values that get used by FoxDot to decide what note or sample gets played when. In no particular order they are: degree, oct, dur, scale, amp, amplify, bpm, sample, and delay. We’ll go through each of these in depth with some example code now.

degree

This is sometimes referred to as “pitch” and refers to the index of the scale we should use to create a note – so to play the first note of a scale, you use the degree value 0. You don’t need to specify this one by name as it is always the first argument used. Here is some example code that plays the first four notes of the default scale (C major).

p1 >> pluck([0, 1, 2, 3])

oct

This is the octave you want to play a note in. By default this is 5 so that the note you play when starting a “blank” player object is middle C. An octave is (usually) 12 semitones, so the 5th octave starts at the 60th semitone. We add this value to our pitch (which we get from using the degree to get the final note value. A smaller number plays a lower note and a larger number plays a higher note:

p1 >> pluck(oct=[4, 5, 6])

dur

This is the duration of a note. Durations cannot be negative and must contain at least one non-zero number. A duration can be any type as long as it can be represented by a floating point value. The following are all valid durations:

# Single value for all notes
p1 >> pluck([0, 1, 2, 3], dur=1/2)

# A list of durations can be integers, fractions, or floating point values
p1 >> pluck([0, 1, 2, 3], dur=[1,1/2,0.5])

p1 >> pluck([0, 1, 2, 3], dur=[0.1,0.3,0.43,0.17])

You can “skip” a note by setting the duration to zero or “mute” it by using a rest object with the duration in brackets:

# Skip every 3rd note
p1 >> pluck([0, 1, 2, 3], dur=[1, 1, 0])

# Rest every 3rd note for 2 beats
p1 >> pluck([0, 1, 2, 3], dur=[1, 1, rest(2)])

scale

As the name suggests, this sets the scale of the player object. This must be a list, Pattern, or a Scale object (which is a subclass of Pattern). To see a list of scales, you can evaluate the following code:

>>> print(Scale.names())
['aeolian', 'chinese', 'chromatic', 'custom', 'default', 'diminished', 'dorian', 'dorian2', 'egyptian', 'freq', 'harmonicMajor', 'harmonicMinor', 'indian', 'justMajor', 'justMinor', 'locrian', 'locrianMajor', 'lydian', 'lydianMinor', 'major', 'majorPentatonic', 'melodicMajor', 'melodicMinor', 'minor', 'minorPentatonic', 'mixolydian', 'phrygian', 'prometheus', 'romanianMinor', 'yu', 'zhi']

By default, FoxDot uses the “major” scale. To change this to the minor scale, for example, you can use the scale keyword argument and the Scale.minor scale like so:

# Play the major scale by default
p1 >> pluck([0, 2, 4, 6, 7])

# Change to minor
p1 >> pluck([0, 2, 4, 6, 7], scale=Scale.minor)

If you want to change the scale for all players, you can set the Scale.default value:

# Start a player in the default scale (Major)
p1 >> pluck([0, 2, 4, 6, 7])

# Change the default scale to Dorian
Scale.default = Scale.dorian

# You can specify the default scale as a string
Scale.default = "dorian"

amp

This is the amplitude of a note, or its volume/loudness. The values usually go between 0 and 1 but you can set larger values to make a note even louder but be careful not to go too loud or you could damage your ears / speakers!

p1 >> pluck([0, 1, 2], dur=[1, 1/2, 1/2], amp=[1, 0.5, 1/3])

We can create quite rhythmic patterns using amp by using values of 0 as well:

p1 >> pluck(dur=1/4, amp=[1, 1/2, 1/2, 1, 0, 1, 0, 1, 1/2, 1/2, 1, 0, 1, 1/2, 1/4, 1])

But what happens if we want to play this rhythm every other bar? One way might be to manually add a lot of 0s to the sequence or use a Pattern object and use its stutter method but we can also use another keyword argument designed for just this purpose; amplify.

amplify

Before a sound gets triggered by a player, the amp value is multiplied by amplify so that you can use things like TimeVar to set an amplitude to be 1 or 0 (i.e. on and off) for certain amount of time:

p1 >> pluck(dur=1/4, amp=[1, 1/2, 1/2, 1, 0, 1, 0, 1, 1/2, 1/2, 1, 0, 1, 1/2, 1/4, 1], amplify=var([1,0],[6,2]))

This is useful if you want to set multiple players to be “on” or “off” at the same time:

p1 >> pluck(dur=1/4, amp=[1, 1/2, 1/2, 1, 0, 1, 0, 1, 1/2, 1/2, 1, 0, 1, 1/2, 1/4, 1])
p2 >> bass(var([0, 3], 8), dur=1/2)

p1.amplify = p2.amplify = var([1,0],4)

The last line is quite clumsy so you can use a Group object in place (see here for more information):

p1 >> pluck(dur=1/4, amp=[1, 1/2, 1/2, 1, 0, 1, 0, 1, 1/2, 1/2, 1, 0, 1, 1/2, 1/4, 1])
p2 >> bass(var([0, 3], 8), dur=1/2)

Group(p1, p2).amplify = var([1,0],4)

bpm

If you want a player object to play at a different tempo you can use the bpm attribute. This is the beats per minute. The global scheduling clock tempo can be set by changing its bpm attribute using Clock.bpm = 140 for example (see here for more information).

# Plays at the Clock.bpm tempo (default 120)
p1 >> pluck([0, 1, 2, 3])

# Force player to use 100 bpm
p2 >> bell([4, 5, 7], bpm=100)

# It will always play at 100 bpm even if Clock.bpm is changed
Clock.bpm = 200

sample

This is only used with the sample player, called play. This SynthDef takes a string as its first argument (known as the “play string”) instead of a list of pitch values and plays audio samples stored on your laptop based on the character in the string. Each character is mapped to a folder of samples and by default, it plays the first sample in that folder. To play a different sample, use the sample keyword:

# Default samples
p1 >> play("x-o-")

# A different set of samples
p1 >> play("x-o-", sample=1)

# Can be a list of values
p1 >> play("x-o-", sample=[0, 1, 2])

Values must be an integer. If a folder contains 3 samples and you use the value 4, the player object will loop back round to the first file in the folder and play that one, so you don’t need to know exactly how many samples are in a folder.

delay

In music (and especially electronic music) delay often refers to a sort of “echo” effect where a sound is played again a short period after it begins, but a little quieter. In FoxDot, however, it literally refers to an amount of time, in beats, to delay a sound from being played. Here, we’ll delay every third note by half a beat:

p1 >> pluck([0, 1, 2, 3], delay=[0, 0, 0.5])

If you want to play the note and play it with a delay, you can use a tuple or PGroup with the first value being 0, meaning no delay. The second value will indicate how long to delay the second note:

# "Stutter" every third note
p1 >> pluck([0, 1, 2, 3], delay=[0, 0, (0, 0.5)])

# Delay a note to play *after* the following one
p1 >> pluck([0, 1, 2, 3], delay=[0, 0, (0, 1.5)])