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