Loud Whistle
A pea-less emergency whistle that is very loud for its size. Air from the mouthpiece passes through a 1 mm windway and is split by a sharp edge into a cylindrical resonator; the window position is solved with a quadratic equation in the code so the edge tip always sits exactly at the center of the airflow. The outer shell joins the mouthpiece to the cylinder with tangent surfaces, and a strap hole is built in. Overall size is 44 x 21 x 16 mm. Print it as exported, standing on the strap-hole end, with no supports; this keeps the windway gap in the plane of each layer, and a fine layer height (0.12-0.16 mm) is recommended. You can change the resonator radius and length, wall thickness, windway width and gap, window length, edge bevel, mouthpiece length, and the strap hole position.
# Loud Whistle - cylindrical resonator, tangential windway and a sharp edge (labium)
# Designed with X = direction of the airflow (mouthpiece on -X), Y = resonator axis, Z = up (edge side),
# then stood up 90 deg around X so the cylinder end with the strap hole becomes the Z = 0 base.
# In this orientation the windway, window and edge profiles (design XZ plane) lie within each layer,
# so the 1.0mm gap comes from nozzle accuracy rather than layer height. The STL can go straight to the slicer
@param 5..12 step:0.5 desc:"Resonator inner radius (mm)"
cham_r = 8.0
@param 6..20 desc:"Resonator inner length along its axis (mm)"
cham_l = 12
@param 1.6..3 step:0.1 desc:"Wall thickness (mm)"
wall = 2.0
@param 4..12 desc:"Windway width (mm)"
ww = 8
@param 0.6..1.6 step:0.1 desc:"Windway height = air gap (mm)"
wh = 1.0
@param 1.5..4.5 step:0.1 desc:"Window length, from windway exit to edge tip (mm)"
win = 2.8
@param 20..45 desc:"Edge top bevel angle (deg)"
bevel = 35
@param 15..35 desc:"Mouthpiece length (mm)"
mouth_l = 22
@param desc:"Put the strap hole at mid-height (floats in print orientation, needs support)"
ring_mid = false
floor_t = 0.6 # floor thickness at the windway exit (height of the floor end face)
body_l = cham_l + 2 * wall
# --- Solve the window position with a quadratic -----------------------------
# With the resonator center at the origin, put both the windway exit x0 and the edge tip x1
# on the resonator inner wall (circle). Conditions:
# point (x0, zl - a) on the circle ... bottom of the floor end face (a = wh/2 + floor_t)
# point (x1, zl) on the circle ... edge tip = airflow center height zl
# x1 - x0 = win
# With p = sqrt(R^2 - (zl-a)^2), q = sqrt(R^2 - zl^2): p - q = win, p + q = (2a*zl - a^2)/win
a = wh / 2 + floor_t
k = 2 * a / win
m = -(a * a) / win - win
qa = k * k + 4
qb = 2 * k * m
qc = m * m - 4 * cham_r * cham_r
zl = (-qb + sqrt(qb * qb - 4 * qa * qc)) / (2 * qa) # airflow center height = edge tip height
x1 = -(k * zl + m) / 2 # edge tip
x0 = x1 - win # windway exit
zf = zl - wh / 2 # windway floor
zr = zl + wh / 2 # windway ceiling
mouth_bot = zf - 3 # mouthpiece bottom face
# Make the cylinder top and the mouthpiece top the same height, so the top meets the cylinder tangentially.
# Keep a full wall above the windway ceiling, so the radius is the larger of the resonator and ceiling sides
outer_r = max(cham_r + wall, zr + wall)
# Tangent point T (lower side) of the tangent from the mouthpiece bottom corner P to the circle.
# The bottom face also meets the cylinder tangentially
px = x0 - mouth_l
pd = sqrt(px * px + mouth_bot * mouth_bot)
ta = atan2(mouth_bot, px) + acos(outer_r / pd)
tx = outer_r * cos(ta)
tz = outer_r * sin(ta)
# Extrude an XZ profile centered in Y
def yslab(pts, w) = workplane XZ | polyline pts | extrude w | translate 0 (-w / 2) 0
def ycyl(r, l) = cylinder r l | rotate 90 0 0
# --- Outer shape -------------------------------------------------------------
# Circle and mouthpiece in one cross-section: P -> tangent point T -> arc (around bottom and right to the top) -> mouthpiece top
$shell = workplane XZ
| sketch [(px, mouth_bot), (tx, tz), arc (tx, tz) (outer_r, 0) (0, outer_r), (px, outer_r)]
| extrude body_l | translate 0 (-body_l / 2) 0
# Strap hole. By default flush with the -Y end (the base when printing); with ring_mid it sits at Y center
ring_y = if ring_mid then 0 else -body_l / 2 + 2
ring = ycyl(4.5, 4) - ycyl(2.5, 6) | translate (outer_r + 3) ring_y 0
# --- Cavities ----------------------------------------------------------------
chamber = ycyl(cham_r, cham_l)
windway = yslab([(x0 - mouth_l - 1, zf), (x0 + 0.5, zf), (x0 + 0.5, zr), (x0 - mouth_l - 1, zr)], ww)
# Window + edge bevel: cut with a line at angle bevel through the edge tip (x1, zl)
zt = outer_r + 3
zb = zl - win * tan(bevel) # bevel line extended back to x0 (inside the resonator)
window = yslab([(x0, zb), (x1 + (zt - zl) / tan(bevel), zt), (x0, zt)], ww)
whistle = union [$shell, ring] | diff (union [chamber, windway, window])
# Print orientation: standing it up 90 deg around X puts the design -Y end (strap hole side) on the bed
whistle | rotate 90 0 0 | floor