Lab Assignment #7
Overview
- You'll be working in pairs: two people to one machine. You just need to login as ONE of you. Either person will do.
- Login: login to the machine in Linux.
- Part A: Experience Illegal Overloading and triple/quadruple-nested loops.
- Part B: Explore ways of accessing and modifying sound by clipping part of a Sound object.
- Quiz: Each person will individually take a short quiz.
Part A
Copy
- Right click on the Desktop and Select "Open a Terminal"
- Paste the following lines of code into the terminal
- cd ../public/Lab7
- make directories
- make install
- cd
- cd Lab7
- mv Lab7A.java.empty Lab7A.java
- mv Lab7B.java.empty Lab7B.java
- mv Picture.java.empty Picture.java
- mv Sound.java.empty Sound.java
- ls
- The previous command should print out six filenames:Lab7A.java Lab7B.java Picture.java Sound.java blur.txt clipinto.txt
- Open, blur.txt (right click, open with text editor) and Picture.java. Copy and paste the TWO methods blur() and blur2() into your Picture.java file (open in Dr. Java and paste it in at the bottom before the last } ). The first blur() method, which is commented out, is the same one from last week's lab (if the account you are using this week was not used in the lab last week – uncomment this blur method). The second blur2() method is the one we will focus on this week (shortly).
Read and Understand the Code
- For Part A we'll use the application in Lab7A.java. This is basically the same as the one we used in Lab 6, but we'll explore a new way to write the blur() method (which averaged the colors across three pixels in the same row). It will do the exact same thing to an image, but with different code. We'll explore and understand why this code is more flexible than the code from last week.
- Look over the code: Note the main difference is blur2() has an additional inner loop (where it says //This loop iterates 3 times). What do you think this does? Let’s look at it in the text below.
Read and Understand
blur2() does not do separate indexing with (x-1,y), (x,y), and (x+1,y) ; instead it uses another loop, whose body is executed three times, accessing pixel (xindx,y) where xindx ranges from x-1 to x+1 inclusive. Let's start by looking at the loop structure. For each pixel x,y (controlled by the two outer loops), we want to work on a subset of pixels near that pixel. So, imagine some point of execution in the middle of our program, for example x = 3, y = 2, shown on the left below, we should iterate over (2,2), (3,2) and (4,2), and average those pixels
blur2() does not do separate indexing with (x-1,y), (x,y), and (x+1,y) ; instead it uses another loop, whose body is executed three times, accessing pixel (xindx,y) where xindx ranges from x-1 to x+1 inclusive. Let's start by looking at the loop structure. For each pixel x,y (controlled by the two outer loops), we want to work on a subset of pixels near that pixel. So, imagine some point of execution in the middle of our program, for example x = 3, y = 2, shown on the left below, we should iterate over (2,2), (3,2) and (4,2), and average those pixels
- And in the next iteration (shown on right above) – we'd be centered around x = 4, y = 2, and therefore iterating over pixels (3,2), (4,2) and (5,2), and averaging them. So it's kind of like we have a "sliding box of three pixels" that slides over the whole picture. The outer two for-loops control the "center" of the sliding box with variables x and y; the inner loop controls the looping over all the pixels in the sliding box with variable xindx.
- This is a common technique in dealing with 2-D sets of data. These triple (or see in your book on p 196: quadruple!) nested loops supports this pattern of access. This is a better idea than our first approach because it is more easily extensible and modifiable. For example, what if your customer said to you, "I don't want to just blur across three pixels, I want to blur across five (two before the current pixel and two after the current pixel)". The old way we did it the code would look like:
redValue = this.getPixel(x-2,y).getRed() +
this.getPixel(x-1,y).getRed() +
this.getPixel(x,y).getRed() +
this.getPixel(x+1,y).getRed()+
this.getPixel(x+2,y).getRed();
And repeated for blue and green! That is very long code to write, and hard to get right or update. - In our new "loop-based" approach , how would you modify the initialization part and the continuation test part of the innermost loop (the for loop over xindx) to blur over five pixels? Write down the for-loop header you would use here – we'll test it out in a few minutes.
Compile, Run and Test
- OK, now that we have an idea of how the code should work, let's compile and run it.
- Drat! – Another ArrayIndexOutOfBoundsException! Let’s fix it.
- The problem is that in the innermost loop, we are using (xindx,y) to index pixels, and xindx is sometimes out of range. Recall that we do not want to modify the pixels in the 1st column or those in the last call.
- So add an if
statement that controls the execution of the four statements in the innermost
loop – assignment to red, blue, and green and incrementing count. Don’t
change any of the for-loop headers; just add this if statement, so that those
four statements should only be executed when
xindx is a legal ‘x’ index into our picture.
Try out your if statement,
but ask the lab instructors if you need them to check your statement – we want
to make sure it modifies all the pixels.
-
Compile and run.
Modify, Compile, Run and Test
- Now, let's try blurring over five pixels. To do this, modify the innermost for loop over xindx with the ‘five-pixel’ version you came up with above. It should now loop over five pixels. If you did your if statement correctly, you won't need to modify the if statement at all.
- Compile, Run and Test.
Can You Do? Do This After the Lab
- Make sure you understand how the calculation of the pixel average color is happening. That is, how the red, green, and blue values are added up and why we use the count variable.
- Can you parameterize the method so that the user can indicate with an integer parameter how much they want to "blur" – e.g. the current state of your code would be blur (5)? The original code would be a blur (3).
- Can you extend this code to a quadruple-nested loop structure that blurs not only over the x-axis pixels, but also over y axis pixels? When you get done, your code should be similar to the code on page 196 of the book.
Part B
Switch driver and navigator: if you've been controlling the mouse and keyboard, it's time for your partner to do so.
Copy
Copy
- Open clipinto.txt (right click, open with text editor).
- Copy and paste the TWO methods (one is commented out) into your Sound.java file (open in Dr. Java and paste it in at the bottom) . Leave the second one commented out for now.
Read and Understand Code
- Start with the application code in Lab7B.java. When choosing which files to open, browse to the mediasources directory. When you run the code you will open two .wav sound files (we suggest you choose sec3silence.wav for result, and Elliot-hello.wav for original).
- The code plays the original sound. The code then calls a method clipInto() which you should have already copied into Sound.java.
- That method takes two parameters: a Sound object that will we copy from, and an integer that determines how many samples to copy from the start of the Sound to copy into the calling object (this). (That is why the calling object should be created from the file named sec3silence.wav; it's essentially a "blank" sound 3 seconds in length.) Then the code plays the resulting sound.
- Look at the clipInto() method in Sound.java. This method is supposed to modify the calling object Sound with a "sound clip" from the beginning of another Sound . NOTE: There are two clipInto() methods, we'll start with the uncommented one first.
- Do you understand what clipInto() is doing? Do you have any questions?
Compile, Run and Test
- Plug in headphones or earbuds so you can hear the sounds as they are played. Did it work? If not, make sure that the sound is not muted on your workstation, and run it again.
- It might be hard to tell which sound is which. Comment out the call to result.blockingPlay(), leaving the call to original.blockingPlay(), and compile and run this code. This will play the original, unmodified Elliot-hello sound file. Now, comment out the call to original.blockingPlay(), and uncomment the call to result.blockingPlay(), compile, and run again . Now you should hear the result of the "sound clip" that you created; it should just say "Hello" and stop.
- If you have trouble getting sound to work on your workstation, uncomment the calls to original.explore() and result.explore() in Lab7B.java. These calls will pop up explorer windows that let you visualize the sound waveform. You should see that the result Sound has sound only for the first 12220 samples; that is the “hello” part from the beginning of Elliot-hello used as the sound clip.
Read and Understand the Code Again
Compile, Debug, Test:
Compile, Test and Debug:
- Next we want to look at another way to code this operation. This will be very similar to how with a Picture, you could access its pixels using getPixel(x,y); or you could get a Pixel array from the Picture, and index directly into that array.
- Luckily we had someone start working on this code for you – but they didn't have time to finish – so you'll finish it up for them.
- Comment out the clipInto() method you've been working with, and uncomment the one below it. This version uses SoundSample arrays instead of using method calls to access SoundSamples by calling Sound methods.
Compile, Debug, Test:
- Compile this code. AHA! As you might have expected, there is an error. Find what line it is on and read the error. What error is this?
- Fix this error by making the appropriate variable declaration for a SoundSample array. (Ask if you need help).
- Compile, Run the application, and make sure this version of clipInto() works the same as the other one.
- DO YOU UNDERSTAND? Why did the line result[i].setValue(value); not include i in the parameter list – like this: result[i].setValue(i, value)
- Now we would like you to create your own method called clipIntoAt that is a little more flexible than the other two
- Copy and paste the following header into your Sound.java file public void clipIntoAt(int nsamples, Sound overwriting, int index ){ //body here }
- Like the previous methods, your code is going to modify the calling Sound object so that nsamples from the overwriting Sound is copied into the original, BUT this time it is not going to do it at the beginning of the calling object.
- Instead, your method should overwrite nsamples in the original file starting from the index value
- HINT: If index + nsamples is larger than the length of the original sample array, an error may be thrown. Use a conditional to check for this.
Compile, Test and Debug:
- In your Lab7B.java test application, uncomment out the bottom lines of code that create two sound objects and then call your clipIntoAtMethod
- At this point you may comment out the top portion of the Lab7B.java file so that you are not constantly prompted to select files.
- For the two files, we suggest using Elliot-hello as the original and one of the coyote sounds from the musicsounds folder.
- If you select your parameters correctly, you can get Elliot to say hello and a coyote to respond back. However, the files currently do not play at all, so you need to add code that causes the sound to play.
- Does your code work like you expect?
Log Out
Click on System > Log Out cs8fzz... > Log Off
Complete Your Lab Quiz
See the instructor to get your individual lab quiz.
The quizzes are open book & note, but closed Dr Java and partner.
The quizzes are open book & note, but closed Dr Java and partner.
Sample Code
/*
* Lab7A.java
*/
public class Lab7A
{
public static void main(String[] args)
{
Picture p = new Picture(FileChooser.pickAFile());
Picture copyP = new Picture(p);
copyP.blur();
p.show();
copyP.show();
}
}
/*
* Lab7B.java
*/
public class Lab7B
{
public static void main(String[] args)
{
// Browse to your mediasources directory to choose these sound files
// Pick sec3silence.wav for this one
Sound result = new Sound(FileChooser.pickAFile());
// Pick Elliot-hello.wav for this one
Sound original = new Sound(FileChooser.pickAFile());
// original.explore();
original.blockingPlay();
// Just clip first 12220 samples from original, and copy into result
result.clipInto(original, 12220);
// result.explore();
result.blockingPlay();
}
}
/**** clipinto.txt ****/
public void clipInto(Sound otherSound, int nsamples)
{
for (int i = 0; i < nsamples; i++)
{
int value = otherSound.getSampleValueAt(i);
this.setSampleValueAt(i, value);
}
}
/****
public void clipInto(Sound otherSound, int nsamples)
{
SoundSample[] result = this.getSamples();
for (int i = 0; i < nsamples; i++)
{
int value = source[i].getValue();
result[i].setValue(value);
}
}
****/
/**** blur.txt ****/
/***************************************************************
** This is the blur() method from Lab 6. Commented out,
** but included here for reference.
public void blur()
{
int redValue, blueValue, greenValue;
Pixel updatePixel;
Color c;
for (int x = 0; x < this.getWidth(); x++)
{
for (int y = 0; y < this.getHeight(); y++)
{
updatePixel = this.getPixel(x,y);
if ((x > 0) && (x < (this.getWidth()-1)) )
{
redValue = this.getPixel(x-1,y).getRed() +
this.getPixel(x,y).getRed() +
this.getPixel(x+1,y).getRed();
blueValue = this.getPixel(x-1,y).getBlue() +
this.getPixel(x,y).getBlue() +
this.getPixel(x+1,y).getBlue();
greenValue = this.getPixel(x-1,y).getGreen() +
this.getPixel(x,y).getGreen() +
this.getPixel(x+1,y).getGreen();
c = new Color(redValue/3, greenValue/3, blueValue/3);
updatePixel.setColor(c);
} // end if
} // end inner for
} // end outer for
} // end blur()
**** end of commented out blur() method ****
*************************************************************/
// This is the blur2() method you will work on in Lab 7
public void blur2()
{
int redValue, blueValue, greenValue;
Pixel updatePixel;
int count;
Color c;
for (int y = 0; y < this.getHeight(); y++)
{
for (int x = 0; x < this.getWidth(); x++)
{
updatePixel = this.getPixel(x,y); // the 'center' pixel
count = 0;
redValue = blueValue = greenValue = 0;
// This loop iterates 3 times...
for (int xindx = x-1; xindx <= x+1; xindx++)
{
redValue = redValue + this.getPixel(xindx, y).getRed();
blueValue = blueValue + this.getPixel(xindx, y).getBlue();
greenValue = greenValue + this.getPixel(xindx, y).getGreen();
count = count + 1;
}
c = new Color(redValue/count, greenValue/count, blueValue/count);
updatePixel.setColor(c);
} // end inner for x
} // end outer for y
} // end blur2()