Last Updated: March 29, 2010
Note: Phase B is not much additional work, but requires basic understanding of defining and using Java generics. The only reason it is not due on Friday April 9 is to avoid making it due the same day as Homework 1. You are encouraged to finish it well before it is due and you are welcome to turn it in with Phase A.
Read the logistics before you submit — poor submissions may lose points.
The purpose of this project is to implement a Stack ADT in the two most common ways, an array and a linked list. In Phase A, you will implement stacks for Java double numbers. In Phase B, you will implement generic stacks and instantiate them with type Double.
Your Stack implementations will be used to do sound manipulation, namely reversing a sound clip. This process, called "backmasking," was used by musicians including the Beatles, Jimi Hendrix, and Ozzy Ozbourne, although it seems to have fallen out of favor in recent years. Click here for a history of this (sometimes controversial!) practice. "But wait," you say, "CSE 143 never taught me how to work with music..." Don't worry! All the music-handling parts have been done for you.
Even though the music-reversing code will use your stack implementations in only certain ways, you should test that your stacks are correct in all cases, not just those used by the reverse program.
This assignment consists of two phases. Each phase involves programming and write-up questions.
You will write a program that reads a sound file in the .dat format (explained below), and writes another .dat sound file which is the reverse of the first. We provide you with a class Reverse whose main method reads in a .dat sound file, pushes all the sound values on a stack, then pops them all off and writes them into a new .dat sound file. We've also provided you interfaces DStack (for Phase A) and GStack (for Phase B), which define the stacks you will implement. Your first job is to familiarize yourself with these files.
In Phase A, you need to provide two stack implementations, one using an array and one using a linked list. They should be called ArrayStack and ListStack, respectively. They should implement the DStack interface given to you. Once you provide these implementations, Reverse should work and create backwards sound files. It should take no more than a page or two of code to provide the implementations. Your array implementation should start with a small array (say, 10 elements) and resize to use an array twice as large whenever the array becomes full, copying over the elements in the smaller array. Both ArrayStack and ListStack should throw an EmptyStackException if pop() or peek() is called when the stack is empty. To use EmptyStackException, add the following line to your file:
import java.util.EmptyStackException;
Note that your solution to Phase A should not involve any changes to Reverse.java.
The Reverse program takes 4 arguments (a.k.a. "command-line arguments"). The first is the word array or list, and specifies which implementation to use. The second is the word double or generic; the latter is for Phase B. The next two are the input and output .dat file names (you need to include the .dat extension). From the command-line, you can run the program with something like:
java Reverse list double in.dat out.dat
To run your program in Eclipse, create a "Run Configuration" and under the "Arguments" tab put the arguments (e.g., list double in.dat out.dat) in the "Program arguments" box.
To test your program, you will need a .dat file, which you can create from a .wav file as explained in the Digital Sound section. It may also be useful for you to create some short .dat files by hand to aid testing.
In Phase B, you need to provide two more stack implementations, these implementing the interface GStack<T>. Create classes called GArrayStack and GListStack that are just like ArrayStack and ListStack except that they are generic in the type of elements kept in the stack. The simplest approach is to copy ArrayStack and ListStack and then make appropriate changes. Normally such copy-and-paste is poor form, but here the pedagogical purpose is to show you how little you need to change to make the code generic — and we want to grade all four stack implementations.
To use your generic stacks you will need to make some additions to Reverse.java by replacing the 3 occurrences of:
System.err.println("no support for generics yet");
System.exit(1);
with appropriate code. Again, the code you write will be only slightly different from non-generic code that is already there. Do not make other changes to Reverse.java.
Turn in a report answering the following questions. The first 7 questions are due with Phase A.
The following list of suggestions are meant for you to try if you finish the requirements early. Recall that any extra-credit points you earn for these are kept separate and will be used to adjust your grade at the end of the quarter, as detailed in the course grading policy.
We will view sound as a continuous function of time from the positive real numbers (time) to the interval [-1.0, 1.0] (amplitude). Since a computer can't "hold" a function defined on the reals, we have to approximate the function. We do this by measuring (or "sampling ") the sound several thousand times per second.
This process is called "Analog to Digital Conversion", or ADC. The number of times per second the sound is sampled is called the sample rate and is measured in Hertz. For example, CDs are recorded at 44100 samples per second, or 44.1kHz. But you don't need to understand ADC for CSE332.
The only sound file format you need to know about is the .dat format described below. You don't even have to know very much about that either, as we're giving you the code that reads and writes that format. In order to play sounds you produce, you need a way to convert between the .dat format and a format that common media players (Windows Media Player, winamp, RealPlayer, etc.) understand, such as .wav. We'll describe one way to do it below; however, you're free to use another converter.
sox is a UNIX command-line utility whose name stands for "SOund eXchange". It lets you convert between many different sound formats including .wav, .au, etc. In particular, sox allows you to convert to and from .dat sound files. .dat files are useful because they are human-readable, text-based, sound files. Note that you will need to perform this conversion to answer one of the write-up questions.
There is a windows version of sox
available, and the source
archive is known to compile and work on OS X 10.4. The program is also
installed on the lab machines. You can download versions from the project
page at
SourceForge.
The windows version is also a command-line program and works in the same
way as the UNIX version described below. See
here for some hints on using it.
The general strategy for using sox
is as follows.
That's all there is to it!
The .dat file format starts with one line describing the sample rate of the sound file. This line is required. The rest of the file is composed of two columns of numbers. The first column consists of the time (measured in seconds) when the sample was recorded, and the second column contains the value of the sample, between -1.0 and 1.0. This is the beginning of a sample .dat file. Notice that the numbers in the first column increase by 1/44100 each step. This is because the sample rate is 44.1kHz.
; Sample Rate 44100 0 0 2.2675737e-05 0 4.5351474e-05 0 6.8027211e-05 0 9.0702948e-05 0 0.00011337868 0 0.00013605442 0 0.00015873016 0 0.00018140590 0 0.00020408163 0
Here is the same file, a little deeper on:
0.22693878 -0.0062561035 0.22696145 -0.0043945312 0.22698413 -0.0068664551 0.22700680 -0.0115661620 0.22702948 -0.0145568850 0.22705215 -0.0145416260 0.22707483 -0.0121917720 0.22709751 -0.0123901370 0.22712018 -0.0145416260 0.22714286 -0.0144958500 0.22716553 -0.0147705080 0.22718821 -0.0157012940 0.22721088 -0.0129547120 0.22723356 -0.0127105710 0.22725624 -0.0181579590 0.22727891 -0.0191497800 0.22730159 -0.0145721440 0.22732426 -0.0122375490 0.22734694 -0.0124359130 0.22736961 -0.0108184810
Note that you shouldn't have to deal much with the .dat file yourself, as the provided Reverse.java does all the work for you. All you have to do is implement the stacks. We are explaining the format because it will be helpful for you if you want to write a short file by hand to run, to verify if your program works.
Like many assignments, this has been passed down to us through the vaporous mists of time. Among all our fore-bearers, we would especially like to thank Ashish Sabharwal, Adrien Treuille, and Adrien's Data Structures professor, Timothy Snyder.