summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--index.html5
-rw-r--r--js/vendor/intonation.js163
2 files changed, 167 insertions, 1 deletions
diff --git a/index.html b/index.html
index f22734a..3bf7924 100644
--- a/index.html
+++ b/index.html
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/>
<link rel="stylesheet" href="../css/css.css">
<style>
-html,body { font-size: 12px; font-weight: normal; background: black; color: white; }
+html,body { font-size: 12px; font-weight: normal; background: black; color: white; overflow: hidden; }
circle { cursor: pointer }
input[type=text] { width: 30px }
#mobile_controls { display: none }
@@ -57,6 +57,7 @@ input[type=text] { width: 30px }
<input id="y" type="number" min="0" max="100" step="0.1" value="1">
<label for="rand">randomize</label>
<input id="rand" type="number" min="0" max="500" step="1" value="32">
+<span style="display: none">
<label for="tet">tet</label>
<input id="tet" type="number" min="2" max="500" step="1" value="12">
<label for="root">root</label>
@@ -65,6 +66,7 @@ input[type=text] { width: 30px }
<input id="duration" type="number" min="0" max="5000" step="50" value="1250">
<label for="intervals">intervals</label>
<input id="intervals" type="text" value="1/1 9/8 5/4 4/3 3/2 5/3 15/8 2/1">
+</span>
<label for="use_grid">use grid</label>
<input id="use_grid" type="checkbox">
<button id="add_ball">+ball</button>
@@ -84,6 +86,7 @@ input[type=text] { width: 30px }
<script src="./js/vendor/wheel.js"></script>
<script src="./js/vendor/Tone.min.js"></script>
<script src="./js/vendor/intersect.js"></script>
+<script src="./js/vendor/intonation.js"></script>
<script src="./js/vendor/StartAudioContext.js"></script>
<script src="./js/mx/mx.js"></script>
<script src="./js/mx/mx.scene.js"></script>
diff --git a/js/vendor/intonation.js b/js/vendor/intonation.js
new file mode 100644
index 0000000..b660fee
--- /dev/null
+++ b/js/vendor/intonation.js
@@ -0,0 +1,163 @@
+var Intonation = (function(){
+ var Intonation = function(opt){
+ opt = this.opt = Object.assign({
+ name: "",
+ 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.scl) {
+ this.generate_scl()
+ }
+ else 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.name = this.opt.name || "interval list"
+ this.intervals = interval_list
+ this.interval = 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.interval = this.opt.interval
+ var ratio = Math.pow( interval, 1/tet )
+ var n = root
+ scale.push(n)
+ for (var i = 0; i < tet-1; i++) {
+ n *= ratio
+ scale.push(n)
+ }
+ this.name = this.opt.name || tet + "-tone equal temperament"
+ this.intervals = null
+ }
+ Intonation.prototype.generate_scl = function(){
+ var root = this.opt.root
+ var scl = this.parse_scl( this.opt.scl )
+ this.intervals = scl.notes
+ this.interval = scl.notes.pop()
+ this.name = this.opt.name || scl.description
+ this.scale = scl.notes.map(function(v){
+ return v * root
+ })
+ }
+ Intonation.prototype.parse_scl = function(s){
+ var scl = {}
+ scl.comments = []
+ scl.notes = []
+ s.trim().split("\n").forEach(function(line){
+ // Lines beginning with an exclamation mark are regarded as comments
+ // and are to be ignored.
+ if ( line.indexOf("!") !== -1 ) {
+ scl.comments.push(line)
+ }
+ // The first (non comment) line contains a short description of the scale.
+ // If there is no description, there should be an empty line. (nb: which is falsey)
+ else if ( ! ('description' in scl) ) {
+ scl.description = line
+ }
+ // The second line contains the number of notes.
+ // The first note of 1/1 or 0.0 cents is implicit and not in the files.
+ else if ( ! scl.notes.length) {
+ scl.notes.push(1)
+ }
+ else {
+ // If the value contains a period, it is a cents value, otherwise a ratio.
+ var note = line.replace(/^[^-\.0-9]+/,"").replace(/[^-\/\.0-9]+$/,"")
+ if ( note.indexOf(".") !== -1 ) {
+ note = Math.pow( 2, (parseFloat(note) / 1200) )
+ }
+ else {
+ note = parseInterval(note)
+ }
+ if (note) {
+ scl.notes.push(note)
+ }
+ }
+ })
+ return scl
+ }
+ 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.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.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
+})()
+