summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md26
-rw-r--r--intonation.js106
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
+})()
+