Each time when a key is pressed the respective LED is enabled immediately after key identification. After the key is released, the LED is disabled. While playing a tune, the LEDs are enabled by calling a user-defined function which is declared as ‘key_led()’. The audio output in the below video shows the difference between one octave to the next octave and shows the pressed key through its LED.
After detecting a key press, if the key is not within 1 to 12 then, the functional keys are verified through a ‘switch statement’. The cases 15th and 16th are the keys 15 and 16 which are dedicated to raise and lower the octave value. A decade counter is used to show the currently selected octave. If a microcontroller with a higher number of I/O Pins is chosen then, this decade counter can be eliminated and indicated directly from the I/O Pins of the controller.
Program Snippet to Show the Present Octave
If Key 15 is pressed then, the octave value is incremented up to 8 otherwise if the key 16 is pressed then the octave value is decremented up to 0. After every incrementation/decrementation, a Master Reset signal is given to the decade counter and a series of counting pulses equal to the updated octave value is given to the decade counter thus, showing the current value of the octave on a Bar-Display. Then after, if any one of the 12 notes is pressed then, the frequency corresponding to this note and the updated octave is played.
Every tune contains some finite number of notes played in a cyclic manner. The note cycle has four bytes of information.
Octave of the note
The note itself
Duration of note
Time Gap that should be given before playing the next note
By placing such note cycles one after the other a tune can be formed. For a single tune, a one-dimensional array can be used to store a tune. The 0th location of the array contains the total number of locations used in the array to know the end point of the tune.
The first location of a note cycle contains the octave of the note and the second location contains the actual note number. Now, the third location contains a value proportional to the duration i.e.., duration=1 indicates the timer counter has to count from 0 to 255 with a clock frequency of Fcpu/256. Similarly, the fourth location contains a proportional value of time gap between two notes. The minimum value of these durations is 1.
Program Snippet to Start a Tune
When the command is received to play a tune, the first note is read from 1st and 2nd (octave and the note) locations of the respective tune array and duration of the note is loaded in the ‘duration’ variable from the third location of the array. Now, the Timer2 is started and the processor enables the column containing the stop button and stays in a ‘do while’ loop until the tune is completed. An interrupt is generated every time after counting 255 and a temporary variable ‘temp3’ is incremented in the interrupt sub-routine and verifies whether this temp3 is equal to duration. If temp3 is equal to duration then the Timer2 is stopped and also Timer1 is stopped to stop playing the note. Now, the temp3 variable is reset to 0 and the fourth location of the ‘tune’ array is read to get the duration between the first note and the second note. Again the Timer2 is started verifies for temp3 equal to duration. If found equal then the Timer2 is stopped temp3 is reset and the cycle is repeated to play the next note. This repetition continues until the tune_index is less than the tune’s array size (the value in the zero location of the tune). The interrupt sub-routine to play the tune continuously after starting is shown below.
Program Snippet to Continue the Tune
When the tune_index reaches the end location of the tune, the variable ‘end_tune’ is updated to a value 1. This ‘end_tune’ variable is verified to come out of the ‘do while’ loop. The ‘do while’ loop also monitors the stop button while the tune is played. If the stop button is pressed while a tune is being played, the ‘do while condition’ becomes false and the tune is stopped playing by stopping the timers.
In the present program, the ‘Happy birthday’ tune is programmed to the tune array. We can add desired tunes in tune, tune and so on.., by defining them in declaration part. The number of programmed tunes is considered while incrementing and decrementing the ‘tune’ variable in the instructions related to the ‘Next’ and ‘Previous’ tune buttons.
To record a tune, the procedure followed to play a tune is followed in reverse direction i.e.., the four bytes of note cycle are read from the inputs given by the key switches to the microcontroller. When a key is pressed the frequency is generated as usual. To record the information a timer is started and then, the octave of the note, the key number is written to the ‘tune’ array. Here, we are writing the recording tune in ‘tune[tune_index]’. After the key is released, the timer/counter is stopped and the count value is stored in the array. Then again the timer is started to get the time gap between first key press and the second key press (the silent duration). When the second key is pressed, the timer is stopped and the count value is stored in the array. The array index is incremented whenever required. Thus, the information for one note cycle is read and written in the RAM of the microcontroller.
After playing the desired ‘tune’, the user will press the stop button. The user can preview (prelisten) the played tune and if satisfied with the tune can be saved by pressing the save button.
The TIMER0 is used to count the durations. The Timer0 is operated at Fcpu/256 (Prescaler=256) and the Overflow Interrupt is enabled. Each time when the overflow occurs, the variable timer0 is incremented by 1 in the Timer0 Overflow Interrupt sub-routine. The register values for the duration counters to play a tune and record a tune in hexadecimal format are given below,
TCCR2=0X0E for Timer2
TCCR0=0X04 for Timer0
The save option will write the tune in the EEPROM. The tune index is written in the 0th location of the tune array. A for loop runs for a number of times equal to this tune_index and writes the tune array into the EEPROM. While POWER ON or restart this tune is copied into the tune array. This tune remains in the EEPROM until another tune is recorded. The program can be easily modified to store multiple tunes by using a microcontroller with sufficient RAM and EEPROM sizes.
Program Snippet to Save a Tune
The speaker used here is the headphones, but this can be connected to an amplifier. One of the possible amplifier circuits is present in the article here:
The program file is provided below which is compiled by using the ‘winavr’ open source software.