/*----------------------------------------------* * File: composer.cc * * Date: Jan 2002 * * Author: Joy M. Schoenberger * * Purpose: Composer Class * *----------------------------------------------*/ #include "composer.h" #include "stdio.h" // ******************** Constructor Composer::Composer(){ genMap = new DNA; if(genMap) initializeBroad(); } // ********************** Destructor Composer::~Composer(void){ if(genMap) delete genMap; } // ********************* display function void Composer::display() { cout << "Chord Progression: " << endl; for(int i = 0;ivalidNotes[i].display(); cout << "Valid Intervals: " << endl; for(int i = 0;ivalidIntervals[0].isValid(i)) cout << " " << i-12 << endl; cout << "Phrase Pattern Set: " << endl; for(int i = 0;iphrasePatterns[i] << endl; cout << "Min/Max Values: " << endl; cout << " maxChordLen: " << genMap->maxChordLen << endl; cout << " minChordLen: " << genMap->minChordLen << endl; cout << " maxVoices : " << genMap->maxVoices << endl; cout << " minVoices : " << genMap->minVoices << endl; cout << " maxRange : " << genMap->maxRange << endl; }//end display //*********************** composePhrase function Phrase Composer::composePhrase(){ Phrase retPhrase; Note nextNote, prevNote; int numVoices, chordCounter; int chordIndex, numPosVoices, noteCount; bool possibleVoices[11]; int chordLens[numChords]; bool first; int minNote, maxNote; minNote = 0; maxNote = 0; // Calculate length of each chord int sum = 0; for(int h = 0;h=genMap->minVoices)&&(i<=genMap->maxVoices) ); if(possibleVoices[i]) numPosVoices++; } while(numPosVoices > 0){ // randomly pick from possible number of voices int pick = Equilikely(1,numPosVoices); numVoices = -1; for(int pindex = 0;pindexvalidNotes[chordIndex], first,minNote,maxNote,noteCount)){ if(first){ maxNote = nextNote.max(); minNote = nextNote.min(); } else{ if(nextNote.max() > maxNote) maxNote = nextNote.max(); if(nextNote.min() < minNote) minNote = nextNote.min(); } numPosVoices = -1; } } retPhrase.notes[noteCount] = nextNote; /* if(first){ cout << "composePhrase setting retPhrase.notes[" << noteCount << "] = nextNote: "; nextNote.display(); } */ prevNote = nextNote; // this will be empty if composition failed chordCounter++; noteCount++; first = false; }//end chordCounter }//end chordIndex // If composition of nextNote fails, insert rests until next chord // While I could change prevNote and try again, this would take // too much time, so we will just assume the chord Progression is // too weird to come up with a good composition return retPhrase; }//end composePhrase Song Composer::composeSong(){ Song retSong; retSong.initialize(); for(int i = 0;ivalidNotes[i].initialize(); genMap->validNotes[i].semitones = Equilikely(0,4095); } for(int i = 0;ivalidIntervals[i].initialize(); genMap->validIntervals[i].intervals = Equilikely(0,16777215); } for(int i = 0;iphrasePatterns[i] = 0; genMap->phrasePatterns[i] += Equilikely(1,4); genMap->phrasePatterns[i] += 10*Equilikely(1,4); genMap->phrasePatterns[i] += 100*Equilikely(1,4); genMap->phrasePatterns[i] += 1000*Equilikely(1,4); } genMap->minChordLen = Equilikely(0,16); genMap->maxChordLen = Equilikely(0,16); if(genMap->minChordLen > genMap->maxChordLen){ temp = genMap->minChordLen; genMap->minChordLen = genMap->maxChordLen; genMap->maxChordLen = temp; } genMap->minVoices = Equilikely(0,12); genMap->maxVoices = Equilikely(0,12); if(genMap->minVoices > genMap->maxVoices){ temp = genMap->minVoices; genMap->minVoices = genMap->maxVoices; genMap->maxVoices = temp; } genMap->maxRange = Equilikely(0,36); genMap->maxConRange = Equilikely(0,36); } // ************************* initializeBroad ****************** void Composer::initializeBroad(){ // pseudo-random initialization that allows for more initial possibility // in the first generation. this should lead to broader evolution int temp; for(int i = 0;ivalidNotes[i].initialize(); genMap->validNotes[i].semitones = Equilikely(4000,4095); // more set bits } for(int i = 0;ivalidIntervals[i].initialize(); genMap->validIntervals[i].intervals = Equilikely(16777120,16777215); // more set bits } for(int i = 0;iphrasePatterns[i] = 0; genMap->phrasePatterns[i] += Equilikely(1,4); genMap->phrasePatterns[i] += 10*Equilikely(1,4); genMap->phrasePatterns[i] += 100*Equilikely(1,4); genMap->phrasePatterns[i] += 1000*Equilikely(1,4); } genMap->minChordLen = Equilikely(0,16); genMap->maxChordLen = Equilikely(0,16); if(genMap->minChordLen > genMap->maxChordLen){ temp = genMap->minChordLen; genMap->minChordLen = genMap->maxChordLen; genMap->maxChordLen = temp; } genMap->minVoices = Equilikely(0,12); genMap->maxVoices = Equilikely(0,12); if(genMap->minVoices > genMap->maxVoices){ temp = genMap->minVoices; genMap->minVoices = genMap->maxVoices; genMap->maxVoices = temp; } genMap->maxRange = Equilikely(0,36); genMap->maxConRange = Equilikely(0,36); } // *************************** Mutate *************************** void Composer::mutate(){ initialize(); } //********************************* initializeByHand ************************** void Composer::initializeByHand(){ for(int i = 0;ivalidNotes[i].initialize();// 0-11 is G-F# genMap->validNotes[0].setTone(0); genMap->validNotes[0].setTone(4); genMap->validNotes[0].setTone(7); genMap->validNotes[1].setTone(5); genMap->validNotes[1].setTone(9); genMap->validNotes[1].setTone(0); genMap->validNotes[2].setTone(7); genMap->validNotes[2].setTone(11); genMap->validNotes[2].setTone(2); genMap->validNotes[3].setTone(0); genMap->validNotes[3].setTone(4); genMap->validNotes[3].setTone(7); genMap->validNotes[4].setTone(0); genMap->validNotes[4].setTone(4); genMap->validNotes[4].setTone(7); genMap->validNotes[5].setTone(5); genMap->validNotes[5].setTone(9); genMap->validNotes[5].setTone(0); genMap->validNotes[6].setTone(7); genMap->validNotes[6].setTone(11); genMap->validNotes[6].setTone(2); genMap->validNotes[6].setTone(9); genMap->validNotes[7].setTone(0); genMap->validNotes[7].setTone(4); genMap->validNotes[7].setTone(7); genMap->validIntervals[0].initialize(); genMap->validIntervals[0].setValid(2+12); genMap->validIntervals[0].setValid(3+12); genMap->validIntervals[0].setValid(4+12); genMap->validIntervals[0].setValid(5+12); genMap->validIntervals[0].setValid(7+12); genMap->validIntervals[0].setValid(12+12); genMap->validIntervals[0].setValid(-2+12); genMap->validIntervals[0].setValid(-3+12); genMap->validIntervals[0].setValid(-4+12); genMap->validIntervals[0].setValid(-7+12); genMap->validIntervals[0].setValid(-12+12); for(int i = 1;ivalidIntervals[i] = genMap->validIntervals[0]; genMap->phrasePatterns[0] = 1234; genMap->phrasePatterns[1] = 1122; genMap->phrasePatterns[2] = 1212; genMap->phrasePatterns[3] = 1231; genMap->phrasePatterns[4] = 1221; for(int i = 5;iphrasePatterns[i] = 1111; genMap->minChordLen = 4; genMap->maxChordLen = 32; genMap->minVoices = 1; genMap->maxVoices = 4; genMap->maxRange = 24; genMap->maxConRange = 12; } /* ------------------------------------------------------------ * * bool composeNextNote(Note&, Note, int, Chord, bool) * * * * Try every possible combination of pitches until one is found * * which may follow nextNote, but try combinations in a random * * order, so that every combination has a equal chance of being * * chosen. Return true if a combination is found * * ------------------------------------------------------------ */ bool Composer::composeNextNote(Note & nextNote, Note prevNote, int numVoices, Chord currChord, bool first, int minPitch, int maxPitch, int noteNum){ if (numVoices < 1) return false; Note possible[numVoices]; // keep track of tried notes for each voice int numPossible[numVoices]; int pick, n, v; int minNext, maxNext; //initialize for(int i = 0;igenMap->maxRange)) || ((n > maxPitch) && ((n - minPitch)>genMap->maxRange)) ){ possible[0].unPlay(n); numPossible[0]--; } } } /* // DEBUG //if(first){ cout << endl << "numVoices = " << numVoices << endl; cout << "Initialized possible notes: "; possible[0].display(); */ for(v = 0; v < numVoices; v++){ //cout << "numPossible[" << v << "] = " << numPossible[v] << endl; if(numPossible[v] > 0){ pick = Equilikely(1,numPossible[v]); // pick n randomly from possible[v] //if(first) //cout << "picked note " << pick ; n = possible[v].getNth(pick); //if(first) //cout << ", which is " << n << endl; possible[v].unPlay(n); // remove n from possible[v] numPossible[v]--; nextNote.setPlay(n); // add n to nextNote if(v == 0){ minNext = n; maxNext = n; } if(n < minPitch) minPitch = n; else if(n > maxPitch) maxPitch = n; // update min/max if(v < numVoices - 1){ // if not done yet possible[v+1] = possible[v]; // initialize next voice numPossible[v+1] = numPossible[v]; for(int p = 0;p<61;p++){ // eliminate out-of-range notes if(possible[v+1].isPlayed(p)){ if( ((p < minPitch) && ((maxPitch - p)>genMap->maxRange)) || ((p > maxPitch) && ((p - minPitch)>genMap->maxRange)) ){ possible[v+1].unPlay(p); numPossible[v+1]--; } // end if out of phrase range if( ((p < minNext) && ((maxNext - p)>genMap->maxConRange)) || ((p > maxNext) && ((p - minNext)>genMap->maxConRange)) ){ possible[v+1].unPlay(p); numPossible[v+1]--; } // end if out of chord range }// end if possible }//end for p } // end if not done } else if(v > 0){ //printf("ELSEIF\n"); v-=2; // if we're not on first voice, back up one, try again // with another pitch (for loop will +1, net -1) nextNote.unPlay(n);// remove previous pitch from nextNote } else{ // no possible combination to follow prevNote with numVoices return false; } }//end for /* if(first){ cout << "returning nextNote: "; nextNote.display(); } */ return true; } /* ---------------------------------------------------------------- * * bool ivalCheckOK(int placedNote,Note prevNote) * * * * Test if placedNote satisfies interval requirements for following * * prevNote * *----------------------------------------------------------------- */ bool Composer::ivalCheckOK(int placedNote,Note prevNote, int noteNum){ int interval; for(int i = 0;i<61;i++){ if(prevNote.isPlayed(i)){ // determine interval from i to placedNote interval = placedNote - i + 12; if(genMap->validIntervals[noteNum].isValid(interval)) return true; } } return false; } /* ---------------------------------------------------------------- * * int Equilikely(int min, int max) * * * * return an equilikely random integer between min and max * * inclusive, where min < max * *----------------------------------------------------------------- */ int Composer::Equilikely(int min, int max){ return (min + (int) ((max - min + 1) * Random())); }//end Uniform /* ---------------------------------------------------------- * * void crossBreed(Composer & Mate) * * * * returns a descendant of This composer and its Mate * *------------------------------------------------------------*/ DNA* Composer::crossBreed(Composer & Mate){ DNA *mateDNA[2]; DNA *childDNA = new DNA; mateDNA[0]= genMap; mateDNA[1] = Mate.genMap; int parent, minVal, maxVal; for(int i = 0;ivalidNotes[i].initialize(); for(int c = 0;cvalidNotes[i].isOn(c)) childDNA->validNotes[i].setTone(c); } } for(int v = 0;vvalidIntervals[v].initialize(); for(int n = 0;nvalidIntervals[v].isValid(n)) childDNA->validIntervals[v].setValid(n); } } for(int p = 0;pphrasePatterns[p] = 0; parent = Equilikely(0,1); childDNA->phrasePatterns[p] = (mateDNA[parent]->phrasePatterns[p]/1000)*1000; parent = Equilikely(0,1); childDNA->phrasePatterns[p] = childDNA->phrasePatterns[p] + ((mateDNA[parent]->phrasePatterns[p]/100)%10)*100; parent = Equilikely(0,1); childDNA->phrasePatterns[p] = childDNA->phrasePatterns[p] + (((mateDNA[parent]->phrasePatterns[p]/10)%100)%10)*10; parent = Equilikely(0,1); childDNA->phrasePatterns[p] = childDNA->phrasePatterns[p] + (((mateDNA[parent]->phrasePatterns[p]%1000)%100)%10); } minVal = min(mateDNA[0]->minChordLen,mateDNA[1]->minChordLen); maxVal = max(mateDNA[0]->minChordLen,mateDNA[1]->minChordLen); childDNA->minChordLen = Equilikely(minVal,maxVal); minVal = min(mateDNA[0]->maxChordLen,mateDNA[1]->maxChordLen); maxVal = max(mateDNA[0]->maxChordLen,mateDNA[1]->maxChordLen); childDNA->maxChordLen = Equilikely(minVal,maxVal); if(childDNA->minChordLen > childDNA->maxChordLen){ minVal = childDNA->maxChordLen; childDNA->maxChordLen = childDNA->minChordLen; childDNA->minChordLen = minVal; } minVal = min(mateDNA[0]->minVoices,mateDNA[1]->minVoices); maxVal = max(mateDNA[0]->minVoices,mateDNA[1]->minVoices); childDNA->minVoices = Equilikely(minVal,maxVal); minVal = min(mateDNA[0]->maxVoices,mateDNA[1]->maxVoices); maxVal = max(mateDNA[0]->maxVoices,mateDNA[1]->maxVoices); childDNA->maxVoices = Equilikely(minVal,maxVal); if(childDNA->minVoices > childDNA->maxVoices){ minVal = childDNA->maxVoices; childDNA->maxVoices = childDNA->minVoices; childDNA->minVoices = minVal; } minVal = min(mateDNA[0]->maxRange,mateDNA[1]->maxRange); maxVal = max(mateDNA[0]->maxRange,mateDNA[1]->maxRange); childDNA->maxRange = Equilikely(minVal,maxVal); minVal = min(mateDNA[0]->maxConRange,mateDNA[1]->maxConRange); maxVal = max(mateDNA[0]->maxConRange,mateDNA[1]->maxConRange); childDNA->maxConRange = Equilikely(minVal,maxVal); return childDNA; }