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;
}