CS 312 Assignment 12 - Guitar Hero - Classes, Objects, Arrays

Programming Assignment 12: Individual Assignment. You must complete this assignment by yourself. You cannot work with anyone else in the class or with someone outside of the class. You are encouraged to get help from the instructional staff. Recall we will check submissions with plagiarism detection software. If you copy code from someone else or the web you will be subject to the penalties for academic dishoesty.

Many Thanks to Kevin Wayne of Princeton University and Andrew Appel, Jeff Bernstein, Maia Ginsburg, Ken Steiglitz, Ge Wang for allowing me to use this assignment.

Provided Files: There are multiple support files you will have to download from the assignment web page.

Purpose: The purposes of this assignment are:

  1. To practice implementing classes
  2. To practice using arrays of objects


Write a program to simulate plucking a guitar string using the Karplus-Strong algorithm. This algorithm played a seminal role in the emergence of physically modeled sound synthesis (where a physical description of a musical instrument is used to synthesize sound electronically).

Digital audio. For more background on digital audio, review Standard audio section of this web page and the Superposition of sound waves section of this web page.

Simulate the plucking of a guitar string. When a guitar string is plucked, the string vibrates and creates sound. The length of the string determines its fundamental frequency of vibration. We model a guitar string by sampling its displacement (a real number between -1/2 and +1/2) at N equally spaced points (in time), where N equals the sampling rate (44,100) divided by the fundamental frequency (rounding the quotient up to the nearest integer).


Why it works? The two primary components that make the Karplus-Strong algorithm work are the ring buffer feedback mechanism and the averaging operation.

From a mathematical physics viewpoint, the Karplus-Strong algorithm approximately solves the 1D wave equation, which describes the transverse motion of the string as a function of time.

Classes to Implement:

RingBuffer: A RingBuffer (also known as a buffered queue) is a data structure that allows access to the item that has been in the data structure the longest amount of time. .

A queue is similar to a line (like the line at Wendy's in the Student Union or Zen at the SAC)

Your first task is to create a class to model a ring buffer.

Ring buffer

Write a class named RingBuffer that implements the following methods: (All methods are public!)

public class RingBuffer
        RingBuffer(int capacity)  // create an empty ring buffer, with given max capacity
    int size()                    // return number of items currently in the buffer
boolean isEmpty()                 // is the buffer empty (size equals zero)?
boolean isFull()                  // is the buffer full  (size equals capacity)?
   void enqueue(double x)         // add item x to the end (as long as the buffer is not full)
 double dequeue()                 // delete and return item from the front (as long as the buffer is not empty)
 double peek()                    // return (but do not delete) item from the front of the buffer
 String toString()                // override toString. Return a String of the form [front, next, next, last] 

RingBuffer shall throw a NoSuchElementException if the client attempts to dequeue() from or peek() at an empty buffer and a IllegalStateException if  enqueue(double val) is called when the buffer is full.

Since the ring buffer has a known maximum capacity, implement it using an array of doubles of that length. For efficiency, use cyclic wrap-around: Maintain one integer instance variable first that stores the index of the item that has been in the RingBuffer the longest. (The front of the line.)  Maintain a second integer instance variable last that stores the index one beyond the most recently inserted item. To insert an item, put it at index last and increment last. To remove an item, take it from index first and increment first. When either index equals capacity, make it wrap-around by changing the index to 0.

Consider this series of operations on a RingBuffer of capacity 4.


You can use the RingBufferTester class to see if your RingBuffer works. Do this before going on to the next class! Note, you may pass all the tests in RingBufferTester, but still have errors in your RingBuffer. (Recall failing tests proves the presence of a bug, but passing all tests does NOT  prove the absence of bugs unless you test every possible condition, which is normally impractical or even impossible.)


Next, create a data type to model a vibrating guitar string. Write a class named GuitarString that implements the following API: (All methods are public!)

public class GuitarString
       GuitarString(double frequency)  // create a guitar string of the given frequency, using a sampling rate of 44,100
       GuitarString(double[] init)     // create a guitar string whose size and initial values are given by the array
  void pluck()                         // set the buffer to white noise
  void tic()                           // advance the simulation one time step
double sample()                        // return the current sample
   int time()                          // return number of tics

Interactive guitar player. GuitarHeroLite.java is a sample GuitarString client that plays the guitar in real-time, using the keyboard to input notes. When the user types the lowercase letter 'a' or 'c', the program plucks the corresponding string. Since the combined result of several sound waves is the superposition of the individual sound waves, we play the sum of all string samples.

Write a program GuitarHero that is similar to GuitarHeroLite, but supports a total of 37 notes on the chromatic scale from 110Hz to 880Hz. In general, make the ith character of the string below play the i note.

String keyboard = "q2we4r5ty7u8i9op-[=zxdcfvgbnjmk,.;/' "; // String ends with a space

This keyboard arrangement imitates a piano keyboard: The "white keys" are on the qwerty and zxcv rows and the "black keys" on the 12345 and asdf rows of the keyboard.

Piano keyboard

Note, you DO NOT have to draw the keyboard on the StdDraw. Simply alter the displayed text to something more meaningful.

The ith character of the string corresponds to a frequency of 440 1.05956(i - 24), so that the character 'q' is approximately 110Hz, 'i' is close to 220Hz, 'v' is close to 440Hz, and ' ' is close to 880Hz.

Challenges for the bored.

Challenge 1. Write a program GuitarHeroVisualizer.java (by modifying GuitarHero.java) that plots the sound wave in real-time, as the user is playing the keyboard guitar. The output should look something like this, but change over time.


Challenge 2. Modify the Karplus-Strong algorithm to synthesize a different instrument. Consider changing the excitation of the string (from white-noise to something more structured) or changing the averaging formula (from the average of the first two samples to a more complicated rule) or anything else you might imagine.

Style: As always, use good style in your classes and methods. If you have more than 3 GuitarStrings would you even THINK about using 3 separate GuitarString variables? Use the String indexOf method to map from the keyboard String to your array of GuitarString variables.

Checklist: Did you remember to:

Back to the CS 312 homepage.