Guitar Hero Checklist

Introduction

To learn how to create user-defined data types (classes) in Kotlin and to learn about digital audio.

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 properties or functions to the API; however, you may add private properties or functions (which are only accessible in the class in which they are declared).

RingBuffer

There are many viable approaches, including one suggested in the assignment specification. Be sure that your representation allows you to implement each operation efficiently (because the GuitarHero client will call the methods 44,100 times per second).

No. The size of a RingBuffer is the number of elements currently in it; the capacity of a RingBuffer is its maximum possible size.

No. Some of the elements in the buffer can be zero.

This application, or a library it uses, is using the deprecated Carbon Component Manager
for hosting Audio Units. Support for this will be removed in a future release.

You can ignore it. It is a warning intended for the authors of the Mac OS X Java sound library.

GuitarString

The assignment specification does not specify, so you are free to choose whichever convention you find most convenient. You may include the starting point but exclude the ending point of an interval. For example, s.substring(i,j) returns the substring of the string s beginning at index i and ending at index j-1 inclusive. Or you may include both endpoints of an interval. For example, s.substring(i..j) returns the substring of the string s beginning at index i and ending at index j inclusive.

Use Double.roundToInt() from kotlin.math.

GuitarHero

Be sure that the standard drawing window has focus by clicking in it. Then, type the keystrokes.

The value is clipped—it is replaced by the value +1.0 or −1.0, respectively. This is fine and can happen when you play chords.

Make sure you have tested with the main() provided for GuitarString. If that works, it is likely something’s wrong with pluck() since the main() provided for GuitarString does not test that method. To diagnose the problem, print the values of sample() and check that they become nonzero after you type the lowercase characters 'a' and 'c'.

It's likely that pluck() is working, but tic() is not. The best test is to run the main() provided for GuitarString.

If keyboard is a String and key is a character, then keyboard.indexOf(key) returns the integer index of the first occurrence of the character key in the string keyboard, or –1 if it does not occur. You can read about it in the Java String documentation page

As usual, we may deduct style points for using an unnamed constant, especially if you use it more than once. We recommend using the name SAMPLING_RATE for 44,100 and CONCERT_A for 440.0 and the expression keyboard.length for 37. You do not need to 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, but these are only partial tests. Write code to test each constructor and each method of each class, especially in RingBuffer.

    fun main(args: Array<String>) {
        val n = args[0].toInt()
        val buffer = RingBuffer(n)
        for (i in 1..n)
            buffer.enqueue(i.toDouble())
        val t = buffer.dequeue()
        buffer.enqueue(t)
        println("Size after wrap-around is " + buffer.size)
        while (buffer.size >= 2) {
            val x = buffer.dequeue()
            val y = buffer.dequeue()
            buffer.enqueue(x + y)
        }
        println(buffer.peek())
    }
    $ kotlin RingBufferKt 10
    Size after wrap-around is 10
    55.0
    $ kotlin RingBufferKt 100
    Size after wrap-around is 100
    5050.0
    fun main(args: Array<String>) {
        val samples = doubleArrayOf(
            0.2, 0.4, 0.5, 0.3, -0.2,
            0.4, 0.3, 0.0, -0.1, -0.3
        )
        val testString = GuitarString(samples)
        val m = 25 // 25 tics
        for (i in 0 until m) {
            val sample = testString.sample()
            println("%6d %8.4f".format(i, sample))
            testString.tic()
        }
    }
    $ kotlin GuitarStringKt
         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

Next, using your implementations of RingBuffer and GuitarString, compile and execute GuitarHeroLiteKt.

x c v x x c v x v g n   v g n   nmngv x nmngv x x p x   x p x
                x c v x x c v x v g n   v g n   nmngv x nmngv x x p x   x p x
                                x c v x x c v x v g n   v g n   nmngv x nmngv x x p x   x p x
                                                x c v x x c v x v g n   v g n   nmngv x nmngv x x p x   x p x

Possible Progress Steps (RingBuffer)

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

Possible Progress Steps (GuitarString)

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

Possible Progress Steps (GuitarHero)

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

Optional: Enrichment

Here are some concrete ideas for synthesizing other instruments. Some come from the paper of Karplus and Strong.

Here is some cool stuff that is closely related.