From eb9ef05774af20edb43118182834c18a4ac70707 Mon Sep 17 00:00:00 2001 From: Davide Morelli Date: Tue, 18 Oct 2005 23:10:53 +0000 Subject: initial checkin svn path=/trunk/externals/frankenstein/; revision=3734 --- harmonizer.c | 629 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 629 insertions(+) create mode 100755 harmonizer.c (limited to 'harmonizer.c') diff --git a/harmonizer.c b/harmonizer.c new file mode 100755 index 0000000..7277723 --- /dev/null +++ b/harmonizer.c @@ -0,0 +1,629 @@ +/* +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