Gaussian random?

I love all the various ways to introduce randomness into Tidal. I'm learning about different kinds of randomness and just found out about gaussian/normal distribution. As I understand it, it is random values that are more likely to be close to a "mean" value.

So if the mean value is 0.5 and the range is 0 to 1, random values are more likely to be around 0.5 (0.45, 0.49, 0.51) than far away (eg 0.1, 1, 0.9)

I can see this being really useful in Tidal, for instance generating random notes that stay around a particular pitch but sometimes jump higher or lower.

Is there anything already like this, or might there be hacks to do something like this?

the way i would do it would be something like (range 0.45 0.65 rand)

I was just wondering the same thing but last time I looked at the source I didn't find any Gaussian noise.

You could use inverse transform sampling to map from rand to Gaussian noise, i.e., apply the inverse of the distribution function to rand. This works for any distribution you want but I don't know where to get the Gaussian distribution function specifically in Haskell.
Alternatively there is the Box-Muller transform which is supposedly more efficient for generating Gaussian noise.

the way i would do it would be something like (range 0.45 0.65 rand)

That does not create Gaussian noise but only rescales the uniform noise. @beckstrom specifically asked that some values are still further away...

If you don't need Gaussian noise specifically you can easily construct a symmetric triangular distribution by adding two independent uniform random values:

triang = (rand + fast 1.1 rand) / 2

We need to apply fast 1.1 to the second rand to make it independent from the first rand.

Reading a bit into Box-Muller, this seems the way to go:

boxmuller x y = (r * cos t, r * sin t)
  where r = sqrt $ (-2) * log x
        t = 2 * pi * y

gauss = fst $ boxmuller rand $ fast 1.1 rand

Didn't get to test it in Tidal directly (only have ghci right now) so let me know if you encounter some problems.

4 Likes

FWIW I've been testing an invnorm function to map the [0:1] range into a parameterized normal-ish distribution. You can change the distribution behavior via the mean and deviation parameters.
The gaussselect helper function already pipes rand through that and passes the result to select - this way it becomes really very simple to use

let invnorm mean dev x = (1.0 - dev) * (dev * atanh (1.99 * x - 0.9999999) / 2 + mean) + dev * x
    gaussselect mean dev = select (invnorm mean dev $ rand)

-- usage
do
  set "pattern" $ gaussselect (slow 32 sine) (slow 2 sine) [0,2,4,6,7,9,11,13]
  d1 $ note (scale "major" "^pattern") # s "superpiano"