Pan Flute
An eight-pipe pan flute playing a major scale, with every pipe length computed from its note: each pipe is a closed tube whose sounding length is a quarter wavelength less the open-end correction, so changing the key or the bore changes all eight lengths. The pipes stand side by side with their mouths level and are joined by two bars across the back; blow across the open tops. The default is C5 to C6 with 10 mm bores and measures 106 x 14 x 163 mm. Prints standing on the closed ends with no supports; the chamfer at each mouth is the blowing edge. Adjustable parameters: lowest note, bore, wall thickness and the air temperature used for the speed of sound.
# Pan Flute - eight closed pipes of a major scale, lengths computed from the note frequencies
#
# Each pipe is a closed tube: its sounding length is a quarter wavelength less the open-end
# correction (0.6 x inner radius), so the length of every pipe comes from its frequency.
# The pipes stand side by side with their mouths level, joined by two bars across the
# back. Blow across the open tops. `key` picks the lowest note; the scale is major.
# Prints standing on the closed ends with no supports; chamfer at each mouth is the
# blowing edge.
@param choices:["C5", "D5", "F5", "G5", "C6"] desc:"Lowest note (the scale is major)"
$key = "C5"
@param 8..14 step:0.5 group:"Pipes" desc:"Inner diameter of the pipes (mm)"
$bore = 10
@param 1.2..2.4 step:0.2 group:"Pipes" desc:"Wall thickness (mm)"
$wall = 1.6
@param 15..30 step:1 group:"Pipes" desc:"Air temperature for the speed of sound (deg C)"
$temp = 20
$f0 = if $key == "C5" then 523.25 else if $key == "D5" then 587.33 else if $key == "F5" then 698.46 else if $key == "G5" then 783.99 else 1046.5
$semis = [0, 2, 4, 5, 7, 9, 11, 12]
$n = 8
$c = (331.3 + 0.606 * $temp) * 1000 # speed of sound, mm/s
$r = $bore / 2
$od = $bore + 2 * $wall
$floor_t = 2
$freqs = [$f0 * 2 ** ($semis[$i] / 12) for $i in range($n)]
$lens = [$c / (4 * $freqs[$i]) - 0.6 * $r for $i in range($n)] # sounding length of each pipe
$top = $lens[0] + $floor_t # mouths level at this height
$xs = [($i - ($n - 1) / 2) * $od for $i in range($n)]
assert $lens[$n - 1] > 30
print "pipe lengths (mm)" $lens
print "frequencies (Hz)" $freqs
# --- Pipes: each stands on the bed, bottom closed, top at the common mouth line ---
def pipe($i) = (cylinder ($od / 2) ($lens[$i] + $floor_t) center:(true, true, false) | faces >Z | chamfer ($wall * 0.6))
- (cylinder $r ($lens[$i] + 1) center:(true, true, false) | translate 0 0 $floor_t)
| translate ($xs[$i]) 0 ($top - $lens[$i] - $floor_t)
$pipes = union [pipe($i) for $i in range($n)]
# --- Bars across the back, at the shortest pipe's foot and just under the mouths ---
$bar_w = ($n - 1) * $od + $od
$bar_z1 = $top - $lens[$n - 1] - $floor_t + 4
$bar_z2 = $top - 14
$bars = union [box $bar_w 3 6 center:(true, false, false) | translate 0 (-$od / 2 - 1) $z for $z in [$bar_z1, $bar_z2]]
$flute = $pipes + $bars
assert $flute.solids == 1
assert $flute.bbox.x ~ $bar_w
assert $flute.bbox.z ~ $top
assert $flute.bbox.min[2] ~ 0 tol:0.01
# the shortest pipe's cavity is really there: the whole flute weighs less than the solid pipes
$solid_pipes = union [cylinder ($od / 2) ($lens[$i] + $floor_t) center:(true, true, false) | translate ($xs[$i]) 0 ($top - $lens[$i] - $floor_t) for $i in range($n)]
assert $flute.volume < $solid_pipes.volume
print "flute" $flute.bbox
$flute