Simulate a very simple woodwind with Lambda (2D fdtd)

We simulate a flute-like woodwind to prove that the air escapes the instrument at the first open hole. The shape of the instrument is actually of little importance for this specific experiment.

In [1]:
import lambdasim as l
from IPython.display import *

Simulation space

We draw the walls of our instrument. The instrument will be around 90cm in width. We define alse a source at the left wall (red), and receivers ("microphones") at the first and last hole, as well as at the right opening (blue pixels).

In [2]:
simimg = "walls4.png"
info = l.util.png_info(simimg)
print(info)
Image("./walls4.png")
{'bitdepth': 8, 'interlace': 0, 'planes': 4, 'greyscale': False, 'alpha': True, 'size': (1100, 165)}
Out[2]:
In [18]:
Xpix, Ypix = info['size']
X = 1

s = l.Simulation(resolution=X/Xpix).set_size_in_pixels(Xpix, Ypix)
first_hole_x, first_hole_y = s.pix2coord(360, 100)
x0, _ = s.pix2coord(17, 0)
resfreq = l.acoustics.wavelen2freq(first_hole_x - x0)*0.5

s.wall_from_image(simimg, color=(255,255,255), dist=0.2)
source_mat = l.img_to_mask(simimg, (255,0,0), dist=0.2)

source = s.source_from_array(source_mat, kind='hannsin', amp=0.3, freq=resfreq)

#s.source_point(x0+0.01, first_hole_y, 'hannsin', freq=resfreq, amp=0.3)
#s.source_point(x0+0.01+s.nodesize, first_hole_y+s.nodesize, 'pinknoise', amp=0.1)

print(s)
print("")
print(source)
Adjusting pixel size: Y 165 -> 166
using backend: pyPNG
(181500, 3)
adapt: crop
using backend: pyPNG
(181500, 3)
Simulation
----------
nodesize    : 0 mm
samplerate  : 533582 Hz
C           : 343.0 m/s
rho         : 1.204 kg/m3
size        : 1.00 m x 0.15 m
matrix      : 1100 pixels x 166 pixels
num. sources: 22
num. recs.  : 0
num. samples: 0

SourceList
----------
numpoints: 22
freqs    : set([555.0831110227851])
coords   : (16, 85) - (16, 106)

Define the receivers

In [19]:
recvpix = l.mask_to_pixels(l.img_to_mask(simimg, (0,0,255), 0.1))
recvpix.sort(key=lambda (x,y):y) # sort by x, then by y
recvpix.sort(key=lambda (x,y):x)
for i, (xpix, ypix) in enumerate(recvpix):
    x, y = s.pix2coord(xpix, ypix)
    
    #s.make_receiver(x, y, "recv%d-%dx%d"%(i, xpix, ypix))
    s.make_receiver(x, y)
using backend: pyPNG
(181500, 3)

Write down the simulation, discard the angles

In [20]:
print(s.receivers)
s.angmat = None
s.write("ww1")
[(0.32711370262390665, 0.069387755102040802, ''), (0.69567983037370784, 0.068486615425390918, ''), (0.897535117943281, 0.086509408958388531, '')]
Out[20]:
Simulation(samplerate=533582.8, size=(1.0, 0.2), duration=1.000)

Open the simulation for testing

In [21]:
s.opensim()
Calling Lamda as: ['/Applications/Lambda.app/Contents/MacOS/Lambda', '-file', '/Users/em/proj/simulations/woodwind/ww1.sim', '-vis', '-walls', '-contrast', '50']
Out[21]:
<subprocess.Popen at 0x107f23590>

When done, render the video

In [7]:
s.render_video(duration=0.5, contrast=70, fps=60, cmap=2)

Render the audio to measure phase shifts, interference and rms

In [10]:
fut = s.render_wav(duration=0.5, resample=88200, split=True)
In [11]:
fut.result()
# the results are sorted first by Y, then by X. My mistake here, 1st and 2nd receiver are swapped
Calling Lamda as: ['/Applications/Lambda.app/Contents/MacOS/Lambda', '-rce', '-exit', '-file', '/Users/em/proj/simulations/woodwind/ww1.sim', '-iterations', '133566']
saving channel 0 to /Users/em/proj/simulations/woodwind/ww1-recv0-363x77.wav
saving channel 1 to /Users/em/proj/simulations/woodwind/ww1-recv1-772x76.wav
saving channel 2 to /Users/em/proj/simulations/woodwind/ww1-recv2-996x96.wav
Out[11]:
['/Users/em/proj/simulations/woodwind/ww1-recv0-363x77.wav',
 '/Users/em/proj/simulations/woodwind/ww1-recv1-772x76.wav',
 '/Users/em/proj/simulations/woodwind/ww1-recv2-996x96.wav']

Plot the results (Sonic Visualizer)

The results are ordered from left (top) to right (botton). Here you see that the leftmost hole is by far were most of the emission is taking place, more than 20dB louder than at the right ending of the instrument.

In [12]:
Image("./ww1-rms.png")
Out[12]:

Phase shift

The normalized soundpressure waves show the phase shifting of the wave across the instrument as well as some resonance at the 1/4 lambda and some comb filtering.

In [14]:
Image("./ww1-phase.png")
Out[14]:

Video rendering

In [22]:
HTML("""
<video controls id="ww1">
<source src="https://dl.dropboxusercontent.com/u/264776/web/luftzug/ww1.webm">
<source src="https://dl.dropboxusercontent.com/u/264776/web/luftzug/ww1.mp4">

</video>
<script type='text/javascript'> 
document.getElementById('ww1').playbackRate = 2;
document.getElementById('ww1').defaultPlaybackRate = 2;
</script>

""")
Out[22]: