/* harmonizer: this external build voicing from a chord to another. takes n voices (midi) as input you can set the current chord you can ask for next note of each voice to get to a target chord usefull to create chorals voicing is built using traditional GA (not co-evolving GA) voicing rules are hardcoded and are: - no parallel 8ths nor 5ths - better no hidden 8ths nor 5ths - better if uniform voice spacing (except for the bass) - better if little intervals - no all voices same direction - no voices outside limits - better complete chords TODO: would be nice to be able so set some rule at runtime or at least set the importance of rules in realtime.. */ #include #include #include // for string manipulation #include #include #include "m_pd.h" #include "common.h" // to sort arrays #include "sglib.h" #define MAX_POPULATION 500 #define DEF_PROB_MUTATION 0.03f #define VOICES 5 #define NOTES_RANGE 80 // this should be multiple of 16 #define LOWER_POSSIBLE_NOTE 24 // lower note possible, it should be a C #define POSSIBLE_NOTES (NOTES_RANGE/12*5) // 5 is the max number of notes in a chord // default values #define DEF_WIDENESS 3 // 3 octaves #define DEF_CENTER_NOTE 72 // central C // testing i noticed that we don't need more than 1 generation.. // this is because we create an initial population that is really good // we may not need to crossover at all! #define GENERATIONS 1 #define DEBUG 0 // messaggi di debug #define DEBUG_VERBOSE 0 // messaggi di debug static t_class *harmonizer_class; // this defines a chord in a tonality typedef struct _chord_abs { chord_type_t mode; abs_note_t note; } chord_abs_t; typedef struct _harmonizer { t_object x_obj; // myself // genotypes int population[MAX_POPULATION][VOICES]; int current_voices[VOICES]; chord_abs_t current_chord; chord_abs_t target_chord; int target_notes[POSSIBLE_NOTES]; t_outlet *l_out; float wideness; int center_note; float i_like_parallelism; float small_intervals; //TODO // int lower_octave; // int notes_range; } t_harmonizer; // I build a table of possible notes // are the notes (midi)that form target_chord void build_possible_notes_table(t_harmonizer *x) { int i, octave, basenote; int n1, n2, n3, n4, n5; n1=n2=n3=n4=0; // there always is the fundamental if (DEBUG_VERBOSE) post("build_possible_notes_table target_chord.mode=%i target_chord.note=%i", x->target_chord.mode, x->target_chord.note); switch (x->target_chord.mode) { case kMaj: n2=4; n3=7; n4=0;n5=0;break; case kMin: n2=3; n3=7; n4=0;n5=0;break; case kDim: n2=3; n3=6; n4=0;n5=0;break; case kAug: n2=4; n3=8; n4=0;n5=0;break; case kMaj7: n2=4; n3=7; n4=11;n5=0;break; case kDom7: n2=4; n3=7; n4=10;n5=0;break; case kMin7: n2=3; n3=7; n4=10;n5=0;break; case kHalfDim7: n2=3; n3=6; n4=10;n5=0;break; case kDim7: n2=3; n3=6; n4=9;n5=0;break; case kMinMaj7: n2=4; n3=7; n4=11;n5=0;break; } if (DEBUG_VERBOSE) post("build_possible_notes_table n2=%i n3=%i n4=%i", n2, n3, n4); basenote=0; switch (x->target_chord.note) { case C: basenote=0;break; case Db: basenote=1;break; case D: basenote=2;break; case Eb: basenote=3;break; case E: basenote=4;break; case F: basenote=5;break; case Gb: basenote=6;break; case G: basenote=7;break; case Ab: basenote=8;break; case A: basenote=9;break; case Bb: basenote=10;break; case B: basenote=11;break; } if (DEBUG_VERBOSE) post("build_possible_notes_table basenote=%i", basenote); i=0; octave=0; while (i<(POSSIBLE_NOTES-3)) { x->target_notes[i++]=octave*12 + LOWER_POSSIBLE_NOTE + basenote + n1; x->target_notes[i++]=octave*12 + LOWER_POSSIBLE_NOTE + basenote + n2; x->target_notes[i++]=octave*12 + LOWER_POSSIBLE_NOTE + basenote + n3; x->target_notes[i++]=octave*12 + LOWER_POSSIBLE_NOTE + basenote + n4; x->target_notes[i++]=octave*12 + LOWER_POSSIBLE_NOTE + basenote + n5; octave++; } if (DEBUG_VERBOSE) { i=0; while (i<(POSSIBLE_NOTES)) { post("x->target_notes[%i]=%i", i, x->target_notes[i++]); } } } // ----------------- normal external code ... void harmonizer_init_pop(t_harmonizer *x) { int i, j, tmp, tmp2, k, steps, note, insertpoint; double rnd; for (i=0; ipopulation[i][j] = x->target_notes[tmp]; */ // not totally random: i start from currend chord's notes // and randomly go up or down insertpoint = 0; while ((insertpoint < POSSIBLE_NOTES) && (x->target_notes[insertpoint] < x->current_voices[j])) insertpoint++; if (insertpoint >= POSSIBLE_NOTES) { // i didn't find my insert point, possible? // i pick a random one rnd = rand()/((double)RAND_MAX + 1); tmp = rnd * POSSIBLE_NOTES; x->population[i][j] = x->target_notes[tmp]; } else { // insert point found rnd = rand()/((double)RAND_MAX + 1); if (rnd < 0.5) { // i go up rnd = rand()/((double)RAND_MAX + 1); steps = rnd * 10; // how many steps (good notes) will I ignore? note = insertpoint + steps; if (note >= POSSIBLE_NOTES) note = POSSIBLE_NOTES-1; } else { // i go down rnd = rand()/((double)RAND_MAX + 1); steps = rnd * 10; // how many steps (good notes) will I ignore? note = insertpoint - steps; if (note < 0) note = 0; } // finally assign the note x->population[i][j] = x->target_notes[note]; } } } } void harmonizer_free(t_harmonizer *x) { // freebytes(x->buf_strum1, sizeof(x->buf_strum1)); // freebytes(x->buf_strum2, sizeof(x->buf_strum2)); } // here i evaluate this voicing int fitness(t_harmonizer *x, int *candidate) { int i, j, tmp, res, last, avgHI, avgLOW, min, max, distance; float wideness, ftmp; short int chord_notes[4]; short int chord_notes_ok[4]; short int transitions[VOICES]; short int directions[VOICES]; // intervals between voices // for parallel and hidden 5ths // voices spacing etc.. short int intervals[VOICES][VOICES]; short int notes[VOICES]; res=50; // starting fitness if (DEBUG_VERBOSE) post("evaluating fitness of %i %i %i %i", candidate[0], candidate[1], candidate[2], candidate[3]); // shared objects for (i=0; icurrent_voices[i]; if (transitions[i]!=0) directions[i] = transitions[i]/abs(transitions[i]); else directions[i] = 0; if (DEBUG_VERBOSE) post("directions[%i]=%i", i, directions[i]); } for (i=0; ii_like_parallelism; if (DEBUG_VERBOSE) post("same direction!"); } // parallel 5ths or octaves? (if yes return 0) // how? // hidden 8ths nor 5ths ? for (i=0; i