scotw-005: Wind Chime in a Box

What is a wind chime?

At its most general, a wind chime is a collection of mutually interacting pendulums. There is a central pendulum (called the clapper) surrounded by 3-5 resonator pendulums, traditionally made of either metal tubes or wooden blocks. All of these are mounted to one or more pivot points and move under the forcing action of the wind. At small displacements, pendulums can be modeled as simple harmonic oscillators, however, this approximation breaks down at larger displacements. The resonators are designed to be much more massive than the clapper, such that most of the energy transferred to them turns into sound (vibration) rather than motion. (Imagine a large cast-iron bell on wheels: if we throw a tennis ball at it, it will ring; if we throw a bowling ball at it, it will roll away.)

Why is it that wind chimes are so pleasant to listen to? Most likely, it is because they strike the right balance between simplicity (or predictability) and complexity (or unpredictability). The wind chime is simple because it generates only a small number of pitches (i.e., the number of resonators), and these pitches are fixed for all time. So, harmonically speaking, nothing is happening. In the time domain, on the other hand, the behavior of the system is quite complex. The coupled, non-linear oscillators of the wind chime give rise to chaotic behavior. In addition, the wind, being a turbulent flow under common atmospheric conditions, is itself chaotic. And so this part of the system is essentially unpredictable.

Let’s not do that

So, a wind chime is quite complicated (as a side note, most implementations use a stochastic approach rather than try to model the physics). Let’s build something simpler:

  • Four resonators are arranged at the sides of square
  • The resonators have infinite mass (i.e. are completely rigid)
  • The clapper is not a pendulum, but rather a ball moving through space
  • The ball loses a fixed proportion of its energy with every collision with a resonator/wall
  • At fixed time intervals, we increase the energy of the ball by a constant factor (this is a kind of “wind gust”)

A pleasant detour

Before getting further into the physics of the system, let’s have some fun talking about synthesis. We are all aware that instruments such as the glockenspiel, marimba, clave, and church bells have unique timbres when compared to more “conventional” instruments such as the violin or the flute. This is because, in the former instruments, the overtone frequencies are not integer multiples of the fundamental frequency. Here is a Csound file that gives harmonic spectra for a variety of percussion instruments. If we scroll down to “uniform wooden bar,” we see the following list of partials: 1, 2.572, 4.644, 6.984, 9.723, 12. Let’s make a synth with these partials! Let’s make the amplitude of the i-th partial fall of as 1/i. And let’s adjust the respective envelopes so that the lower partials have a slow attack and slow decay and the higher partials have a fast attack and fast decay. Here’s what it all looks like in Supercollider code:

1
2
3
4
5
6
7
8
SynthDef(\woodBlock, {|freq = 220, amp = 0.1, sustain = 1, bright = 1, pan = 0, out=0|
    var partials = [1, 2.572, 4.644, 6.984, 9.723, 12];
    var sig = Array.fill(6, {|i|
        var i_sig = SinOsc.ar(partials[i]*freq, rrand(0,2pi), (1+(bright*((i/5)-1)))/(i**2.5+1));
        var i_env = EnvGen.kr(Env.perc(0.06/(i+1), sustain/(i+1), amp/2), doneAction: (i==0).asInt * 2);
        i_sig*i_env}).sum;
    Out.ar(out, Pan2.ar(sig, pan));
}).add;

We use the Array.fill method to generate an array of enveloped partials. It takes the number of items to make as its first argument and a function to evaluate as its second. We then sum this array using the sum method. You’ll also notice a bit of tomfoolery surrounding the doneAction, which sets the action that Supercollider will take once the envelope is completed. The normal doneAction is 2, which will free the enclosing Synth. In this case, however, if we simply set a doneAction of 2, then the shortest envelope (which corresponds to the highest partial) will end up gating the whole Synth. In this case, we’d only like to release the Synth when the longest envelope (lowest partial) has completed. We do this by multiplying by the Boolean value (i==0). When i != 0, we get a doneAction of 0, which simply means do nothing. (Note that if statements are not allowed in SynthDefs.)

Implementation

Ok, back to work. The implementation is actually pretty simple. We set up variables to represent the position and velocity of the ball. We make a square window and put a UserView inside it. We then drive everything from the associated DrawFunc. First, we check if we’ve hit a wall. If so, we do the following things:

  1. Move the ball one pixel away from the wall (to avoid retriggering at low velocities)
  2. Play the pitch corresponding to that wall with a loudness proportional to the impinging velocity
  3. Reverse the velocity
  4. Add a bit of energy to the wall

(What, what’s that last bit about? In order to give the windchime a little more complexity, I decided to introduce the idea of wall energy. Every time the ball hits the wall, it transfers some energy to it in an amount proportional to its velocity (in the impinging direction). Meanwhile, the wall is losing energy to the environment at a constant rate (called ~dissipation). We use the wall energy to scale the detuning of the pitch. When the wall has a lot of energy, the resulting pitch is more likely to be out of tune. This may not be physically realistic, but I like the way it sounds.)

After checking for collisions, we simply increment the position using the current velocity values and draw the ball. Finally, we update all the energies using the ~dissipation constant.

To create periodic wind gusts, we simply define a routine that multiplies the current velocity by a (randomly-chosen) constant. For fun, we change the color of the ball every time this happens.

Code below, sounds even better if you play two at a time…