Random Variation

In the previous tutorials we created a few very simple melodies. We will now enrich them by random variation.

Table of Contents

Random choices

Each entity carries its own pseudo-random number generator. It can be easily accessed by @Rnd. The following example illustrates how it can manipulate the velocity and span to produce a randomly bad interpretation of the original score.

var melodyQ = [ 0, 1, 2, 3, 3, 4, 3, 1, 2, 2 ];
var melodyA = [ 0, 2, 0, 1, 1, 2, 1, 2, 0, 0 ];
var durations = [ 1f, 1f, 1f, 2f, 2f, 1f, 1f, 1f, 2f, 2f ];

function Main() {
  @span = melodyQ.Length + melodyA.Length;
  @Split(2).Then(idx => @Sentence(idx.Index == 0 ? melodyQ : melodyA));
}

function Sentence(int[] melody) =>
  @Split(durations).Then(idx => {
    @degree = melody[idx.Index];
    @span *= @Rnd.UnitFloat(); //a random float in [0, 1]
    @velocity = @Rnd.Float(0.3f, 1f); //a random float in [0.3, 1]
});
19.91 s

Important: Since Rnd is bound to an entity, the random value can be only applied to that very same entity, but neither to a different entity, nor to top-level scope variables like melodyQ and melodyA in the previous example.

We plan to add a GLOBAL namespace with an own random generator in the future, to allow for randomizing the global scope variables.

For now, manipulation of the melody notes needs to be done in the transformer next to the other Rnd calls. The following example adds three shift options for each of three notes in the middle of the phrase, so in total 3³ = 27 variations per sentence. Q and A are not distinguished by Sentence so the shift applies to both, resulting in 27² = 729 possible variations. Not all of them sound great, but that is not the goal of this example.

var melodyQ = [ 0, 1, 2, 3, 3, 4, 3, 1, 2, 2 ];
var melodyA = [ 0, 2, 0, 1, 1, 2, 1, 2, 0, 0 ];
var durations = [ 1f, 1f, 1f, 2f, 2f, 1f, 1f, 1f, 2f, 2f ];

function Main() {
  @span = melodyQ.Length + melodyA.Length;
  @Split(2).Then(idx => @Sentence(idx.Index == 0 ? melodyQ : melodyA));
}

function Sentence(int[] melody) =>
  @Split(durations).Then(idx => {
    @degree = melody[idx.Index];
    if (idx.Index >= 3 && idx.Index < 6)
      @degree += @Rnd.PositiveInt(2); //a random integer from {0, 1, 2}
});
20.60 s

Important: Upper limits in the random functions are in D inclusive. Note that this is a different behavior compared to the standard Random in C#

Persistent variations

If you hit play for the previous examples you will hear the same music over and over again. Remember to hit the left-most button to request a new execution of the script which yields a new variation. Often, it is nice to enforce the same variation for all script executions. This can be achieved by manually setting the seed attribute; Compare the following three examples, execute each of them several times.

function Main() {
  var melodyLength = 8;
  @span = melodyLength * 0.5;
  @Split(melodyLength).Then(() =>
    @degree = @Rnd.Int(-4, 5) //a random element from {-4, -3, -2, -1, 0, 1, 2, 3, 4, 5}
  );
}
4.60 s
function Main() {
  var melodyLength = 8;
  @span = melodyLength * 0.5;
  @seed = 42;
  @Split(melodyLength).Then(() => @degree = @Rnd.Int(-4, 5));
}
4.60 s
function Main() {
  var melodyLength = 8;
  @span = melodyLength * 0.5;
  @Split(melodyLength).Then(() => {
    @seed = 42; //sets the same seed for all entities produced by the split
    @degree = @Rnd.Int(-4, 5); //the same random element from {-4 .. 5}
  });
}
4.60 s
  1. The first example will always sound differently. Its seed is not set, so the system implicitly uses a random value as the seed.

  2. The second example sets the seed to a constant value. It follows, that the random sequence of integers generated inside of the Split is always the same, but it contains different tones.

  3. The last example sets the seed inside the Split which results in each Rnd call to be performed with the same constant seed. Thus, all tones will be always the same.

  4. How could you generate a sequence of constant but always different tones? For completeness, the next example shows that.

function Main() {
  var melodyLength = 8;
  @span = melodyLength * 0.5;
  @seedBackup = @seed;
  @Split(melodyLength).Then(() => {
    @seed = @seedBackup;
    @degree = @Rnd.Int(-4, 5);
  });
}
4.60 s

Another alternative would be to fix the seed to the split length either before or inside the Split, of course both with different effects. This is left as a small exercise for you.

Choices

The random generator also supports a choice from a set of alternatives. This way it is possible to work not only with floats and integers, but basically with any type.

The following example does not alter melody degrees, but combines whole pieces together. The number of resulting variations is lower, but the results do not suffer by mismatch sometimes created by free randomization.

var melodyQ = [ [ [ 0, 1, 2, 3, 3 ], [ 0, -1, 0, 3, 3 ], [ 0, 1, 2, 3, 5 ] ],
                [ [ 4, 3, 1, 2, 2 ], [ 4, 3, 1, 2, 4 ], [ 1, 2, 3, 2, 4 ] ] ];
var melodyA = [ [ [ 0, 2, 0, 1, 1 ], [ 0, 1, 2, 2, 1 ], [ 0, 0, 2, 3, 3 ] ],
                [ [  2, 1, -1, 0, 0 ], [ 3, 1, 2, 0, 0 ], [ 1, 2, 1, 0, 0 ] ] ];
var durations = [ 1f, 1f, 1f, 2f, 2f, 1f, 1f, 1f, 2f, 2f ];

function Main() {
  @span = durations.Sum() * 0.8;
  @Split(2).Then(idx => @Sentence(idx.Index == 0 ? melodyQ : melodyA));
}

function Sentence(int[][][] melody) {
  @joinedMelody = @Rnd.Choice(melody[0]).Concat(@Rnd.Choice(melody[1])).ToArray();
  @Split(durations).Then(idx => @degree = @joinedMelody[idx.Index]);
}
11.80 s

Random vectors

If you ever need a vector of random values, Rnd offers functions with Vec suffix which produce a whole array of random values at once. The array length is the first argument of the function.

function Main() {
  @melody = @Rnd.PositiveIntVec(8, 5); //eight random elements from {0, 1, 2, 3, 4, 5}
  @span = @melody.Length * 0.5;
  @Split(@melody.Length).Then(idx => @degree = @melody[idx.Index]);
}
4.60 s

The next tutorial will show how to create harmonic progressions and chords.

Search for something