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.
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
the chord notes should be each played by a different instrument? Slice makes it possible.
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-formard, no parameters are necessary. Remember that behind the scenes each entity is a chord. Even simple notes are by default single-note chords. A Slice without parameters will have no effect on them. But for a proper chord with several notes, Slice will separate each of them into a dedicated 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.
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.
Instead of anonymous lambdas, it may be better to use explicit custom functions. That will allow easier intruments switching (on/off) as the functions will grow more complex in the following steps.
Moving away from a single chord, it is now easy to assign individual melodies to the instruments .
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).
Timpani will provide a bit of rhytmic 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 accomodate 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 Timapni()
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 the drum membrane resonates much longer than a 32nd note, so a cut of
at the end of the note would note sound naturally. 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.
Clone is a twin rule to Slice. The only differences are that:
Slice destroys the original entity while Clone always keeps is 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 is not able to separate tones of a chord.
The following example demontrates the differences. Then
changes the color to orange for both Clone and Slice, but one tone does not. It is the original entity that was kept back by Clone.
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.
Clone is more useful in voicing when one voice holds the harmonic support while other voices produce the melody or ornaments. The folowing short motive shows how the main soprano line can be cloned to allow alto to trill around a bit.
Since chords only support positive degrees, Clone can be used to double the bass like in the following example.
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.
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.
The last example shows Clone with the count of copies passed as an arguemnt. To make the example interesting, it is not a constant number, but grows with the beat index.