The previous tutorial on instruments provided an extensive walk-through the whole orchestra available in D♭. In this final tutorial of the first chapter we will combine everything together to produce some of the first orchestral scores.
Table of Contents
• •
Each entity has the chord attribute which controls how many notes are actually played.
So instead of playing the default single note, you may play the whole triad. But what if
their chord tones should be each played by a different instrument? Slice makes it possible.
function Main()
{
@span = 5;
@octave = 3;
@chord = [0, 4, 7, 14, 16];
//all played by the violin
@instrument = "violin";
}function Main()
{
var instruments = ["contrabass", "cello", "viola", "violin", "violin"];
@span = 5;
@octave = 3;
@chord = [0, 4, 7, 14, 16];
//each chord tone played by a different instrument
@Slice().Then(i => @instrument = instruments[i]);
}Slice is the sister rule to Split. While Split divides the entity horizontally along the time axis, Slice divides it vertically.
The first variant of Slice is straight-forward, no parameters are necessary. Each entity has a chord attribute. By default it contains only the single base chord offset, so parameterless Slice has no effect. But for a multi-note chord, Slice separates each chord tone into a dedicated single-note entity.
There is a second variant when Slice takes the number of results as a parameter. In this mode, values of the chord attribute are being copied over, not partitioned. The following example achieves just the same as the previous one, but using the second approach. An attached batch function then transforms all of the results.
function Main()
{
var instruments = ["contrabass", "cello", "viola", "violin", "violin"];
var degrees = [0, 4, 7, 14, 16];
@span = 5;
@octave = 3;
@Slice(degrees.Length).Then(i =>
{
@instrument = instruments[i];
@degree = degrees[i];
});
}The last variant of Slice accepts an array of batch functions. Each of them transforms a distinct copy of the input entity. It is mainly useful for complex transformations, e.g. when Slice is used to create several instrument layers. Like in the previous variant, the chord attribute is being copied over, not partitioned. This last example achieves just the same like the previous two, only using a slightly different approach.
function Main()
{
@span = 5;
@octave = 3;
@Slice(
() => { @instrument = "contrabass"; },
() => { @instrument = "cello"; @degree = 4; },
() => { @instrument = "viola"; @degree = 7; },
() => { @instrument = "violin"; @degree = 14; },
() => { @instrument = "violin"; @degree = 16; }
);
}Instead of anonymous lambdas, it may be better to use explicit custom functions. That will allow easier instruments switching (on/off) as the functions will grow more complex in the following steps.
function Main()
{
@span = 5;
@octave = 3;
@Slice(Contrabass, Cello, Viola, Violin);
}
function Contrabass()
{
@instrument = "contrabass";
}
function Cello()
{
@instrument = "cello";
@degree = 4;
}
function Viola()
{
@instrument = "viola";
@degree = 7;
}
function Violin()
{
@instrument = "violin";
@degree = 14;
@chord += 2; //alternative one could split this between Violin I and Violin II player(s)
}Moving away from a single chord, it is now easy to assign individual melodies to the instruments .
function Main()
{
@span = 5;
@octave = 3;
@Slice(Contrabass, Cello, Viola, Violin);
}
function Contrabass()
{
@instrument = "contrabass";
}
function Cello()
{
@instrument = "cello";
@degree = 4;
var melody = [0, -2, -1];
@Split(2, 1, 1, 2, 1, 1)
.Then(i => @degree += melody[i % melody.Length]);
}
function Viola()
{
@instrument = "viola";
@degree = 7;
var melody = [0, 1, 2, 1];
@Split(melody.Length * 2).Then(i => @degree += melody[i % melody.Length]);
}
function Violin()
{
@instrument = "violin";
@degree = 14;
@chord += 2;
var melody = [0, -1, 0, 1, 2, 3, 2];
@Split(4, 1, 1, 1, 1, 2, 2).Then(i => @degree += melody[i % melody.Length]);
}The piece can be made longer by repeating the melodic sentence several times. A simple harmonic progression will twice change between the tonic (0) and submediant (-2 ≡ 5), so in sum there will be four sentences played. The submediant sentence will be similar to the tonic sentence, forming a subject and answer pair. The second pair of sentences will slightly vary from the first pair.
Custom attributes make the implementation of variations easy. sentence will indicate the sentence index
and harmony the respective harmonic degree. A split at the very beginning is responsible for
setting the structure with four sentences before slicing each of them into single instruments.
At the end of Main, when all instrument notes have been generated,
all the entities will be assigned a color following their chroma (i.e. degree invariant of octave).
function Main()
{
@span = 20;
@octave = 3;
@tutti = true;
@articulation = Articulations.Vibrato;
@Split(4).Then(i => @sentence = i);
@harmony = (@sentence % 2 == 0) ? 0 : -2;
@Slice(
Contrabass
,Cello
,Viola
,Violin
);
var colors = ["#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7"];
@color = colors[(256 + @harmony + @degree) % colors.Length]; //the constant assures a positive remainder even for negative harmony and/or degree
}
function Contrabass()
{
@instrument = "contrabass";
if (@sentence >= 2) @articulation = Articulations.Tremolo;
}
function Cello()
{
@instrument = "cello";
@degree = 4;
@melody = (@sentence % 2 == 0) ? [0, -1] : [0, -1, 1];
@rhythm = (@sentence % 2 == 0) ? [1, 1, 1, 1] : [2, 1, 1, 2, 1, 1];
@Split(@rhythm).Then(i => @degree += @melody[i % @melody.Length]);
}
function Viola()
{
@instrument = "viola";
@degree = 7;
var melody = [0, 1, 2, 1];
if (@sentence >= 2) @articulation = Articulations.Pizzicato;
@Split(melody.Length * 2).Then(i => @degree += melody[i % melody.Length]);
}
function Violin()
{
@instrument = "violin";
@degree = 14;
@chord += 2;
@melody = (@sentence % 2 == 0) ? [0, -1, 0, 1, 2, 3, 2] : [0, 2, 5, 4, 3, 2, 1];
@Split(4, 1, 1, 1, 1, 2, 2).Then(i => @degree += @melody[i % @melody.Length]);
if (@span <= 1) {
@octave += 2;
@degree -= 12;
@chord = Chord.Base;
}
@Slice(); //partition the chord, just to get the colors right
}Timpani will provide a bit of rhythmic guidance separating the sentences. A pair of hits will sound much better as a single one. It would be great to have the first hit as pickup (also known as anacrusis) right before the actual measure starts and the following one on the first beat of each sentence.
Starting the timpani line earlier to accommodate the pickup can be realized
by a time shift. But first, the Meter utility is used to determine the
right tempo from the desired duration and the timpaniHit then stores the
hit duration set to a 32nd note.
Within the Timpani() function, the original span is stored in a helper
attribute, so that it can be restored later. To obtain correct timing the span
must be shortened to a 16th note before the Split which produces a pair of 32nd notes.
But a timpani’s resonance lasts much longer than a 32nd note,
so cutting it off at that boundary would sound unnatural. Therefore, restoring
the original duration (in fact it is just 70% of it) makes a better sound.
For drums it does not matter that the two stretched hits actually overlap.
A better way to set the tempo and determine the duration of a certain note will be discussed in the tempo and meter tutorial.
function Main()
{
@span = 20;
@octave = 3;
@tutti = true;
@articulation = Articulations.Vibrato;
//just for fun try @scale = [0,2,4,5,8];
@tempo = 16 * 60 / @span; //4 measures, each with 4 beats = 16 beats; multiplying by (60 / @span) converts to qpm
@timpaniHit = (@span / 16) / 8; //corresponds to a 32nd note
@Split(4).Then(i => @sentence = i);
@harmony = (@sentence % 2 == 0) ? 0 : -2;
@Slice(
Contrabass
,Cello
,Viola
,Violin
,Timpani
);
var colors = ["#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7"];
@color = colors[(colors.Length * 10 + @harmony + @degree) % colors.Length];
}
function Contrabass()
{
@instrument = "contrabass";
if (@sentence % 2 == 0) @articulation = Articulations.Tremolo;
}
function Cello()
{
@instrument = "cello";
@degree = 4;
@melody = (@sentence % 2 == 0) ? [0, -1] : [0, -1, 1];
@rhythm = (@sentence % 2 == 0) ? [1, 1, 1, 1] : [2, 1, 1, 2, 1, 1];
@Split(@rhythm).Then(i => @degree += @melody[i % @melody.Length]);
}
function Viola()
{
@instrument = "viola";
@degree = 7;
var melody = [0, 1, 2, 1];
if (@sentence >= 2) @articulation = Articulations.Pizzicato;
@Split(melody.Length * 2).Then(i => {
if (@sentence >= 2 && (i % 4) == 3) @Rest();
@degree += melody[i % melody.Length];
});
}
function Violin()
{
@instrument = "violin";
@degree = 14;
@chord += 2;
@melody = (@sentence % 2 == 0) ? [0, -1, 0, 1, 2, 3, 2] : [0, 2, 5, 4, 3, 2, 1];
@Split(4, 1, 1, 1, 1, 2, 2).Then(i => @degree += @melody[i % @melody.Length]);
if (@span <= 1) {
@octave += 2;
@degree -= 12;
@chord = Chord.Base;
}
@Slice(); //just to get the colors right
}
function Timpani()
{
@instrument = "timpani";
@time -= @timpaniHit; //shift it backwards in time so that the first hit occurs before the beat
@tmpSpan = @span; //remember the span
@span = @timpaniHit * 2;
@motive = (@sentence % 2 == 0) ? [-3, 0] : [2, 0];
//there is a shortcut for the following construct, see intermediate tutorials
@Split(@motive.Length).Then(i => {
@degree = @motive[i];
@velocity = 0.8 + i * 0.05;
});
@span = @tmpSpan * 0.7; //set back the span to avoid cutting away the resonance
}Clone is a twin rule to Slice. The only differences are that:
Slice destroys the original entity while Clone always keeps it as is.
Transformers like Clone.Then(...) are applied only to the newly created clones, not to the original entity.
There is no manipulation of the chord attribute at all, Clone cannot separate a chord into single-note entities.
The following example demonstrates the differences.
Then changes the color to orange for both Clone and Slice,
but one entity remains unchanged — the original entity retained by Clone.
function Main()
{
@span = 8;
@Split(CloneExample, SliceExample);
}
function CloneExample() => @Clone(() => @degree += 4, () => @degree += 9).Then(() => @color = "orange");
function SliceExample() => @Slice(() => @degree += 4, () => @degree += 9).Then(() => @color = "orange");Slice is usually preferred for orchestration. Replacing Slice with a Clone in the first example would result in an awkward sound. The chord will not be partitioned with Clone, so all instruments will try to play the whole chord. Moreover, the initial entity with the default piano will be included.
function Main()
{
var instruments = ["contrabass", "cello", "viola", "violin", "violin"];
@span = 5;
@octave = 3;
@chord = [0, 4, 7, 14, 16];
//each string instrument plays one chord tone
//and the default piano is still playing the whole chord
@Clone().Then(i => @instrument = instruments[i]);
}Clone is more useful in voicing when one voice holds the harmonic support while other voices produce the melody or ornaments. The following short motive shows how the main soprano line can be cloned to allow alto to trill around a bit.
function Main() { //fragment of the Flower Duet from Lakmé by Leo Delibes
@span = 8;
@Split(2);
@key = -1;
@octave = 5;
@instrument = "violin";
@Split(2,4,2,4).Then(i => {
if (i == 0 || i == 2)
@Rest(); //1st beat empty, 3rd beat resolved by stretching the 2nd
else if (i == 1) {
@degree = 2;
@span *= 1.5; //soprano holds
@Clone(() => @Split(6).Then(j => {
if (j.IsLast) @Rest();
else if(j == 4) @degree = -2;
else @degree = -(j % 2); //alto trills
}));
}
else
@Split(4).Then(j => {
@degree = -1 + j; //ascend jointly
@chord += 2;
if (j.IsLast) @Rest();
});
});
}Since chords only support positive degrees, Clone can be used to double the bass like in the following example.
function Main()
{
@span = 20;
@scale = [0, 2, 4, 6, 8];
@Split(
() => {
@release += 2;
@Split(
() => @chord = [0,2,4,7],
() => @chord = [0,3,4,6],
() => { @chord = [0,2,5,6]; @release += 8; }
).Then(i => @harmony -= i);
//add the bass
@Clone(() => {
@octave -= 1;
@Slice().Then(i => {
if (i > 1) @Rest(); //take the lowest two chord tones
//writing @chord = [0,2] instead would be wrong as the 2nd chord has [0,3] as the lowest tones
});
});
},
() => {
@instrument = "flute";
@chord = [0];
@Split(
() => @SetMelody([5,6,5,4]),
() => @SetMelody([5,6,5,4]),
() => @Slice(() => @degree = 3, () => @instrument = "piano"));
},
() => {
@release += 4;
@octave -= 1;
@Split(
() => @chord = [0,3,4,6],
() => @chord = [0,4,5,6]
).Then(i => @harmony -= i);
});
}
//Melody as a name for the custom function would be shorter,
//but it is a reserved word so SetMelody is a meaningful alternative
function SetMelody(IList<int> melody) => @Split(melody.Count).Then(i => @degree = melody[i]);If you wish to see how the previous example is developed further, check out its full composition blog.
In the previous two examples Clones and many of the Splits
were called with anonymous functions as parameters in the same fashion as Slice
in the earlier above. For convenience and compatibility Slice, Split and Clone all support
the same common signatures: parameterless, count and function reference. Their interpretation
is almost the same, except for the parameterless case which is considered their default behavior.
@Clone() creates a single copy of the input entity while @Split()
and Slice() partition the chord.
There are different options for passing function references as parameters. The following minimal example isolates them. A Split with function references divides the input entity equally in time by their count. Each resulting entity is additionally transformed by the respective function.
function Main() {
@span = 4;
@Split(4); //repeat 4x
@Split(
() => @octave = 4,
() => @octave = 3
);
//alternatively an explicit array can be used as well, e.g. using the C#12 collection literals
//var a = [() => @octave = 4, () => @octave = 3];
//@Split(a);
//or
//@Split([() => @octave = 4, () => @octave = 3]);
}Custom functions can be passed as well, but since they are passed by function references,
the @ is not needed. Otherwise they would be executed immediately resulting in a type mismatch.
The next example contains very simple custom functions, but during real composition work,
it is common that they are much more complex.
function Main() {
@span = 4;
@Split(2); //repeat 2x
@Split(A, B);
//alternatively an explicit array can be used as well, e.g. using the C#12 collection literals
//var a = [A, B];
//@Split(a);
//or
//@Split([A, B]);
}
function A() {
@Split(4).Then(i => @octave -= (i % 2));
}
function B() {
@octave += 1;
@Split(4).Then(i => @degree -= i);
}The last example shows Clone with the count of copies passed as an argument. To make the example interesting, it is not a constant number, but grows with the beat index.
var melody = [0, 2, 4, 5, 6, 5, 4, 3];
function Main() {
@mode -= 1;
@span = 6;
@Split(4).Then(idx => @measureIndex = idx); //4 measures
//let the harmony appear only after the 2nd measure
if (@measureIndex >= 2)
@Clone(() => @isMelody = true); //custom attribute to mark the copied entities
@Split(4).Then(idx => {
if (@isMelody) @SetMelody(idx); else @Drums(idx);
});
}
function SetMelody(Indexer beat) {
@harmony = melody[(@measureIndex * 4 + beat) % melody.Length];
@Clone(beat + 1).Then(idx => {
@instrument = "piano";
@velocity = 0.5 + (beat.IsOdd ? 0.1 : 0) + 0.03 * @harmony;;
if (beat.IsEven && idx == 0) //double the bass for the first (i.e. lowest) entity
@Clone(() => {
@octave -= 2;
@instrument = "contrabass";
@velocity -= 0.1;
@articulation = Articulations.Pizzicato;
});
@degree = idx * 2;
});
}
function Drums(Indexer idx) {
if (idx.IsEven)
{
@velocity = 0.85;
@instrument = "bass drum";
}
else
{
@velocity = 0.8;
@instrument = "snare";
}
if (idx == 2 && @measureIndex.IsOdd)
{
@velocity = 0.8;
@Split(3f, 1f);
}
}D♭ Tutorials — Basic
D♭ Tutorials — Intermediate
D♭ Reference
D♭ Examples
Links