diff options
Diffstat (limited to 'env.js')
| -rw-r--r-- | env.js | 402 |
1 files changed, 402 insertions, 0 deletions
@@ -0,0 +1,402 @@ +var environment = (function(){ + + var environment = {} + + var image_index = -1 + var snap, dot_grid, polysynth + var strided_wires = [], wires = [] + var scale = [], root, tet, interval + var dot_color = "#333" + + var palette = [ + "#ff0000", + "#00ffff", + "#0000ff", + "#ff8800", + "#ffff00", + ] + + environment.init = function(){ + environment.ready() + } + environment.ready = function(){ + environment.build() + environment.bind() + } + environment.build = function(){ + environment.scale() + snap = new Snap (window.innerWidth, window.innerHeight - $("#controls").height()) + + dot_grid = new DotGrid () + dot_grid.load() + + polysynth = new Tone.PolySynth(8, Tone.synth) + polysynth.set({ + oscillator: { type: $("#waveform").val() }, + envelope:{ + attack: 0.01, + decay: 0.1, + sustain: 0.3, + release: 0.2, + } + }) + var comp = new Tone.Compressor(-30, 3).toMaster() + var reverb = new Tone.Freeverb () + reverb.wet.value = 0.05 + polysynth.connect(reverb) + reverb.connect(comp) + // environment.stride() + // environment.randomize() + } + environment.bind = function(){ + $(window).focus(environment.focus) + $(window).blur(environment.blur) + $(window).mousemove(environment.mousemove) + window.addEventListener("keydown", environment.keydown, true) + $("#waveform").on("input", environment.setWaveform) + $("#x").on("keydown", environment.stride) + $("#y").on("keydown", environment.stride) + $("#rand").on("input", environment.randomize) + $("#tet").on("input", environment.scale) + $("#root").on("input", environment.scale) + $("#interval").on("input", environment.scale) + $("#use_scale").on("change", environment.use_scale) + $(snap.node).on("mousedown", dot_grid.mousedown.bind(dot_grid)) + $(snap.node).on("mouseup", dot_grid.mouseup.bind(dot_grid)) + } + environment.blur = function(){ + last_p = null + } + environment.focus = function(){ + } + environment.use_scale = function(){ + use_scale = $("#use_scale").get(0).checked + } + environment.keydown = function(e){ + if (e.altKey || e.ctrlKey || e.metaKey) { + e.stopPropagation() + return + } + if (e.keyCode == 69) { // charcode: e + e.preventDefault() + } + if (document.activeElement instanceof HTMLInputElement && + (e.keyCode in key_numbers)) { + e.stopPropagation() + return + } + if (! (e.keyCode in keys)) return + var i = keys[e.keyCode] + if (e.shiftKey) i += letters.length + var wire = wires[ i % wires.length ] + wire && wire.play() + } + environment.setWaveform = function(){ + var w = $("#waveform").val() + polysynth.set({ oscillator: { type: w } }) + } + environment.last_p = { x: 0, y: 0 } + environment.positionFromEvent = function(e){ + var offset = $(snap.node).offset() + var p = { + x: e.pageX - offset.left, + y: e.pageY - offset.top, + } + return p + } + environment.mousemove = function(e){ + var p = environment.positionFromEvent(e) + if (dot_grid.active) { + dot_grid.active.update({ point: p, muted: false, duration: 0.2 }) + } + else { + wires.forEach(function(wire){ + wire.intersect(p, environment.last_p) + }) + } + environment.last_p = p + } + environment.stride = function(e){ + var dx = parseFloat( $("#x").val() ) + var dy = parseFloat( $("#y").val() ) + environment.reset() + if (! dx && ! dy) return + if (dx < 1.1 && ! dy) return + var x0 = 0, x1 = dx, y0 = 0, y1 = dy + var wire + do { + wire = new Wire ({ + head: { x: x0, y: y0 }, + tail: { x: x1, y: y1 }, + color: { h: Math.round(y0 / dot_grid.dots[0].length * 180 + 180), s: 100, l: 50 }, + integer: true, + }) + strided_wires.push(wire) + if (dx || ! dy) x0 += 1 + y0 += 1 + x1 += dy + y1 += dx + } + while ( y0 < dot_grid.dots.length) + } + environment.reset = function(){ + strided_wires.forEach(function(wire){ + wire.remove() + }) + strided_wires = [] + } + environment.randomize = function(){ + var n = parseInt( $("#rand").val() ) + environment.reset() + var x0, y0, x1, y1 + while (n--) { + x0 = randint( dot_grid.dots.length ) + x1 = randint( dot_grid.dots.length ) + y0 = randint( dot_grid.dots[0].length ) + y1 = randint( dot_grid.dots[0].length ) + wire = new Wire ({ + head: { x: x0, y: y0 }, + tail: { x: x1, y: y1 }, + color: { h: Math.round(y0 / dot_grid.dots[0].length * 180 + 180), s: 100, l: 50 }, + integer: true, + }) + strided_wires.push(wire) + } + } + environment.scale = function(e){ + if (e && e.keyCode && ! key_numbers(e.keyCode)) { + e.preventDefault() + return + } + var ratio, n + tet = parseFloat( $("#tet").val() ) + root = parseFloat( $("#root").val() ) + interval = parseInterval( $("#interval").val() ) + ratio = Math.pow( interval, 1/tet ) + n = root + scale = [n] + for (var i = 0; i < tet; i++) { + n *= ratio + scale.push(n) + } + wires.forEach(function(wire){ + wire.updateColor(true) + }) + } + // quantize a frequency to the scale + environment.quantize = function(f, get_index){ + 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 + } + if (get_index) { return i } + scale_f *= Math.pow(2, pow) + // console.log(scale_f) + return scale_f + } + + function DotGrid (opt){ + this.opt = defaults(opt, { + dot_min: 2, + dot_max: 10, + dot_spacing: 19, + mouse_radius: 200, + wave_width: 200, + fill: "#ffffff", + speed: 4, + ease: 10, + }) + this.active = null + this.group = snap.g() + this.wire_group = snap.g() + this.wire_group.attr({ "style": "pointer-events: none" }) + this.dots = [] + } + DotGrid.prototype.indexToPoint = function(p){ + var dx = this.opt.dot_spacing + var q = {} + q.x = (p.x + 1/2) * dx + q.y = (p.y + 1/2) * dx + return q + } + DotGrid.prototype.quantize = function(p){ + var dx = this.opt.dot_spacing + var q = {} + q.x = quantize(p.x - dx/2, dx) + dx/2 + q.y = quantize(p.y - dx/2, dx) + dx/2 + return q + } + DotGrid.prototype.load = function(){ + var dots = this.dots + var dx = dy = this.opt.dot_spacing + var x, y, i, j, u, r, a + + var dot_min = this.opt.dot_min + var dot_max = this.opt.dot_max + + var w = this.w = window.innerWidth + var h = this.h = window.innerHeight - $("#controls").height() - dy + + for (x = dx/2; x < w; x += dx) { + a = [] + dots.push(a) + for (y = dy/2; y < h; y += dy) { + dot = this.group.circle(x, y, 4).attr({ + fill: dot_color, + }) + a.push(dot) + } + } + } + DotGrid.prototype.mousedown = function(e){ + var p = environment.positionFromEvent(e) + var q = this.quantize(p) + if (! this.active) { + this.active = new Wire ({ + head: q + }) + } + else if (this.active.head.x == q.x && this.active.head.y == q.y) { + return + } + else if (this.active) { + e.preventDefault() + this.active.setTail(q) + this.active = null + } + } + DotGrid.prototype.mouseup = function(e){ + if (this.active && this.active.length > 0) { + var q = this.quantize(environment.last_p) + this.active.setTail(q) + this.active = null + } + } + + function Wire (opt){ + this.opt = opt + if (opt.integer) { + opt.head = dot_grid.indexToPoint(opt.head) + if (opt.tail) { + opt.tail = dot_grid.indexToPoint(opt.tail) + } + } + if (opt.color && opt.color.h) { + opt.color = Snap.hsl(opt.color.h, opt.color.s, opt.color.l) + } + else if (! opt.color) { + opt.color = choice(palette) + } + this.length = 0 + this.head = opt.head + var path_str = "M" + 0 + "," + 0 + this.path = dot_grid.wire_group.path(path_str).attr({ + stroke: opt.color, + fill: "none", + strokeWidth: 3, + }) + if (opt.tail) { + this.setTail(opt.tail, true) + } + } + Wire.prototype.update = function(opt){ + var q = dot_grid.quantize(opt.point) + var length = this.length = dist(this.head.x, this.head.y, q.x, q.y) + var theta = angle(this.head.x, this.head.y, q.x, q.y) * 180 / Math.PI + var path_str = "M" + 0 + "," + 0 + + "t" + length.toFixed(2) + "," + 0 + var tran_str = "translate(" + this.head.x + "," + this.head.y + ") " + + "rotate(" + theta + ")" + var color = this.updateColor() + this.path.attr({ + d: path_str, + transform: tran_str, + stroke: color, + }) + if (! opt.muted) { + this.play(opt.duration) + } + return length + } + Wire.prototype.updateColor = function(should_set){ + var index = this.index() + var color = Snap.hsl(mod(index / tet * 240 + 180, 360), 100, 50) + if (should_set) { + this.path.attr({ + stroke: color, + }) + } + return color + } + Wire.prototype.setTail = function(p, muted){ + this.tail = p + this.update({ point: p, muted: true }) + wires.push(this) + } + Wire.prototype.intersect = function (a,b) { + if (a.x - b.x == 0 || a.y - b.y == 0) return + if (doLineSegmentsIntersect(a, b, this.head, this.tail)) { + this.play() + } + } + Wire.prototype.index = function(){ + var f = this.length / 340.29 * root + return environment.quantize( f, true ) + } + Wire.prototype.play = function(duration){ + var f = this.length / 340.29 * root + if (use_scale) { + f = environment.quantize(f) + } + polysynth.triggerAttackRelease(f, duration || randrange(1.0, 2.5)) + } + Wire.prototype.remove = function(){ + this.path.remove() + wires.splice(wires.indexOf(this), 1) + } + + environment.update = function(t){ + } + + var keys + var keys = {} + var key_numbers = {} + var letters = "qwertyuiop[]\\asdfghjkl;'zxcvbnm,./" + var numbers = "1234567890"; + letters.toUpperCase().split("").map(function(k,i){ + keys[k.charCodeAt(0)] = i + }) + numbers.split("").map(function(k,i){ + keys[k.charCodeAt(0)] = true + key_numbers[k.charCodeAt(0)] = true + }) + + function parseInterval (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 2 + if (isNaN(den) || den == 0) return num + if (num == den) return 2 + if (num < den) return den/num + return num / den + } + + return environment + +})() + |
