In the previous tutorials we created a few very simple melodies. We will now enrich them by random variation.
Table of Contents
• • • •
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]
});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}
});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#
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}
);
}function Main() {
var melodyLength = 8;
@span = melodyLength * 0.5;
@seed = 42;
@Split(melodyLength).Then(() => @degree = @Rnd.Int(-4, 5));
}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}
});
}The first example will always sound differently. Its seed is not set,
so the system implicitly uses a random value as the seed.
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.
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.
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);
});
}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.
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]);
}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]);
}The next tutorial will show how to create harmonic progressions and chords.
D♭ Tutorials — Basic
D♭ Tutorials — Intermediate
D♭ Reference
D♭ Examples
Links