Skip to content

Melody

A melody is a complex data-type that helps to create musical compositions for instruments. They can only be used inside the composition code block.

Creating A Melody

To compose a new melody by hand:

1
2
3
4
5
6
var bassLine = melody(1 bar)[
    [3] for 2 beats,
    [-] for 1 beat,
    [4] for 4 beats,
    [3] for 2 beats
];

You should interpret the above code as saying:

Interpretation

Create a new melody called bassLine, that has a maximum musicalTime of 1 bar. It will play a play the pitch 3 for a duration of 2 beats, followed by silence for a duration of 1 beat. Then it will play the pitch 4 for a duration of 4 beats, followed by the pitch 3 for a duration of 2 beats.

The maximum duration of the melody is 1 bar, which usually equals 16 beats. However, to gain a bit more precision and consistency, we can express the same melody by using ticks as our measurement of time, instead of beats. There is always exactly 96 ticks in a bar.

1
2
3
4
5
6
var bassLine = melody(1 bar)[
    [3] for 12 ticks,
    [-] for 6 ticks,
    [4] for 4 ticks,
    [3] for 6 ticks
];

Alternatively, we can use more traditional ways of measuring the length of our notes, using wholeNotes, halfNotes, quarterNotes, eighthNotes, sixteenthNotes, and thirtySecondNotes:

1
2
3
4
5
var bassLine = melody(1 bar)[
    [3] for 1.5 quarterNotes,
    [-] for 1 eighthNote,
    [4] for 1 halfNote
];

Info

Modulo1 defines a beat as being equivalent to a sixteenthNote by default.

To compose a new melody by hand without specifying what the note pitches are:

1
2
3
4
5
6
var bassLine = melody(1 bar)[
    [?] for 2 beats,
    [-] for 1 beat,
    [?] for 4 beats,
    [?] for 2 beats
];

In this example, Modulo1 will randomly determine what pitches to use wherever it finds a ? symbol instead of a pitch number. And every time the code runs, a different set of random pitch numbers will be assigned.

There will probably be circumstances where we want to restrict just how random the pitches are. Here is how we can achieve that:

1
2
3
4
5
6
7
var r = between 1 to 3;
var bassLine = melody(1 bar)[
    [A=?] for 3 beats,
    [A+r] for 5 beats,
    [B=A-r] for 3 beats,
    [B-r] for 5 beats
];

In this example, above, our chord has used relative pitches, which means that they are calculated relative-to a preceding note. You should interpret the above code as saying:

Interpretation

Create a numberRange between 1 to 3, and call it r. Then create a melody called bassLine that has a duration of 1 bar. The first note will be a random pitch value that we will temporarily refer to as A, and it will play for a duration of 3 beats. Then, we have a note that has a pitch that is equal to A plus a random number within the numberRange contained in r (i.e. from 1 to 3), that plays for a duration of 5 beats. Next, we have a note that has a pitch equal to A minus a random number within the numberRange contained in r, that we will temporarily refer to as B, that plays for a duration of 3 beats. Finally, we have a note that has a pitch equal to B minus a random number within the numberRange contained in r.

To compose a melody by hand without specifying what the note durations are:

1
2
3
4
5
6
var bassLine = melody(1 bar)[
    [3] for ? beats,
    [-] for ? beats,
    [4] for ? beats,
    [3] for ? beats
];

There will probably be circumstances where we want to restrict just how random the note durations are. Here is how we can achieve that:

1
2
3
4
5
6
var r = between -1 to 1;
var bassLine = melody(1 bar)[
    [3] for A=? beats,
    [4] for A+r beats,
    [3] for A+r beats
];

You should interpret the above code as saying:

Interpretation

Create a numberRange between -1 to 1, and call it r. Then create a melody called bassLine that has a duration of 1 bar. The first note will play the pitch 3 for a random duration which we will temporarily refer to as A. The second note will play the pitch 4 for a period of time equal to A plus a random number within the numberRange contained in r (i.e. from -1 to 1). The third note will play the pitch 3 for a period of time equal to A plus another random number within the numberRange contained in r.

To compose a melody by hand that has chords:

1
2
3
4
var leadMelody = melody(1 bar)[
    [1,3,5] for 4 beats,
    [2,4,6] for 4 beats
];

To compose a melody by hand with chords that have unspecified note pitches:

1
2
3
var leadMelody = melody(1 bar)[
    [A=?,A+2,A+4] for 8 beats
];

You should interpret the above code as saying:

Interpretation

Create a new melody called leadMelody, that will be 1 bar in duration. It will play one chord for 8 beats and it will have a random root note which we will temporarily refer to as A. The second note in the chord will be two intervals higher than whatever A is. And the third note in the chord will be four intervals higher than whatever A is.

To compose a melody that features repeating patterns:

1
2
3
4
5
6
7
8
9
var pattern1 = innerMelody()[
    [$] for 1 beat,
    [$+3] for 2 beats
];

var leadMelody = melody(8 beats)[
    [$=2] in pattern1 for 3 beats,
    [$=1] in pattern1 for 5 beats
];

You should interpret the above code as saying:

Interpretation

Create a new pattern called pattern1 that will chop up our melody into smaller notes that can each have a different pitch relative to the inputted pitch. The first note within each pattern will play the inputed pitch (which is represented by the $ symbol) for 1 beat. The second note within each pattern will play for 2 beats and will have a pitch equivalent to the inputed pitch + 3.
Create a new melody called leadMelody that will be 8 beats in duration. It will play the pattern for 3 beats, with a pitch input of 2. Then, it will play the pattern again, this time with a pitch input of 1, and for a duration of 5 beats. Since this duration is longer than the pattern, it will cause the pattern to loop.

To create an empty melody:

1
var bassLine = melody(1 bar);

Alternatively, we could use a different measurement unit to specify the length of the melody:

1
2
var bassLine = melody(16 beats);
var leadMelody = melody(96 ticks);

To make a duplicate of a melody:

1
var copyOfMelody = bassLine.copyAll();

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

1
2
3
4
5
6
var source = melody(8 beats)[
    [2] for 8 beats,
    [3] for 8 beats
];
var destination = melody(16 beats);
destination.overwrite(source, 3 beats);

The example above creates a result that is the equivalent of writing:

1
2
3
4
5
var destination = melody(16 beats)[
    [-] for 3 beats,
    [2] for 8 beats,
    [3] for 8 beats,
];

An alternative way to overwrite part or all of another melody:

1
2
3
4
5
var myMelody = melody(16 beats);
melody.createNotes()[
    [2] for 8 beats,
    [3] for 8 beats
];

Providing Requirements For Automatic Composition

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

  • noteRange specifies the total span between the lowest and highest pitches in the melody
  • silenceSum specifies the total amount of silence within the melody
  • silenceMax specifies the maximum amount of consecutive silence within the melody
  • durationMode specifies the statistical mode of the note durations within the melody
  • shortestNote specifies the shortest note within the melody
  • longestNote specifies the longest note within the melody
  • intervalMedian specifies the statistical median of intervals between consecutive notes

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 myMelody = melody(1 bar);
    myMelody.compose({ noteRange : 0.3, silenceSum : 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 myMelody = melody(1 bar);
    myMelody.compose();
};

Preparing a melody for playback

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

1
2
3
4
5
tracks {
    var inst1 = autoDesigner({ name : "Phat Bass", type : "bass" });
    inst1.design({ pitchBend : 0.9, distortion: 0.12 });
    use (inst1) 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 inst1 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) {
    inst1.compose({ noteRange : 0.3, silenceSum : 0.5 });
};

You will notice that we did not have to create a melody variable inside our composition code block. Instead, Modulo1 has allowed us to converted the inst1 variable into a melody 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
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
tracks {
    var inst1 = autoDesigner({ name : "Phat Bass", type : "bass" });
    inst1.design({ pitchBend : 0.9, distortion: 0.12 });

    var inst2 = autoDesigner({ name : "Powerful", type : "lead" });
    inst1.design({ pitchBend : 0.2 });

    use (inst1, inst2) in (composition);
};

composition (1 bar) {
    var ending = melody(2 beats)[
        [3] for 1 beat,
        [4] for 1 beat
    ];
    inst1.compose({ noteRange : 0.3, silenceSum : 0.5 });
    inst1.overwrite(myMelody, 12 beats);

    var pattern1 = innerMelody()[
        [$] for 1 beat,
        [$+1] for 1 beat
    ];

    var r1 = between 4 to 6;
    var r2 = between -1 to 1;
    inst2.createNotes()[
        [A=?] for B=r1 beats,
        [$=A+1] in pattern1 for B+r2 beats,
        [A-1] for B+r2 beats,
        [$=A+1] in pattern1 for B+r2 beats,
        [A-1] for B+r2 beats
    ];
};

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 melody. 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 = inst1.beatsPerBar;

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

1
inst1.beatsPerBar = 24;

totalTime

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

1
var result = inst1.totalTime;

type

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

1
var result = inst1.type;

Constructor

melody( musicalTime duration )
melody( musicalTime duration )[ ... ]

The melody constructor creates a new melody with the specified duration. It is also possible to attach a melody literal.

1
2
3
4
5
6
var myMelody = melody(1 bar);
var myMelody2 = melody(1 bar)[
    [2] for 1 beat,
    [3] for 1 beat,
    [4,5] for 3 beats
];

Functions

compose( object requirements )

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

1
inst1.compose({ silenceMax : 0.2 });

copyAll()

The copyAll function will return a new melody that contains a duplicate of all notes in the melody

1
var copyOfMelody = inst1.copyAll();

copy( musicalTime start, musicalTime totalToCopy )

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

1
var copyOfMelody = inst1.copy(4 beats, 8 beats);

createNotes()[ ... ]

The createNotes function allows notes to be written to an existing melody.

1
2
3
4
5
myMelody.createNotes()[
    [2] for 1 beat,
    [3] for 1 beat,
    [4,5] for 3 beats
];

distribute( melody other1, melody other2, ... )

The distribute function copies the notes of a melody,then pastes each small note onto one of several other melodies that are provided as inputs.

1
myMelody.distribute(synth1, synth2, synth3);

eraseAll()

The eraseAll function erase all notes within a particular melody

1
inst1.eraseAll();

erase( musicalTime start, musicalTime totalToErase )

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

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

fillWithNotes()[ ... ]

The fillWithNotes function is similar to the createNotes function, except that it will repeat the inputted notes as many times as will fit in the melody

1
2
3
4
myMelody.fillWithNotes()[
    [5] for 2 beats,
    [-] for 1 beats
];

joinAtEnd( melody source )

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

1
myMelody.joinAtEnd(otherMelody);

joinAtStart

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

1
myMelody.joinAtStart(otherMelody);

makeContrast( number amtOfContrast )

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

1
var result = inst1.makeContrast(0.67);

overwrite( melody source, musicalTime startPosition )

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

1
inst2.overwrite(inst1, 12 beats);

reverse()

The reverse function does exactly what its name implies: it reverse the melody's notes

1
inst1.reverse();

rotate( number amtToRotate )

The rotate function shifts the notes within a melody (either left or right) according to the input number which can be either positive or negative.

1
inst1.rotate(-5);

replaceAll( melody source )

The replaceAll function replaces the notes in one melody with the notes of another melody. If the source melody is too short to fill the entire destination melody, then the melody will be repeated until it does fill the space

transposeAll( number amtToTranspose )

The transposeAll function shift the notes within a melody (either up or down in pitch) according to the input number which can be either positive or negative

1
inst2.transposeAll(3);

transpose( number amtToTranspose, musicalTime start, musicalTime total )

The transpose function shifts some the notes within a melody (either up or down in pitch) according to the input number which can be either positive or negative. The function begins with the first note after the time identified by start, and continues for a duration identified by total

1
inst2.transpose(3, 12 beats, 15 beats);

tryTransposeAll( number amtToTranspose )

The tryTransposeAll function is the almost the same as the transposeAll function, except that it takes extra care to prevent you from causing shifted notes from exceeding the range of the instrument

1
inst2.tryTransposeAll(3);

tryTranspose( number amtToTranspose, musicalTime start, musicalTime total )

The tryTransposeAll function is the almost the same as the transpose function, except that it takes extra care to prevent you from causing shifted notes from exceeding the range of the instrument

1
inst2.tryTranspose(3, 12 beats, 15 beats);