// Generating music using mathematical morphology
//
// Alexis THIBAULT March 2023
//
// Inspired by Gonzalo Romero-García's talk at Ircam
// https://www.youtube.com/watch?v=-EGbZ3Qre70
ditty.bpm = 60;
const bars_per_second = 0.5; // Use my own framework for time
// A rhythmic "texture" is a list of rhythms,
// each rhythm being a list of hits,
// each hit being defined by its onset and its duration, in ticks
let textures = {
alpha: [[[-1/4,1/16]],[[-3/16,1/16],[-1/16,1/16]], [[-1/8,1/16]], [[0,1/4]]],
beta: [[[0, 1/8]], [[1/8,1/8]], [[2/8,1/8],[3/8,1/8]]],
simple: [[[0,1]]],
}
// A "harmony" is a list of chords,
// each chord being a list of notes
// defined by their MIDI number
let harmonies = {
a: [[71], [69], [68], [72]],
b: [[74], [72], [71], [76]],
c: [[77], [76], [75], [ ]],
d: [[83], [81], [80], [ ]],
a_minor: [[57], [60,64], [60,64]],
a_minor2: [[57], [60,64], []],
simple: [[60]],
}
// A score is a list of quadruplets (texture, harmony, rhythmic offset, note offset)
let score = [
['alpha','a',1,0],
['alpha','b',3/2,0],
['alpha','c',2,0],
['alpha','d',9/4,0],
['alpha','a',5/2,12],
['beta', 'a_minor', 1, 0],
['beta', 'a_minor', 3/2, 0],
['beta', 'a_minor2', 2, 0],
['beta', 'a_minor2', 9/4, 0],
['beta', 'a_minor', 5/2, 0],
];
//let score = [['simple', 'simple', 0, 0]];
const saw = (x) => (x-(~~x))-0.5;
class Note {
constructor(hit, nn) {
this.hit = hit;
this.nn = nn;
this.f = midi_to_hz(nn);
debug.log('hit', this.hit[0]);
debug.log('nn', this.nn);
debug.log('f', this.f);
}
value_at(t) {
var t_rel = t*bars_per_second - this.hit[0];
if(t_rel > 0 && t_rel < this.hit[1] ) {
return saw(this.f * t);
}
return 0;
}
}
function tensorize(tex, harm, r_off, c_off) {
let notes = [];
for(let i=0; i<tex.length; i++) {
for(var hit of tex[i]) {
let hit_shifted = [hit[0] + r_off, hit[1]];
for(var nn of harm[i]) {
let nn_shifted = nn + c_off;
notes.push(new Note(hit_shifted, nn_shifted));
}
}
}
return notes;
}
function generate_score(score) {
let notes = [];
for(let i=0; i < score.length; i++) {
let [tid, hid, r_off, c_off] = score[i];
let tex = textures[tid];
let harm = harmonies[hid];
let new_notes = tensorize(tex, harm, r_off, c_off);
notes.push(...new_notes);
}
return notes;
}
//var all_notes = tensorize(textures['a'], harmonies['a'], 0, 0);
//let [tid, hid, r_off, c_off] = score[0];
//let all_notes = tensorize(textures[tid], harmonies[hid], r_off, c_off);
var all_notes = generate_score(score);
const add = (x,y) => x+y;
var mySynth = synth.def((p,e,t,o) => all_notes.map(n => n.value_at(t)).reduce(add, 0), {env:one, amp:0.3});
loop( () => {
mySynth.play(c4);
sleep(1000);
});