/* 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" // to sort arrays #include "sglib.h" #define MAX_POPULATION 100 #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*4) // 4 is the max number of notes in a chord // 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; typedef enum { kMaj=0, kMin=1, kDim=2, kAug=3, kDom7=4, kMaj7=5, kMin7=6, kMinMaj7=7, kDim7=8, kHalfDim7=9 } mode_t; typedef enum {C=0, Db=1, D=2, Eb=3, E=4, F=5, Gb=6, G=7, Ab=8, A=9, Bb=10, B=11 } note_t; // this defines a chord in a tonality typedef struct _chord { mode_t mode; note_t note; } chord_t; typedef struct _harmonizer { t_object x_obj; // myself // genotypes int population[MAX_POPULATION][VOICES]; int current_voices[VOICES]; chord_t current_chord; chord_t target_chord; int target_notes[POSSIBLE_NOTES]; t_outlet *l_out; } 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; 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;break; case kMin: n2=3; n3=7; n4=0;break; case kDim: n2=3; n3=6; n4=0;break; case kAug: n2=4; n3=8; n4=0;break; case kMaj7: n2=4; n3=7; n4=11;break; case kDom7: n2=4; n3=7; n4=10;break; case kMin7: n2=3; n3=7; n4=10;break; case kHalfDim7: n2=3; n3=6; n4=10;break; case kDim7: n2=3; n3=6; n4=9;break; case kMinMaj7: n2=4; n3=7; n4=11;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; octave++; } if (DEBUG_VERBOSE) { i=0; while (i<(POSSIBLE_NOTES)) { post("x->target_notes[%i]=%i", i, x->target_notes[i++]); } } } // tries to find out absolute tones names in this string note_t string2note(const char *substr) { if (strstr(substr, "C")) return C; if (strstr(substr, "Db")) return Db; if (strstr(substr, "D")) return D; if (strstr(substr, "Eb")) return Eb; if (strstr(substr, "E")) return E; if (strstr(substr, "F")) return F; if (strstr(substr, "Gb")) return Gb; if (strstr(substr, "G")) return G; if (strstr(substr, "Ab")) return Ab; if (strstr(substr, "A")) return A; if (strstr(substr, "Bb")) return Bb; if (strstr(substr, "B")) return B; return C; } mode_t string2mode(const char *substr) { if (strstr(substr, "minor/major 7th")) return kMinMaj7; if (strstr(substr, "major 7th")) return kMaj7; if (strstr(substr, "major")) return kMaj; if (strstr(substr, "minor 7th")) return kMin7; if (strstr(substr, "minor")) return kMin; if (strstr(substr, "half diminished 7th")) return kHalfDim7; if (strstr(substr, "diminished 7th")) return kDim7; if (strstr(substr, "diminished")) return kDim; if (strstr(substr, "augmented")) return kAug; if (strstr(substr, "dominant 7th")) return kDom7; // TODO: other chords // beware when adding new chords // put shorter names at end of this function! return C; } // ----------------- 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 * 5; // how many step (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 * 5; // how many step (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; 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; i