When a harp string is plucked, the string vibrates and creates sound. The length of the string determines its fundamental frequency of vibration. We model a harp 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).
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
double dequeue() // delete and return item from the front
double peek() // return (but do not delete) item from the front
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
}
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..% java RingBuffer 10 Size after wrap-around is 10 55.0 % java RingBuffer 100 Size after wrap-around is 100 5050.0
public class HarpString
------------------------------------------------------------------------------------------------------------------------
HarpString(double frequency) // create a harp string of the given frequency, using a sampling rate of 44,100
HarpString(double[] init) // create a harp 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
% java HarpString 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.2991
11 -0.4487
12 -0.3988
13 -0.0498
14 -0.0997
15 -0.3490
16 -0.1496
17 0.0499
18 0.1994
19 0.2987
20 0.3728
21 0.4225
22 0.2237
23 0.0746
24 0.2237
String keyboard = "q2we4r5ty7u8i9op-[=zxdcfvgbnjmk,.;/' ";

The MiniHarp.java Example Program MiniHarp.java is a two-string version of Harp.java that you can use to test your RingBuffer.java, HarpString.java, and as a starting point for Harp.java. Type the lowercase letters 'a' or 'c' to pluck its two strings. Since the combined result of several sound waves is the superposition of the individual sound waves, we play the sum of all string samples. After you've completed RingBuffer.java and HarpString.java, run MiniHarp in order to check to see if everything works properly. You should hear two different pitches corresponding to A and C everytime you press the key.public class MiniHarp { public static void main(String[] args) { // create two harp strings, for concert A and C double A = 440.0; double C = A * Math.pow(2, 3.0/12.0); HarpString stringA = new HarpString(A); HarpString stringC = new HarpString(C); while (true) { // check if the user has typed a key; if so, process it if (StdDraw.hasNextKeyTyped()) { char key = StdDraw.nextKeyTyped(); if (key == 'a') { stringA.pluck(); } else if (key == 'c') { stringC.pluck(); } } // compute the combined sound of all notes double sample = stringA.sample() + stringC.sample(); // play the sample on standard audio StdAudio.play(sample); // advance the simulation of each harp string by one step stringA.tic(); stringC.tic(); } } }
- Note: In order to enter keystrokes in MiniHarp, make sure to first click on the standard draw window before typing the keystrokes. If you are having trouble running MiniHarp, refer to the FAQ tab.
- Also, note how this code above uses an infinite loop to continually receive keystrokes from the user and generate new music samples. This infinite loop ends when the program terminates.
Debugging Harp.java can be a little tricky. The probably is that you can only hear sound if you are sending new samples to your speaker fast enough. "Fast enough" means 44,100 times per second. System.out.println() is surprisingly slow—it almost always takes longer that 1/44,100th of a second to print something. The result is that you will hear only clicks or silence if you have any print statements to help you debug. Your best bet is to thoroughly test and debug RingBuffer.java, then thoroughly test and debug HarpString.java. Next, comment out, but do not delete any print statements you add for debugging and test MiniHarp. You may need to uncomment these statements and do further testing later (by adding new test cases to RingBuffer's and/or HarpString's main()). Do not start on Harp.java until you are certain everything else, including MiniHarp, works.
nn//SS/ ..,,mmn //..,,m //..,,m nn//SS/ ..,,mmn (S = space)
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
Do not redraw the wave on every sample because StdDraw will not be able to keep up. Instead, draw the wave of the last n samples every n timesteps for an appropriate value of n. Experiment with different values of n to find one that you think looks good and draws smoothly. There is more than one way to handle the drawing — there is not a "right" way to do this. You may also do a different visualization, as long as it is tied to the audio samples.
Alexander Strong suggests a few simple variants you can try: