Guitar Simulation using the Karplus-Strong Algorithm

Overview

The program I wrote attempted to simulate the sound of a guitar using MatLab code.  I used the Karplus-Strong algorithm to generate this sound. This algorithm is a method of string synthesis that uses a phase delay and a finite impulse response (FIR) filter to condition a random signal into a wave that can be audibly played to sound like notes and chords of a guitar in the key of G.  There were three stages to this program. The first was to analyze an open string, individually. The second was to introduce the use of frets to the strings. Frets are the divisions along the neck of a guitar where the player will place his or her fingers to change the pitch generated by the string. The third stage was to put these two together, and incorporate all six strings to generate chords.

Initializing Variables

The first thing we must do is define our variables.  We have our sampling frequency,                 F_s = 44100.  We then set the frequencies which the strings are tuned to. They are as follows:

E = 105 Hz

A = 110 Hz

D = 115 Hz

G = 120 Hz

B = 124 Hz

E2 = 129 Hz

We also define the “offset” in the strings by finding the difference between a given string and the A string. (So for string X we take the frequencies X – A = Xoffset).  Next, we allocate a frequency vector and discretize time.

Open String

The Karplus-Strong algorithm works by taking an input of noise and channeling it into a delay line, then through the filter and outputs this sound.  The phase delay here can be calculated with the following equation, where F_s is the sampling frequency, or Nyquist frequency, and F_0 is the base frequency, or frequency of the string.  

D = F_s / F_0

We use the A string for the open string analysis, so we use the frequency of 110 Hz and our sampling frequency, 44100 Hz.  We can now create the FIR filter.  Using the MatLab code firls, we use a FIR filter with a least squares approximation of the noise signal to the desired harmonics of the A string.  We define our initial conditions for this by inputing poles into our filter which approximate these harmonics, so as the signal passes through the filter, it takes the shape of our desired harmonics, so when it is output, it sounds like the A string being plucked.  While a Fourier transform could also be used to filter the noisy input into a more coherent output, it does not achieve the same level of replication as the FIR filter does.  From here, we simply normalize the note and using the MatLab code audioplayer, we format it to be played aloud.

Fig. 1

Here, we see the harmonics of the A string from the generated note.  As desired, the harmonics are evenly spaced.  It can also be observed that there are certain harmonics have a greater magnitude than the rest, and those are the ones most audible to us.

Fig. 2

In this figure, we compare the harmonics of the A string, from above, to the harmonics of a D string.  It is apparent that the D string has a marginally larger harmonic than the A string, and that the difference perpetuates as we take into account more and more harmonics until they are entirely out of sync.  We can also note that the D harmonic has the same properties as the A harmonic in that it has significant peaks, and the the D string’s peaks are higher than that of the A string.

Fig. 3

Here we can compare the harmonics of all six strings individually, and then look at when all six are superimposed.  When we look at all six on the same graph, it is very easy to see how they all have a similar sized harmonic and how quickly they disperse as they multiply.  The lack of coincidence here is why it is not used as a chord in music.  Chords manipulate the strings using frets to create more coincidence in the harmonics so that certain frequencies stand out above the rest.

String with Frets

The second stage is to introduce the use of frets to our simulation.  Each fret that we move down the neck of the guitar alters the pitch by a half step.  That is, one fret changes the base frequency by a factor of 2^1/12.  So, our new equation for the delay is:

D = F_s / (F_0 * 2^(fret/12))

where “fret” is the number of the fret we are using, where 1 is nearest the top of the neck.

Fig. 4

This figure compares the harmonics of the open A string, as we saw in the previous section, and compares it to the A string on the 1st fret.  The 1st fret, as previously mentioned, changes the pitch by a half step, and here we see how it affects the harmonics.  The highest peak of the 1st fret is not nearly as high in magnitude as that of the open string, but the frequency of the harmonic only slightly varies from that of the open string.

Fig. 5

Here, we see the difference in the harmonics as we progress on the A string from the 1st fret to the 5th fret, and can compare them to each other and to the open A string.  Similarly to the open strings, we do not see much coincidence at any particular frequency.  This should change as we move to look at chords.

Chords 

The third stage is to put together what we have accomplished in the first to stages, and expand on it to generate chords. We must account for all six strings, and the use of frets.  So, instead, we have to make our single values for one string into vectors of length six for the six strings.  When we define our fret vector, it corresponds to finger positions on the strings while playing a chord.  Then, the delay equation will be the same as above in stage 2, but now since delay is a vector, it will depend on the fret vector.  We then define and use the filter again, but this time we use a for loop to incorporate all the strings.  The last step is to synthesize the notes that we have created with the loop into one coherent chord.

Fig. 6

In looking at a G major chord, we should first understand how we manipulate the strings.  The fret vector for a G major (or 1 (I) in the key of G) chord looks like:

fret = [3 2 0 0 0 2]

So, the first (E) string is played on the third fret, second (A) string on the second fret, sixth (E2) string on the second fret, and the other strings left open with no manipulation.  So, the figure above (Fig. 6), shows the harmonics of a G major chord.  When we looked at the open A chord, where we played the six open strings together, there was no frequency where the harmonics of the strings coincided.  In this case, we see several examples of this, including around 200 Hz and 480 Hz, just to name a few.  These sounds “work” together because the harmonics of the individual strings are multiples of each other.  For example, the frequency of the first string with the shortest harmonic is 1/2 the frequency of the fourth string, so they align.  These are considered to be an octave apart.  Also, the frequency of the sixth string is double that of the fourth string, so it aligns with both the first and fourth strings.  The second and fifth string have the same relationship as the first and fourth, but at different frequencies.  This sound is considered to be more consonant, whereas the open A chord is considered to be more dissonant, and that is due to these harmonics.

Fig. 7

Here, we look at a C major chord, which has a fret vector:

fret = [0 3 2 0 1 0]

We see the same relationship between harmonics, where similar strings align with each other, but they are at different frequencies because they are at different notes.  Though every chord may not show the same exact string relationships as these two, they will have some relationship between the harmonics of the strings, as evident below in figure 8.

Fig. 8

The last thing to do was to create a way to play these sounds.  So, we can utilize user input, so the user can choose what note or chord to play.  By using nested if statements, we can correspond the sound the user wishes to play, via input, to the code for the associated sound.  This way, by typing in A, the program will play the A string.  In this case, as often used in music, we named the chords by numbers 1 through 7.  There was no way to export the MatLab sound, so please refer to the MatLab code, GuitarPlay.m, that I submitted for that.  To play a string, type ‘E, A, D, G, B, E2’ (with caps), and to play a chord, type ‘1, 2, 3, 4, 5, 6, 7’ when prompted.

Future Aspirations

In the future, I would like to improve the efficiency of the program by making the coding more elegant.  As far as specific plans for the program, I would like to work out a way to play chords sequentially, so I could include chord progressions, and potentially parts of a song.  I would also like to compare how it would compare if a note was played on one open string, and then the same note was played on a different fretted string.  I would also like to import the sound from a real guitar and analyze the sound and the harmonics side by side to this simulation.

 

Share