CS 202 - Computer Science II - Spring 2007
Lab 4 - Binary Files
Loyola College >
Department of Computer Science >
Dr. James Glenn >
CS 202 >
Labs >
Lab 4
Due
Wednesday, February 14th at 11:59pm.
Labs submitted one day late will be assessed a
20% penalty. Labs will not be accepted more than one day late.
Objectives
- to read structured text files
- to write binary files
Reading
Morelli & Wade Chs. 10,11
Introduction
Sound is created by air (or whatever) pressure changing over time.
If you graph pressure versus time for a given sound, you may come up
with a graph that looks something like this:
To represent a continuous curve digitally, we can sample it at
regular intervals. At each sample point, we measure the pressure
to the desired resolution and record the result as an integer.
As we increase the sample frequency and resolution, we will digitize the
sound more accurately. In picture above, the blue curve represents the
original sound. The tick marks along the bottom show the times at which
we take samples. The tick marks along the side reflect the resolution
of our samples; the value recorded will be that represented by the closest
tick to the actual curve. The red dots represent the samples and the white
curve represents the approximation of the original curve that our
samples give us.
If we want to create an audio file without an original sound source,
we can synthesize sounds by coming up with what the pressure vs. time
graph would be if the sound were actually played.
For example, a pure tone has a sine curve for a graph. The shorter the
period of the sine curve, the higher the tone.
Higher amplitudes represent louder pure tones. For example, if the
range of our samples is -128 to 127, then
0 61 108 127 115 76 18 -45 -96 -124 -122 -90 -35 27 83 119 126
could be the series of samples for our hypothetical sine curve.
Audio file formats
An audio file must contain information about the sample range and
frequency in order to be played back correctly. Different formats
specify different ways of recording this information. For this lab,
we will read .pas files and write .au files.
.pas files are a text-based file format similar to .pnm image files
(I made up .pas specifically for this lab). Each .pas file starts with
a 5 line header. The five lines contain
- the string "S1" to identify the file as a .pas file,
- the number of samples in the file (counting a left and right value together as one sample),
- the sample frequency,
- the number of channels per sample (2 for stereo for the left and right channels), and
- the maximum value for a sample (32767 for 16-bit audio); the minimum
value is assumed to be one less than the additive inverse of the maximum.
After the header, the value of each sample will be recorded, with all
channels for one sample on the same line separated by spaces.
.au files are binary files that begin with 6 pieces of information,
each written as a 32-bit integer. Common values are given below.
| Magic number |
0x2e736e64 (".snd" in ASCII) |
| Data offset |
24 (bigger if there is optional data between the header and samples) |
| Data size |
-1 (most players will be able to figure it out on their own) |
| Encoding format |
3 |
| Sample rate, in Hz |
44100 |
| Number of channels |
2 |
By setting up the header this way, the rest of the file can contain
samples in the range -32768 to 32767.
Assignment
You must complete the part of the AudioClip class that
reads .pas files and writes .au files. AudioClip already has
fields for the sample rate, format (which should be set to
AU_ENCODING_PCM_16), and number of channels declared, as well as
the declaration (but not instantiation) of a 2-D array to hold the
samples. The rows of the array correspond to the time intervals
and the columns correspond to the channels, so samples[10][0] would be
the 11th sample of the left channel and samples[10][1] would be the 11th
sample on the right channel. There are also some methods that
manipulate the arrays to change the sample rate and resolution of the file.
Your code to read .pas files will go
in the AudioClip constructor and the code to write .au files
will go in the write method, which will make use of the
RandomAccessFile class. In particular, you will need to use
the following methods and constructor.
- RandomAccessFile(String name, String mode), which opens
a file for reading or writing depending on the mode string.
mode should be "r" for an input file and "rw"
for an output file.
- void writeShort(int v) and void writeInt(int v), which
write 16- and 32-bit integers.
- long getFilePosition() and
void seek(long pos), which get and
sets the current position in the
file.
This is what makes
RandomAccessFiles random access -- you don't have to read or
write data in sequential order. If you want to overwrite data
already written to the file, you can use getFilePosition before
you write the data the first time, write some more data after than,
and then seek
to the position where the original data was first written
and then rewrite it.
More information can be found, of course, in the
Java documentation.
In the constructor, you should
- open the file for reading by creating an instance of BufferedReader,
- read and ignore the first line of the header (or perhaps check that it is indeed "S1" and throw an exception if it is not),
- read the information from rest the header and save it in the appropriate
fields or local variables (if the maximum sample value is 32767 then the format field should be 3),
- instantiate the samples array,
- use a loop that uses a StringTokenizer to break the lines
read from the file into the samples for the left and right channels, and
saves those values in the samples array.
In write, you should
- open the output file by creating an instance of RandomAccessFile,
- write the 6 values in the header with writeInt (the
only ones that need to be different for each file are the 5th entry
for the sample rate and the 6th entry for the number of channels;
all the other values can be as in the table above),
- use a loop that invokes writeShort to write the data in the
sample array to the file (each sample should have its left value written
and then its right value), and
- close the file.
To test your output files, you can play them in Windows Media Player.
Neither the constructor nor the write method should throw
checked exceptions.
Extra credit
Modify write to put a message in the file after the header. To do
this, you will have to add code after you write the header but before you
write the sample data that
- writes a message using writeBytes,
- saves the current position in the file as reported by getFilePointer,
- seeks to the 4th byte in the file to position the file pointer just
before the data offset field,
- write the saved file pointer using writeInt, and
- seek back to the position reported by getFilePointer
using seek.
Files
The files are contained in a Java archive.
You should edit AudioClip and run AUTest. The
archive contains
an audio sample called posse_clip.pas to test your code on.
Exercises
- Change AudioClip's write method to use
writeInt instead of writeShort to write the sample data.
Change main so it writes an .au file to posse_clip.au
before processing the clip with resample or changeResolution.
Listen to the resulting file in Windows Media Player. Explain what is
going on.
Submissions
Submit the source code (.java file) for AudioClip.