Different types of TimeVar

This section introduces the various incarnations of TimeVars that are available to use in FoxDot. So far we have seen the basic, vanilla TimeVar that holds a value for a certain number of beats and then changes to the next value. There also exists a number of TimeVars that gradually change between these values as well as TimeVars for representing whole Pattern objects. Let’s delve into these now:

Gradual-Change TimeVars

There are three types of gradual-change TimeVars that (unsurprisingly) gradually change between values over time instead of instantly changing once the assigned duration has elapsed. These are linvar, sinvar, and expvar and their names relate to the way in which the values change over time, as described below:

linvar

This TimeVar changes between values on a linear scale, hence the name linvar. As with all TimeVars, it takes a series of values and durations as its input:

>>> my_linvar = linvar([0, 1], 4)
>>> print(Clock.now(), my_linvar)
0, 0
>>> print(Clock.now(), my_linvar)
1, 0.25
>>> print(Clock.now(), my_linvar)
2, 0.5
>>> print(Clock.now(), my_linvar)
3, 0.75

As time progresses, the value changes such that after the given duration (4 beats in this example) it will be exactly 1. Once that duration has elapsed, the linvar will start to linearly change its value in the direction of the next input value, which is 0. Over the next 4 beats, the value will decrease at a linear rate towards 0.

>>> print(Clock.now(), my_linvar)
4, 1
>>> print(Clock.now(), my_linvar) 
5, 0.75
>>> print(Clock.now(), my_linvar) 
6, 0.5
>>> print(Clock.now(), my_linvar) 
7, 0.25

sinvar

Instead of changing between values at a linear rate, a sinvar changes at a rate derived from a sinusoidal wave. You can see below that the rate is seemingly much faster but will still reach the final value of 1 at the same time as the linvar.

>>> my_sinvar = sinvar([0, 1], 4)
>>> print(Clock.now(), my_linvar, my_sinvar)
1, 0.25, 0.38
>>> print(Clock.now(), my_linvar, my_sinvar)  
2, 0.50, 0.71
>>> print(Clock.now(), my_linvar, my_sinvar)  
3, 0.75, 0.92
>>> print(Clock.now(), my_linvar, my_sinvar) 
4, 1, 1

Expvar

The rate of change in the expvar is exponential, so starts small but finishes in large steps. You can see this is also the case when decreasing values:

>>> my_expvar([0, 1], 4)
>>> print(Clock.now(), my_linvar, my_sinvar, my_expvar)
1, 0.25, 0.38, 0.06
>>> print(Clock.now(), my_linvar, my_sinvar, my_expvar) 
2, 0.50, 0.71, 0.25
>>> print(Clock.now(), my_linvar, my_sinvar, my_expvar) 
3, 0.76, 0.93, 0.57
>>> print(Clock.now(), my_linvar, my_sinvar, my_expvar) 
4, 1, 1, 1
>>> print(Clock.now(), my_linvar, my_sinvar, my_expvar) 
5, 0.74, 0.92, 0.93
>>> print(Clock.now(), my_linvar, my_sinvar, my_expvar) 
6, 0.50, 0.71, 0.75
>>> print(Clock.now(), my_linvar, my_sinvar, my_expvar) 
7, 0.25, 0.38, 0.43

Notes and examples

It should be noted when a player is using a gradual change TimeVar it will use the value stored in it at the time the note is triggered. That means, once a note is played, you will not hear a change in value over time in the note itself. Try these lines of code out yourself:

# No gradual change in the high pass frequency
p1 >> dirt(dur=4, hpf=linvar([0, 4000], 4))

# Apparent gradual change in the high-pass frequency
p2 >> dirt(dur=1/4, hpf=linvar([0, 4000], 4)) 

You can also use a duration of 0 to immediately skip the gradual change and move on to the next value; this is useful for ‘resetting’ values and creating drops:

# Ramp up to 4000Hz then reset to 0
p1 >> dirt(dur=1/4, hpf=expvar([0, 4000], [8, 0]))

Just as you can with regular TimeVars, gradual-change TimeVars can be nested within other TimeVars to better manage how the values are applied. For example, we can ramp the high-pass filter frequency only on the last 4 beats of a 32 beat cycle like so:

# Use a regular TimeVar to set the value to 0 for 28 beats
p1 >> dirt(dur=1/4, hpf=var([0, expvar([0, 4000], [4, 0])], [28, 4]))

Pattern TimeVars (Pvar)

So far we have only stored single values in a TimeVar but sometimes it is useful to store a whole Pattern object. You can’t do this using a regular TimeVar as any Pattern in the input list of values is treated as a nested list of single values. To avoid this behaviour, you need to use a Pvar, short for Pattern-TimeVar. It is created exactly like any other TimeVar but values can be whole lists/Patterns:

>>> a = Pvar([[0, 1, 2, 3], [4, 5, 6]], 4)
>>> print(Clock.now(), a)
0, P[0, 1, 2, 3]
>>> print(Clock.now(), a) 
4, P[4, 5, 6]

Mathematical operations and transformations also work identically to the regular TimeVars:

>>> a = Pvar([[0, 1, 2, 3], [4, 5, 6]], 4)
>>> b = (a * 2) + 1
>>> print(Clock.now(), a, b)
0, P[0, 1, 2, 3], P[1, 3, 5, 7]
>>> print(Clock.now(), a, b) 
4, P[4, 5, 6], P[9, 11, 13]
>>> def odd_test(num):
...     """ Convert an even number to 3 and an odd to 5 """
...     return 5 if num % 2 == 1 else 3
>>>
>>> c = c.transform(odd_test)
>>> print(a, b, c)
P[0, 1, 2, 3], P[1, 3, 5, 7], P[3, 5, 3, 5]
>>> print(a, b, c)
P[4, 5, 6], P[9, 11, 13], P[3, 5, 3]

You can even nest a Pvar within a Pattern as you would a normal Pattern to alternate values being played… and then change this based on time!

# Alternate the alternating notes every 8 beats
p1 >> pluck([0, 1, 2, Pvar([[4, 5, 6, 7], [11, 9]], 8)], dur=1/4, sus=1)