//whistle stack
//Holland Hopson
//2021
//generates a stack of whistles with a specific fundamental pitch and each segment tuned an interval above the segment below
//requires hopson_attachment_bar.scad
//Adjust Hz to tune whistle and interval to determine the relative tuning of each segment in the stack
include ;
$fn = 100; //make high resolution
//customize these variables
Hz = 1350; //fundamental frquency of whistle
interval = 0.666666; //specifies reduction of size of each segment and thus the sounding interval of each whistle segment.
//sample values:
//0.5 = octaves
//0.666666 = perfect 5th 3/2
//0.75 = perfect 4th 4/3
//0.8 = pythagorean major 3rd 5/4
//0.88888 = pythagorean second 9/8
//etc.
minSegmentHeight = 15; //set the minimum height of the smallest segment.
//this effectively controls the number of whistle segments in the stack
//<15mm is typically too small to sound, but this value depends on the resolution of your printer
diameter = 30; //width of object. 30mm works well here. Could be larger, but a stack of whistles requires significant material to print.
z=30; //amount of rotation in degrees for each whistle segment. Experiment with this value to create arpeggio-like effects
//constants and calculations
speed_of_sound = 343.; //
height = ((speed_of_sound / (Hz * 8.))*1000.); //multiply by 1000 to get mm
sectionHeight = height; //initialize first section. defined as special variable so it can be updated
//echo(height, " mm");
//echo(height * 0.1, " cm"); //print height in cm
offset = 0.001; //constant to use when ofsetting an object so it joins another object rather than resting against it
//variables for attachment bar. These overwrite the local variables in hopson_attachment_bar.scad
bar_radius = 2.5; //was 1.5mm, but was prone to breakage
cavity_diameter = 20; //At least 10mm, but less than 40mm (or diameter of whistle)
cavity_depth = 2*(bar_radius + 5); //2mm to provide clearance for smaller swivel. 5mm for larger swivel. May need to be closer to 10mm deep. Multiply by 2 because we're using a sphere, so this number is the diameter
//build the whistle stack
whistle_stack(height);
//attachment bar bottom
translate([0,0,(-1*cavity_depth/2)+offset]){ //move to bottom of cylinder
rotate([180,0,0]){
attachmentCavity();
attachmentBar();
}
}
module whistle_stack(sectionHeight) //s = section height
{
//constants from module parameters. Hard-coded here
slit_width=sectionHeight*0.125; //this was hard-coded at 5.5mm
wall_thickness=2;
top_bottom_thickness=1;
angle=50; //angle of slit
//constants and calculations
//echo(sectionHeight);
slit_length = sectionHeight/3; //one third of height of cylinder
translate([0,0,sectionHeight/2]) //position bottom of object on origin
{
difference()
{
union() { //objects to be added to the design
//outer shell
cylinder(h = sectionHeight, r = diameter/2, center=true);
}
union() { //objects to be subtracted from the design
//inner space
cylinder(h = sectionHeight-(2*top_bottom_thickness), r = (diameter/2)-wall_thickness, center=true);
//slit
translate([0, -diameter*0.5, 0]) { //move slit so it pokes out the front side
rotate([0, 0, angle]) { //rotating too far eats into the inside wall
cube([slit_width,diameter*0.65,slit_length], center=true);
}
}
//channel to direct air to edge
translate([0, -diameter*0.5-(slit_length/2)+(wall_thickness*0.25), 0]) {
rotate([90, 90, angle*2]) { //rotate cylinder so rounded edge is approx. perpendicular to slit
scale([2,1,1]) //squash cylinder so it contacts the surface more broadly
cylinder(r = slit_length/2, h = slit_length);
}
}
}
}
}
if(sectionHeight>minSegmentHeight) //conditional to call recursion as long as height is large enough
translate([0,0,sectionHeight-offset]){ //move base of next branch of tree
rotate([0,0,z]) whistle_stack(sectionHeight*interval); //recursion
echo("section height ", sectionHeight);
}
}