I’m a bit over excited as I just managed to successfully implement Karplus-Strong’s guitar string algorithm.
http://en.wikipedia.org/wiki/Karplus-Strong_string_synthesis
The Wikipedia description was too complicated for me. I had to get the original paper and try to study it. It’s rather old and the terminology was a bit weird, so it took me a while to really understand it.
This snippet assumes you have SRATE
defined somewhere (the sample rate), and that you already managed to setup the audio output and the mixer and everything else. I’m using audiodata.js
to output audio using Firefox4’s Audio Data API.
The function guitar
here returns a function which takes a sample index/point and returns the sample. It assumes that it will be called with all the samples sequentially, otherwise it won’t work. Something like:
signal_fn = guitar(440) # 440 being the frequency we want to sound
point = 0
while not done
sample = signal_fn(point++)
# output sample
Not exactly that, but something like that.
Here’s the code:
period_len = (freq) -> Math.floor (SRATE/freq)
avg = (a, b) -> (a + b) / 2
ks_noise_sample = (val) ->
# get either val or -val with 50% chance
if Math.random() > 0.5
val
else
-val
# karplus strong algorithm
guitar = (freq) ->
samples = period_len freq
table = new Float32Array(samples)
getsample = (index) ->
point = index % samples
if index == point
table[point] = ks_noise_sample(1)
else
prev = (index - 1) % samples
table[point] = avg(table[point], table[prev])
Note that this is coffee-script, where the last expression in a function is returned, and A = B
expressions return B
. So, guitar
returns the getsample
function, and getsample
return table[point]
after we calculate it.
Here’s what this does:
Fill a table of length p
with random values (actually: fill it with either A or -A, with 50% chance each). Where p
is the period of the signal, which is the sample_rate/frequency
.
To produce samples, just keep cycling on this table. That is, to get any point t
, take table[t % p]
.
BUT, as you loop on the table, average each two adjacent samples. That is, everytime you get table[t]
(except the first time around), let table[t]
be (table[t] + table[t-1]) / 2
.
This causes the signal to decay overtime, or something like that.
That’s it.