Sound is simply a vibration of the air molecules around you. Pleasing sounds have some nice structure to the vibrations. These are continuous structures, but our computers are inherently discrete. For a computer to play music, we must create a discrete representation of the continuous wave. We do this by generating a series of samples of the wave. 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. 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 by creating a list of randomly generated floating point numbers between -127 and 127. The length of the list determines the pitch of the string. The length of the list can be computed by dividing the sample rate, 44,100 samples per second is a commonly used sample rate in audio files, by the frequency of the note to be played.
$$list\_length = \frac{sample\_rate}{note\_frequency}$$
To generate the samples of a note, repeatedly remove the value contained at the front of the list. This is a single sample of the note. In order to maintain the pitch of the note, a new value must be added to the end of the list. According to the Karplus-Strong algorithm, the value added to the end of the list should be the “decayed” average of the produced sample, the value just removed from the list, and the value that is now at the front of the list. To do this, multiply the average of the two values by a floating point value less than one. A decay of .996 is a good value to start with.
$$new\_list\_value = \frac{produced\_sample + new\_front\_value}{2} \times decay$$
The above process will create one sample of the note to be played. Each note must have many samples generated. For example, assuming there are 44,100 samples per second, it takes 44,100 samples to create 1 second of audio. The following equation can be used to compute the number of samples needed for a note given the beats per minute and the note duration in beats:
$$number\_of\_samples = \frac{60}{BPM} \times note\_duration \times sample\_rate$$
Details
Create a program that converts a textual music file to an au audio file using the Karplus-Strong algorithm to generate the sound samples.
The text music file lists the pitch and duration of all of the files to play. The first line of the text music file is a 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 number specifies 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. 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
Using the above encoding technique, the frequency of any note can be computed from the number of steps it is from concert A using the following equation:
note_frequency = 440 × 2(n/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.
An au file is a simple sound file that encodes audio in binary. The header of an au file is the following integers in 8-bit binary:
46 | 115 | 110 | 100 | 0 | 0 | 0 | 24 | 255 | 255 | 255 | 255 | 0 | 0 | 0 | 2 | 0 | 0 | 172 | 68 | 0 | 0 | 0 | 1 |
After the header, each sample of the audio is a single, signed, 8-bit, binary integer in the range -127 to 127.
The program must use a class that implements a circular array for the list of values used by the Karplus-Strong algorithm. The class must have the following functions:
// default constructor, intializes the array with size 8
CircularArray();
// add element to the end of the array, if array is full double size
// before adding element
void CircularArray::push_back(double element);
// remove and return the element at the front of the array
double CircularArray::pop_front();
// return a copy of the element at the front of the array
double CircularArray::peek_front();
The program should use command line arguments to read both the name of the input file and the name of the output file. For example:
./music_synthesis test_song_1.txt test_song_1.au
Once you’ve tested your program on simple one or two note examples, you can test your program on these files from previous years.
- test_song_1.txt (Scotty)
- test_song_2.txt (Andrew and Nora)
- test_song_3.txt (Sean)
- test_song_4.txt (Ryan)
Extra Credit
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 list for each of the notes. Each list should be initialized to the length that corresponds to the note’s frequency. Sum the result of the samples generated from all of the 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.txt.
The music text file was created for this assignment, so it is not possible to download files test your program on. If you know how to read music, transpose a song into the music text file format and I will use it as the exaple files for this assignment next year.
Collaboration
You may collaborate with a partner on this assignment. If you do, both partners must work on the same computer and actively contribute to every line of code. Only one partner needs to submit code, but both partners names should be in the code.
Grading
The project will be graded according to the following weights:
20% - Style, design, and documentation
20% - Test cases (and assertions)
10% - Has a circular array class with all above functions in a separate file
10% - Reads a musical text file specified with a command line argument
10% - Uses Karplus-Strong to create samples
10% - Can create samples for multiple notes
20% - Writes converted audio to an au file
Submission
Submit an h and cpp files containing your circular array class and a cpp file containing your test code for the class on the course Inquire page before class on Monday, April 2nd.
Submit a cpp file containing your final program on the course Inquire page before class on Monday, April 9th.