diff options
| -rw-r--r-- | README.md | 26 | ||||
| -rw-r--r-- | intonation.js | 106 |
2 files changed, 132 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..d20eaf0 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +intonation-js +============= + +This module is a general-purpose tuning library used to work with musical scales. It supports: + +- equal temperament generator +- scales based on intervals (just intonation) +- scales based on frequencies +- scl file format + +Once a scale is established you should be able to - + +- generate frequencies based on a root note +- find the closest frequency within a scale to a given frequency + +links +----- + + - <http://www.huygens-fokker.org/scala/> - the Scala tuning generator + - <https://github.com/abbernie/tune> - another tuning library with scales preloaded + +> Scala is a powerful software tool for experimentation with musical tunings, such as just intonation scales, equal and historical temperaments, microtonal and macrotonal scales, and non-Western scales. It supports scale creation, editing, comparison, analysis, storage, tuning of electronic instruments, and MIDI file generation and tuning conversion. All this is integrated into a single application with a wide variety of mathematical routines and scale creation methods. Scala is ideal for the exploration of tunings and becoming familiar with the concepts involved. In addition, a very large library of scales is freely available for Scala and can be used for analysis or music creation. + + + + diff --git a/intonation.js b/intonation.js new file mode 100644 index 0000000..4485d81 --- /dev/null +++ b/intonation.js @@ -0,0 +1,106 @@ +var Intonation = (function(){ + var Intonation = function(opt){ + opt = Object.assign({ + root: 440, + octave: 0, + interval: 2, + tet: 0, + }, opt || {}) + this.generate() + } + Intonation.prototype.generate = function(){ + if (this.opt.tet) { + this.generate_tet() + } + else { + this.generate_intervals() + } + } + Intonation.prototype.generate_intervals = function(){ + var root = this.opt.root + var interval_list = this.opt.intervals + if (typeof interval_list == "string") { + interval_list = interval_list.split(" ") + } + var intervals = .map(function(v){ + if (v.indexOf("/") !== -1) return parseInterval(v) // intervals + if (v.indexOf("f") !== -1) return parseFloat(v) // pure frequencies + return parseFloat(v) + }).filter(function(v){ + return !! v + }) + if (! intervals.length) return + this.opt.interval = intervals.pop() + scale = intervals.map(function(v){ + if (v < 20) { + return v * root + } + else { + return v + } + }) + } + Intonation.prototype.generate_tet = function(){ + var scale = this.scale = [] + var root = this.opt.root + var tet = this.opt.tet + var interval = this.opt.interval + var ratio = Math.pow( interval, 1/tet ) + var n = root + scale = [n] + for (var i = 0; i < tet; i++) { + n *= ratio + scale.push(n) + } + } + Intonation.prototype.index = function(i, octave){ + octave = octave || this.opt.octave + var f = this.scale[ mod(i, this.scale.length)|0 ] + var pow = Math.floor(norm(i, 0, this.scale.length)) + octave + f *= Math.pow(this.opt.interval, pow) + return f + } + Intonation.prototype.set_root = function(f){ + this.opt.root = f + this.generate() + } + Intonation.prototype.quantize_frequency = function(f){ + if (f == 0) return 0 + var scale_f = f + var pow = 0 + while (scale_f < root) { + scale_f *= interval + pow -= 1 + } + while (scale_f > root*interval) { + scale_f /= interval + pow += 1 + } + for (var i = 0; i < scale.length; i++) { + if (scale_f > scale[i]) continue + scale_f = scale[i] + break + } + scale_f *= Math.pow(2, pow) + return scale_f + } + Intonation.prototype.quantize_index = function(i){ + return mod(index-1, this.scale.length)|0 + } + Intonation.prototype.parse_interval = function parseInterval (interval) { + if (typeof s == "number") return s + if (! s.indexOf("/") == -1) return parseInt(s) + var pp = s.split("/") + var num = parseInt(pp[0]) + var den = parseInt(pp[1]) + if (isNaN(num)) return 1 + if (isNaN(den) || den == 0) return num + if (num == den) return 1 + return num / den + } + function norm(n,a,b){ return (n-a) / (b-a) } + function mod(n,m){ return n-(m * Math.floor(n/m)) } + + return Intonation +})() + |
