First, listen to the 2 samples below, produced by the algorithm:
1. simple.wav
2. ks.wav
Now for the code that produced these:
Here is a simple Python program that generates the sound from a single plucked string, and writes it out to a WAV file.
############################################################
#
# karplus-simple.py
#
# Author: MV (http://electro-nut.blogspot.com/)
#
# Generates a plucked string sound WAV file using the
# Karplus-Strong algorithm. (Simple version.)
#
############################################################
from math import sin, pi
from array import array
from random import random
import wave
# KS params
SR = 44100
f = 220
N = SR/f
# WAV params
NCHANNELS = 1
SWIDTH = 2
FRAME_RATE = 44100
NFRAMES = 44100
NSAMPLES = 44100
# max - 128 for 8-bit, 32767 for 16-bit
MAX_VAL= 32767
# pluck
buf = [random() - 0.5 for i in range(N)]
#init samples
samples = []
# KS - ring buffer
bufSize = len(buf)
for i in range(NSAMPLES):
samples.append(buf[0])
avg = 0.996*0.5*(buf[0] + buf[1])
buf.append(avg)
buf.pop(0)
# samples to 16-bit to string
tmpBuf = [int(x*MAX_VAL) for x in samples]
data = array('h', tmpBuf).tostring()
# write out WAV file
file = wave.open('simple.wav', 'wb')
file.setparams((NCHANNELS, SWIDTH, FRAME_RATE, NFRAMES,
'NONE', 'noncompressed'))
file.writeframes(data)
file.close()
Here is a more object oriented version of the code above, which lets you mix sounds from multiple string plucks.
############################################################
#
# karplus.py
#
# Author: MV (http://electro-nut.blogspot.com/)
#
# Generates a plucked string sound WAV file using the
# Karplus-Strong algorithm.
#
############################################################
from math import sin, pi
from array import array
from random import random
import wave
# KS params
SR = 44100
f = 220
# WAV params
NCHANNELS = 1
SWIDTH = 2
FRAME_RATE = 44100
NFRAMES = 44100
NSAMPLES = 44100*2
# max - 128 for 8-bit, 32767 for 16-bit
MAX_VAL= 32767
class String:
def __init__(self, freq, SR):
self.freq = freq
self.N = SR/freq
# 'pluck' string
def pluck(self):
self.buf = [random() - 0.5 for i in range(self.N)]
# return current sample, increment step
def sample(self):
val = self.buf[0]
avg = 0.996*0.5*(self.buf[0] + self.buf[1])
self.buf.append(avg)
self.buf.pop(0)
return val
str1, str2 = String(196, SR), String(440, SR)
str1.pluck()
str2.pluck()
samples = []
for i in range(NSAMPLES):
sample = str1.sample()
if(i > NSAMPLES/8):
sample += str2.sample()
samples.append(sample)
# samples to 16-bit to string
tmpBuf = [int(x*MAX_VAL) for x in samples]
data = array('h', tmpBuf).tostring()
# write out WAV file
file = wave.open('ks.wav', 'wb')
file.setparams((NCHANNELS, SWIDTH, FRAME_RATE, NFRAMES,
'NONE', 'noncompressed'))
file.writeframes(data)
file.close()
It remains to be seen how I can adapt this to the Arduino. But it has been a fun project!
References
1. You can read details about the algorithm here:
http://en.wikipedia.org/wiki/Karplus-Strong_string_synthesis
2. Here is a programming assignment from Princeton which started me off on this project:
http://www.cs.princeton.edu/courses/archive/spring09/cos126/assignments/guitar.html
2 comments:
Very nice!...
Will you continue in your vacation, and implement a whole physically accurate digital waveguide model? :)
How is your Arduino project? Do you plan to make a guitar synthesizer? One idea you might use to make the sound more interesting is synthesizing two or three strings at the same time, with approximate parameters, and sum them all...
I was just wondering if you had ever gotten this running on the Arduino?
Post a Comment