// // Programmer: Craig Stuart Sapp // Creation Date: Sat May 23 21:08:48 PDT 1998 // Last Modified: Fri Jul 3 14:18:04 PDT 1998 // Last Modified: Sat Oct 14 20:26:15 PDT 2000 revised for museinfo 1.0 // Last Modified: Wed Nov 29 12:14:41 PST 2000 use internal analysis // Last Modified: Tue Apr 21 00:41:11 PDT 2009 fixed spine manip printing // Last Modified: Thu May 14 20:25:17 PDT 2009 -U option added // Last Modified: Mon Apr 26 06:21:58 PDT 2010 -n, -s options added // Last Modified: Thu Mar 10 15:06:00 PST 2011 -i option added // Last Modified: Wed Mar 16 14:16:01 PDT 2011 added --iv option // Last Modified: Wed Mar 16 14:16:01 PDT 2011 added --iv option // Filename: ...sig/examples/all/sonority2.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/sonority2.cpp // Syntax: C++; museinfo // // Description: Analyzes **kern data for timeslice chord qualities // #include #include #include #include "humdrum.h" #include "Matrix.h" // function declarations void checkOptions (Options& opts, int argc, char* argv[]); void example (void); void processRecords (HumdrumFile& infile); void usage (const string& command); void fillStringWithNotes (char* string, ChordQuality& quality, HumdrumFile& infile, int line); int identifyBassNote (vector& notes, HumdrumFile& infile, int line, vector& sounding); int transitionalSonority(ChordQuality& quality, HumdrumFile& infile, int line); void printTriadImage (HumdrumFile& infile, int rows, int cols); void printBarlines (HumdrumFile& infile, int numberheight, int numberwidth); double getMeasureSize (HumdrumFile& infile, int width); void printLegend (int legendheight, int legendwidth); void printAttackMarker (HumdrumFile& infile, int line); void printAttackMarker (HumdrumFile& infile, int line); void printFinalis (HumdrumFile& infile); // global variables Options options; // database for command-line arguments char unknown[256] = {0}; // space for unknown chord simplification int chordinit; // for initializing chord detection function int explicitQ = 0; // used with -U option int notesQ = 0; // used with -n option int suppressQ = 0; // used with -s option int parenQ = 1; // used with -P option int ivQ = 0; // used with --iv option int forteQ = 0; // used with --forte option int finalisQ = 0; // used with -F option int base40Q = 0; // used with -F option int tnQ = 0; // used with --tn option int tniQ = 0; // used with --tni option int attackQ = 0; // used with -x option int appendQ = 0; // used with -a option int imageQ = 0; // used with -I option int imagex = 800; // used with -I option int imagey = 20; // used with -I option int octaveVal = -100; // used with -o option int barlinesQ = 0; // used with -b option int legendQ = 0; // used with -l option int outlineQ = 1; // int filenameQ = 0; // used with --filename option string notesep; // used with -N option const char* colorindex[26]; /////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { checkOptions(options, argc, argv); HumdrumStream streamer(options); HumdrumFile infile; while (streamer.read(infile)) { // analyze the input file according to command-line options if (imageQ) { printTriadImage(infile, imagey, imagex); } else if (finalisQ) { printFinalis(infile); } else { processRecords(infile); } } return 0; } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // printFinalis -- // void printFinalis(HumdrumFile& infile) { vector cq; infile.analyzeSonorityQuality(cq); char buffer[128] = {0}; if (filenameQ) { cout << infile.getFilename() << "\t"; } for (int i = (int)cq.size()-1; i>=0; i--) { if (infile[i].isData()) { int root = cq[i].getRoot(); if (root > 4000) { // skip rests continue; } if (base40Q) { cout << root-2; } else { cout << Convert::base40ToKern(buffer, root + 3 * 40); } break; } } cout << endl; } ////////////////////////////// // // printTriadImage -- // void printTriadImage(HumdrumFile& infile, int rows, int cols) { vector cq; infile.analyzeSonorityQuality(cq); infile.analyzeRhythm("4"); vector > triads; triads.resize(3); triads[0].resize(cols); std::fill(triads[0].begin(), triads[0].end(), 24); std::fill(triads[1].begin(), triads[1].end(), 24); std::fill(triads[2].begin(), triads[2].end(), 24); colorindex[0] = "0 255 0"; // C major colorindex[1] = "38 255 140"; // C-sharp major colorindex[2] = "63 95 255"; // D major colorindex[3] = "228 19 83"; // E-flat major colorindex[4] = "255 0 0"; // E major colorindex[5] = "255 255 0"; // F major colorindex[6] = "192 255 0"; // F-sharp major colorindex[7] = "93 211 255"; // G major colorindex[8] = "129 50 255"; // A-flat major colorindex[9] = "205 41 255"; // A major colorindex[10] = "255 160 0"; // B-flat major colorindex[11] = "255 110 10"; // B major colorindex[12] = "0 161 0"; // C minor colorindex[13] = "15 191 90"; // C-sharp minor colorindex[14] = "37 61 181"; // D minor colorindex[15] = "184 27 75"; // E-flat minor colorindex[16] = "175 0 0"; // E minor colorindex[17] = "220 200 0"; // F minor colorindex[18] = "140 200 0"; // F-sharp minor colorindex[19] = "65 163 181"; // G minor colorindex[20] = "100 28 181"; // G-sharp minor colorindex[21] = "136 13 181"; // A minor colorindex[22] = "181 93 20"; // B-flat minor colorindex[23] = "211 107 0"; // B minor colorindex[24] = "255 255 255"; // background colorindex[25] = "0 0 0"; // silence double start; double end; int inversion; int starti; int endi; int minQ; int majQ; int i, m, j, ii; int rootindex; for (i=0; i= cols) { starti = cols-1; } if (endi >= cols) { endi = cols-1; } rootindex = Convert::base40ToMidiNoteNumber(cq[i].getRoot()); if (minQ) { rootindex += 12; } inversion = cq[i].getInversion(); for (ii=starti; ii<=endi; ii++) { triads[inversion][ii] = rootindex; } } int barheight = 0; int barwidth = 0; if (barlinesQ) { barheight = 11; barwidth = cols; } int legendheight = 0; int legendwidth = 0; if (legendQ) { legendheight = 100; legendwidth = cols; } int value = 24; Matrix image(rows*2, cols, value); for (i=(int)triads.size()-1; i>=0; i--) { int start = int(i/2.0 * rows); for (m=0; m= image.getRowCount()) { ii = image.getRowCount() - 1; } for (j=0; j<(int)triads[i].size(); j++) { if (triads[i][j] < 24) { image.cell(ii,j) = triads[i][j]; } // cout << colorindex[triads[i][j]] << " "; //cout << triads[i][j] << " "; } //cout << "\n"; } } // print Image: cout << "P3\n"; cout << cols << " " << rows*2 + barheight + legendheight << "\n"; cout << "255\n"; for (i=image.getRowCount()-1; i>=0; i--) { for (j=0; j > xaxis(numberheight-1); for (i=0; i<(int)xaxis.size(); i++) { xaxis[i].resize(numberwidth); std::fill(xaxis[i].begin(), xaxis[i].end(), 24); } // bar counter keeps track of multiple repeats of the same // music. vector barcount(10000, 0); double measuresize = getMeasureSize(infile, numberwidth); int size; int style = 0; int position; int number; for (i=0; i= 0) && (number < (int)barcount.size())) { style = barcount[number]; barcount[number]++; } else { style = 0; } position = int(numberwidth * infile[i].getAbsBeat() / infile.getTotalDuration() + 0.5); if ((number % 100) == 0) { size = 10; } else if ((number % 50) == 0) { size = 8; } else if ((number % 10) == 0) { size = 6; } else if ((number % 5) == 0) { size = 4; } else { size = 2; if (measuresize < 3) { // don't display single measure ticks if they // are too closely spaced size = 0; } } } int color; if ((position >= 0) && (size > 0)) { for (j=0; j0) { xaxis[j][position-1] = color; } if (position<(int)xaxis[0].size()-1) { xaxis[j][position+1] = color; } } } } } // print a empty line so that small measure markers can be seen for (i=0; i<(int)xaxis[0].size(); i++) { cout << ' ' << colorindex[24]; } cout << '\n'; for (i=0; i<(int)xaxis.size(); i++) { for (j=0; j<(int)xaxis[i].size(); j++) { cout << ' ' << colorindex[xaxis[i][j]]; } cout << '\n'; } } ////////////////////////////// // // getMeasureSize -- return the pixel size of a single measure. // double getMeasureSize(HumdrumFile& infile, int width) { int i; int bar = -100; int lastbar = -1000; int line = -100; int lastline = -1000; int number; for (i=0; i > legend(legendheight); for (int i=0; i<(int)legend.size(); i++) { legend[i].resize(legendwidth); // set to background color: std::fill(legend[i].begin(), legend[i].end(), 24); } int startrow = legendheight / 5; int endrow = legendheight - 1; int startcol = legendwidth / 5; int endcol = legendwidth - startcol - 1; int dooutline = outlineQ; if (legendwidth < 100) { // don't print outline on a small legend dooutline = 0; } vector diatonic(8); // why not 7? diatonic[0] = 0; diatonic[1] = 2; diatonic[2] = 4; diatonic[3] = 5; diatonic[4] = 7; diatonic[5] = 9; diatonic[6] = 11; int v, lastv = -1; for (int i=startrow; i<=endrow; i++) { for (int j=startcol; j<=endcol; j++) { v = diatonic[int((double)(j-startcol)/(endcol-startcol+1)*7)]; if (dooutline && ((v != lastv) || (j == endcol) || (i==startrow) || (i==endrow))) { legend[i][j] = 25; } else if (i > (endrow + startrow)/2) { // major keys legend[i][j] = v; } else { // minor keys legend[i][j] = v+12; } lastv = v; } } int blackkeyheight = 2 * (endrow - startrow) / 3; int blackend = startrow + blackkeyheight; int blackstartcol = startcol; int blackendcol = endcol; blackstartcol = blackstartcol + (endcol - startcol) / 96; lastv = 0; for (int i=startrow; i<=blackend; i++) { for (int j=blackstartcol; j<=blackendcol; j++) { v = int((double)(j-blackstartcol)/(blackendcol-blackstartcol+1)*12); if ((v != lastv) || (i==startrow)) { if ((v != 0) && (v != 5)) { legend[i][j] = 25; } lastv = v; continue; } if (!((v==1)||(v==3)||(v==6)||(v==8)||(v==10))) { continue; } if (i==blackend) { legend[i][j] = 25; } else if (i > (blackend + startrow)/2) { // major keys legend[i][j] = v; } else { // minor keys legend[i][j] = v+12; } lastv = v; } } vector transcolor(26, 0); transcolor[24] = colorindex[24]; transcolor[25] = colorindex[25]; int transpose = 0; int rrotate = 0; for (int i=0; i<12; i++) { transcolor[i] = colorindex[(i+transpose+rrotate) % 12]; transcolor[i+12] = colorindex[((i+transpose+rrotate) % 12)+12]; } for (int i=0; i<(int)legend.size(); i++) { for (int j=0; j<(int)legend[i].size(); j++) { cout << ' ' << transcolor[legend[i][j]]; } cout << "\n"; } } ////////////////////////////// // // checkOptions -- validate and process command-line options. // void checkOptions(Options& opts, int argc, char* argv[]) { opts.define("t|type=b", "show only chord type"); opts.define("base40|base-40|40|b40=b", "show finalis as interval down to C"); opts.define("i|inversion=b", "show only chord inversion"); opts.define("r|root=b", "show only chord root"); opts.define("a|assemble|append=b", "append analysis to input data"); opts.define("f|format=s:t:i:r", "control display style"); opts.define("u|unknown=s:X", "control display of unknowns"); opts.define("U|unknown-pcs=b", "print pcs of unknown sonrities"); opts.define("d|debug=b", "determine bad input line num"); opts.define("n|notes=b", "display pitch classes in sonority"); opts.define("iv=b", "print interval vector"); opts.define("F|finalis=b", "print finalis pitch"); opts.define("x|sonor|suspension=b", "print marker if not all start attacks"); opts.define("forte=b", "print forte interval vector set name"); opts.define("Tn|tn=b", "print forte set with subsets"); opts.define("Tni|tni=b", "print forte set with subsets/inversion"); opts.define("s|suppress=b", "suppress data if overlapping sonority"); opts.define("I|image=s:800x20", "display image of major/minor chords"); opts.define("P|paren-off=b", "suppress parentheses for overlapping"); opts.define("N|separator=s: ", "characters to separate pitch classes"); opts.define("o|octave=i:4", "characters to separate pitch classes"); opts.define("b|barlines=b", "display barlines at bottom of image"); opts.define("l|legend=b", "display color mapping"); opts.define("filename=b", "display filename for finalis output"); opts.define("author=b", "author of program"); opts.define("version=b", "compilation info"); opts.define("example=b", "example usages"); opts.define("h|help=b", "short description"); opts.process(argc, argv); // handle basic options: if (opts.getBoolean("author")) { cout << "Written by Craig Stuart Sapp, " << "craig@ccrma.stanford.edu, May 1998" << endl; exit(0); } else if (opts.getBoolean("version")) { cout << argv[0] << ", version: Nov 2000" << endl; cout << "compiled: " << __DATE__ << endl; cout << MUSEINFO_VERSION << endl; exit(0); } else if (opts.getBoolean("help")) { usage(opts.getCommand().data()); exit(0); } else if (opts.getBoolean("example")) { example(); exit(0); } if (opts.getBoolean("root")) { ChordQuality::setDisplay("r"); } else if (opts.getBoolean("type")) { ChordQuality::setDisplay("t"); } else if (opts.getBoolean("inversion")) { ChordQuality::setDisplay("i"); } else { ChordQuality::setDisplay(opts.getString("format").data()); } Convert::chordType.setNullName(opts.getString("unknown").data()); Convert::chordInversion.setNullName(opts.getString("unknown").data()); Convert::kernPitchClass.setNullName(opts.getString("unknown").data()); strncpy(unknown, opts.getString("unknown").data(), 64); strcat(unknown, ":"); strncat(unknown, opts.getString("unknown").data(), 64); strcat(unknown, ":"); strncat(unknown, opts.getString("unknown").data(), 64); explicitQ = opts.getBoolean("unknown-pcs"); imageQ = opts.getBoolean("image"); if (imageQ) { imagex = 800; imagey = 20; sscanf(opts.getString("image").data(), "%dx%d", &imagex, &imagey); } filenameQ = opts.getBoolean("filename"); ivQ = opts.getBoolean("iv"); attackQ = opts.getBoolean("suspension"); forteQ = opts.getBoolean("forte"); tnQ = opts.getBoolean("Tn"); if (tnQ) { forteQ = 1; } tniQ = opts.getBoolean("Tni"); if (tniQ) { tnQ = 1; forteQ = 1; } finalisQ = opts.getBoolean("finalis"); base40Q = opts.getBoolean("base-40"); notesQ = opts.getBoolean("notes"); suppressQ = opts.getBoolean("suppress"); parenQ = !opts.getBoolean("paren-off"); appendQ = opts.getBoolean("append"); barlinesQ = opts.getBoolean("barlines"); legendQ = opts.getBoolean("legend"); if (opts.getBoolean("separator")) { notesep = opts.getString("separator").data(); } if (opts.getBoolean("octave")) { octaveVal = opts.getInteger("octave"); } } ////////////////////////////// // // example -- example usage of the sonority program // void example(void) { cout << " \n" "# example usage of the sonority program. \n" "# analyze a Bach chorale for chord qualities: \n" " sonority chor217.krn \n" " \n" "# display the chord analysis with original data: \n" " sonority -a chor217.krn \n" " \n" "# display only the roots of chords: \n" " sonority -r chor217.krn \n" " \n" << endl; } ////////////////////////////// // // processRecords -- looks at humdrum records and determines chord // sonority quality; // void processRecords(HumdrumFile& infile) { vector cq; infile.analyzeSonorityQuality(cq); ChordQuality quality; int foundstart = 0; char aString[512] = {0}; for (int i=0; i iv; infile.getIntervalVector(iv, i); cout << "<"; for (int ii=0; ii<(int)iv.size(); ii++) { if (iv[ii] < 9) { cout << iv[ii]; continue; } if (iv[ii] < 36) { cout << char(iv[ii]-10+'A'); continue; } if (ii > 0) { cout << ","; } cout << iv[ii]; if (ii < 5) { cout << ","; } } cout << ">" << endl; } else if (forteQ) { const char* name = infile.getForteSetName(i); cout << name; if (tnQ) { if (strcmp(name, "3-11") == 0) { if (strcmp(cq[i].getTypeName(), "min") == 0) { cout << "A"; } else if (strcmp(cq[i].getTypeName(), "maj") == 0) { cout << "B"; } } } if (tniQ) { int inversion = -1; if (strcmp(name, "3-11") == 0) { if ((strcmp(cq[i].getTypeName(), "min") == 0) || (strcmp(cq[i].getTypeName(), "maj") == 0)) { inversion = cq[i].getInversion(); } if (inversion >= 0) { cout << char('a'+inversion); } } } if (attackQ) { printAttackMarker(infile, i); } cout << endl; } else if (notesQ == 0) { quality = cq[i]; quality.makeString(aString, explicitQ); if (strcmp(aString, "") != 0) { if (strcmp(aString, unknown) == 0 || ( quality.getType()==E_chord_note && options.getBoolean("root")) ) { char tempbuffer[128] = {0}; strcpy(tempbuffer, aString); strcpy(aString, options.getString("unknown").data()); strcat(aString, tempbuffer); } if (suppressQ && transitionalSonority(quality, infile, i)) { strcpy(aString, "."); } } else { strcpy(aString, "rest"); } cout << aString << endl; } else { quality = cq[i]; fillStringWithNotes(aString, quality, infile, i); cout << aString << endl; } break; default: cerr << "Error on line " << (i+1) << " of input" << endl; cerr << "record type = " << infile[i].getType() << endl; exit(1); } } } ////////////////////////////// // // printAttackMarker -- print an "x" if the there is any note at // the start of the region which is not attacked (i.e., suspended // from a previous sonority. // void printAttackMarker(HumdrumFile& infile, int line) { int j, ii, jj; int& i = line; int dotQ; for (j=0; j notes; quality.getNotesInChord(notes); //vector octave(notes.size(), 0); vector sounding(notes.size(), 0); // int bassindex = identifyBassNote(notes, infile, line, sounding); for (int i=0; i<(int)sounding.size(); i++) { if (sounding[i] == 0) { return 1; } } return 0; } ////////////////////////////// // // fillStringWithNotes -- // void fillStringWithNotes(char* string, ChordQuality& quality, HumdrumFile& infile, int line) { string[0] = '\0'; vector notes; quality.getNotesInChord(notes); vector octave(notes.size(), 4); vector sounding(notes.size(), 0); int bassindex = identifyBassNote(notes, infile, line, sounding); if (bassindex >= 0) { octave[bassindex] = 3; //if (notes[bassindex] >= 40) { // octave[bassindex] += -2; //} } int i; if (suppressQ) { for (i=0; i<(int)sounding.size(); i++) { if (sounding[i] == 0) { strcpy(string, "."); return; } } } string[0] = '\0'; char buffer[32] = {0}; for (i=0; i<(int)notes.size(); i++) { //if (octaveVal >= 0) { // Convert::base40ToKern(buffer, (notes[i]%40) + octaveVal * 40); //} else { // Convert::base40ToKern(buffer, notes[i] + ((octave[i]+4) * 40)); //} Convert::base40ToKern(buffer, notes[i]%40 + (octave[i] * 40)); if (parenQ && (sounding[i] == 0)) { strcat(string, "("); } strcat(string, buffer); if (parenQ && (sounding[i] == 0)) { strcat(string, ")"); } if (i < (int)notes.size() - 1) { strcat(string, notesep.c_str()); } } } ////////////////////////////// // // identifyBassnote -- // int identifyBassNote(vector& notes, HumdrumFile& infile, int line, vector& sounding) { int j, k; int output = -1; int minval = 1000000; int value; int tcount; char buffer[128] = {0}; vector soundQ(40, 0); sounding.resize(notes.size()); std::fill(sounding.begin(), sounding.end(), 0); int pline; int pspine; int dotQ = 0; if (notes.size() == 0) { return -1; } for (j=0; j 100000) { return -1; } minval = minval % 12; int i; int tval; for (i=0; i<(int)notes.size(); i++) { if (notes[i] >= 0) { if (soundQ[notes[i]%40]) { sounding[i] = 1; } } tval = Convert::base40ToMidiNoteNumber(notes[i]); if (tval < 0) { continue; } tval = tval % 12; if (tval == minval) { output = i; // break; need to supress this because of sounding tests } } return output; } ////////////////////////////// // // usage -- gives the usage statement for the sonority program // void usage(const string& command) { cout << " \n" "Analyzes **kern data and generates a **qual spine which gives the chord \n" "quality of the **kern spines. Currently, input spines cannot split or \n" "join. \n" " \n" "Usage: " << command << " [-a][-i|-r|-t] [input1 [input2 ...]] \n" " \n" "Options: \n" " -a = assemble the **qual analysis spine with input data \n" " -i = displays the **qual chord inversion only \n" " -r = displays the **qual chord root only \n" " -t = displays the **qual chord type only \n" " --options = list of all options, aliases and default values \n" " \n" << endl; }