Skip to content

Drum Riff

A drumRiff is a complex data-type for storing musical compositions for drums. They can only be used inside the composition code block.


Creating A DrumRiff

To compose a drumRiff by hand:

1
var myBeats = |X---X---X---X-xx|;

In the example above, the new variable myBeats will hold a drumRiff**. The duration of the riff is 16 beats. This code has an X on every beat that we want to play loudly, an x on every beat that we want to play quietly, and a - on every beat that we want to be silent. The | symbols represent the edges of our drumRiff.

To create an empty drumRiff:

1
var myBeats = drumRiff(16 beats);

To make a duplicate of a drumRiff:

1
var copyOfBeat = myBeats.copyAll();

To make a contrasting derivative of a drumRiff

1
var otherBeat = myBeats.makeContrast(0.4);

In the example, above, we made use of the makeContrast() function. This function requires us to specify the degree of contrast it should impose upon the new drumRiff that it will compose. This must be a number between 0.0 and 1.0.

To use one drumRiff to overwrite part or all of another drumRiff:

1
2
3
var source = |XXXXX|;
var destination = |----------------|
destination.overwrite(source, 3 beats);

In this example, above we started by manually composing a drumRiff called source and another called destination. On the next line we used the overwrite() function, specifying that we want to overwrite destination with source, and that the overwriting should begin after 3 beats. After this function has been performed, destination will look like this:

1
|---XXXXX--------|

Providing Requirements For Automatic Composition

The drumRiff has several aspects that can be targetted by our list of requirements:

  • hitsMax specifies the maximum number of consecutive drum hits that can be found within a drumRiff
  • hitsGroups specifies the total number of groups of consecutive drum hits
  • nearHitsMax specifies the maximum number of consecutive beats that are drum hits or that immediately follow a drum hit
  • nearHitsGroups specifies the total number of groups of consecutive beats that are drums hits or that immediately follow a drum hit
  • nearHitsSum specifies the total sum of beats that are drum hits or that immediately follow a drum hit
  • silenceMax specifies the maximum number of consecutive beats that are silent
  • silenceSum specifies the total number of beats that are silent

Each of these aspects must be set to a normalised number. A normalised number is similar to a percentage, but instead of being from 0 to 100, a normalised number must be between 0.0 and 1.0. Here is an example:

1
2
3
4
composition (1 bar) {
    var myBeats = drumRiff(16 beats);
    myBeats.compose({ hitsMax : 0.3, nearHitsMax : 0.5 });
};

Notice that in this example, we don't have to specify requirements for all seven of the aspects. In fact, we could even specify no aspects at all:

1
2
3
4
composition (1 bar) {
    var myBeats = drumRiff(16 beats);
    myBeats.compose();
};

Info

It is almost always best to use less than seven requirements for the simple fact that some of the aspects contradict each other. For example, if you specify that you require both silenceSum and nearHitsSum to be 0.9, then you're essentially asking for the composition to be both mostly silent and wildly energetic. This is a paradox that will only lead to disappointment. Its much easier to avoid making contradictions like this when there are only two or three requirements at a time.


Preparing a drum riff for playback

Modulo1 will allow you to create as many drum riffs as you like, but they cannot make any sound unless you attach them to some drums. After you have created your drums, within the tracks code block, you need to let Modulo1 know that you intend to compose for those drums:

1
2
3
4
tracks {
    var kick = find drum in (catalogue) where (id == "xUYRHGDJBGR");
    use (kick) in (composition);
};

The use keyword tells Modulo1 that you intend to make something available in a different section of your code. In this instance, we are telling Modulo1 that we want to use the kick in the composition section of our code.

The next step, is to actually write the code for our composition section:

1
2
3
composition (1 bar) {
    kick.compose({ nearHitsSum : 0.4 });
};

You will notice that we did not have to create a drumRiff variable inside our composition code block. Instead, Modulo1 has allowed us to converted the kick variable into a drumRiff and has given it a length of 1 bar, which is a measurement of time that is usually equal to 16 beats.


Putting It All Together

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
tracks {
    var kick = find drum in (catalogue) where (id == "xUYRHGDJBGR");
    var snare = find drum in (catalogue) where (id == "xJKSGHTWJDG");
    var hat1 = find drum in (catalogue) where (id == "xHJKSGHWYUG");

    use (kick, snare, hat) in (composition);
};

composition (1 bar) {
    var myBeats = |X--X|;
    kick.compose({ nearHitsSum : 0.4 });
    kick.overwrite(myBeats, 12 beats);

    snare = |---X---X---X---X|;

    hat = snare.makeContrast(0.6);
}

Properties

A property is a special feature of a variable that can be used to get and/or set some aspect of that variable.

beatsPerBar

The beatsPerBar property can be used to find out how many beats will be played per bar for a particular drumRiff. This value bears a strong correlation to the concept of a time signature. That is, setting 12 beats per bar is equivalent to 3/4 time, 16 beats per bar is equivalent to 4/4 time.

The default value is 16.

1
var result = kick.beatsPerBar;

The beatsPerBar property can also be used to set how many beats will be played per bar for a particular drumRiff. Valid options for this property, are 6, 8, 12, 16, 24, and 32.

1
kick.beatsPerBar = 24;

name

The name property can be used to retrieve the drum name of a drumRiff. This may differ from its variable name

1
var result = kick.name

The name property can also be used to change the drum name of a drumRiff. This will not affect its variable name

1
kick.name = "Massive Thump";

totalTime

The totalTime property can be used to receive a musicalTime variable that indicates the length of the drumRiff

1
var result = kick.totalTime;

type

The type property can be used to find out the data-type of a particular drumRiff. The result will always be "drumRiff"

1
var result = kick.type;

Constructor

drumRiff( musicalTime duration )

The drumRiff constructor creates a new drumRiff with the specified duration

1
var myRiff = drumRiff(16 beats);

Functions

compose( object requirements )

The compose function allows an object containing a list of requirements to be passed to the drumRiff, which will then compose a drumRiff based on those requirements.

1
kick.compose({ nearHits : 0.2 });

contains( drumRiff other )

The contains function will return true or false depending on whether it finds a pattern matching the other drumRiff inside the current drumRiff.

1
var result = kick.contains(|X-xx|);

containsAtTime( drumRiff other, musicalTime position )

The containsAtTime function will return true or false depending on whether it finds a pattern matching the other drumRiff at the position specified

1
var result = kick.containsAtTime(|X-xx|, 12 beats);

copyAll()

The copyAll function will return a new drumRiff that contains a duplicate of all beats in the drumRiff

1
var copyOfBeats = kick.copyAll();

copy( musicalTime start, musicalTime totalToCopy )

The copy function will return a new drumRiff that contains a duplicate of beats, between at the position specified as start and continuing for a period equal to the duration specified as totalToCopy

1
var copyOfBeats = kick.copy(4 beats, 8 beats);

distribute( drumRiff other1, drumRiff other2, ... )

The distribute function copies the beats of a drumRiff, chops them up into smaller sections and then pastes each small section onto a one of several other drumRiffs that are provided as inputs.

1
myBeats.distribute(hihat1, hihat2, hihat3);

eraseAll()

The eraseAll function erase all drum hits within a particular drumRiff

1
kick.eraseAll();

erase( musicalTime start, musicalTime totalToErase )

The erase function erases a limited amount of drum hits within a particular drumRiff, between at the position specified as start and continuing for a period equal to the duration specified as totalToErase

1
snare.erase(2 beats, 12 beats);

fillAll( drumRiff source )

The fillAll function causes one drumRiff to overwrite another, making sure to fill the entire destinatio

getBeat( musicalTime position )

The getElement function allows you to retrieve a single-character string that signifies the value of one particular beat within a drumRiff

1
var result = kick.getBeat(4 beats);

invert()

The invert function changes a drumRiff so that silent beats become loud beats, and vice versa

1
kick.invert();

joinAtEnd( drumRiff source )

The joinAtEnd function allows one drumRiff to be used to extend another drumRiff by adding it to the end

1
myBeats.joinAtEnd(otherBeats);

joinAtStart( drumRiff source )

The joinAtStart function allows one drumRiff to be used to extend another drumRiff by adding it to the start

1
myBeats.joinAtStart(otherBeats);

makeContrast( number amtOfContrast )

The makeContrast function is an intelligent tool that generates a new drumRiff that is based on another drumRiff, but with a specified amount of contrast, which is determined by an input number between 0 and 1.

1
var result = kick.makeContrast(0.67);

merge( drumRiff source, musicalTime startPosition )

The merge function causes two drumRiffs to be blended together. The startPosition parameter specifies how much of the beat to skip before beginning the merge

1
hihat.merge(|X--X--x|, 4 beats);

overwrite( drumRiff source, musicalTime startPosition )

The overwrite function causes one drumRiff to overwrite another. The startPosition parameter specifies how much of the beat to skip before beginning overwriting

1
snare.overwrite(|X---X|, 12 beats);

reverse()

The reverse function does exactly what its name implies: it reverse the drumRiff's beats

1
kick.reverse();

rotate( number amtToRotate )

The rotate function shifts the beats within a drumRiff according to the input number which can be either positive or negative.

1
kick.rotate(-5);

replaceAll( drumRiff source )

The replaceAll function replaces the beats in one drumRiff with the beats of another drumRiff. If the source beat is too short to fill the entire destination drumRiff, then the beats will be repeated until they do fill the space

setBeat( musicalTime position, string x )

The setElement function allows you to set one particular beat at a specified position, by using a single-character string as input

1
kick.setElement(12 beats, 'X');