Lab 5
Get started now
## Overview The Java [`Queue<E>`](http://docs.oracle.com/javase/8/docs/api/java/util/Queue.html) interface declares the interface for the `Queue` data structure. A number of JCF classes implement this interface. In this assignment you will make use of the queue data structure and the Jaffe-Smith algorithm (described below) to synthesize the sound a guitar makes when one of its strings is plucked. ### About Sounds The fundamental tone of a musical note is represented by a sine wave at a specific frequency. The table below gives the frequencies of different notes in the diatonic (common) scale. | Note | A | A# | B | C | C# | D | D# | E | F | F# | G | G# | | ------------------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | **Frequency (Hz)** | 220 | 233 | 247 | 262 | 277 | 294 | 311 | 330 | 349 | 370 | 392 | 415 | Note: The # symbol following the above letters (e.g. A#) means "A-sharp". On a piano, the "sharps" are the black keys. In modern music (which employs something called "equal temperament"), A-sharp is the same note as B-flat, C-sharp is the same as D-flat, and so on, but this was not always the case. Early keyboard instruments actually divided the black keys into two parts that produced slightly different tones. Doubling the frequency gives you the same note one octave higher while halving the frequency gives you the same note one octave lower. Sending a sinusoidal waveform with the appropriate frequency to your computer's speakers will generate the desired tone. The [`play()`](lab5/edu/msoe/taylor/audio/SimpleAudio.html#play-java.util.List-) method from the [`SimpleAudio`](lab5/edu/msoe/taylor/audio/SimpleAudio.html) class accepts a `List<Float>` and sends them to the computer's speakers. In order to do this, we need to chose how often to send a value to the speakers. This is known as the **sampling rate** and typically is in the range of 8,000 to 48,000 samples per second. If we wished to hear the note **middle C** for a quarter of a second at 8,000 samples per second, we would need to generate a sequence of 2,000 samples (if we need 8,000 samples for one second of sound, a quarter second of sound will require a quarter of 8,000, i.e., 2,000 samples). The samples should be such that they produce a sinusoidal waveform that repeats its sinewave 262 Hz (times per second). Therefore, our 2,000 samples will describe a periodic sinewave that completes 65.5 cycles (262/4). Such a waveform can be seen in the first plot below. If you were to listen to this sound, it would be the "hum" a tuning fork makes when it is struck. The second plot is this sound **decaying** (getting softer or dying away) over time. <figure>[![Sound Waveforms](lab5.png)](lab5.png)<figcaption>Figure 1: Sound Waveforms</figcaption></figure> ### Jaffe-Smith Algorithm The Jaffe-Smith algorithm makes use of a queue data structure to generate a waveform that simulates the sound made by plucking a string on a guitar. The waveform of a guitar plucking sound is not purely sinusoidal - it is more complex, but still periodic. The Jaffe-Smith algorithm needs to know the duration of the sound, the desired frequency of the simulated guitar string, the sampling rate, and something called the decay rate (which controls how quickly the sound fades to silence). To begin, you must initialize an empty `List<Float>` instance, `samples`, that will hold the waveform generated by the Jaffe-Smith algorithm. Then for each note in the Guitar object, the following must be done: #### Initialization Phase The algorithm begins by: 1. Calculating the number of samples per period, `samplesPerPeriod`, as the sampling rate divided by the desired frequency. 1. Calculating the number of samples, `numberOfSamples`, as the sampling rate multiplied by the duration of the sound. Keep in mind that the sampling rate is in units of samples per second and the duration is in units of milliseconds. 1. Initialize a `Queue<Float>`. This queue, `periodSamples`, is populated with **n** random floating point values between -1.0 and 1.0, where **n** is `samplesPerPeriod`. Don't worry about why for now, we'll get to that later. 1. A variable called `previousSample` is initialized to zero. At this point, the `periodSamples` queue contains one period of the sound waveform. #### Looping Phase If the `periodSamples` queue contained one period of a 262 Hz sinusoidal waveform, it would be easy to generate 65.5 cycles of the waveform (like in the example above) by looping `numberOfSamples` times where each time through the loop we would take the sample off the front of the queue, add it to the back of the `samples` list and also add it to the back of the `periodSamples` queue. In the process of doing this, we would have copied one cycle of the sinusoid into the `samples` list 65.5 times. However, instead of just adding the samples from the `periodSamples` queue onto the back of the `samples` list, the algorithm calculates a new sample value by multiplying the `decayRate` by the average of the current sample and `previousSample`. The looping phase begins with an empty `samples` list and ends with a full `samples` list. The loop runs `numberOfSamples` times and each time through the loop the following are done: 1. Dequeue the current sample off of the `periodSamples` queue. 1. Calculate the new sample value as the product of the decay rate and the average of the previous sample and current sample. 1. The new sample value is enqueued on the `periodSamples` queue and added to the `samples` list. 1. The `previousSample` variable is given the value of the current sample. (Note: the current sample is the value that was dequeued at the beginning of the loop, not the newly calculated value.) Once the looping phase has been completed for each note, the `samples` list contains all of the samples to be sent to the speakers. ### Jaffe-Smith Algorithm Intuition It is not critical to understand the algorithm in order to complete the lab assignment; however, a brief (and simplified) description follows. The `periodSamples` queue stores one complete period of the waveform to be repeated. As discussed previously, if the `periodSamples` queue stored one cycle of a sinusoidal wave, then a waveform representing a note of a given frequency and duration could be generated by just making multiple copies of the samples found in the `periodSamples` queue. The Jaffe-Smith algorithm does two things differently: 1. The samples stored in the `periodSamples` queue are random in nature instead of a nice sinusoidal wave. 1. The samples are modified slightly each time through the queue. #### Randomized Starting Point for periodSamples At first glance it may seem rather odd to use a bunch of random values as the starting point for the periodic waveform since the random nature of the values may generate additional frequencies within; however, consider the initial pluck of a guitar string. A guitar pluck begins with an abrupt plucking of a string followed by a duration of time when the string "finds" its resonant frequency, i.e., the frequency at which it likes to vibrate. Immediately after the guitar string has been plucked, it is not vibrating at its resonant frequency. Therefore, it does not seem unreasonable to begin with random values. If we were to eliminate the second change made by the Jaffe-Smith algorithm, the waveform would still produce the desired note, but the sound would be distorted. The third waveform in graph above, titled "Random Samples with No Feedback Loop" shows an example of such a waveform. #### Feedback Loop The second thing that the Jaffe-Smith algorithm does is modify the value of each sample slightly each time the sample is encountered again. There are essentially two things going on here: 1) the amplitude of the waveform is gradually reduced by the decay rate (set to 0.99 for the example graphs above); and 2) the waveform is becoming smoother as a result of the current sample being averaged with the previous sample. The averaging with the previous value makes the curve smoother because two neighbors that have similar values retain their similar value while two neighbors that have significantly different values are brought closer to the middle value by the averaging process. The averaging process also serves to further reduce the amplitude of the waveform since the magnitude of the result of averaging two numbers is never more than the maximum of the two numbers (and that only happens if both numbers are equal, which is highly unlikely). The magnitude reduction is most pronounced when the two numbers being averaged are least alike, i.e., if the two numbers being averaged are equal, then no change occurs; however, if one is 1.0 and the other is -1.0, the average is 0.0. The second plot, entitled "Sinewave with Feedback Loop," shows how the amplitude of the waveform diminishes over time. Since the waveform was very smooth to begin with, the averaging process does not exhibit any smoothing. In contrast, the fourth plot, entitled "Random Samples with Feedback Loop," clearly demonstrates the smoothing effect of the averaging process. The amplitude of the waveform experiences a more rapid reduction since the neighbors that are being averaged together may not have similar values. This is an example of something called Digital Signal Processing. By writing this algorithm, you're implementing one kind of Digital Signal Processor that synthesizes a guitar pluck sound. Commercial musical synthesizers implement many other variations of Digital Signal Processors as well. ## Assignment Details Your assignment is to write a program that will read in one or more notes specified in a text file and use the Jaffe-Smith algorithm to simulate the sequence of notes being plucked on a guitar. You will be working with the four classes shown in the following UML class diagram: <figure>[![Class Diagram](guitarUML.png)](guitarUML.png)<figcaption>Figure 2: Class Diagram</figcaption></figure> The [`SimpleAudio`](lab5/edu/msoe/taylor/audio/SimpleAudio.html) and [`Note`](lab5/edu/msoe/taylor/audio/Note.html) classes have already been written for you. You should not modify these files. You will complete the implementation of two classes: [`Lab5`](lab5/lab5/Lab5.html) and [`Guitar`](lab5/lab5/Guitar.html). You can find documentation describing how each class should function by following the links to the Javadoc. Note that the following methods in the `Guitar` class are optional: `getNotes()` `write()`. You are not required to implement these methods. Your program must be able to read text files in the following format. Each line in the file should consist of at least two fields. The first field specifies the pitch of the note using [scientific pitch notation](http://en.wikipedia.org/wiki/Scientific_pitch_notation). The second field specifies the duration of the note in milliseconds (could be a floating point number). Each field must be separated by whitespace. Any additional text on a line in the file should be ignored. Blank lines are allowed in the file and should be ignored by your program. If a line in the file does not conform to the above requirements, your program should display a warning message indicating that the line was ignored and then continue to the next line. Your program should not crash (terminate due to an exception being thrown). An example input file is shown below: <pre> G4 312.5 // Deck F4 125 // the E4 250 // halls D4 250 // with C4 250 // boughs D4 250 // of E4 250 // hol C4 250 // ly D4 125 // Fa E4 125 // la F4 125 // la D4 125 // la E4 312.5 // la D4 125 // la C4 250 // la B3 250 // la C4 500 // la </pre> Here is [another song file](ballGame.txt). The files needed for this project are available in [this .zip file](guitar.zip). ## Lab Deliverables > See your professor's instructions for details on submission guidelines and due dates. > * Dr. Taylor's students: See below > * All other students should refer to Blackboard > >If you have any questions, consult your instructor. ## Acknowledgements This assignment was written by [Dr. Chris Taylor](/taylor/).

Tuesday, 10-Apr-2018 14:43:24 CDT