Aufgabe 3: Gitarre

 

Ansprechpartner:  Fabian Kaczmarczyck ( kaczmarf at informatik.hu-berlin.de )

 

Aufgabe

Mithilfe des Karplus-Strong-Algorithmus lässt sich die Schwingung einer Saite simulieren.  Dazu wird ein Ringbuffer wie in Aufgabe 1 benötigt, der in einer Java-Klasse implementiert werden soll. In diesem werden Auslenkungen der Saite gespeichert. Eine Saite benutzt diesen Ringbuffer, indem sie darauf den Karplus-Strong-Algorithmus anwendet. Dafür wird ein Ringbuffer mit einer Länge N verwendet, die der Grundfrequenz der Saite entspricht, wobei N der Quotient aus Samplingrate (44100) und Frequenz des Grundtons ist, auf die nächste ganze Zahl gerundet.

Beim Anschlagen der Saite wird der Ringbuffer mit weißem Rauschen gefüllt, so dass jedes Element des Ringbuffers ein Zufallswert zwischen -1/2 und 1/2 ist.

Der Algorithmus löscht in jedem Schritt das erste Element des Buffers und hängt das arithmetisches Mittel dieses Elements mit des darauf folgenden an das Ende des Buffers an, nachdem es mit dem Faktor 0.996 multipliziert wurde, der den Energieverlust darstellt.

Die Klasse Guitar soll wiederum 6 Instanzen der Klasse GuitarString verwenden, um eine Gitarre zu simulieren. Um die Frequenzen der Saiten zu bestimmen, wählt man den Kammerton A als Referenz. Dieser hat eine Frequenz von 440 Hertz. Damit die Saite eine Oktave höher oder tiefer schwingt, muss man diese Frequenz verdoppeln bzw. halbieren. Da eine Oktave 12 Halbtonstufen umfasst, muss man die Frequenz des Kammertons A für jeden Halbton, den man die Saite tiefer stimmen will, die Frequenz 440 Hertz durch die zwölfte Wurzel aus 2 teilen. Die hohe E-Saite der Gitarre liegt beispielsweise 5 Halbtöne unter dem Kammerton A, daher bestimmt man die Frequenz wie folgt:

frequency = 440.0 * Math.pow(2.0, -5.0 / 12.0);

Alternativ findet man die Frequenzen und weitere Informationen zur Stimmung der Gitarre hier:

http://en.wikipedia.org/wiki/Guitar#Standard

 

Ihre Lösungen bis hierhin können Sie mithilfe einer Klasse testen, die Ihnen erlaubt, mit Ihrer Tastatur Bünde zu drücken und Saiten anzuschlagen.

 

Aufgabe 1: RingBuffer. Your first task is to create a data type to model the buffer. Write a class named RingBuffer that implements the following API.

        RingBuffer(int capacity)  // constructor to create an empty 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
Since the buffer has a known maximum capacity, you can implement a RingBuffer using a double array of that length. For efficiency, use cyclic wrap-around: Maintain one integer instance variable first that stores the index of the least recently inserted item; 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.
 

Aufgabe 2: GuitarString. Next, write a data type to model a vibrating guitar string of a given frequency. Implement a class named GuitarString with the following API.

       GuitarString(double frequency)
  void pluck()                         // set the buffer to white noise
  void mute()                          // fill the buffer with zeros
  void pressFretDown(int fret)         // change the length of the buffer according to the number of frets
  void tic()                           // advance the simulation one time step
double sample()                        // return the current sample
   int time()                          // return number of tics

 

Aufgabe 3: Guitar. Now use your GuitarString to build a guitar. This is mainly a wrapper for the methods of your strings.

       Guitar()
  void void pluckString(int string)                // pluck the corresponding string
  void mute()                                      // mute every string
  void pressFretDown(int string, int fret)         // press a fret on a string down
  void tic()                                       // advance the simulation one every string
double sample()                                    // compute a sample of the guitar output

 

Aufgabe 4: Test. Use the following classes to test your class Guitar: 

    teaching/2012-w/2012-w GdP/final-pr/03-gitarre/GuitarTesting.zip

    You can now play guitar on your keyboard.

 

Erweiterung 1: Welle visualisieren. Modify GuitarPlayerTest.java to plot 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.

 

 

Erweiterung 2: Andere Instrumente simulieren. 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.

 

Erweiterung 3: Lautstärke regeln. Modify the class Guitar.java to have a method setVolume(double newVolume) that rescales the volume of your guitar by simply multiplying every sample by the new volume.

 

Erweiterung 4: Gitarre verzerren. Modify the output of your function sample() in Guitar.java to first scale the return value by a factor called "gain" and then clip the result at a "clip" value. If you set a volume, apply the volume multiplication afterwards. Now you have a cool distortion effect. More information can be found here:
http://en.wikipedia.org/wiki/Distortion_%28music%29#Theory_and_circuits

 

Erweiterung 5: Gitarrenstimmung. Add methods your class Guitar.java to allow you to tune your guitar. You could switch between the standard tuning and a so called "Drop D" tuning or lower the hole guitar by a half note.

 

Erweiterung 6: Improvisation. Write a program GuitarImpro.java that plays pseudorandom notes. Improvisation and composition require some skills in theory of music, but nice results can also be achieved with easy programs. At first you could periodically play a random note of the pentatonic scale.

 

Erweiterung 7: Dateien abspielen. Write a program GuitarParser.java that reads files containing songs for one or more guitars. You can try to read midi files or specify your own file format, as I did. If you want to copy my file format, you can ask me for sample songs I tested my parser with.

 

Erweiterung 8: Komposition. Write a program GuitarComposition.java that generates files containing songs for one or more guitar. Those files should be readable by your GuitarParser class. You can start with easy harmonics like those proposed in improvisation.

 

Hinweise

Tipps zur Bearbeitung:

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

Abgabe

Geben Sie ein .tgz-Archiv ab, welches alle Quelltexte Ihres Gitarrenprogramms enthält. Darüber hinaus muss eine Datei README im Archiv enthalten sein, die beschreibt, welche Ausbaustufe sie implementiert haben und wie das Programm zu starten ist (Namen der main-Klassen, Parameter etc.)

 

Viel Spaß :-)