// // Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu> // Programmer: Sachiko Deguchi <deguchi@ccrma.stanford.edu> // Creation Date: Thu Jun 27 12:17:39 PDT 2002 // Last Modified: Sat Aug 16 13:15:53 2003 // Filename: ...sig/doc/examples/all/kotomel2.cpp // Syntax: C++; batonImprov 2.0 // // Description: Melodic pattern player. Reads an input file // example syntax given at bottom of file. // Tries to read k-pattern.txt in current directory. // #define USE_TABLET #include "batonImprov.h" #include "Array.h" #include <ctype.h> #ifndef OLDCPP #include <fstream> #include <iostream> using namespace std; #else #include <fstream.h> #include <iostream.h> #endif // function declarations void readfile(const char* filename); void processLine(const char* line); void clearpatterns(Array<Array<Array<Array<int> > > >& dp); void printpatterns(void); // variables Array<int> degrees; Array<int> positions; Array<Array<Array<Array<int> > > > degpatterns; // scale degree patterns Voice voice; int transpose = 2; // transpose value for tonic (0 = C) int cpat = 0; // current pattern int csub = 0; // current subpattern int npat = 0; // new pattern choosen by stick 2 int nsub = 0; // new subpattern choosen by stick 2 int cycle= 0; // used to chose the next note in pattern int csd = 0; // current scale degree /////////////////////////////////////////////////////////////////////////// // // Improv interface functions // ////////////////////////////// // // description -- // void description(void) { cout << "kotomel2\n" " Left Hand: choose melodic pattern\n" " Right Hand: choose starting scale degree\n" " , = koto . = chinese / = german melodic patterns\n" " space = print current pattern, \\ = print all patterns\n" " transpose tonic note: [ = down, ] = up\n" "Keyboard control:\n" "\n" " Patterns: Scale Degrees:\n" " 123456 67890\n" " qwerty yuiop\n" " asdfgh\n" " zxcvbn\n" << endl; } ////////////////////////////// // // initialization -- // void initialization(void) { voice.setPort(synth.getPort()); voice.setChannel(0); degrees.setSize(7); degrees.allowGrowth(0); // put mode in for default: // major: {0, 2, 4, 5, 7, 9, 11} // koto: {0, 1, 5, 6, 7, 8, 10} degrees[0] = 0; degrees[1] = 1; degrees[2] = 5; degrees[3] = 6; degrees[4] = 7; degrees[5] = 8; degrees[6] = 10; positions.setSize(5); positions.allowGrowth(0); positions[0] = 0; positions[1] = 1; positions[2] = 2; positions[3] = 4; positions[4] = 5; clearpatterns(degpatterns); readfile("k-pattern.txt"); } ////////////////////////////// // // stick1trig -- chooses next note to play based // on pattern chosen by stick2trig. // void stick1trig(void) { static int oldsd = -1; int xchoice = midiscale(baton.x1t, 0, 4); int ychoice = midiscale(baton.y1t, 0, 1); csd = positions[xchoice]; if (csd != oldsd) { oldsd = csd; cycle = 0; cpat = npat; csub = nsub; cout << "Starting pattern " << cpat << (csub == 0 ? 'a' : 'b') << " on scale degree " << csd + 1 << endl; } if (cycle >= 4) { cycle = 0; } if (cycle == 0) { cpat = npat; csub = nsub; } int octave = ychoice + 4; int basepitch = degrees[csd] + 12 * octave + transpose; int note = basepitch + degpatterns[csd][cpat][csub][cycle++]; voice.play(note, 127); cout << "playing note: " << note << "\t(based on s"<< csd+1 << "-" << cpat << (csub == 0? 'a':'b') << "[" << cycle << "])" << endl; } ////////////////////////////// // // stick2trig -- chooses the melodic pattern // void stick2trig(void) { int xchoice = midiscale(baton.x2t, 0, 4); int ychoice = midiscale(baton.y2t, 0, 3); switch (10 * ychoice + xchoice) { case 0: npat = 4; nsub = 1; break; case 1: npat = 5; nsub = 1; break; case 2: npat = 6; nsub = 1; break; case 3: npat = 7; nsub = 1; break; case 4: voice.off(); break; case 10: npat = 4; nsub = 0; break; case 11: npat = 5; nsub = 0; break; case 12: npat = 6; nsub = 0; break; case 13: npat = 7; nsub = 0; break; case 14: voice.off(); break; case 20: npat = 0; nsub = 1; break; case 21: npat = 1; nsub = 1; break; case 22: npat = 2; nsub = 1; break; case 23: npat = 3; nsub = 1; break; case 24: voice.off(); break; case 30: npat = 0; nsub = 0; break; case 31: npat = 1; nsub = 0; break; case 32: npat = 2; nsub = 0; break; case 33: npat = 3; nsub = 0; break; case 34: voice.off(); break; } cout << "Next Pattern: " << npat << (nsub == 0? 'a' : 'b') << endl; } ////////////////////////////// // // keyboardchar -- // void keyboardchar(int key) { int i; switch (key) { case '\\': printpatterns(); break; case ' ': cout << "current pattern:\ts" << csd+1 << ":" << cpat << (csub == 0? 'a':'b') << "=[ "; for (i=1; i<4; i++) { cout << degpatterns[csd][cpat][csub][i] - degpatterns[csd][cpat][csub][i-1]; if (i<3) { cout << ", "; } else { cout << " "; } } cout << "]" << endl; break; case ',': clearpatterns(degpatterns); readfile("k-pattern.txt"); break; case '.': clearpatterns(degpatterns); readfile("c-pattern.txt"); break; case '/': clearpatterns(degpatterns); readfile("g-pattern.txt"); break; case '[': transpose--; cout << "Tonic: " << transpose << endl; break; case ']': transpose++; cout << "Tonic: " << transpose << endl; break; case '1': baton.x2t = 12; baton.y2t = 112; stick2trig(); break; case '2': baton.x2t = 36; baton.y2t = 112; stick2trig(); break; case '3': baton.x2t = 60; baton.y2t = 112; stick2trig(); break; case '4': baton.x2t = 84; baton.y2t = 112; stick2trig(); break; case '5': baton.x2t = 108; baton.y2t = 112; stick2trig(); break; case 'q': baton.x2t = 12; baton.y2t = 80; stick2trig(); break; case 'w': baton.x2t = 36; baton.y2t = 80; stick2trig(); break; case 'e': baton.x2t = 60; baton.y2t = 80; stick2trig(); break; case 'r': baton.x2t = 84; baton.y2t = 80; stick2trig(); break; case 't': baton.x2t = 108; baton.y2t = 80; stick2trig(); break; case 'a': baton.x2t = 12; baton.y2t = 48; stick2trig(); break; case 's': baton.x2t = 36; baton.y2t = 48; stick2trig(); break; case 'd': baton.x2t = 60; baton.y2t = 48; stick2trig(); break; case 'f': baton.x2t = 84; baton.y2t = 48; stick2trig(); break; case 'g': baton.x2t = 108; baton.y2t = 48; stick2trig(); break; case 'z': baton.x2t = 12; baton.y2t = 16; stick2trig(); break; case 'x': baton.x2t = 36; baton.y2t = 16; stick2trig(); break; case 'c': baton.x2t = 60; baton.y2t = 16; stick2trig(); break; case 'v': baton.x2t = 84; baton.y2t = 16; stick2trig(); break; case 'b': baton.x2t = 108; baton.y2t = 16; stick2trig(); break; case '6': baton.x1t = 12; baton.y1t = 112; stick1trig(); break; case '7': baton.x1t = 36; baton.y1t = 112; stick1trig(); break; case '8': baton.x1t = 60; baton.y1t = 112; stick1trig(); break; case '9': baton.x1t = 84; baton.y1t = 112; stick1trig(); break; case '0': baton.x1t = 108; baton.y1t = 112; stick1trig(); break; case 'y': baton.x1t = 12; baton.y1t = 16; stick1trig(); break; case 'u': baton.x1t = 36; baton.y1t = 16; stick1trig(); break; case 'i': baton.x1t = 60; baton.y1t = 16; stick1trig(); break; case 'o': baton.x1t = 84; baton.y1t = 16; stick1trig(); break; case 'p': baton.x1t = 108; baton.y1t = 16; stick1trig(); break; } } // // Other improv interface functions // void b14plustrig(void) { } void b15plustrig(void) { } void b14minusuptrig(void) { } void b14minusdowntrig(void) { } void b15minusuptrig(void) { } void b15minusdowntrig(void) { } void finishup(void) { } void mainloopalgorithms(void) { } // // /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // clearpatterns -- // void clearpatterns(Array> > >& dp) { int sd, pat, sub, i; dp.setSize(7); dp.allowGrowth(0); for (sd=0; sd<dp.getSize(); sd++) { dp[sd].setSize(8); dp[sd].allowGrowth(0); for (pat=0; pat<dp[sd].getSize(); pat++) { dp[sd][pat].setSize(2); dp[sd][pat].allowGrowth(0); for (sub=0; sub<dp[sd][pat].getSize(); sub++) { dp[sd][pat][sub].setSize(4); dp[sd][pat][sub].allowGrowth(0); for (i=0; i<dp[sd][pat][sub].getSize(); i++) { dp[sd][pat][sub][i] = 0; } } } } } ////////////////////////////// // // readfile -- // void readfile(const char* filename) { ifstream infile(filename); if (!infile.is_open()) { cout << "Cannot open file: " << filename << endl; return; } char buffer[2048] = {0}; infile.getline(buffer, 256, '\n'); while (!infile.eof()) { processLine(buffer); infile.getline(buffer, 256, '\n'); } } ////////////////////////////// // // processLine -- // void processLine(const char* line) { int length = strlen(line); if (length > 256) { return; } char buffer[2048] = {0}; strcpy(buffer, line); char* ptr = strtok(buffer, " \t\n"); if (ptr == NULL) { // cout << "nothing on line" << endl; return; } if (ptr[0] == '#') { // print comment line cout << line << endl; return; } // first data field must start with an 's' or 'p': if (ptr[0] == 'S') { ptr[0] = 's'; } if (ptr[0] == 'P') { ptr[0] = 'p'; } if ((ptr[0] != 's') && (ptr[0] != 'p')) { return; } int scaledegree = -1; int position = -1; int pattern = -1; int count = 0; int tpose; if (strchr(ptr, '=') != NULL) { if (ptr[0] == 'p') { count = sscanf(ptr, "p%d=s%d", &position, &scaledegree); if (count != 2) { return; } if (count != 2) { cout << "First token invalid" << endl; return; } position--; scaledegree--; positions[position] = scaledegree; return; } else { count = sscanf(ptr, "s%d=%d", &scaledegree, &tpose); if (count != 2) { return; } // cout << "SCALE DEGREE " << scaledegree << " is " << tpose // << " half-steps above the tonic." << endl; if (scaledegree < 1 || scaledegree > 7) { cout << "Scale degree out of range: " << scaledegree << endl; } scaledegree = scaledegree - 1; // offset from zero if (scaledegree != 0) { degrees[scaledegree] = tpose; } else { transpose = tpose; } return; } } else { count = sscanf(ptr, "s%d:%d", &scaledegree, &pattern); if (count != 2) { cout << "First token invalid" << endl; return; } } char subpattern = ' '; int len2 = strlen(ptr); if (isalpha(ptr[len2-1])) { subpattern = tolower(ptr[len2-1]); } // cout << "S=" << scaledegree << "\tP=" << pattern // << "\tV=" << subpattern; scaledegree = scaledegree - 1; // offset from zero if (scaledegree < 0 || scaledegree > 6) { cout << "Scale degree is out of range:" << scaledegree << endl; return; } if (pattern < 0 || pattern > 7) { cout << "Pattern is out of range:" << pattern << endl; return; } if (subpattern != ' ' && subpattern != 'a' && subpattern != 'b') { cout << "Sub pattern is out of range:" << subpattern << endl; return; } ptr = strtok(NULL, " ,:\t\n;"); int degree; int icount = 0; Array<int> intervals; intervals.setSize(3); intervals.setAll(0); intervals.allowGrowth(0); while (ptr != NULL) { count = sscanf(ptr, "%d", °ree); if (count == 1) { // cout << "\t" << degree << "\t"; intervals[icount++] = degree; } ptr = strtok(NULL, " ,:\t\n;"); if (icount >= 3) { // only 3 intervals allowed break; } } // cout << endl; int i; if (subpattern == 'a' || subpattern == ' ') { degpatterns[scaledegree][pattern][0][0] = 0; for (i=1; i<4; i++) { degpatterns[scaledegree][pattern][0][i] = degpatterns[scaledegree][pattern][0][i-1] + intervals[i-1]; } } if (subpattern == 'b' || subpattern == ' ') { degpatterns[scaledegree][pattern][1][0] = 0; for (i=1; i<4; i++) { degpatterns[scaledegree][pattern][1][i] = degpatterns[scaledegree][pattern][1][i-1] + intervals[i-1]; } } } ////////////////////////////// // // printpatterns -- // void printpatterns(void) { int deg, pat, i; for (deg = 0; deg < 7; deg++) { for (pat = 0; pat < 8; pat++) { cout << "s" << deg+1 << ":" << pat << " :: a = "; for (i=1; i<4; i++) { cout << degpatterns[deg][pat][0][i] - degpatterns[deg][pat][0][i-1] << " "; } cout << "\tb ="; for (i=1; i<4; i++) { cout << degpatterns[deg][pat][1][i] - degpatterns[deg][pat][1][i-1] << " "; } cout << endl; } } } /* sample pattern file: # Chinese melodic patterns p1=s1 # position 1 = scale degree 1 p2=s2 # position 2 = scale degree 2 p3=s3 # position 3 = scale degree 3 p4=s5 # position 4 = scale degree 5 p5=s6 # position 5 = scale degree 6 s1=2 # scale degree 1 is 2 half-steps above C(i.e.: tonic is on D) s2=2 # scale degree 2 is 2 half-step above the tonic(s1) s3=4 # scale degree 3 is 4 half-steps above the tonic(s1) s5=7 # scale degree 5 is 7 half-steps above the tonic(s1) s6=9 # scale degree 6 is 9 half-steps above the tonic(s1) s1:0 -3 -2 -3 s1:1a -3 -2 2 s1:1b -3 -2 5 s1:2 -3 3 -3 s1:3 -3 3 2 s1:4 2 -2 -3 s1:5 2 -2 2 s1:6 2 2 -2 s1:7 2 2 3 s2:0 -2 -3 -2 s2:1a -2 -3 3 s2:1b -2 -3 5 s2:2 -2 2 -2 s2:3 -2 2 2 s2:4a 2 -2 -2 s2:4b 5 -3 -2 s2:5a 2 -2 2 s2:5b 2 -4 2 s2:6a 2 3 -3 s2:6b 2 3 -5 s2:7 2 3 2 s3:0 -2 -2 -3 s3:1 -2 -2 2 s3:2 -2 2 -2 s3:3 -2 2 3 s3:4 3 -3 -2 s3:5a 3 -3 3 s3:5b 3 -5 2 s3:6 3 2 -2 s3:7 3 2 3 s5:0 -3 -2 -2 s5:1a -3 -2 2 s5:1b -3 -2 5 s5:2 -3 3 -3 s5:3 -3 3 2 s5:4a 2 -2 -3 s5:4b 5 -3 -2 s5:5 2 -2 2 s5:6 2 3 -5 s5:7 2 3 2 s6:0 -2 -3 -2 s6:1a -2 -3 3 s6:1b -2 -3 5 s6:2 -2 2 -2 s6:3 -2 2 3 s6:4 3 -3 -2 s6:5a 3 -3 3 s6:5b 3 -5 2 s6:6 3 2 -2 s6:7 3 2 2 */ // md5sum: 47cb15d6c3221eef0686ba7e65938192 kotomel2.cpp [20050403]