Assignment 13: Infiniharp

Starter code: infiniharp-student.zip

Overview

In this assignment, you will write a program to simulate plucking a harp string entirely algorithmically, using the Karplus-Strong algorithm. This approach is a form of physically modeled sound synthesis, where a physical description of a musical instrument is used to synthesize sound electronically.

Implementing this algorithm efficiently requires the use of an array-based circular queue, so you will build one of those for this assignment, too.

Like other recent assignments, we’ve not provided a large set of unit tests to help with automated testing or your design process.

The Gradescope autograder includes some tests of your program’s overall correctness. We have left the names of these tests visible as a hint to what they’re testing, but you may need to write some tests of your own to complete this assignment.

Goals

Downloading and importing the starter code

As in previous assignments, download and decompress the provided archive file containing the starter code. Then import it into Code in the same way; you should end up with a infiniharp-student project in the “Project Explorer”.

Digital Audio

According to Wikipedia, “Digital audio is sound that has been recorded in, or converted into, digital form. In digital audio, the sound wave of the audio signal is encoded as numerical samples in continuous sequence.” You may want to glance at that Wikipedia page for some background.

In short, though, we can represent a sound as a sequence of values. CDs, for example, use 16-bit values that are sampled 44,100 times per second. The sound card in your computer, if provided with a sequence of values in the correct range, will play sounds corresponding the sound wave those values describe. The 44,100 rate is pretty widely supported, and it’s what the provided code will use.

If you want to read about this stuff in more detail, including some neat interactive Javascript demos, read “Karplus-Strong String Synthesis”.

Modeling a harp string

Suppose you want to simulate the plucking of a stringed instrument, such as a harp. When it’s plucked, it vibrates and creates a sound. The length and tension of the string determines its fundamental frequency; typically musicians adjust the tension of strings to tune them. A well-known musical frequency is the “concert A” at 440 Hz, that is, a vibration of 440 times per second.

We will model a vibrating string by sampling its displacement (a number between -12 and +12) at N equally spaced points in time. How will we compute N? It is the desired sampling rate (in our case, 44,100) divided by the desired fundamental frequency of the string, rounded up to the nearest integer.

Plucking the string

When you pluck a string, you impart energy at a mixture of different frequencies. In physics, we call the imparting of this energy the excitation the string. To simulate this excitation, we’ll set each of the N displacements to a random number between -12 and +12.

white noise

You’ll need a random number generator to do this. In Java, you can create a new generator like so:

	Random r = new Random(0);

Then, you can ask it for a random number using r.nextDouble(), which gives you a number between 0.0 and 1.0 – you’ll have to adjust it to the correct range.

Modeling the vibrations

After the string is plucked, the string vibrates. The pluck causes a displacement which spreads in a wave-like way over time. The Karplus-Strong algorithm simulates this vibration by maintaining a queue of the N samples: the algorithm repeatedly:

In this assignment, we’ll use a decay factor of -0.994 (note the negative!), which gives a nice harp-like sound.

Karplus-String

Why and how

This section is optional, but here for the curious.

The two primary components that make the Karplus-Strong algorithm work efficiently are an array-based circular queue, and the averaging operation. (Efficiency was very important when this algorithm was developed in the early 1980s – home PCs at the time ran at about 5 MHz, had about 128 KB of RAM, and cost about $4,000 in 2019-inflation-adjusted dollars!)

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.

What to do

CircularDoublesQueue

Your first task is to implement the queue. You will implement an array-based circular queue. For efficiency, the array should be an array of primitive doubles, and not a generic array.

Your implementation of CircularDoublesQueue should more-or-less mirror what we did in class.

HarpString

Next, implement the HarpString to model a vibrating string. Here are some implementation notes on some of the methods:

HarpPlayer

If you want to listen to your results, there’s an interactive player already written for you in HarpPlayer. WARNING: Start with the volume low! If there’s a mistake in your Karplus-Strong update code, you might get loud static instead of the sound of a string being plucked.

If you open it up, you’ll see it uses the following definition:

    String keyboard = "q2we4r5ty7u8i9op-[=zxdcfvgbnjmk,.;/' ";

This hardcodes the musical steps on a piano keyboard to your computer’s keyboard. 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:

keyboard

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

Once you have HarpString working, you can use HarpPlayer to create some sweet sweet early-80s-style synth music!

Submitting the assignment

When you have completed the changes to your code, you should export an archive file containing the src/ directory from your Java project. To do this, follow the same steps as from Assignment 01 to produce a .zip file, and upload it to Gradescope.

Remember, you can resubmit the assignment as many times as you want, until the deadline. If it turns out you missed something and your code doesn’t pass 100% of the tests, you can keep working until it does.