If you love Arduino and have a rudimentary understanding of music then why not have some fun with Let's Make Music?
With a very basic kit list and using very few, very short and basic functions, it is possible to craft quite detailed musical scores to play on a simply configured Arduino and speaker. The results can be quite startling and rewarding!
CapabilitiesTo give you some idea of the 'lets make music' sketch capabilities, I crafted several quite complex scores by a number of legendary popular artists all of which played faithfully. Unfortunately, I am not able to publish these due to copyright. I have, though, included other examples out-of-the-box (OOTB) which provide helpful demonstrations of capability and guidance to help you get started on your own scores.
The HardwareCouldn't be simpler - all you will need is an Arduino microcontroller (any should be okay), a 100 ohm resistor, a small bread board, some wires and, preferably, an 8 ohm 0.25 watt speaker (or a buzzer if you don't have a speaker to hand).
The wiring diagram is as shown below at Schematics, but it is a very simple circuit to put together. The sketch uses digital output pin 11 to drive the speaker, but if you wish to use another, then choose a suitable alternative pin and change the definition '#define speaker
' in the sketch as required.
I should advise that the microcontroller should not be directly connected to anything but a speaker/buzzer as outlined, otherwise you may damage your microcontroller.
Let's Make MusicThe sketch includes everything needed to put together your own musical scores and compositions using basic and standard musical terms and concepts. For example, the sketch includes a comprehensive set of standard tempos, preset notes covering several octaves and common note/rest time values based on the selected tempo.
The sketch provides five functions you can use for creating and playing music. These are:
set_tempo - sets the tempo (pace) of a musical piece in beats per minute (i.e. crotchets per minute), egset_tempo(allegro)
,set_tempo(144)
, etc. The value of a crotchet is calculated as 60/tempo seconds. All other note durations are then determined from this calculated value. At sketch start up, the tempo is set to thedefault_tempo
(animato
), or 120 beats (crotchets) per minute). The tempo will remain at this setting until changed by the user code.
play - will play the given note for the given duration, egplay(note_C4,
minim)
will play middle C for the duration of a minim,play(note_FS2,
crot + quav)
will play F2 sharp for the duration of a crotchet + a quaver, etc. Of course, how quickly these play depends on the tempo set.
rest - rests for the given duration, during which time silence is maintained, egrest(quav)
,rest(0.5)
, etc. Remember that rests also work with respect to the current tempo.
trill - performs a trill with the given two notes continually one after the other for the given duration, egtrill(note_C4, note_CS4, minim)
,trill(note_F3, note_E3, crot)
, etc. By default and OOTB, the trill performs eight note changes per crotchet, or part thereof, depending on the trill duration given in its function call, irrespective of the tempo set. If fewer or more note changes per crotchet are required then reset the definition '#define
trills_per_crotchet
'. Remember that trills also work with respect to the current tempo.
wait - waits for the given duration (seconds or part thereof), egwait(5.5)
waits for 5.5 seconds,wait(minim)
waits for the duration of a minim, etc. This function is used by the play and rest functions but it may also for used in the end user code.
Let's look at an example of a familiar tune (Happy Birthday):
// Happy_Birthday
set_tempo(default_tempo);
// bar 1
play(note_G3, quav);
play(note_G3, quav);
play(note_A3, crot);
play(note_G3, crot);
// bar 2
play(note_C4, crot);
play(note_B3, minim);
// bar 3
play(note_G3, quav);
play(note_G3, quav);
play(note_A3, crot);
play(note_G3, crot);
// bar 4
play(note_D4, crot);
play(note_C4, minim);
// bar 5
play(note_G3, quav);
play(note_G3, quav);
play(note_G4, crot);
play(note_E4, crot);
// bar 6
play(note_C4, crot);
play(note_B3, crot);
play(note_A3, crot);
// bar 7
play(note_F4, quav);
play(note_F4, quav);
play(note_E4, crot);
play(note_C4, crot);
// bar 8
play(note_D4, crot);
play(note_C4, minim);
To notice about the above example is:
- the tempo for the score has been set to
default_tempo
(120 beats per minute)
- the score has been structured in bars. In this example there are three beats to the bar (3/4 time). This is a helpful approach when you are entering your own scores as it allows you to follow the music and easily pick out the incorrect notes and/or time durations that you will undoubtedly introduce in error!
- the notes are played one after the next (single channel design) referencing note values from the sketch's note definitions (see the Crib Sheet for a readily accessible list of these and other useful bits and pieces)
- each note is played for a specified time/duration using standard musical notation, eg
crot
(chet),minim
,quav
(er), etc. Again, use the Crib Sheet for easy access to these.
Let's look at another short example, one you will know when you play it:
set_tempo(default_tempo * 2); // lively pace
// 3/4 time
// bar 1
play(note_C4, minim);
play(note_G3, crot);
// bar 2
play(note_G3, crot);
play(note_GS3, minim);
// bar 3
play(note_G3, minim);
rest(note_crot);
// bar 4
play(note_B3, minim);
play(note_C4, crot);
set_tempo(default_tempo); // reset tempo
To notice about the above example is:
- we up the tempo to twice the default - 240 beats per minute
- we introduce a short
rest
in the middle of the piece at bar 3 worth onecrot
(chet)
- we reset the tempo to default when the piece finishes.
And, finally in this section, let's look back at Happy Birthday, this time we will add a trill at the start of each odd bar:
//_Happy_trill_Birthday
set_tempo(default_tempo);
// 3/4 time
// bar 1
trill(note_G3, note_GS3, quav);
play(note_G3, quav);
play(note_A3, crot);
play(note_G3, crot);
// bar 2
play(note_C4, crot);
play(note_B3, minim);
// bar 3
trill(note_G3, note_GS3, quav);
play(note_note_G3, quav);
play(note_A3, crot);
play(note_G3, crot);
// bar 4
play(note_D4, crot);
play(note_C4, minim);
// bar 5
trill(note_G3,
play(note_G3, quav);
play(note_G4, crot);
play(note_E4, crot);
// bar 6
play(note_C4, crot);
play(note_B3, crot);
play(note_A3, crot);
// bar 7
trill(note_F4, note_FS4, quav);
play(note_F4, quav);
play(note_E4, crot);
play(note_C4, crot);
// bar 8
play(note_D4, crot);
play(note_C4, minim);
It is the same melody we are used to but with a little twist. The trill function is a simple implementation of a musical trill, but should provide some interest in your scores.
Hopefully, by this point you understand the principles being applied?
A Few Useful Pointers and Techniques...Triplets
A triplet is a single beat divided into three notes of equal time duration and is normally indicated by a '3' above the triplet notes. This is easily modelled, for example if we have the following three notes defined as a triplet - A3, AS3 and B3 and worth one crotchet then we would simply transcribe this as:
// crotchet triplet
play(note_A3, crot/3);
play(note_AS3, crot/3);
play(note_B3, crot/3);
The same idea would apply for any division.
Ties
A tied note is a musical notation represented by a curved line that connects two notes of the same pitch. In a tie, the second note is not played but its duration value is added to the first note. So, for example, if a score shows two tied notes, say note_AF3
both with duration of a crotchet then we would represent this as play(note_AF3, crot + crot)
, or play(note_AF3, minim)
. The first representation is better as it infers we are playing a tied note. Be aware that tied notes can often extend from the end of one bar to the next. In these instances, it helps to mark this in any comment you include describing the bar/s.
Another example might be two notes, say note_F4
the first with a duration value of a crotchet and the second with a duration value of a quaver. We would represent this as play(note_F4, crot + quav)
. And so on.
Compounded Note and Rest Durations
We saw in the above techniques how we were able to compound and manipulate note durations to meet the precise needs of a score to represent triplets and ties. This same technique is equally applicable to the rest
and trill
functions where note durations are required. In fact, any arithmetic combination is permissible so long as it makes sense, for example play(note_D5, minim + crot + quav)
, rest(crot + quav)
, trill(note_G2, note_GS2, dot_minim + quav/2)
, etc.
Tempos
Whilst the sketch provides a list of standard tempo definitions, any value may be specified by the set_tempo
function. So, if you need a tempo that is not in the standard list then simply specify your own. For example set_tempo(95)
which is between maestroso
(88 beats per minute) and moderato
(100 beats per minute), and so on.
Repeats
Often a section of a score is repeated. Rather than duplicating the same set and series of notes/rests it can be helpful to use labels and the (dreaded) goto
statement. To do this it will be necessary to detect if a section of the score has already been repeated or not. This can be managed using a simple variable which has one of two states - 'false
' if the repeat section has not yet been repeated or 'true
' otherwise. For example:
...
bool repeated = false;
...
repeat_1:
play(note_B3, crot);
rest(quav);
play(note_C4, quav);
play(note_D4, crot+quav); // dotted crotchet
play(note_C4, quav);
...
If (repeated == false){
repeated = true;
play(note_A3, crot);
rest(crot);
play(note_C4, crot);
play(note_D4, crot);
goto repeat;
}
// continue with the rest of the score...
My apologies to the purists amongst you but in this instance the use of a goto
is simple, clear and easily implemented, even with scores having multiple repeated sections. Feel free to make use of do/while or while constructs if these fit your needs better.
Transcribing a Score
It helps to be orderly and structured when coding a musical score into music commands. Work bar by bar according to the score's time signature, commenting each bar with its bar number and adding any other comments that may be helpful. This approach assists in debugging your code as I can guarantee that you will introduce incorrect notes and/or note/rest durations. Listening to the score playing for accuracy whilst reading through the code bar by bar soon identifies where things are wrong.
Out of the BoxThe Let's Make Music sketch includes all of the musical data and functions needed to put together your own scores. Also included are a number of musical score examples so you can see how the various sketch functions are used and also so that you may play music on your Arduino straight away.
The example scores are provided as functions, for convenience, and may be referenced directly from the main void loop in any order. If assembling several score functions, one after the other, then add a wait(..)
function call between each so that you get a short break between one ending and the next starting, eg wait(3)
will wait for three seconds.
The musical score functions provided OOTB are:
middle_C
twinkle_twinkle
(little star)
silent_night
jingle_bells
happy_birthday
ditty_1
door_bell_1
door_bell_2
ode_to_joy
(Beethoven)
canon_in_D
(Pachelbel)
scarborough_fair
The Let's Make Music sketch is all about having some fun making music with Arduino. It is not overly sophisticated, but provides a few simple and basic commands that yield very good results. Download and print the Crib Sheet as a handy and quick reference to all of the sketch's musical data definitions and functions.
Above all, enjoy making music on Arduino and have fun!
Further ReadingYou might also find these contributions interesting and useful, by the same author:
- Use unlimited timers - a simple method for creating any number of non-blocking timers in your sketches
- Add a heart beat to your sketches - include a visible means to see that your code is running on your microcontroller without any additional components or wiring!
- A Music & Lights Workbench - designed to introduce those new to computer programming to the subject, using easy commands with cool effects. The approach is one of tutor and student
- A flexible, scalable library (ez_SIPO8_lib) - supporting the implementation of multiple serial-in/serial-out ICs, 74HC595, either individually or in cascaded banks, up to 255 ICs (2040 output pins)
- A general switch library (ez_switch_lib) - suitable for most switch types and wiring schemes, incorporating novel features
- UnderstandIng and UsIng Button SwItches, the basIcs - button switches, a simple but often tricky piece of kit. The tutorial provides the ins and outs of implementing a simple button switch, with flexibility to explore differences in circuit design, different reading methods and debouncing.
- Interrupts Driven Button Switches - an approach and example of tying a button switch to an external interrupt.
- Toggle Switches - how to reliably read a toggle style switch.
- Buttons & Lights Game - a bit of fun using button switches and LEDs.
- External Interrupts - a generic framework supporting concurrent asynchronous multiple interrupts. Configure multiple external interrupts with different characteristics and add code to provide post-interrupt asynchronous processing.
- REM_SYS, A Programmatic Timed Reminder Alerting - a programmatic framework for both elapsed and real-time asynchronous timed alerting. Define any number of timer (reminder) alerts (sub second to hours) and process asynchronously.
Comments