//A genetic algorithm to find a melody line that sounds good //when played forward and backward at the same time. #include #include #define checkcadence 16 #define ll36 3 #define ll58 -5 #define inchord 2 #define beginsI 2 #define endsI 2 #define endsV 1 #define endsnothing -3 #define VIcadence 3 #define IVIcadence 2 #define halfcadence 1 #define deceptivecadence 1 #define reps -2 #define passing 2 #define neighboring 1 #define suspension 3 #define escape 1 #define pedal -2 #define stepwise 4 #define numreps 2 #define numbars 50 #define numnotes 32 #define stopscore 1000 #define mutate 150 #define matewho 24 #define highnote 28 class melody { public: melody(int init = 1); ~melody(); int analyze(); int getscore(); unsigned char* getnotes(); private: unsigned char notes[numnotes]; melody* bassparent; unsigned char *bass; melody* tenorparent; unsigned char *tenor; int iscore; void separate(); int repetition(); int cadences(); int nonchordal(); int susp(); int pedalt(); int passt(); int neighbort(); int escapet(); int con(unsigned char, unsigned char); int interval(unsigned char, unsigned char); unsigned char min(unsigned char p1, unsigned char p2); unsigned char max(unsigned char p1, unsigned char p2); }; class app { public: app(); ~app(); private: melody* bars[numbars]; melody* maters[matewho]; void sortbars(); void mate(); void oddmate(); void evenmate(); void intermate(melody* parent1, melody* parent2); void lastmate(melody* p1, melody* p2, melody* p3); void insertnew(); int insertable(melody* ins); int same(unsigned char* p1, unsigned char* p2); int odd(int num); }; melody::melody(int init) { if(init) { for(int i = 0; i < numnotes; i++) notes[i] = (unsigned char)(rand() % (highnote + 1)); analyze(); } } melody::~melody() { } int melody::con(unsigned char low, unsigned char hi) { unsigned char consonant; if(low == 0) consonant = (hi % 7); else consonant = (hi % low); return ((consonant == 0) || (consonant == 2) || (consonant == 4)); } int melody::interval(unsigned char low, unsigned char hi) { if(low == 0) return ((hi % 7) + 1); return ((hi % low) + 1); } void melody::separate() { for(int i = 0; i < (numnotes >> 1); i++) { bass[i] = bass[numnotes - i - 1] = min(notes[i], notes[numnotes - i - 1]); tenor[i] = tenor[numnotes - i - 1] = max(notes[i], notes[numnotes - i - 1]); } if(i < (numnotes - i)) bass[i] = tenor[i] = notes[i]; } int melody::analyze() { int tally = 0, i = 0, dist; bassparent = new melody(0); bass = bassparent -> getnotes(); tenorparent = new melody(0); tenor = tenorparent -> getnotes(); separate(); if ((notes[0] % 7) == 0) tally += beginsI; if ((notes[numnotes - 1] % 7) == 0) tally += endsI; else if ((notes[numnotes - 1] % 7) == 4) tally += endsV; else tally += endsnothing; tally += repetition(); tally += cadences(); tally += nonchordal(); if(con(bass[numnotes], tenor[numnotes])) tally += inchord; for(i = 0; i < (numnotes - 1); i++) { if(con(bass[i], tenor[i])) tally += inchord; if(abs(bass[i + 1] - bass[i]) == 1) tally += stepwise; if(abs(tenor[i + 1] - tenor[i]) == 1) tally += stepwise; if((dist = interval(min(bass[i], bass[i+1]), max(bass[i], bass[i+1]))) == interval(min(tenor[i], tenor[i+1]), max(tenor[i], tenor[i+1]))) { if((dist == 3) || (dist == 6)) tally += ll36; else if((dist == 5) || ((dist == 1) && (bass[i] != tenor[i]))) tally += ll58; } } iscore = tally; delete bass; delete tenor; return tally; } int melody::repetition() { unsigned char lastnote = notes[0]; int total = 0, repetitions = 0; for(int i = 1; i < numnotes; i++) { if (notes[i] != lastnote) {lastnote = notes[i]; repetitions = 0;} else if (++repetitions > numreps) total += reps; } return total; } int melody::cadences() { int check = checkcadence - 2; int total = 0; while (check < (numnotes - 1)) { switch(notes[check + 1] % 7) { case 0: if ((notes[check] % 7) == 4) total += VIcadence; else if ((notes[check] % 7) == 3) total += IVIcadence; break; case 4: total += halfcadence; break; default: if ((notes[check] % 7) == 4) total += deceptivecadence; break; } check += checkcadence; } return total; } int melody::nonchordal() { return (pedalt() + susp() + passt() + neighbort() + escapet()); } int melody::pedalt() { int total = 0, length; for(int i = 0; i < (numnotes - 2); i++) { int length = 0; while( (bass[i] == bass[i + 1] == bass[i + 2]) && (i < (numnotes - 2))) { if ((con(bass[i], tenor[i]) && !con(bass[i+1], tenor[i+1]) && con(bass[i+2], tenor[i+2])) || (!con(bass[i], tenor[i]) && con(bass[i+1], tenor[i+1]) && !con(bass[i+2], tenor[i+2]))) {total += pedal; length++;} i++; } if(length) total += (pedal << 1); } return total; } int melody::susp() { int total = 0; for(int i = 0; i < (numnotes - 1); i++) if(bass[i] == bass[i + 1]) if((tenor[i] - tenor[i + 1]) == 1) { int ret = interval(bass[i], tenor[i]); if((ret == 2) || (ret == 6) || (ret == 4)) total += suspension; } return total; } int melody::passt() { int total = 0; for(int i = 0; i < (numnotes - 2); i++) if(bass[i] == bass[i + 1] == bass[i + 2]) if(con(bass[i], tenor[i]) && !con(bass[i + 1], tenor[i + 1]) && con(bass[i + 2], tenor[i + 2])) if(((tenor[i + 2] - tenor[i + 1]) == (tenor[i + 1] - tenor[i]) == 1) || ((tenor[i + 2] - tenor[i + 1]) == (tenor[i + 1] - tenor[i]) == -1)) total += passing; return total; } int melody:: neighbort() { int total = 0; for(int i = 0; i < (numnotes - 2); i++) if(bass[i] == bass[i + 1] == bass[i + 2]) if(con(bass[i], tenor[i]) && !con(bass[i + 1], tenor[i + 1]) && con(bass[i + 2], tenor[i + 2])) if((tenor[i] == tenor[i + 2]) && (abs(tenor[i] - tenor[i + 1]) == 1)) total += neighboring; return total; } int melody::escapet() { int total = 0; for(int i = 0; i < (numbars - 2); i++) if(con(bass[i], tenor[i]) && !con(bass[i + 1], tenor[i + 1]) && con(bass[i + 2], tenor[i + 2])) if ((abs(tenor[i] - tenor[i + 1]) == 1) && (abs(tenor[i + 1] - tenor[i + 2]) != 1)) total += escape; return total; } int melody::getscore() { return iscore; } unsigned char melody::min(unsigned char p1, unsigned char p2) { if(p1 > p2) return p2; return p1; } unsigned char melody::max(unsigned char p1, unsigned char p2) { if(p1 > p2) return p1; return p2; } unsigned char* melody::getnotes() { return notes; } app::app() { int crossed = 0; srand(1); //seed the random # generator for(int i = 0; i < numbars; i++) bars[i] = new melody; sortbars(); for(i = 0; i < matewho; i++) maters[i] = NULL; do { crossed++; mate(); insertnew(); cout << crossed << " " << bars[numbars - 1] -> getscore() << endl; } while((bars[numbars - 1] -> getscore()) < stopscore); } app::~app() { for(int i = 0; i < numbars; i++) delete bars[i]; } void app::sortbars() { int top; melody* temp; for(int i = 0; i < numbars; i++) { top = 0; for(int j = 0; j < (numbars - i); j++) if ((bars[j] -> getscore()) > (bars[top] -> getscore())) top = j; temp = bars[numbars - i - 1]; bars[numbars - i - 1] = bars[top]; bars[top] = temp; } } void app::mate() { if(odd(matewho)) oddmate(); else evenmate(); } void app::oddmate() { for(int i = 0; i < ((numbars >> 1) - 2); i++) intermate(bars[i << 1], bars[(i << 1) + 1]); lastmate(bars[(i << 1)], bars[(i << 1) + 1], bars[(i << 1) + 2]); } void app::evenmate() { for(int i = 0; i < matewho; i += 2) intermate(bars[numbars - i - 1], bars[numbars - i - 2]); } void app::intermate(melody* parent1, melody* parent2) { unsigned char* bar1 = parent1 -> getnotes(); unsigned char* bar2 = parent2 -> getnotes(); unsigned char *result1, *result2; int rip = rand() % (numnotes - 1); for(int i = -1; maters[i] != NULL; i++); maters[i] = new melody(0); result1 = maters[i] -> getnotes(); maters[i + 1] = new melody(0); result2 = maters[i + 1] -> getnotes(); for(int j = 0; j < (rip + 1); j++) { result1[j] = bar1[j]; result2[j] = bar2[j]; } for(; j < numnotes; j++) { result1[j] = bar2[j]; result2[j] = bar1[j]; } if((rand() % mutate) == 0) result1[rand() % numnotes] = (rand() % (highnote + 1)); if((rand() % mutate) == 0) result2[rand() % numnotes] = (rand() % (highnote + 1)); maters[i] -> analyze(); maters[i + 1] -> analyze(); } void app::lastmate(melody* p1, melody* p2, melody* p3) { unsigned char* bar1 = p1 -> getnotes(); unsigned char* bar2 = p2 -> getnotes(); unsigned char* bar3 = p3 -> getnotes(); unsigned char *r1, *r2, *r3; int rip = rand() % (numnotes - 1); int pos = matewho - 3; maters[pos] = new melody(0); r1 = maters[pos] -> getnotes(); maters[pos + 1] = new melody(0); r2 = maters[pos + 1] -> getnotes(); maters[pos + 2] = new melody(0); r3 = maters[pos + 2] -> getnotes(); for(int j = 0; j < (rip + 1); j++) { r1[j] = bar1[j]; r2[j] = bar2[j]; r3[j] = bar3[j]; } for(; j < numnotes; j++) { r1[j] = bar3[j]; r2[j] = bar1[j]; r3[j] = bar2[j]; } if((rand() % mutate) == 0) r1[rand() % numnotes] = (rand() % (highnote + 1)); if((rand() % mutate) == 0) r2[rand() % numnotes] = (rand() % (highnote + 1)); maters[pos] -> analyze(); maters[pos + 1] -> analyze(); maters[pos + 2] -> analyze(); } void app::insertnew() { int pos; for(int i = 0; i < matewho; i++) { pos = insertable(maters[i]); if(pos != -1) { delete bars[0]; for(int j = 0; j < pos; j++) bars[j] = bars[j + 1]; bars[pos] = maters[i]; } else delete maters[i]; maters[i] = NULL; } } int app::insertable(melody* ins) { int ret = 0; for(int i = (numbars - 1); (i > -1) && (ret == 0); i--) { if((ins -> getscore()) > (bars[i] -> getscore())) ret = i; else if((ins -> getscore()) == (bars[i] -> getscore())) if(same(ins -> getnotes(), bars[i] -> getnotes())) ret = -1; } return ret; } int app::same(unsigned char* p1, unsigned char* p2) { int ret = 1, i = 0; while(ret && (i < numbars)) { if(p1[i] != p2[i]) ret = 0; i++; } return ret; } int app::odd(int num) {return ((num % 2) == 1);} void main() {app theapp; cout<< "Done!";}