Since the rest of my final projects were music-related, I thought I would give the final final project the same treatment, but with a bit of a twist. 3D printing has found itself a great place in a special education classroom, as it allows teachers to print specifically-designed tools for students with special needs. Since teaching to students with special needs is a large area of discussion and research within the music education community, I thought I might design something that would enable a student to be more involved in classroom music activities. This led me to think of Boomwhackers.
For those that are not familiar, Boomwhackers are plastic tubes designed to be hit to sound a melodic pitch. These are becoming more and more common in schools, especially at the elementary school level. While various tools have already been created to accommodate students with special needs, little tools exist for those that are visually impaired. Students with a good ear could hit each one until they find the right note, but this is time-consuming and assumes that the student has decent absolute pitch. To correct this, I thought I would design Boomwhackers with braille embossed on them to better assist these students.
Research
This required a fair amount of research (and relearning math) to know everything needed to put this project together. First, I had to learn some braille, as I previously knew none at all. Fortunately, there are many charts online for learning braille, including ones for learning music braille. This chart (pictured below) from Wikipedia and this table of braille dimensions were my primary sources for reference with regards to braille.
After getting the information needed to write braille, I then needed to figure out a formula for finding the frequencies of each note (so that I would not have to spend a lot of time copying data from a table) and then use that to figure out wavelength. After spending a couple of hours relearning math, I figured out that the best way to approach calculating the frequencies was to use an exponential function. Using the notes A2, A3, and A4 as examples, we know that the note A4 is equal to 440Hz, as this is what all modern music is tuned to. Looking at a table for reference I found that it was an exponential function because an octave below A4, A3, is 220Hz and an octave below that, A2, is 110Hz. This make the rate of change 2x, making it exponential. Unfortunately, traditional music notation has no way of uniquely identifying each note in order to complete this formula (yes, there is the previously mentioned A2, A3 etc., but this would not work on a graph). Luckily, there is a solution in modern music technology with the MIDI. Musical Instrument Digital Interface (often shortened to MIDI) is an interface that allows for electronic instruments to interact with computers. They accomplish this by giving each note a unique value, as shown in the table below.
Now that each note has a unique identifier, I can calculate the function needed to get the frequencies of the notes.
Luckily, getting the function for wavelength is not near as complicated, a simple Google search gave me the function I would need.
With this research under my belt, it was time to start the design.
Design
First, I created the modules needed to generate braille code. I achieved this using a set of boolean matrices, with true representing a bump in the braille cell, and using nested for loops to display the braille accurately. I use two braille characters to convey the note: one for the octave, and one for the note name. Because of this, I created a braille
module to create one character and a braille_note
module to create the set.
To make the tube, I created a dictionary variable with the note name being the key and the MIDI number being the value. Since OpenSCAD treats arrays and for loops a little differently than most programming languages, I used Python to generate the dictionary and copied the results into OpenSCAD as the MIDI_TABLE
variable. After this, it was a matter of plugging the functions in to OpenSCAD and creating a module that will generate the hollow cylinder. After that, it was mostly tinkering with rotate and translate to get the braille lined up just right.
Lastly, I made some changes to the top-level variables to work with a customizer. This way, I can upload this to Thingiverse and it becomes very easy for anyone to print one of these at any diatonic pitch. I would eventually like to add support for sharp and flat notes and curve the braille along the edge of the cylinder, but this is good for now.
Code
If you would like to download, follow this link: https://drive.google.com/open?id=1gliw4M0FjgyQnX1jxHYEl7uoe-UbUiHL
// Title: Braille Boomwhackers
// Desc: Accurately generates boomwhackers with the appropriate braille inscriptions on it
// Note: Does not split after a certain size, must manually split
/////////// CUSTOMIZER VARIABLES \\\\\\\\\\\\\
// What octave the tube will be tuned to
Octave = 5; // [2:7]
// Letter value of pitch
Letter = "c"; // [a,b,c,d,e,f,g]
// How thick the tube will be (in mm)
Thickness = 1; // [0.5:0.5:10]
// Upscale braille text
Braile_Scale = 2; // [1:5]
// The higher, the smoother the final product will be
Print_Quality = 15;
/* [Hidden] */
//////////// GLOBAL VARIABLES \\\\\\\\\\\\\\\\
pitch = str(Letter,Octave);
$fn = Print_Quality;
// NOTE_A4 = 440;
// MIDI_A4 = 69;
SOUND = 345;
// MIDI_C0 = 12;
DIAMETER = 44.45;
MIDI_TABLE = [["a2", 45], ["b2", 47], ["c3", 48], ["d3", 50], ["e3", 52], ["f3", 53], ["g3", 55], ["a3", 57], ["b3", 59], ["c4", 60], ["d4", 62], ["e4", 64], ["f4", 65], ["g4", 67], ["a4", 69], ["b4", 71], ["c5", 72], ["d5", 74], ["e5", 76], ["f5", 77], ["g5", 79], ["a5", 81], ["b5", 83], ["c6", 84], ["d6", 86], ["e6", 88], ["f6", 89], ["g6", 91], ["a6", 93], ["b6", 95], ["c7", 96], ["d7", 98], ["e7", 100], ["f7", 101], ["g7", 103], ["a7", 105], ["b7", 107]];
////////////// MAIN \\\\\\\\\\\\\\\\\\\
midi = get_midi([pitch]);
length = wavelength(midi);
echo(pitch[0],pitch[1],midi,hertz(midi),length);
// braille_note(full_braille,full_braille);
hollow_cylinder(length,Thickness);
rotate([0,-90,0]) translate([10,-2*Braile_Scale,DIAMETER/2]) braille_note(get_octave(pitch[1]),get_braille_note(pitch[0]),Braile_Scale);
///////////// MODULES \\\\\\\\\\\\\\\\\\
// Braille spacing and dimensions: 2.5mm space between dots in same cell, 3.75mm space between dots of adjacent cells, dots are raised 0.5mm
// Total cell dimension with margin:
module braille_note(octave,note,scaling=1) {
scale([scaling,scaling,scaling]) union() {
translate([-1,-1,-0.5]) cube([11+4.5,7,0.5]);
braille(octave);
translate([3.75+2.5,0,0]) braille(note);
}
}
module braille(matrix=blank_braille) {
color("Black",1.0)
difference() {
for (i=[0:1]) {
for (j=[0:2]) {
if (matrix[i][j]) {
translate([2.5*i,2.5*j,0])
sphere(0.25);
}
}
}
translate([-10,-10,-1]) cube([20,20,1]);
}
}
module hollow_cylinder(l,t,dia=DIAMETER) {
difference() {
cylinder(l,d=dia);
translate([0,0,-10])
linear_extrude(l+100)
offset(-t)
circle(d=dia);
}
}
//////////////// FUNCTIONS \\\\\\\\\\\\\\\\\\
// Find hertz value based on MIDI note number (A4 = MIDI 69 = 440Hz)
function hertz(x) = 8.1758 * pow((440/8.1758),x/69);
// Find wavelength value based on hertz (in mm)
function wavelength(midi) = 1000*SOUND/hertz(midi);
// Return MIDI Number
function get_midi(note) = MIDI_TABLE[search(note,MIDI_TABLE)[0]][1];
function get_octave(octave) = (octave=="2") ? O2 : (octave=="3") ? O3 : (octave=="4") ? O4 : (octave=="5") ? O5 : (octave=="6") ? O6 : (octave=="7") ? O7 : blank_braille;
function get_braille_note(note) = (note=="c") ? C : (note=="d") ? D : (note=="e") ? E : (note=="f") ? F : (note=="g") ? G : (note=="a") ? A : (note=="b") ? B : blank_braille;
//////////////// BRAILLE VERTICES \\\\\\\\\\\\\\\\\\\\
blank_braille = [[false,false,false],[false,false,false]];
full_braille = [[true,true,true],[true,true,true]];
O2 = [[false,false,false],[true,true,false]];
O3 = [[false,false,false],[true,true,true]];
O4 = [[false,false,false],[false,true,false]];
O5 = [[false,false,false],[true,false,true]];
O6 = [[false,false,false],[false,true,true]];
O7 = [[false,false,false],[false,false,true]];
// FLAT = [[true,true,false],[false,false,true]];
// SHARP = [[true,false,false],[true,false,true]];
// NATURAL = [[true,false,false],[false,false,true]];
C = [[true,false,true],[true,true,false]];
D = [[true,false,true],[false,true,false]];
E = [[true,true,true],[true,false,false]];
F = [[true,true,true],[true,true,false]];
G = [[true,true,true],[false,true,false]];
A = [[false,true,true],[true,false,false]];
B = [[false,true,true],[true,true,false]];