I tried to make the maths background in Part 1 understandable for a general audience, but this Dojo section is going to have to assume you know a few programming terms. If that doesn't sound like your thing, then maybe you're just looking for the string bend calculator.
What's that in old money?Guitar related measures (scale, string gauge) are often given in imperial units, so scale is 24.5 inches rather than 648mm and string gauges are given in thousandths of an inch. Metric on the other hand is much simpler for conversions and calculations. So I wanted to have two controls for each of these that would allow you to adjust in either system and update both boxes.
This turns out to be tricky, you set each control to update the other when updated. Now when you update the metric control it updates the imperial control, which wants to update the metric control again, which... etc. While the cycle does stop it leads to weird effects like not being able to set certain metric lengths. Dojo should let you pass a parameter to stop this propagation, but it doesn't work in this situation. The simplest solution is that each box checks whether the other box has focus first and doesn't try to update it if it does.
Incidentally, an inch is now defined to be exactly 25.4mm, so perfect conversion is possible.
- Variables in a scope get declared with a single 'var' statement, separating multiple declarations and assignments with a "," rather than making multiple ";" ended statements. This includes defining function names.
- Being statements, anonymous function declarations should have a ";" after the braces (unless the assignment is in a list as above, where a "," might be used).
Ready: get, set, callBecause there are a lot of variables in the model for string bending that need to be exposed to the interface and update each other in complex ways a lot of getters and setters and callbacks are needed. Writing them all is hard to maintain and makes the JS bulky, there may be other ways to do this, but the solution I came up with is to place this in the class constructor for the guitar string after a list of "this.property=" assignments:
(Formatting courtesy of hilite.me)
Only a few of the properties are shown, but each one simply defines the next property that should be updated. The loop at the end creates a setter and a null callback for each property. When set a property calls a recalculate function for the next property in the chain (if there is one) and the callback function which can be set by the interface. The buildSet, buildUpdRecalc and buildUpdOnly methods are used to add these functions to the class, using the "this[name]" method to address the class properties (variables or methods) by name. Since they're methods, "this" refers to the object being constructed, rather than a separate function scope.
Of course all the "recalc_property" methods still need to be written, and will use the corresponding "set_property" method to do the update, e.g.
Since they might be needed by the graphing functionality each "calcProperty" method needs all the input parameters to be passed and doesn't attempt to use them from the class. For completeness, here's calcbendforce:
So either an update to the string tension or semitones (number of semitones to bend) needs to update the bendforce. In this particular model there's a hierarchy of variables that control others, so it's possible to have each parameter only need to update the next parameter in the chain.
Stringing it upThe interface is a fairly straightforward HTML page with Dojo input and display boxes attached to elements. To get it to talk to the physics model (which is defined as a Dojo class) each input object can be given a callback to run when the value is updated in the interface and can set a callback on the physics object to allow it to update the interface. Some elements don't depend on anything (like scale), so they don't set the callback in the physics object. Others can't be set and can only be updated as the result of other changes (like bend distance), so they don't use the set method.
String tension can both be set directly (bypassing the tension calculations) and updated by other changes:
tension_box is a "dijit/form/NumberSpinner" assigned to the element name "tension":
tension_updated is a function which will update the wire tension when the value in the input box is being changed:
The "wire" callback is set so it can update the tension (for elements that are not input boxes the function can instead set the element value directly) when it changes in the model. Checking whether the box is focused prevents the callback trying to update the box while it's being edited.
And finally this has to appear somewhere, I've collected them in a startup function:
Odd things happen if you forget to startup a widget, it may initialise controls, but refuse to honour settings like size.
ProgressionIn part 1 I mentioned that things were done in terms of tension. You can eliminate calculating the tension if you just want the bend distance, but since it's something that might be interesting (for string sets, or knowing how much harder a certain setup is to bend) instead a lot of the model works by finding the tension and using it to find the other numbers.
- Scale, string gauge, density and note are all used to calculate tension.
- The bend tonal distance is used to find how much extra tension is needed.
- Total string length (including past headstock), stiffness, gauge and bend tension are used to find the length stretch.
- Length stretch is used to find the sideways stretch.
- The sideways bend force needed is found using the total tension, sideways stretch and scale length.
Graphs and hair lossThe graphs are done with Dojo's "Chart" widget, using the Lines type ("dojox/charting/plot2d/Lines"). One thing school science lessons should have drummed into you is that you need to label the axes on graphs. And this one isn't very useful:
I was starting the charts with an empty series to be filled in on the first update, the "scale length" x axis is okay, we can give it min and max since it doesn't change, but y needs to be automatic. Since Dojo can't guess and hasn't been given a range to start with it doesn't leave enough space for it once it gets the data. The solution is to generate the data when the chart is first created. Final result:
To use Dojo to do the page layout you need your own .html file, and that rules out most free websites and blogging. I've ended up using paid-for-hosting that I already had, but one interesting alternative is Amazon Web Services S3 which can host static content and provides 5GB free for the first year (though you need a credit card), even after a year hosting a few MB of JS (or even a few hundred) should cost less than a pound per year.
The finished product can be found at http://www.imalone.co.uk/stringbendcalc.html.