For this assignment you will create a program that can read a textual music notation file and synthesize a digital representation of a simple stringed instrument playing the music. Computers represent audio as a series of discrete samples. The program will use the Karplus-Strong string synthesis algorithm to generate the samples.
The Karplus-Strong algorithm heuristically generates samples that model a simple stringed instrument, such as a harp, piano, or guitar. The algorithm begins with a linked list of randomly generated numbers between -1 and 1. The length of the linked list determines the pitch of the sting. The first item in the linked list is the next sample to produce. When you produce a sample from the linked list, you will remove the first element. The algorithm assures that the list stays the same size by appending a new value to the end of the list. It does this by computing the average of the produced sample with the new first value in the list. That average is then multiplied by a decay (a value between 0 and 1), and then adding the decayed average to the end of the linked list.
Create a program that creates a wave file of the music in a text file specified as a command line argument. The program should use the karplus-strong algorithm to generate the sound samples. The first line of the music file is the header that specifies information about the rest of the file. The header line consists of two space separated integers. The first integer is the number of beats per minute to play the rest of the file and the second integer is the total number of beats of the music in the file. All subsequent lines of the music file specify notes to be played. A note line consists of an integer and a real number separated with a space. The integer is a note to be played. The real is the number of beats to wait until playing the next note. The note 0 is concert A, or 440Hz. A positive note with value n is n steps higher in pitch than concert A. A negative note with value -n is n steps lower in pitch than concert A. Note that the relationship between the difference in pitch of two notes and frequency is not linear. The frequency of any note can be computed from the number of steps it is from concert A using the following equation:
440 * 2n / 12
Where 440 is the frequency, in Hertz, of concert A, n is the number of steps that a note is away from concert A, and 12 is the number of steps in an octave. The following is an example of a music file that contains four ascending notes played one beat apart:
120 4
-1 1.0
0 1.0
1 1.0
2 1.0
To generate the samples for a single note, first compute the number of samples in the Karplus-Strong linked list by dividing the number of samples per second by the frequency of the note to play. A good sample frequency to use 44,100 Hertz, or 44,100 samples per second. Then generate samples by repeatedly getting the first element of the linked list then adding to the end of the linked list according the the Karplus-Strong algorithm. A good decay is 0.996. The number of times that samples are generated is determined by the duration of the note as specified in the music file. Note that you will have to convert the number of beats to a number of samples by using the sample rate and the number of beats per second of the music file.
The file sound.py contains a class called Sound
that can
be used to create audio files. Samples generated with the
Karplus-Strong algorithm can be added to a sound with the add_sample
method. Once all of the samples have been added, the samples can be
written to a wave file using the write method.
Once you've tested your program on simple examples like the one above, you can test your program on the file test_song_1.
Submission: Submit your code as a zip file with your name as the zip file name on the course Inquire site.
Chords: Music files can specify that multiple notes be played at once by specifying that the wait until the next note is 0 beats. The following is an example of a music file that plays a C major chord for four beats:
120 4
-2 0.0
-5 0.0
-9 4.0
In order to play multiple notes simultaneously, you will need a linked list for each of the notes. Each should be initialized to the length that corresponds to the note's frequency. Then, sum the result of the samples generated from all of the linked lists Note that the sum may be outside of the range of -1 to 1. If this occurs, just clamp the value into the appropriate range. That is, if the sum is greater than 1, set the sum to 1, or if it is less than -1, set the sum to -1.
Once you've tested your program on simple examples like the one above, you can test your program on the file test_song_2.
Compose or Transpose: Write your own song in or transpose an existing song to the text music file format. Be prepared to play your composition in class.
Visualize: If you are terribly disappointed that this assignment does not have any graphics, you can add some. Create a program that displays the waveform of the sample array that corresponds to a music file that is generated with your program. Each sample can be represented as a point in two dimensions where the sample value is the y value and the depth in the linked list is the x value. Note both the x and y values will have to be scaled so that they fit the window dimensions. Create a window that is wide but not tall. The sample points can then be drawn to the window by either drawing a small oval for each, or drawing lines connecting neighboring samples.