Morphology

Music generation using mathematical morphology

Log in to post a comment.

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