Microtonal Nonsense π©
If the note falls on the beat in the right key, you can play any note in between.
Log in to post a comment.
// k=kick,s=snare,h=hihat
let rythms = [
{name: "clean", r:"----h-hhk---k---k---k---k---k---k---k---k---k---k---k---k-----hh"},
{name: "basic", r:"k---k--hk---k---k---k-hhk-s-s-hh"},
{name: "swing", r:"k-h-k-shk-h-k-shk-h-k-shk-h-k-shk-h-k-shk-h-k-shk-h-k-shk-h-k-s-"},
{name: "backbeat", r:"k---h---k-s-h---k---h---k-s-h-s-k---h---k-s-h---k--sh---k-s-h---"},
{name: "afro", r:"k-h-s-h-k-h-s-h-k-h-s-h-k-h-s-h-k-h-s-h-k-h-s-h-k-h-s-h-k-h-s-h-"},
{name: "brush", r:"k---h---s---h---k---h---s---h---k---h---s---h---k---h-s-h-h-h---"},
{name: "edm", r:"k-h-s-h-k-h-s-h-k-h-s-h-k-h-shhhk-h-s-h-k-h-s-h-k-h-s-h-k-h-shhh"},
{name: "hip hop", r:"k-h-h-h-s-h-h-h-k-h-k-h-s-h-h-h-k-h-h-h-s-h-k-h-k-h-k-h-s-h-h-h-"},
{name: "drum bass", r:"k---h---s---h-----k-h---s---h---k---h---s---h-----k-h--ks--hh---"},
{name: "bossa-nova", r:"k---s--hk--h-s--k---s--hk--h-s--k---s--hk--h-s--k---s--hk-sh-s--"},
{name: "trap", r:"k---h-h-s---h-h-k--kh-h-s--hh-h-k---h-h-s-h-h-h-k--kh-h-s-hhhhh-"},
{name: "bass solo", r:"k-k-k-k-k-kkk-k-k-k-k-kk"},
{name: "kkhhss", r:"k-k-h-h-s-s-"},
{name: "hithat solo", r:"h-hhhhhhhhh-"},
{name: "snare solo", r:"s-s-s-s-s-ss"},
{name: "breath", r:"----"},
{name: "breath", r:"--------"},
{name: "breath", r:"------------"},
{name: "breath", r:"----------------"},
];
const jazziness = 1/3; // min=0, max=1, step=0.05
const step = 0.45;
const melodyChords = [
scale(c3, chords['major'], 2),
scale(a4, chords['minor'], 2),
chord(c3, chords['major']),
chord(f3, chords['major7']),
chord(g3, chords['sus4']),
chord(a4, chords['min7']),
];
console.log(melodyChords)
const tri = p => Math.abs(p % 1 - .5) * 4 - 1;
const osc = synth.def((phase, env) => tri(phase) * env.value);
const kick = synth.def( (phase, env, tick, options) => Math.sin(phase * 2 * Math.PI * (1.5 - tick * 4)) * env.value);
const hihat = synth.def( (phase, env, tick, options) => (Math.random() * 2 - 1) * Math.pow(1 - env.progress, options.pow) * env.value, { attack: 0.0, release: 0.4, amp: 0.5, pow: 10, env: adsr2 });
const snare = synth.def((phase, env) => (Math.random() * 2 - 1) * env.value * 0.2, { attack: 0.001, release: 0.4 });
// delay / reverb from <https://dittytoy.net/ditty/827b1b3e63>
class Delayline {
constructor(n) {
this.n = ~~n;
this.p = 0;
this.lastOut = 0;
this.data = new Float32Array(n);
}
clock(input) {
this.lastOut = this.data[this.p];
this.data[this.p] = input;
if(++this.p >= this.n) {
this.p = 0;
}
}
tap(offset) {
var x = this.p - offset - 1;
x %= this.n;
if(x < 0) x += this.n;
return this.data[x];
}
}
function allpass(delayline, x, k) {
var delayin = x - delayline.lastOut * k;
var y = delayline.lastOut + k * delayin;
delayline.clock(delayin);
return y;
}
// Simple allpass reverberator, based on this article:
// http://www.spinsemi.com/knowledge_base/effects.html
const reverb = filter.def(class {
constructor(options) {
this.lastReturn = 0;
this.krt = .7;
this.delaylines = [];
// Create several delay lines with random lengths
for(var i = 0; i < 12; ++i) {
this.delaylines.push(new Delayline(10 + Math.floor((1-Math.random()*Math.random()) * (options.length || 5000))));
}
}
process(input, options) {
var inv = input[0] + input[1];
var v = this.lastReturn;
// Let the signal pass through the loop of delay lines. Inject input signal at multiple locations.
v = allpass(this.delaylines[0], v + inv, .5);
v = allpass(this.delaylines[1], v, .5);
this.delaylines[2].clock(v);
v = this.delaylines[2].lastOut * this.krt;
v = allpass(this.delaylines[3], v + inv, .5);
v = allpass(this.delaylines[4], v, .5);
this.delaylines[5].clock(v);
v = this.delaylines[5].lastOut * this.krt;
v = allpass(this.delaylines[6], v + inv, .5);
v = allpass(this.delaylines[7], v, .5);
this.delaylines[8].clock(v);
v = this.delaylines[8].lastOut * this.krt;
v = allpass(this.delaylines[9], v + inv, .5);
v = allpass(this.delaylines[10], v, .5);
this.delaylines[11].clock(v);
v = this.delaylines[11].lastOut * this.krt;
this.lastReturn = v;
// Tap the delay lines at randomized locations and accumulate the output signal.
var ret = [0, 0];
ret[0] += this.delaylines[2].tap(111);
ret[1] += this.delaylines[2].tap(2250);
ret[0] += this.delaylines[5].tap(311);
ret[1] += this.delaylines[5].tap(1150);
ret[0] += this.delaylines[8].tap(511);
ret[1] += this.delaylines[8].tap(50);
ret[0] += this.delaylines[11].tap(4411);
ret[1] += this.delaylines[11].tap(540);
// Mix wet + dry signal.
ret[0] = ret[0] * .1 + input[0];
ret[1] = ret[1] * .1 + input[1];
// Slight stereo widening:
var m = (ret[0] + ret[1]) * .5;
var s = (ret[1] - ret[0]) * .5;
ret[0] = m + s * 1.5;
ret[1] = m - s * 1.5;
return ret;
}
});
// source: <https://dittytoy.net/ditty/24373308b4>
const fract = (x) => x - Math.floor(x);
const triangle = x => Math.abs(fract(x + .5) - .5) * 2;
var phaser = filter.def(class {
constructor(opt) {
this.stages = [[], []];
this.n = 16;
this.lastOut = [0, 0];
this.p = 0;
this.feedback = 0.5;
this.speed = 0.25;
this.mix = .25;
for(var i = 0; i < this.n; ++i) {
this.stages[0].push({z: 0, ap: 0});
this.stages[1].push({z: 0, ap: 0});
}
}
__allpass(s, input, a) {
var z = input - a * s.z;
s.ap = s.z + a * z;
s.z = z;
return s.ap;
}
process(inv, opt) {
//inv[0] = inv[1] = Math.random() - .5;
var vl = inv[0] + clamp(this.stages[0][this.n-1].ap * this.feedback, -1, 1);
var vr = inv[1] + clamp(this.stages[1][this.n-1].ap * this.feedback, -1, 1);
var lfo = (2**triangle(this.p))*.5-1.4;
this.p += ditty.dt * this.speed;
for(var i = 0; i < this.n; ++i) {
vl = this.__allpass(this.stages[0][i], vl, lfo);
vr = this.__allpass(this.stages[1][i], vr, lfo);
}
vl = lerp(inv[0], vl, this.mix);
vr = lerp(inv[1], vr, this.mix);
return [vl, vr];
}
});
loop((_) => {
if (Math.random() > 0.5) {
// smol break
const pause = [4,8].choose()
sleep(step * pause);
debug.log(`M`, "β¬".repeat(pause) + " breath");
} else if (Math.random() > 0.75) {
// good notes bar
const duration = [4,8].choose();
const mode = ["random","order"].choose();
debug.log(`M`,"π₯".repeat(duration) + " " + mode + " good");
const notes = melodyChords.choose();
for(let i=0;i<duration;i++) {
osc.play(mode === "order" ? notes[i%notes.length] : notes.choose(), {amp:0.5});
sleep(step);
}
} else if (Math.random() > 0.75) {
// bad duration bar, kinda going upwards
const duration = [4,8,16].choose();
debug.log(`M`,"π¦".repeat(duration) + " up bad");
const notes = melodyChords.choose();
for(let i=0;i<duration;i++) {
sine.play(lerp(notes.at(0), notes.at(-1) + 12, Math.random() * i/duration), {amp:0.5});
sleep(step);
}
} else {
const notes = melodyChords.choose();
const mode = ["random","order"].choose();
const sequence = Array.from({ length: 16 }, (_, i) =>
i % 4 === 0
// notes in key, on count
? (mode === "order" ? notes[i%notes.length] : notes.choose()) + 12
: (Math.random() < jazziness
// microtones
? lerp(notes.at(0), notes.at(-1)+12, Math.random())
: null)
);
if (Math.random() > 0.5) {
// dont play on the 2, 3 or 4
let idx = 1+Math.random()*3|0;
sequence[idx*4] = null;
}
if (Math.random() > 0.5) {
// "good" note 2e, 3e or 4e
let idx = Math.random()*4|0;
sequence[idx*4+2] = notes.choose() + 12;
}
debug.log(`M`, sequence.map((note,idx) => note == null ? "β¬": Number.isInteger(note) ? "π₯" : "π¦").join("")+ " "+mode + " melody");
for (let i=0; i<sequence.length; i++) {
let note = sequence[i];
if (note) (Number.isInteger(note) ? osc : sine).play(note, {amp:0.5});
sleep(step);
}
}
}, { name: 'M' }).connect(reverb.create({length: 3500}));
const beatMap = {
"-":"β¬",
"k": "π₯",
"h": "π©",
"s": "π₯’",
}
function getVis({view, visStart, i}) {
// not sure why this indicator βΊοΈ doesnt work
return view.split("").map((beat, idx) => (visStart + idx === i) ? "βΊοΈ" : beatMap[beat]).join("")
}
loop((_) => {
const current = _%2===0 ? rythms[0] : rythms.choose();
const rythm = current.r;
for(let i=0;i<rythm.length;i++) {
let totalVis = 16;
let visStart = Math.floor(i / totalVis) * totalVis;
let currentView = rythm.slice(visStart, visStart + totalVis);
debug.log(`B`, getVis({view:currentView, visStart, i}) + " " + current.name);
let beat = rythm[i%rythm.length];
if (beat === "k") {
const bassNote = [c2,c2,g2,e2].choose();
sine.play(bassNote, {amp:.5});
osc.play(bassNote-12, {amp:.5, release: 0.4*4});
kick.play(c2, { attack: 0.025, release: 0.25, amp: 1.5 });
}
else if (beat === "h") hihat.play(null, { amp: Math.random(), pow: 7+Math.random()*3, pan: lerp(-1,1,Math.random())});
else if (beat === "s") snare.play(d4, {pan: [-0.25, 0.25].choose(), release: lerp(0.3, 0.4, Math.random())});
sleep(step/2);
}
}, { name: 'B' }).connect(phaser.create()).connect(reverb.create({length: 450}));