This page describes my project for Music 220a: Introduction to Sound Synthesis and Signal Processing. The goal of my project was to synthesize drum and percussion sounds using Common Lisp Music (CLM). I tried several different synthesis methods: subtractive, additive, FM, Karplus-Strong algorithm, and granular, and found that each had its own distinct characteristics for making percussive sounds.
There are two key elements in percussive sounds, the amplitude envelope shape and the frequency content. The amplitude envelope usually has a sharp attack followed by a slow exponential decay. In the frequency content of the sound usually consists of non-integer harmonics or noise, with little or no pitch. There are also often many frequencies at the beginning of a sound fading into only a few frequencies at the end.
For this project, I tried several variations of five different synthesis techniques: subtractive, additive, frequency modulation (FM), Karplus-Strong, and granular. I made nine different instruments in CLM (and one in Matlab), and also made some sounds using Jan Mattox's fm-drum and Fernando Lopez-Lezcano's grani instruments. All of the instruments can be in the following files:
The next five sections discuss each of the five synthesis techniques that I explored. After that I mention some ideas for future research that this project inspired. Last, I conclude with a summary what I learned from this project.
Subtractive synthesis is fairly straight forward. You start with random wideband noise, subtract out undesired frequencies using filters, and use an amplitude envelope to control the attack and decay.
I wrote three algorithms which are all included in the file drum-subtract.ins.
Each algorithm starts with a random square wave generated by CLM's randh function, with amplitudes ~U[0,1] and frequency 0.49*sampling-rate. Then a simple filter with controllable coefficients is used to tune the frequency content of the sound. Finally, an amplitude envelope is used to control the attack and decay of the sound (with an optional parameter to control the curvature of the envelope interpolation).
The subtract-op instrument uses a one-pole filter which is low-pass when b1 < 0.0 and high-pass when b1 > 0.0. The strength of the filter increases as the magnitude of b1 increases.
The subtract-pp instrument creates band-limited noise with a somewhat controllable bandwidth and center frequency. The bandwidth, controlled by r where 0 < r < 1, gets narrower as r approaches 1. However, as the bandwidth gets narrower, the gain of the filter at its center frequency also increases, so care must be used to lower the amplitude. Also, at very low frequencies the filter is unstable.
The subtract-oz instrument has a one-zero filter that makes mostly low frequency noise when a1 > 0.0 and high frequency noise when a1 < 0.0. The strength of the filter increases as the magnitude of a1 increases.
I used these three algorithms to create a number of sounds. I found by controlling the frequency content of the noise, the envelope shape (particularly the attack), and the duration, I was able to control the noise fairly well and predict what a sound would sound like. Anyway, here's what I came up with:
These use high frequency noise (subtract-op with b1 > 0 or subtract-oz with a1 < 0) and an envelope that starts at 0 initially, builds quickly, and then decays (0 0 10 1 100 0). I used highly curved envelopes (large amp-env-base) to quicken (and curve) the decay.
; Brushed Snare (with-sound () (subtract-op 0 .2 .4 '(0 0 5 1 100 0) :b1 0.9 :amp-env-base 10)) (with-sound () (subtract-op 0 .5 .6 '(0 0 5 1 100 0) :b1 0.9 :amp-env-base 1000)) (with-sound () (subtract-op 0 1 .6 '(0 0 2 1 100 0) :b1 0.9 :amp-env-base 100000)) (with-sound () (subtract-oz 0 .25 .4 '(0 0 5 1 100 0) :a1 -0.5 :amp-env-base 10)) (with-sound () (subtract-oz 0 .5 .6 '(0 0 5 1 100 0) :a1 -0.5 :amp-env-base 1000))
With a very sharp attack, short duration, and a weak filter (any algorithm), snare-like sounds can be created.
; Snare (with-sound () (subtract-op 0 .2 .3 '(0 1 100 0) :b1 0.2 :amp-env-base 100)) (with-sound () (subtract-oz 0 .2 .3 '(0 1 100 0) :a1 -0.2 :amp-env-base 100)) (with-sound () (subtract-op 0 .2 .3 '(0 1 100 0) :b1 -0.2 :amp-env-base 100)) (with-sound () (subtract-oz 0 .2 .3 '(0 1 100 0) :a1 0.2 :amp-env-base 100)) (with-sound () (subtract-pp 0 .2 .2 '(0 1 100 0) :r 0.2 :frequency 400))
The two-pole algorithm makes a noise bass drum when it is given a narrow band (r near 1). Note how the frequency decreases as r increases, as well as the obscenely low amplitudes needed with a narrow band-pass region.
; Noisy Bass (with-sound () (subtract-pp 0 0.5 .02 '(0 0 5 1 100 0) :r .9 :frequency 100 :amp-env-base 1000)) (with-sound () (subtract-pp 0 0.5 .002 '(0 1 10 1 100 0) :r .99 :frequency 100 :amp-env-base 1000)) (with-sound () (subtract-pp 0 0.5 .0007 '(0 1 10 1 100 0) :r .999 :frequency 100 :amp-env-base 1000))
This one was inspired by an example from Scott Levine's Ph.D. Thesis that sounded like a rattle. In his example, he was modeling the high-frequency components of a piece of music (after the transients were removed) as Bark-band noise. My rattle has high-frequency noise (one-zero with a1 < 0 or one-pole with b1 > 0) and a triangular envelope.
; Rattle (with-sound () (subtract-op 0 .2 .2 '(0 0 30 1 50 1 100 0) :b1 0.5)) (with-sound () (subtract-oz 0.0 .2 .2 '(0 0 30 1 50 1 100 0) :a1 -0.5) (subtract-op 0.3 .2 .2 '(0 0 30 1 50 1 100 0) :b1 0.5) (subtract-oz 0.4 .2 .2 '(0 0 30 1 50 1 100 0) :a1 -0.5) (subtract-op 0.5 .2 .2 '(0 0 30 1 50 1 100 0) :b1 0.5) (subtract-oz 0.6 .6 .2 '(0 0 30 1 50 1 100 0) :a1 -0.5) )
This non-percussive sound was a side effect discovered when I was playing around with tuning noise to a single frequency using a very narrow band-pass filter (two-pole with r very near 1). With a very sharp frequency peak (two pole with n very near 1), this sounds like a person blowing across the top of a bottle. This is a good sound to look at in SND because it shows how the highly tuned noise looks like a single sinusoidal oscillator.
(with-sound () (subtract-pp 0 1 .0004 '(0 1 70 1 100 0) :amp-env-base 1000 :frequency 400 :r .9999))
Finally, you can't have any fun without setting off some firecrackers! These are band-limited noise (two-pole) with a short duration and a sharp attack. Of course, a 100-pack is even more fun!
; Firecrackers (with-sound () (subtract-pp 0 .4 .1 '(0 1 100 0) :r 0.7 :frequency 4000 :amp-env-base 1000000)) (with-sound () (loop for i from 0 to 100 do (let ((r1 (random 5.0)) (r2 (random 4000)) (r3 (random .2))) (subtract-pp r1 .4 (+ .1 r3) '(0 1 100 0) :r 0.7 :frequency (+ 3000 r2) :amp-env-base 1000000) )))
As I have shown, I found that subtractive synthesis is very good for making noisy percussive sounds, such as rattles and snares. The envelope shape is very important.
For additive synthesis you add together sinusoidal oscillators with different frequencies and amplitudes, and then use an amplitude envelope to control the attack and decay. I also attempted to use wideband and tuned noise to expand the frequency content.
I wrote four algorithms for additive synthesis which are all included in the file drum-add.ins:
The first algorithm, add-partials, is based on an instrument I wrote for a homework assignment. It takes a fundamental frequency and a list of partial numbers and amplitudes, and plays a bank of oscillators based on this data. I also added an optional noise-amp parameter that adds a floor of wideband noise to the sound, in a feeble attempt to widen the frequency content in a useful way.
With this algorithm, I made sounds by trial and error. I followed the basic rule of non-harmonic partials, and used amplitude envelopes to control the attacks.
With a strong fundamental and weak random low non-integer partials, I found a steel drum sound.
; Steel drum (setf steel-drum '(1 1 2 .2 2.6 .1 3.2 .1 5.6 .1 8.2 .1 2.9 .1 3 .1 4.2 .1 6.6 .1)) (with-sound () (add-partials 0 .5 300 .5 steel-drum)) (with-sound () (add-partials 0 .5 350 .5 steel-drum)) (with-sound () (add-partials 0 .5 390 .5 steel-drum))
A medium fundamental amplitude and weak evenly spaced (by 0.3) low non-integer partials led to a clock bell sound.
; Clock bell (setf low-bell '(1 .5 1.3 .1 1.6 .1 1.9 .1 2.2 .1)) (with-sound () (add-partials 0 2 300 .5 low-bell :amp-env '(0 1 10 1 90 .05 100 0)))
I found that either odd or even harmonics, with amplitudes that decreased as the frequency increased, make a high metallic chime sound. The even harmonics produce a cleaner but somewhat more synthetic sound. I also tried both even and odd, but this didn't sound good.
; High metallic chime (setf odd-chime '(1 .3 3 .2 5 .1 7 .1 9 .1)) (with-sound () (add-partials 0 1 1300 .5 odd-chime :amp-env '(0 1 10 1 100 0))) (with-sound () (add-partials 0 1 2078 .5 odd-chime :amp-env '(0 1 10 1 100 0))) (setf even-chime '(1 .3 2 .2 4 .1 6 .1 8 .1)) (with-sound () (add-partials 0 1 1300 .5 even-chime :amp-env '(0 1 10 1 100 0))) (with-sound () (add-partials 0 1 2078 .5 even-chime :amp-env '(0 1 10 1 100 0)))
Next I measured the frequency peaks of some real sounds ( tubular bell, small gong, Turkish cymbal) using SND. Then, after wondering why my data didn't work with the add-partials instrument, I wrote an additive algorithm called add-freqs that took frequency/amplitude pairs as input instead of partial number/amplitude pairs (duhh!). When I tried the instrument with the measured data, here's what I found:
By listening to the original sound and looking at its FFT in SND, it is apparent that the tubular bell has only a few main frequencies that are relatively constant. So, as expected, it reproduces reasonably well with additive synthesis. Strangely, I found that using peak data measured using the length 65536 FFT made a better sound than data from either a shorter (length 16384) FFT or a longer (length 262144) FFT.
; Tubular Bell ; ; original (with-sound (:sampling-rate 44100) (mix-in "/usr/ccrma/web/CCRMA/Courses/220a/Lectures/5/Sounds/tubular-bell.snd" 0)) ; ; using peaks from size 16384 FFT (setf bell '(3.6 .199 161.9 .016 316.4 .038 520.6 .269 770.8 .476 974.7 .007 1062 1.003 1163 .01 1266 .01 1391 .98 1537 .006 1752 1 1899 .012 2141 .134 2555 .345 2988 .124 3191 .01 3438 .047 3870 .009 3957 .009 4389 .014 4870 .037 5357 .013)) (with-sound () (add-freqs 0 2 .1 bell :amp-env '(0 1 10 1 100 0))) ; ; using peaks from size 65536 FFT (setf bell '(.91 0.54 162 .017 317 .035 521 .409 771 .782 943 .005 1062.5 1.003 1182 .005 1266 .018 1391 .670 1511 .005 1633 .006 1752 .662 1899 .009 2142 .057 2377 .005 2555 .118 2988 .019 3437 .015 3813 .006 4868 .004)) (with-sound () (add-freqs 0 2 .1 bell :amp-env '(0 1 10 1 100 0))) ; ; using peaks from size 262144 FFT (setf bell '(.24 3.34 520.85 .342 770.95 .540 1062.6 .624 1391 .324 1752.3 .234)) (with-sound () (add-freqs 0 2 .1 bell :amp-env '(0 1 10 1 100 0)))
By listening to the original sound you can tell that the small gong sound has a moving fundamental frequency, so obviously won't break down into a set of constant frequencies. Additive synthesis captured some of the strange non-integer harmonics, but doesn't do the sound justice.
; Small Gong ; ; original (with-sound (:sampling-rate 44100) (mix-in "/usr/ccrma/web/CCRMA/Courses/220a/Lectures/5/Sounds/small-gong.snd" 0)) ; ; using peaks from size 16384 FFT (setf gong '(3.6 .110 273.2 1.101 371.7 .176 547.6 .352 644.6 .246 717.3 1.075 780 .074 991.5 .165 1082 .244 1150 .108 1203 .093 1257 .104 1357 1.39 1908 .128 2014 .091 2089 .134 2167 .067 2551 .074 2638 .0153 2875 .104 2917 .068 3184 .101 3532 .073 3592 .092 3834 .07 4280 .069 4630 .062 4697 .071)) (with-sound () (add-freqs 0 1 .1 gong :amp-env '(0 0 1 1 10 1 100 0)))
SND shows that the FFT of this sound doesn't have distinct frequency peaks, so it doesn't break down into only a few sinusoids. Additive synthesis doesn't work well on it.
; Turkish Cymbal ; ; original (with-sound (:sampling-rate 44100) (mix-in "/usr/ccrma/web/CCRMA/Courses/220a/Lectures/5/Sounds/turkish-cymbal-1.snd" 0)) ; ; using peaks from size 262144 FFT (setf cymbal '(.24 3.002 100.41 .163 194.65 .156 309.34 .357 399.6 .265 503.7 .271 667 .276 922 .16 1037 .144 1449 .092 1524 .105 1699 .117 1871 .256 2047.6 .125 2920 .121 2196 .19 2269 .157 2415 .163 2720 .117 2867 .225 3540 .162 3644 .112 3733 .091 4430 .116)) (with-sound () (add-freqs 0 1 .1 cymbal :amp-env '(0 1 10 1 100 0))) ; ; using peaks from size 16384 FFT (setf cymbal '(307 .573 398 .619 533 1.005 641 .329 689 .482 1449 .349 1513 .319 2032 .390 3543 .502 3648 .445 3709 .310 4324 .342 4556 .808 .4587 .467)) (with-sound () (add-freqs 0 1 .1 cymbal :amp-env '(0 1 10 1 100 0)))
Seeing the FFT of the Turkish Cymbal sound, which had noisy frequency peaks that were wider than just one frequency gave me my next idea. I wanted to create an additive synthesis algorithm that included tuned noise at each frequency, rather than just a single-frequency sinusoidal oscillator. This would expand the frequency content, and (hopefully) make them more realistic.
First I added a noise floor to the add-partials algorithm which simply added random noise (randh at a frequency of 0.49 * sampling-rate) at a specified level (noise-amp). This didn't help much, and I didn't find any good sounds with this broad-band noise.
Next I created a variation of the add-freq algorithm, called add-noisy-freqs, that adds random noise at a specified level to each frequency. The noise at each frequency is generated by (make-randh :frequency partial-frequency). Unfortunately, when I wrote this I didn't understand how randh worked and I thought that it tuned the noise to the given frequency. So what the algorithm really does is adds random wide-band noise, rather than tuned random noise, to the sum-of-sines signal.
The Turkish cymbal data with a noise amplitude of 1 does sound a little better than the Turkish cymbal without noise, but still doesn't come close to a real cymbal sound.
; Noisy Turkish Cymbal ; ; using peaks from size 262144 FFT (setf cymbal '(.24 3.002 100.41 .163 194.65 .156 309.34 .357 399.6 .265 503.7 .271 667 .276 922 .16 1037 .144 1449 .092 1524 .105 1699 .117 1871 .256 2047.6 .125 2920 .121 2196 .19 2269 .157 2415 .163 2720 .117 2867 .225 3540 .162 3644 .112 3733 .091 4430 .116)) (with-sound () (add-noisy-freqs 0 1 .1 cymbal 1 :amp-env '(0 1 10 1 100 0)))
Finally, I created an instrument called add-noise that uses the ppolar filter with a small radius (r = 0.99) to make tuned noisy oscillators. Thus this algorithm is basically a combination of additive and subtractive synthesis. Since the radius is so small, the gain of the filter (at its tuned frequency) is quite high so care must be taken to lower the signal amplitude considerably. Unfortunately, the ppolar filter isn't stable at low frequencies and it is impossible to control the peak amplitude, so it doesn't work with real sounds such as the Turkish Cymbal data. However, I did manage to create a Noisy Bass Drum, which must be played very quietly or it will distort.
; Noisy Bass Drum (with-sound () (add-noise 0 .5 .0005 '(100 1 200 .5 400 .2) :amp-env '(0 0 1 1 10 1 100 0) :r .99))
As you can hear, additive synthesis works best for pitched sounds with only a few frequencies, such as bells and chimes. It is very difficult to guess what frequencies to use for a sound, but they can be measured using the FFT. Unfortunately, for this project I didn't have time to sample and analyze any real drum sounds.
For FM synthesis, I went right to the source: an algorithm given by John Chowning in "The Synthesis of Complex Audio Spectra by Means of Frequency Modulation", Journal of the Audio Engineering Society, September 1973 (over a year before I was born!).
I made one FM instrument based on one of Chowning's algorithms, which is included in the file drum-fm.ins (along with Jan Mattox's fm-drum).
This instrument, simply called fm, has two oscillators (a carrier and a modulator) and two amplitude envelopes (one for each oscillator). I made some modifications to the instrument, such adjusting the gains of both oscillators and using the frequency ratio of the modulator to carrier as input, rather than the absolute modulator frequency. I also used linear envelopes rather than exponential ones, though I did make optional parameters for my instrument to modify the base (curvature) of each envelope.
Here are some sounds that I created:
First I recreated a bell sound that Chowning describes in his paper, with some slight modifications. This shows that my algorithm works!
; Bell (with-sound () (fm 0 10 1 200 1.4 :car-env '(0 1 50 .2 100 0) :mod-env '(0 1 50 .2 100 0) :mod-index2 1))
Although Chowning gives parameters for a "drum-like" sound, when I tried to reproduce them I got a wooden sound. I also used Chowning's "wood drum" parameters to create a very similar (slightly smoother) wood drum sound. These have a slightly weaker attack than the bell sound, and a quick decay on the modulation envelope. The wood drums shift in pitch quite nicely.
; Wood Drum (with-sound () (fm 0 .2 1 200 1.4 :car-env '(0 .8 20 1 50 .2 100 0) :mod-env '(0 1 12 0 100 0) :mod-index2 .2)) (with-sound () (fm 0 .2 1 300 1.4 :car-env '(0 .8 20 1 50 .2 100 0) :mod-env '(0 1 12 0 100 0) :mod-index2 .2)) (with-sound () (fm 0 .2 1 400 1.4 :car-env '(0 .8 20 1 50 .2 100 0) :mod-env '(0 1 12 0 100 0) :mod-index2 .2)) (with-sound () (fm 0 .2 1 200 0.6875 :car-env '(0 .8 20 1 50 .2 100 0) :mod-env '(0 1 12 0 100 0) :mod-index2 .2))
To design this, I started with the idea that when you hit a metal chime, you get a complex frequency spectrum which decays into a single frequency. Thus I made the modulation envelope decay quickly. I also used a slightly non-harmonic frequency ratio. Wind chimes can be made by playing a sequence of pairs at somewhat random times and frequencies.
; Metallic Chime (with-sound () (fm 0 1 .5 1000 2.005 :car-env '(0 1 50 .2 100 0) :mod-env '(0 1 25 .2 100 0) :mod-index2 1)) ; Wind Chimes (with-sound () (loop for i from 0 to 4 by .15 do (let ((r1 (random .2)) (r2 (random 5)) (r3 (random 5))) (fm (+ r1 i) 1 .6 (+ 800 (* 100 r2)) 2.005 :car-env '(0 1 50 .2 100 0) :mod-env '(0 1 25 .2 100 0) :mod-index2 1) (fm (+ r1 i) 1 .4 (+ 900 (* 100 r3)) 2.005 :car-env '(0 1 50 .2 100 0) :mod-env '(0 1 25 .2 100 0) :mod-index2 1) )))
This was a complete accident (an attempt to create the drum-like sound), but it sounds like a marimba to me.
; Marimba (with-sound () (fm 0 .2 1 400 2.4 :car-env '(0 0.8 10 1 50 .2 100 0) :mod-env '(0 1 50 .2 100 0) :mod-index2 .2)) (with-sound () (fm 0 .2 1 610 2.4 :car-env '(0 0.8 10 1 50 .2 100 0) :mod-env '(0 1 50 .2 100 0) :mod-index2 .2)) (with-sound () (fm 0 .2 1 800 2.4 :car-env '(0 0.8 10 1 50 .2 100 0) :mod-env '(0 1 50 .2 100 0) :mod-index2 .2))
These were attempts at creating bass drums by lowering the frequency. They don't sound natural, but would fit into some dance music.
; Dance Bass (with-sound () (fm 0 .5 5 50 1.4 :car-env '(0 .8 20 1 50 .2 100 0) :mod-env '(0 1 12 0 100 0) :mod-index2 .2)) (with-sound () (loop for i from 1 to 4 by 0.8 do (fm (+ i 0.0) .4 5 80 0.6875 :car-env '(0 .8 20 1 50 .2 100 0) :mod-env '(0 1 12 0 100 0) :mod-index2 2.5) (fm (+ i 0.1) .4 3 80 0.6875 :car-env '(0 .8 20 1 50 .2 100 0) :mod-env '(0 1 12 0 100 0) :mod-index2 2.5) (fm (+ i 0.2) .4 3 80 0.6875 :car-env '(0 .8 20 1 50 .2 100 0) :mod-env '(0 1 12 0 100 0) :mod-index2 2.5) (fm (+ i 0.3) .4 5 80 0.6875 :car-env '(0 .8 20 1 50 .2 100 0) :mod-env '(0 1 12 0 100 0) :mod-index2 2.5) (fm (+ i 0.5) .4 3 80 0.6875 :car-env '(0 .8 20 1 50 .2 100 0) :mod-env '(0 1 12 0 100 0) :mod-index2 2.5) (fm (+ i 0.6) .4 3 80 0.6875 :car-env '(0 .8 20 1 50 .2 100 0) :mod-env '(0 1 12 0 100 0) :mod-index2 2.5) (fm (+ i 0.7) .4 4 80 0.6875 :car-env '(0 .8 20 1 50 .2 100 0) :mod-env '(0 1 12 0 100 0) :mod-index2 2.5)))
When I tried playing with mod-index1 it distorted and made a brushed snare sound. It's not a great sound, but it does have some snare-like qualities.
; Brushed Snare (with-sound () (fm 0 .3 .5 200 1.4 :car-env '(0 1 10 1 50 .2 100 0) :mod-env '(0 1 50 .2 100 0) :mod-index1 2 :mod-index2 1))
This sound just happened, and I couldn't throw it away. The modulation envelope is what gives it its cool sound. I have a friend who writes Industrial music who would love it!
; Sheet Metal (with-sound () (fm 0 1 1 40 2.2 :car-env '(0 .8 20 1 50 .2 100 0) :mod-env '(0 0 12 1 100 0) :mod-index2 2))
In a desperate attempt to create SOMETHING that sounded like an acoustic drum, I also played with Jan Mattox's FM Drum. By randomly playing with the parameters, I was able to make some bass, electronic snare, and tom sounds, but none of them are particularly good.
; Bass Drum (with-sound () (fm-drum 0 1 40 .2 5)) (with-sound () (fm-drum 0 4 20 .4 5))
; Electronic Snare (with-sound () (fm-drum 0 .3 100 .1 10000))
; Electronic Tom Tom (with-sound () (fm-drum 0 .3 240 .1 4)) (with-sound () (fm-drum 0 .3 220 .1 4)) (with-sound () (fm-drum 0 .3 200 .1 4)) (with-sound () (fm-drum 0 .3 180 .1 4))
With FM synthesis, I was able to create sounds with a lot of frequencies, while only using two oscillators and envelopes (and a few other scalar parameters). I found that the envelopes and indexes controlled the sound in a fairly confusing way. Unfortunately, I wasn't able to get the "drum-like" sounds described in Chowning's paper (perhaps simply because my ears are pickier).
Another interesting synthesis technique that I tried was an algorithm created by Kevin Karplus and Alex Strong, and described in their paper "Digital Synthesis of Plucked-String and Drum Timbres", Computer Music Journal, September 1983. The algorithm is as follows:
Start with a wavetable X of length p, and use randomness to create each sample from two previous samples.
/ +1/2 * [X(t-p) + X(t-p-1)] with probability b X(t) = { (t > p) \ -1/2 * [X(t-p) + X(t-p-1)] with probability 1-b
I created two versions of this algorithm - one in Matlab and one in CLM. In this paper I will only discuss the CLM version which is included in the file drum-ks.ins; if you are interested in the Matlab version, see drum_KS.m and drum-ks.txt.
Since b introduces randomness into the sound, the initial wavetable can be anything from a completely random signal to a sine wave to a constant! I found a random wavetable easiest to work with, and use it exclusively in my CLM version of the instrument.
The probability b is called the blend factor and can range from 0 to 1 (although values of exactly 0 and 1 only work with a random wavetable). b = 1/2 introduces the most randomness and produces the best "snare" sounds. b near 1 produces metallic and weird electric crash cymbal-like sounds where most of the pitches, except for the fundamental, die out quickly. b near 0 simply averages the samples, and produces string-like sounds.
The wavetable length p effects the decay rate of the sound (large p = long decay) as well as the pitch somewhat (large p = low pitch). I found that the algorithm works with p in a range from 20 to 4000, depending b and what kind of sound you want.
Here are some of the sounds I was able to create:
With a blend factor of about 0.5 and a wavetable length of about 1000, I was able to create a snare sound.
; Snare (with-sound () (drum-ks 0 .5 :p 800 :b 0.5)) (with-sound () (drum-ks 0 .5 :p 1000 :b 0.6))
With a blend factor near 1 and a long wavetable, a weird kind of electronic crash cymbal is created. I had to add an amplitude envelope to decay the sound.
; Cymbal (with-sound () (drum-ks 0 .5 :p 4000 :b 1 :amp-env '(0 1 90 1 100 0))) (with-sound () (drum-ks 0 .5 :p 2000 :b 1 :amp-env '(0 1 80 0 100 0)))
A short wavetable with a blend factor near 1 creates a metallic sound; again I had to use an envelope to decay some of the sounds. These have an interesting timbre.
; Metallic Plink (with-sound () (drum-ks 0 .5 :p 100 :b .98 :amp-env '(0 1 90 1 100 0))) (with-sound () (drum-ks 0 .5 :p 20 :b .997)) (with-sound () (drum-ks 0 .5 :p 50 :b .99)) (with-sound () (drum-ks 0 .5 :p 150 :b .99 :amp-env '(0 1 90 1 100 0))) (with-sound () (drum-ks 0 .5 :p 25 :b 1))
The Karplus-Strong algorithm was originally created to make plucked string sounds, and I found that it does just that with a blend factor of 0.
; Plucked String (with-sound () (drum-ks 0 .5 :p 40 :b 0 :amp-env '(0 1 90 1 100 0))) (with-sound () (drum-ks 0 1 :p 25 :b 0)) (with-sound () (drum-ks 0 .5 :p 200 :b 0 :amp-env '(0 1 50 1 100 0) :duration 2)) (with-sound () (drum-ks 0 .5 :p 400 :b 0 :amp-env '(0 1 50 1 100 0) :duration 2))
One interesting characteristic of the Karplus-Strong algorithm is that it is designed to be very hardware-efficient. It only requires 1-bit of randomness, one addition/subtraction, and a 1-bit shift (to divide by 2) for each sample. However, in software where every 1-bit shift requires at least one 32-bit instruction and sequential code is very inefficient compared to block codes, it is actually a very slow algorithm.
Another interesting characteristic of this algorithm is that it has a built-in decay, although for some sounds an envelope was required to speed it up.
I didn't play with Granular Synthesis too much, except to make a few sounds. A copy of Fernando Lopez-Lezcano's grani instrument, along with some sample sounds, can be found at drum-grani.ins.
For this, I simply used grani to make a Turkish cymbal sound by taking apart a Turkish cymbal and putting it back together again, with a somewhat different envelope.
; Fixed Broken Cymbal (with-sound()(grani 0 2 20 "/usr/ccrma/web/CCRMA/Courses/220a/Lectures/5/Sounds/turkish-cymbal-1.snd" :grain-envelope '(0 1 100 0) :amp-envelope '(0 0 10 1 100 0))) ; (with-sound()(grani 0 4 5 "/usr/ccrma/web/CCRMA/Courses/220a/Lectures/5/Sounds/turkish-cymbal-1.snd" :grain-envelope '(0 1 100 0) :amp-envelope '(0 0 10 1 100 0) :grain-density 20 :reverse t))
With only a few grains per second, a tubular bell can be given a built-in rhythm.
; Rhythm Bell (with-sound() (grani 0 2 10 "/usr/ccrma/web/CCRMA/Courses/220a/Lectures/5/Sounds/tubular-bell.snd" :grain-envelope '(0 1 100 0) :amp-envelope '(0 1 50 1 100 0) :grain-density 4 :reverse t) (grani 1 2 10 "/usr/ccrma/web/CCRMA/Courses/220a/Lectures/5/Sounds/tubular-bell.snd" :grain-envelope '(0 1 50 1 100 0) :amp-envelope '(0 1 50 1 100 0) :grain-density 4) (grani 2 2 10 "/usr/ccrma/web/CCRMA/Courses/220a/Lectures/5/Sounds/tubular-bell.snd" :grain-envelope '(0 1 100 0) :amp-envelope '(0 1 50 1 100 0) :grain-density 8 :reverse t) (grani 3 2 10 "/usr/ccrma/web/CCRMA/Courses/220a/Lectures/5/Sounds/tubular-bell.snd" :grain-envelope '(0 1 100 0) :amp-envelope '(0 1 50 1 100 0) :grain-density 4 :reverse t) )
Like any project, this one probably created more questions than it answered. Fortunately, I intend to do quite a bit more research in this area. Here are some of the ideas that this project inspired:
First, I would like to analyze more real sounds, especially actual drums, and try to figure out what other measurable parameters affect their timbre.
I would like to try subtractive synthesis with a variety of other filters, including comb filters and better band-pass filters. I would like to use a better noise generation function in order to get a flatter (unfiltered) frequency response. I would also like to try filters with time-varying coefficients to change the frequency characteristics of the sound over time.
For additive synthesis, I would like to use more complex methods that involve amplitude and frequency envelopes, rather than stationary ones. I would like to correctly implement my additive-subtractive algorithm that uses highly tuned noise as oscillators. I would also like to develop automated methods that analyze real sounds and simulate them using only a few parameters.
I would also like to make an FM drum that sounds good. I know that it is possible with the right combination of oscillators and envelopes, if I can figure out how to control them. I definitely have a lot more to learn about the mathematics of FM and drum sounds.
Finally, I would like to try other synthesis techniques, such as breaking up sounds into different frequency bands and then performing different techniques on each band (like Scott Levine's Thesis).
I learned a lot from this project. I learned how to characterize percussive sounds by their amplitude envelope and frequency content, creating a sharp attack, slower decay, and non-integer harmonics or noise in the frequency spectrum. I learned how to try to synthesize these parameters using CLM, and in the process I learned a lot about CLM.
I found that subtractive synthesis is good for making noisy sounds, such as snares, firecrackers and rattles, because it contains many random frequencies. Additive synthesis is good for somewhat pitched percussive sounds, such as bells, steel drums and chimes. It is difficult to guess what frequencies and amplitudes to use for the osicllators, but it is easy to analyze real sounds using FFT measurements (although only some sounds are suitable for additive synthesis). FM synthesis provides an simple way to generate a lot of frequencies from only a few parameters, although it was hard to figure out how to control. It is also easy to go from many pitches at the beginning of a sound to only one at the end by decaying the modulator amplitude envelope. Finally, the Karplus-Strong is a very different way of creating sounds, because it uses a probabilistic sample-by-sample function. It is good for snares, strings, and weird cymbal sounds.
Overall, I would have to say that the two easiest sounds to synthesize decently were the snare and the bell (chime). This is because the snare is mostly noise with a sharp attack and exponential decay, and the bell (chime) can reasonably be simulated with only a few non-harmonic frequencies. The hardest sound to synthesize was the tom-tom, which I was never sucessfully able to reproduce. Of course that's a good thing, because it means that there's more research to be done...
© Copyright 1998 Stephen Dill.