⌈⌋ branch:  Bitrhythm


Artifact Content

Artifact bb0853a1ed8d7cf0bb0938f76fd05bc13700d4ab1000d97bdbcaeec7b37548a8:

  • File public/misc.js — part of check-in [4cb0fff742] at 2022-03-27 22:44:18 on branch trunk — Updating JUCE link and build scripts (user: dev size: 15197)


function getRandomInt(max) {
  return Math.floor(Math.random() * max);
}

function initWinamp(preset) {
    var can = document.getElementById("visual");
    can.height = window.innerHeight - (document.getElementById("header-playback").clientHeight / 2);
    can.width = window.innerWidth;
    var can_container = document.getElementById("canvas-container");
    can_container.width = window.innerWidth;
    var visualizer = window.butterchurn.default.createVisualizer(Tone.getContext().rawContext, can, {
        height: window.innerHeight - (document.getElementById("header-playback").clientHeight / 2),
        width: window.innerWidth,
        meshWidth: 24,
        meshHeight: 18,
    });
    visualizer.connectAudio(Tone.getContext().destination);
    const presets = window.butterchurnPresets.getPresets();
    const presetParam = presets[preset];
    visualizer.loadPreset(presetParam, 0.0); // 2nd argument is the number of seconds to blend presets
    return visualizer;
}

function guard(range) {
    var state = null;
    return function (val) {
        if ((val >= range[0]) && (val <= range[1])) {
            state = val;
            return val;
        } else {
            return state;
        }
    }
}

function romantogypsy(hex) {
    var letters = hex.replace('`0','a');
    letters = letters.replace('`1','b');
    letters = letters.replace('`2','c');
    letters = letters.replace('`3','d');
    letters = letters.replace('`4','e');
    letters = letters.replace('`5','f');
    return letters;
}

function lettertodec(letter) {
    var bin = "";
    if (letter.match(/\d/)) {
        no = parseInt(letter);
    }
    else if (letter == "a") {
        no = 10;
    }
    else if (letter == "b") {
        no = 11;
    }
    else if (letter == "c") {
        no = 12;
    }
    else if (letter == "d") {
        no = 13;
    }
    else if (letter == "e") {
        no = 14;
    }
    else if (letter == "f") {
        no = 15;
    }
    for (i = 1; i <= no; i++) {
        bin += "0";
    }
    return bin;
}

function lettertobin(letter) {
    var bin = "";
    if (letter == "0") {
        bin += "0000";
    }
    if (letter == "1") {
        bin += "0001";
    }
    else if (letter == "2") {
        bin += "0010";
    }
    else if (letter == "3") {
        bin += "0011";
    }
    else if (letter == "4") {
        bin += "0100";
    }
    else if (letter == "5") {
        bin += "0101";
    }
    else if (letter == "6") {
        bin += "0110";
    }
    else if (letter == "7") {
        bin += "0111";
    }
    else if (letter == "8") {
        bin += "1000";
    }
    else if (letter == "9") {
        bin += "1001";
    }
    else if (letter == "a") {
        bin += "1010";
    }
    else if (letter == "b") {
        bin += "1011";
    }
    else if (letter == "c") {
        bin += "1100";
    }
    else if (letter == "d") {
        bin += "1101";
    }
    else if (letter == "e") {
        bin += "1110";
    }
    else if (letter == "f") {
        bin += "1111";
    }
    return bin;
}

function hex2bin(hex) {
    var letters = romantogypsy(hex)
    letters = letters.split('');
    var bin = "";
    letters.map(function(letter) {
        bin += lettertobin(letter)
    })
    return bin;
}

function get_char(str, index) {
    if ((index > 0) && (index < str.length)) {
        return str[index];
    } else {
        return null
    }
}

function pattern_meta(p) {
    if (!p) {
        return null;
    }
    p = p.replace(/ /g, "");
    var fc = p.split('')[0];
    if (fc== "p") {
        var ptype = "xo";
        var l = (p.length - 1);
    }

    if (ptype == "xo") {
        var fp = p.substr(1);
        fp = fp.replace(/x/g, "1");
    }

    if (!fp) {
        return null;
    }

    var done = false;
    var index = 0;
    var meta = {}
    var one_index = 1;

    while(1) {
        if (index > fp.length) {
            break;
        }
        var current_meta = {}
        var current_letter = fp[index]
        if (current_letter == "_") {
            meta[one_index -1] = {"volume": "off" };
            index += 2;
            one_index += 2;
            continue;
        }
        else if (current_letter == "1") {
            var next_letter = get_char(fp,index + 1);
            if (next_letter == "[") {
                var jump_index = 1;
                var buffer = "";
                while(1) {
                    if (((index + 1) + jump_index) > fp.length) {
                        break;
                    }
                    let b_next_letter = get_char(fp, ((index + 1) + jump_index));

                    if (b_next_letter == "]") {
                        jump_index += 2;
                        break;
                    } else {
                        buffer += b_next_letter;
                        jump_index += 1;
                    }
                }
                var individual_meta = buffer.split(";")
                individual_meta.map(function (e) {
                    if (e.startsWith("_")) {
                        current_meta["pan"] = e.substring(1)
                    }
                    if (e.startsWith("^")) {
                        current_meta["pitch"] = e.substring(1)
                    } else if (e.startsWith("+")) {
                        current_meta["delay"] = e
                    } else {
                        current_meta["volume"] = e
                    }
                })
                meta[one_index -1] = current_meta
                index += jump_index;
                one_index += 1;
                continue;
            } else {
                meta[one_index -1] = current_meta
                one_index += 1;
                index += 1;
                continue;
            }
        } else {
            if (current_letter == "*") {
                var next_letter = get_char(fp,index + 1);
                if (next_letter == "`") {
                    index += 3;
                } else {
                    index += 2;
                    continue;
                }
            } else {
                index += 1;
                one_index += 1;
                continue;
            }
        }
    }
    return meta;
}

window.pattern_meta = pattern_meta;


function cue(html, seconds = 5) {
        $("#cued").html(html);
        setTimeout(function () {
            $("#cued").html("");
        }, seconds * 1000)
}

window.cue = cue;

async function loadSamplesToWorklet(urls) {
    var context = Tone.getContext();
    window.context = context;
    await context.addAudioWorkletModule('/sampler.js', 'sampler');
    var sampler = await context.createAudioWorkletNode('sampler', {
        outputChannelCount: [2],  // stereo
    });
    window.sampler = sampler;
    var files = []
    for (var i = 0; i < urls.length; i++) {
        var url = urls[i]
        const source = context.createBufferSource();
        const audioBuffer = await fetch(url)
            .then(res => res.arrayBuffer())
            .then(ArrayBuffer => context.decodeAudioData(ArrayBuffer));

        const pcmLeft =  audioBuffer.getChannelData(0)
        const pcmRight = audioBuffer.getChannelData(1)
        files.push({ pcmLeft, pcmRight })
    }
    sampler.port.postMessage({ init:  files })
    context.rawContext.resume();
    sampler.connect(Tone.getContext().rawContext.destination);
}

function Sample(name, no, filter, volume) {
    name = name
    filter = filter || 10000
    volume = volume || 0
    mem[name + "_filter"] = new Tone.Filter(filter, 'lowpass', -96);
    mem[name + "_channel"] = new Tone.Channel({channelCount: 2, volume: volume}).chain(mem[name + "_filter"], mem.master)
    samples[no].connect(mem[name + "_channel"]);
    hit_map[name] = no;
}

window.Sample = Sample;

function pw(s, vol, note, len, delay, pan=0) {
    window.sampler.port.postMessage({ noteOn: true, sample: s, volume: vol});
}

window.pw = pw;

function p(s, vol, note, len, delay, pan=0) {
    note = note || "C3"
    len = len || "16n"
    vol = vol || 1
    delay = delay || "+0";
    for (const [key, value] of Object.entries(hit_map)) {
        if (value == s) {
            mem[key + "_last"] = count
            mem[key + "_channel"].pan.value = pan
        }
    }
    samples[s].triggerAttackRelease(note, len, delay, vol);
}

window.p = p;

function p1(s, vol, note, len, delay, pan=0) {
    note = note || "C3"
    len = len || "16n"
    vol = vol || 1
    delay = delay || "+0";

   for (const [key, value] of Object.entries(hit_map)) {
        if (value == (s - 1)) {
            mem[key + "_last"] = count
            mem[key + "_channel"].pan.value = pan

        }
    }
    samples[s - 1].triggerAttackRelease(note, len, delay, vol);
 
}

window.p1 = p1;

function pn(s, vol, note, len, delay, pan=0) {
    sample_no = hit_map[s]
    note = note || "C3"
    len = len || "16n"
    vol = vol || 1
    delay = delay || "+0";

    
    for (const [key, value] of Object.entries(hit_map)) {
    	if (value == sample_no) {
	    	mem[key + "_last"] = count
            mem[key + "_channel"].pan.value = pan

	    }
  	}
    
    samples[sample_no].triggerAttackRelease(note, len, delay, vol);

}

window.pn = pn;



function pattern_parse(p) {
    if (!p) {
        return "";
    }
    p = p.replace(/ /g, "");
    p = p.replace(/\[.+?\]/g, "");
    var fc = p.split('')[0];
    if (fc== "p") {
        var ptype = "xo";
        var l = (p.length - 1);
    } else {
        var ptype = "hex";
        var l = (p.length) * 4;
    }

    if (ptype == "xo") {
        var fp = p.substr(1);
        fp = fp.replace(/x/g, "1");
    }

    if (ptype == "xo") {
        var fin = "";
        var done = false;
        var index = 1;
        while(1) {
            if (done) {
                break;
            }
            if (index >= fp.length) {
                done = true;
                continue;
            }
            var current_letter = fp[index - 1]
            if (current_letter) {
            if (current_letter == "*") {
                var next_letter = get_char(fp,index);
                if (next_letter == "`") {
                    var next_next_letter = get_char(fp,index + 1);
                    fin +=  lettertodec(romantogypsy(next_letter + next_next_letter));
                    index += 3;
                } else {
                    fin += lettertodec(next_letter);
                    index += 2;
                }
            } else {
                fin += current_letter;
                index += 1;
            }
            }
        }
        return fin;
    }
    else {
        var fp = hex2bin(p);
    }

    return fp;
}

window.pattern_parse = pattern_parse;

function download(data, filename, type) {
    var file = new Blob([data], { type: type });
    if (window.navigator.msSaveOrOpenBlob) // IE10+
        window.navigator.msSaveOrOpenBlob(file, filename);
    else { // Others
        var a = document.createElement("a"),
            url = URL.createObjectURL(file);
        a.href = url;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        setTimeout(function () {
            document.body.removeChild(a);
            window.URL.revokeObjectURL(url);
        }, 0);
    }
}

// https://stackoverflow.com/questions/15762768/javascript-math-round-to-two-decimal-places
function roundTo(n, digits) {
    var negative = false;
    if (digits === undefined) {
        digits = 0;
    }
    if (n < 0) {
        negative = true;
        n = n * -1;
    }
    var multiplicator = Math.pow(10, digits);
    n = parseFloat((n * multiplicator).toFixed(11));
    n = (Math.round(n) / multiplicator).toFixed(digits);
    if (negative) {
        n = (n * -1).toFixed(digits);
    }
    return n;
}


function knob(options) {
    options = options || {};
    var context = {};
    context.ramp = options.ramp || [0 , 1];
    context.count_skip = options.speed || 4;
    context.step = options.step || 0.01;
    context.reverse = options.reverse || true;
    context.number = options.number || null;

    context.current_count = 0;
    context.index = 0;

    // Smooth transition from previous knob values
    if (context.number) {
        context.val = window.cellx.cellx(context.number())
    } else {
        context.val = window.cellx.cellx(options.initial || 0.5)
    }

    function changeContext() {
        context.next_val = context.ramp[context.index + 1];

        context.val(context.ramp[context.index]);
        if (context.val() > context.next_val) {
            context.direction = -1;
        } else {
            context.direction = 1;
        }
    }

    changeContext();

    return {
        "cell": context.val,
        "push": function (val) {
            context.ramp.push(val);
        },
        "replace": function (val) {
            context.ramp = val;
        },
        "speed": function (val) {
            context.count_skip = val;
        },
        "step": function (val) {
            context.step = val;
        },
        "up": function (val) {
            val = val || 0.1;
            context.ramp.push(context.ramp[context.ramp.length - 1] + val);
        },
        "down": function (val) {
            val = val || -0.1;
            context.ramp.push(context.ramp[context.ramp.length - 1] + val);
        },
       "move": function () {
            if (context.current_count >= context.count_skip) {
                context.current_count = 1;

                if (context.direction == 1) {
                    var cmp = function () {
                        return (context.val() >= context.next_val);
                    };
                } else {
                    var cmp = function () {
                        return (context.next_val >= context.val());
                    };
                }

                if (cmp()) {
                    context.val(context.next_val);
                    context.index = context.index + 1;
                    if (context.index === context.ramp.length -1) {
                        if (context.reverse) {
                            context.index = 0;
                            context.ramp = context.ramp.reverse();
                        } else {
                            context.index = context.index - 1;
                        }
                    }
                    changeContext();
                    context.val(context.val() + context.step * context.direction);
                    if (context.number) context.number(context.val());
                } else {
                    context.val(context.val() + context.step * context.direction);
                    if (context.number) context.number(context.val());
                }
            } else {
                context.current_count += 1;
            }

           return context.val();
        }
    }
}

function timedKnob(options) {
    options = options || {};
    var context = {};
    context.interval = options.interval || 100;
    context.knob = knob(options);

    context.timer = setInterval(function () {
       context.knob.move();
    }, context.interval);

    context.knob["clear"] = function () {
        clearInterval(context.timer);
    }

    return context.knob;
}