CIS 110 Fall 2012 - Introduction to Computer Programming

Java | /r/compsci (reddit) | PennPortal |
upenn.edu | directories | van pelt library
seas.upenn.edu | CETS | engineering library
computer graphics & game dev (SIGGRAPH @ Penn) | dining philosophers (DP) | science & tech wing (STWING) | women in cs (WICS)
CETS Answers

Checklist: Plucking a Guitar String

Frequently Asked Questions

What are the goals of this assignment? To learn how to create user-defined data types in Java and to learn about digital audio.

Do I need to follow the prescribed API? Yes, we will be testing the methods in the API directly. If your method has a different signature or does not behave as specified, you will lose a substantial number of points. You may not add public methods or instance variables to the API; however, you may add private methods (which are only accessible in the class in which they are declared). You may also add private instance variables for data that must be shared between methods.

Where do I enter keystrokes in GuitarHeroLite and GuitarHero? Be sure that the standard draw window has focus by clicking in it. Then, type the keystrokes.

How do I determine the length of an array? If you're asking this, you probably need to review Section 1.4.

Is the size of a RingBuffer equal to the number of nonzeros? No. Some of the elements in the buffer can be zero. To get an accurate count, increment the instance variable size each time you call enqueue() and decrement it each time you call dequeue().

What should RingBuffer do if the client attempts to dequeue() from an empty buffer or enqueue() into a full buffer? We recommend throwing an exception to indicate the error. This will cause your program to crash and print a stack trace that will help you identify the bug:


  if (isEmpty())
    throw new RuntimeException("The ring buffer is empty.");
See Vector.java for some other examples and p. 446 in the book for a slighty expanded explanation of exceptions..

How do I round a double to the nearest int? See the toGray() method in Luminance.java (Program 3.1.3).

What happens if I call StdAudio.play(x) where x is greater than 1 or less than -1? The value is clipped—it is replaced by the value 1.0 or -1.0, respectively.

I get an ArrayOutOfBounds or NullPointerException error in RingBuffer. What could cause this? Does your constructor correctly initialize all of the instance variables? Did you allocate memory for your array? Did you inadvertently redeclare an instance variable in a method or constructor, thereby shadowing the instance variable with the same name?

I get a Ring buffer underflow error in GuitarHeroLite before I type any keystrokes. Why? Did you forget to initialize the ring buffer to contain N zeros in your GuitarString constructor?

When I run GuitarHeroLite for the first time, I hear no sound. What am I doing wrong? Make sure you have tested with the main() provided for GuitarString. If that works, it is likely something wrong with pluck() since the main() provided for GuitarString does not test that method. To diagnose the problem, print out the values of sample() and check that they become nonzero after you type lower case characters 'a' and 'c'.

When I run GuitarHeroLite, I hear static (either just one click, and then silence or continual static). What am I doing wrong? It's likely that pluck() is working, but tic() is not. The best test is to run the main() provided for GuitarString.

How do I use keyboard.indexOf(key)? If keyboard is a String and key is a character, then keyboard.indexOf(key) return the integer index of the first occurrence of the character key in the string keyboard (or -1 if it does not occur).

Should I hardwire the constants 44,100, 110.0, 440.0, 880.0, and 37 in my program? No, in general, we will deduct if you use an unnamed constant (such as 37) in your program more than once. We recommend using the name SAMPLING_RATE for 44,100 and CONCERT_A for 440. But you need not name all of the constants in the formula 2(i - 24) / 12.

Testing

Be sure to thoroughly test each piece of your code as you write it. We offer some suggestions below.

  • Ring buffer. You can test your RingBuffer data type on the following toy client, using the main() provided. It enqueues the numbers 1 through N, and then repeatedly dequeues the first two, and enqueues their sum.
    
      public static void main(String[] args) {
          int N = Integer.parseInt(args[0]);
          RingBuffer buffer = new RingBuffer(N);  
          for (int i = 1; i <= N; i++) {
              buffer.enqueue(i);
          }
          double t = buffer.dequeue();
          buffer.enqueue(t);
          System.out.println("Size after wrap-around is " + buffer.size());
          while (buffer.size() >= 2) {
              double x = buffer.dequeue();
              double y = buffer.dequeue();
              buffer.enqueue(x + y);
          }
          System.out.println(buffer.peek());
      }
    
    % java RingBuffer 10
    Size after wrap-around is 10
    55.0
    
    
    % java RingBuffer 100
    Size after wrap-around is 100
    5050.0
    

  • Guitar string. You can test your GuitarString data type with the following toy client, using the main() provided.
    
      public static void main(String[] args) {
          int N = Integer.parseInt(args[0]);
          double[] samples = { .2, .4, .5, .3, -.2, .4, .3, .0, -.1, -.3 };  
          GuitarString testString = new GuitarString(samples);
          for (int i = 0; i < N; i++) {
              int t = testString.time();
              double sample = testString.sample();
              System.out.printf("%6d %8.4f\n", t, sample);
              testString.tic();
          }
      }
    
    % java GuitarString 25
         0   0.2000
         1   0.4000
         2   0.5000
         3   0.3000
         4  -0.2000
         5   0.4000
         6   0.3000
         7   0.0000
         8  -0.1000
         9  -0.3000
        10   0.2988
        11   0.4482
        12   0.3984
        13   0.0498
        14   0.0996
        15   0.3486
        16   0.1494
        17  -0.0498
        18  -0.1992
        19  -0.0006
        20   0.3720
        21   0.4216
        22   0.2232
        23   0.0744
        24   0.2232
    

  • Guitar Hero. Type the following into your guitar to get the beginning of Led Zeppelin's Stairway to Heaven. Multiple notes in a column are dyads and chords.
                                                  w q q
            8       u       7       y             o p p
    i p z v b z p b n z p n d [ i d z p i p z p i u i i
    
    What is this familiar melody?
    nn//SS/ ..,,mmn //..,,m //..,,m nn//SS/ ..,,mmn   (S = space)
    

Possible Progress Steps

These are purely suggestions for how you might make progress. You do not have to follow these steps.

  • Download the templates for RingBuffer.java and GuitarString.java to get you started. Also make sure you have GuitarHeroLite.java, StdAudio.java, and StdDraw.java, along with this assignment's readme_guitar.txt template.

  • Review the material in the textbook on digital audio (pp. 147-151, 202-206).

  • We recommend defining the instance variables as follows:
    public class RingBuffer {
        private double[] rb;          // items in the bufer
        private int first;            // rb[first]  = first item in the buffer
        private int last;             // rb[last-1] = last  item in the buffer
        private int size;             // current number of items in the buffer
    }
    

  • The design of your program should look like RingBuffer.java, except that you will need to fill in all of the constructors and methods.

  • Your constructor for RingBuffer will need to allocate and initialize an array of double using new. Observe that you have to do this in the constructor (and not when you declare the instance variables) since otherwise you wouldn't know how big to make the array.

  • After you fill in the methods for RingBuffer, your first test should be to run it using the given test code in main().

  • Start the guitar string class with GuitarString.java. It contains one of the private instance variables that you will need, and all of the constructors and methods ready to be filled in. The constructors will both initialize the instance variables and use enqueue().

  • To implement pluck(), use a combination of the RingBuffer methods size(), dequeue(), and enqueue() to replace the buffer with values between -0.5 and 0.5.

  • To implement tic(), use a combination of enqueue(), dequeue(), and peek().

  • To implement sample(), use peek().

  • When you have GuitarString.java working, change the program GuitarHeroLite.java to GuitarHero.java, by creating an array of notes.
Enrichment

  • ChucK. ChucK as specialized programming language for real-time synthesis, composition, and performance originated by Ge Wang and Perry Cook at Princeton University. Here's the Karplus-Strong algorithm in ChucK.

  • Slide flute. Here's a description of a physically modeled slide flute by Perry Cook.

  • Electric guitar synthesis. This is a Jimi Hendrix-ified version of the The Star Spangled Banner implemented as a class project by Charlie Sullivan (Princeton '87) by extending the Karplus-Strong algorithm to synthesize electric guitar timbres with distortion and feedback.