The first four ominous notes of the Fifth Symphony are well known: da-da-da-dum! That musical idea seems so obvious now, but think just for a moment about how radical it was and is. First, this is a symphony that doesn’t begin with a note. It begins with a rest, an eighth note rest. It begins with a moment of silence. Second, the motif is only four notes, but the first three notes are the same note, the G, repeated three times. The fourth note, the Eb, is held until the conductor motions to continue. The orchestra is barely two bars into the music when the conductor stops the performance. Finally, if you have the entire score, you can see that every instrument plays those exact same notes (G, G, G, Eb) and does so at ff.
So, Beethoven notated a very loud repetitive group of notes to be played by an orchestra in unison – and, based on the tempo, to play it very fast. He started with silence and, two bars into the symphony, stopped the entire orchestra. And that’s why Beethoven was a genius. That’s why we’re starting with Beethoven. Because… Beethoven.
MUSICAL SPECIFICATIONS
Although we are living in the age of machine learning algorithms in which smart electronic devices adapt and evolve, every “computer” starts as just a box of silicon chips, plastic, and a bit of metal. To get the Arduino to play a few notes of Beethoven, we need to provide it explicit instructions. We need to program the Arduino to incorporate everything we just described that makes this motif unique:
- Which notes to play? (G, G, G, and Eb)
- How long to play each? (1/8, 1/8, 1/8, half-note)
- What tempo to play the notes? (108 half-notes per minute)
- How loud to play the notes? (ff)
- When not to play? (at the start, for an eighth-note length)
Each of these parameters must be programmed into the Arduino. Below is a program that does all of this and, with the push of a button, will play the opening motif.
In this program, the musical notes are translated into MIDI note values (G=67, Eb=63). Midi allows for a velocity of 0 to 127. We have assigned ff to a value of 120, almost the highest velocity, but leaving some room for a potential fff in the future.
The tempo is specified as “108” for a half note. That translates into approximately 555ms using the following math:
- Marked tempo = 108 half notes per minute
- Minute = 60 seconds
- 60 seconds / 108 per second = .55555 of a second for a half note
- .55555 of a second = ~556 milliseconds
An eighth note is one fourth of a half note, so an eighth note would be:
556 milliseconds / 4 = 139 milliseconds
Thus, each of the first four events (eighth rest plus three eighth notes) should each play for 139ms each. The half note should play for at least 556ms and more, depending on the fermata.
But there is a problem using those exact values for timing notes. Those precise values assume absolutely no time between notes, a perfect legato, one note blending directly into the next. But the score is not marked legato. So, we have to provide a bit of an offset. In this case, we are using 10 milliseconds. But instead of just adding that time in between the notes, we are subtracting it from the playing time of the note and adding the 10 milliseconds in as silence.
In this program, all of the parameters described are spelled out in detail: individual notes, time each notes is played, time between notes, and dynamics. In terms of MIDI, this is set up to use the Hairless MIDI option using MIDI over serial, but you could make a slight change to send MIDI over a traditional MIDI cable (see here for more on that).
SPECIFICATIONS IN CODE
/* Binary_Beethoven This program does only one thing: Play the opening motif to Beethoven's Fifth Symphony. It demonstrates how to define variables and use them to trigger the notes. LOOP checks for a button press and, if pressed, plays the motif. All of the variables (note, note length, etc.) are fixed -- coded as exact values that will never change. This is a rigid approach to coding a musical idea, but demonstrates which variables are necessary to represent everything in Beethoven's first two bars. For more on this circuit and programs, see: www.amateurmusicology.com Elliot Inman Spring 2016 */ // Create variables to use throughout the program // One digital switch connected to pin D2 // We will use this to trigger the motif int switch1; // Opening Motif notes: 67=G, 63=Eb int note_to_play1 = 67; int note_to_play2 = 67; int note_to_play3 = 67; int note_to_play4 = 63; // Opening Motif note durations // 139 = eighth note // 556 = half note (108 half notes a minute) // Values are reduced by 10ms to allow some time // between the sounding of each note int time_to_play1 = 139 - 10; int time_to_play2 = 139 - 10; int time_to_play3 = 139 - 10; int time_to_play4 = 556 - 10; // variable to provide the 10ms delay between notes int time_between_notes = 10; // Variable to define the eighth note rest int rest = 139; // Variable to define the sustain on Eb int firmata = 556 * 2; // Variable to define the dynamic (scale of 0 to 127) int ff = 120; // SETUP the Arduino with a baud rate and tell // it what to do with various pins void setup() { // Set MIDI baud rate for HAIRLESS MIDI: Serial.begin(115000); // for standard MIDI use 31250 pinMode(2, INPUT_PULLUP); // breadboard button input pinMode(12, INPUT); // On-Off Switch input } // LOOP that will cycle endlessly. void loop() { // Check to see if the ON/OFF switch is on. // If so, proceed. Otherwise, keep checking. while (digitalRead(12) == HIGH) { // Check switches // Because these are INPUT_PULLUP switches, // Pressed = LOW or 0 and Not Pressed = HIGH or 1 switch1 = digitalRead(2); // If switch1 is pressed, do this: if (switch1 == LOW) { delay(rest); // play the eighth note rest // play the 1st g eighth note at ff noteOn(0x90, note_to_play1, ff); delay(time_to_play1); noteOn(0x90, note_to_play1, 0); // stop playing delay(time_between_notes); // play the 2ndt g eighth note at ff noteOn(0x90, note_to_play2, ff); delay(time_to_play2); noteOn(0x90, note_to_play2, 0); // stop playing delay(time_between_notes); // play the 3rd g eighth note at ff noteOn(0x90, note_to_play3, ff); delay(time_to_play3); noteOn(0x90, note_to_play3, 0); // stop playing delay(time_between_notes); // play the Eb half note at ff noteOn(0x90, note_to_play4, ff); delay(firmata); // hold for specified duration noteOn(0x90, note_to_play4, 0); // stop playing delay(time_between_notes); } } } /* noteOn function to play a MIDI note COMMAND 0x90 sends NOTE ON on Channel 1. PITCH is the note from 0 to 127 with 60 as middle C. Keep in mind that some MIDI instruments may not sound outside of their normal real acoustic range. VELOCITY is how hard the note is struck (from 0 to 127), but is often perceived as volume. Using 0 for velocity is the same as turning a note OFF, so that is often used instead of a note OFF command. */ void noteOn(int command, int pitch, int velocity) { Serial.write(command); Serial.write(pitch); Serial.write(velocity); }
BUGS and FEATURES
This program works, but it isn’t very interesting for very long. All of the variables are set to values that can never change. The program will flawlessly execute the same exact pattern of sounds over and over and over… like a machine. It is precisely rendered, but minimally musical. There are many ways to make the program more musical: using a potentiometer to change the speed; using a photocell to control the dynamics; or using an array of sensors to control individual notes – until Beethoven’s original idea is no longer recognizable.
Likewise, there are many ways to make the program more computationally efficient: using a for loop to repeat the repeated notes; rewriting the entire note structure into an array; and creating a more abstract timed event that can be executed as a sound or a silence depending on the requirements of the score.
But this is Beethoven, pure and simple. Because… Beethoven.
Apologies to Max Mathews who explained all of this and more in The Technology of Computer Music in 1969. Everything you need to know in two pages, give or take a couple of lines:
~ WEI 2016
You must be logged in to post a comment.