summaryrefslogtreecommitdiff
path: root/intonation.js
blob: 509addd7f868640a3e9177aa577b440a6d43f67b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
var Intonation = (function(){
  var Intonation = function(opt){
    opt = this.opt = Object.assign({
      root: 440,
      octave: 0,
      interval: 2,
      tet: 0,
      intervals: null,
    }, opt || {})
    this.generate()
  }
  Intonation.prototype.generate = function(opt){
    opt = Object.assign(this.opt, opt || {})
    if (opt.tet) {
      this.generate_tet()
    }
    else if (opt.intervals) {
      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(" ")
    }
    this.intervals = interval_list
    this.opt.interval = parseInterval.call(this, interval_list.pop() )
    this.scale = interval_list.map( parseIntervalString.bind(this) ).filter(function(v){
      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.push(n)
    for (var i = 0; i < tet; i++) {
      n *= ratio
      scale.push(n)
    }
    this.intervals = null
  }
  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.range = function(min, max){
    var a = []
    for (var i = min; i < max; i++) {
      a.push( this.index(i) )
    }
    return a
  }
  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
    var interval = this.opt.interval
    var scale = this.scale
    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
  }
  var parseInterval = Intonation.prototype.parse_interval = function (s) {
    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
  }
  var parseIntervalString = Intonation.prototype.parse_interval_string = function(s){
    if (s.indexOf("/") !== -1) return parseInterval(s) * this.opt.root // intervals
    if (s.indexOf("f") !== -1) return parseFloat(s)    // pure frequencies
    return parseFloat(s)
  }
  function norm(n,a,b){ return (n-a) / (b-a) }
  function mod(n,m){ return n-(m * Math.floor(n/m)) }

  return Intonation
})()