There seems to be little documentation on how to get the Lego Mindstorms computer to play music. The one example I found explaining how to play Happy Birthday was not too helpful- it explained that you need to have Wait's to make the music work, but the comment saying that you need one every 5 tone blocks does not really explain how/why to use them. After playing around with it for a bit I figured out a model that seems to explain how it works. Understanding this model can help you to make your music programs work.
The two commands you want to use when making music are: the Tone command, and the Wait command. The Tone synthesizer and the Wait processor each have a queue (first in/first out) data structure for the commands that they are given. If there is a command in the queue, it is executed and then removed from the queue. The Tone/Wait commands in a program are actually commands that put a message into the appropriate queue. If nothing else is there, they get executed right away. If there are other things in the queue, they have to wait their turns to be executed.
For all commands but Wait the command is completed when the message is placed in the appropriate queue and the program does not wait for the appropriate processor to take the message out of the queue and execute the associated instruction before it moves on to the next step in the program stream.
The Wait is different. The program stream does not move on to the next step until the associated Wait message has been removed from the Wait queue and the Wait time then passes. There is one exception to this that was causing me great confusion. If you use My Program to play part of a melody and there is a Wait at the end of your sub-program, then the sub-program returns control to the stream that the My Program is inserted into after the Wait message has been placed in the queue. It does not wait until the Wait time has passed. Thus, inserting a My Program in the middle of some instructions does not have the same affect with respect to Wait's as actually inserting the sequence of instructions that corresponds to My Program.
There are two typical styles of hitting notes: tongued (there is a pause between the notes) and legato (notes are slurred together). If you have two of the same note side by side in a song and they are not tongued, it just sounds like one repetition of that note whose time is the sum of the two previous notes. Tonguing is required to make the notes sound distinct. Also, you may prefer the sound of having the other notes in a song tongued as well.
If you want to program a sophisticated song, you need to use My Program blocks to create the pieces and then link the pieces together. The trick to maintain proper tonguing is to use the Wait that you might want to put at the end of a My Program block directly into your main stream of instruction.
To get a quarter note that is tongued you can make a Tone for 0.5 seconds and then you do a Wait for 0.6 seconds to get a break of 0.1 seconds before the next note starts. Eighth notes can be represented by a Tone for 0.2 seconds, and a Wait of 0.3 seconds which again creates a 0.1 break. A half note would be a Tone for 1.1 seconds followed by a Wait for 1.2 seconds. If you prefer the legato sound instead, make the Wait time equal to the Tone time. If you want the music faster or slower, scale things appropriately.
This information is summarized in the following table under the assumption that each beat of the music takes 0.6 seconds time.
Note Length | Tone time | Wait time |
---|---|---|
Eighth Note | 0.2 | 0.3 |
Quarter Note | 0.5 | 0.6 |
Half Note | 1.1 | 1.2 |
Whole Note | 2.3 | 2.4 |
The Main Program for Happy Birthday with tonguing is as follows. I will use a % to indicate that I am including a comment.
Repeat forever My Program: hbd1 % Happy birthday Wait 0.6 My Program: toyou1 % to you. Wait 1.2 My Program: hbd1 % Happy birthday (same as first one) Wait 0.6 My Program: toyou2 % to you. Wait 1.2 My Program: hbd2 % Happy birthday, Wait 0.6 My Program: hbd3 % happy birthday, Wait 0.6 My Program: hbd4 % happy birthday, Wait 0.6 My Program: toyou3 % to you. Wait 1.2 Wait 0.6 % Quarter note rest before repeat. End repeat
The sub-programs hbd1, hbd2, hbd3, hbd4, toyou1, toyou2, and toyou3 are:
My program: hbd1 Tone 526 for 0.2 % Ha- Wait 0.3 Tone 526 for 0.2 % ppy Wait 0.3 Tone 587 for 0.5 % birth Wait 0.6 Tone 526 for 0.5 % day End of hdb1
My program: toyou1 Tone 699 for 0.5 % to Wait 0.6 Tone 660 for 1.1 % you. End of toyou1
My program: toyou2 Tone 785 for 0.5 % to Wait 0.6 Tone 699 for 1.1 % you. End of toyou2
My program: hbd2 Tone 526 for 0.2 % Ha- Wait 0.3 Tone 526 for 0.2 % ppy Wait 0.3 Tone 1047 for 0.5 % birth Wait 0.6 Tone 880 for 0.5 % day End of hdb2
My program: hbd3 Tone 880 for 0.2 % Ha- Wait 0.3 Tone 880 for 0.2 % ppy Wait 0.3 Tone 693 for 0.5 % birth Wait 0.6 Tone 660 for 0.5 % day End of hdb3
My program: hbd4 Tone 466 for 0.2 % Ha- Wait 0.3 Tone 466 for 0.2 % ppy Wait 0.3 Tone 440 for 0.5 % birth Wait 0.6 Tone 349 for 0.5 % day End of hdb4
My program: toyou3 Tone 392 for 0.5 % to Wait 0.6 Tone 349 for 1.1 % you. End of toyou2
The main point is: include Wait's that you would be tempted to include at the end of a My Program block in the main stream of the program instead.
You can rescale the values if you prefer to have the music running at a different pace. For example, if one beat takes 0.8 seconds instead of 0.6, you would use:
Note Length | Tone time | Wait time |
---|---|---|
Sixteenth Note | 0.1 | 0.2 |
Eighth Note | 0.3 | 0.4 |
Quarter Note | 0.7 | 0.8 |
Half Note | 1.5 | 1.6 |
Whole Note | 3.1 | 3.2 |