bit crusher

by mohayonao

HTML

<script src="http://mohayonao.github.io/neume.js/build/neume.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/0.10.6/vue.min.js"></script>
<div id="app">
    <button id="start" v-on="click:start">start</button>
    <input type="range" min="1" max="16" v-model="bits"/> {{ bits}}
</div>
<canvas id="canvas"></canvas>

CSS

canvas { width:100%; height:240px; }

JavaScript

var Neume = neume(new AudioContext());
var synth = null;

function makeBitCurve(bits) {
    var n = 1 << bits;
    var curve = new Float32Array(65536);
    
    for (var i = 0; i < curve.length; i++) {
        var x = i / curve.length;
        
        x = Math.floor(x * n) / n;
        
        curve[i] = x * 2 - 1;
    }
    
    return curve;
}

var Synth = new Neume(function($, curve) {
    var out;
    
    out = $("sin", { freq: 440, mul: 1 });
    out = $("shaper", { curve: curve }, out);
    
    return out;
});

function draw(curve) {
    var canvas = document.getElementById("canvas");
    var context = canvas.getContext("2d");
    canvas.width = 1024;
    canvas.height = 256;
    
    context.fillStyle = "#fff";
    context.clearRect(0, 0, canvas.width, canvas.height);
    
    context.beginPath();
    for (var i = 0; i < curve.length; i++) {
        var x = i / curve.length * canvas.width;
        var y = curve[i] * canvas.height * -0.5 + canvas.height * 0.5;
        context.lineTo(x, y);
    }
    context.stroke();
}

new Vue({
    el: "#app",
    data: {
        bits: 7
    },
    methods: {
        start: function() {
            if (synth) {
                synth.fadeOut("+0", 0.15);
                synth = null;
            } else {
                var curve = makeBitCurve(this.bits);
                
                draw(curve);
                
                synth = new Synth(curve).fadeIn("+0", 0.15);
            }
        }
    }
});