// // Programmer: Craig Stuart Sapp // Creation Date: Fri Apr 14 09:41:45 PDT 2000 // Last Modified: Sun Apr 16 14:27:17 PDT 2000 // Last Modified: Sun May 24 09:15:43 PDT 2009 (para octs removed from #6) // Last Modified: Sun May 24 09:15:43 PDT 2009 (added -s, -d, and -d options) // Last Modified: Sun May 24 19:23:l2 PDT 2009 (added -f option) // Last Modified: Sun May 24 19:23:l2 PDT 2009 (exclude unison motion in #3) // Last Modified: Fri Jun 12 22:58:34 PDT 2009 (renamed SigCollection class) // Filename: ...sig/examples/all/chorck.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/chorck.cpp // Syntax: C++; museinfo // // Description: Analyzes choral-style harmony exercises for possible // errors such as parallel fifths and octaves. The input // Humdrum data must contain 4 spines of kern data which // represents the four voices: bass, tenor, alto and soprano. // If there are more than 4 **kern spines, then the first // 4 **kern spines are assumed to be the SATB voices. // The order of the voice spines does not matter, the program // will automatically sort them before analysis starts. // Errors are reported as global comments before the line // on which offending error starts, and is of the form: // !! Warning: // // Error types detected by this program: // // 1. Parallel 5ths between two voices when moving to // different pitch classes. // 2. Parallel Octaves between two voices when moving to // different pitch classes. // 3. Contrary parallel 5ths -- when two voices move in // parallel 5ths displaced by an octave. // 4. Unequal 5ths -- when the bass part and another // voice move from dim 5ths to perfect 5ths or vice versa. // 5. Hidden 5ths -- when the soprano moves in similar // motion with another voice and the soprano leaps // to a perfect 5th with that voice. // 6. Hidden 8va -- when the soprano moves in similar // motion with another voice and the soprano leaps // to a perfect octave with that voice. // 7. Voice crossing -- when an inner voice goes above // the soprano voice or below the bass voice. // 8. Open spacing -- when the interval between successive // voices other than the bass exceeds an octave. // #include "humdrum.h" #include #include #include #include #ifndef OLDCPP #include #else #include #endif #define ERROR_PARA5 1 #define ERROR_PARA8 2 #define ERROR_CPARA5 3 #define ERROR_NEQ5 4 #define ERROR_HIDDEN5 5 #define ERROR_HIDDEN8 6 #define ERROR_VOICEX 7 #define ERROR_OPEN 8 class Error { public: int line; // line number on which the error occurs char message[128]; // error message to display Error(void) { line = -1; message[0] = '\0';} }; // function declarations void checkForErrors (void); void checkOptions (Options& opts, int argc, char* argv[]); int errorCompare (const void* a, const void* b); void errormessage (int errornumber, const char* voice1, const char* voice2, int linenumber); void example (void); void initialize (HumdrumFile& infile); void prepareVoices (void); void printRules (const char* ruleString); void printVoiceArray (void); void processRecords (HumdrumFile& infile); void usage (const char* command); void sortErrorMessages (Error* errors, int size); void sortVoices (void); void writeoutput (HumdrumFile& infile); int getVoiceCount (HumdrumRecord& record); void error1(void); void error2(void); void error3(void); void error4(void); void error5(void); void error6(void); void error7(void); void error8(void); // global variables Options options; // database for command-line arguments int chordinit; // for initializing chord detection function int errorCheck[20] = {0}; // command line check for errors exclusion const char* header = ""; // error message start string const char* marker = ""; // Warning string start int voicemin = 0; // used with -s, -d, -t option int fileQ = 0; // used with -f option const char* Filename = ""; // used with -f option // Analysis variables SigCollection errorList; // a list of detected errors in chorale Array linenum; // line number in file of given pitch set Array voices[4]; // pitches from SATB lines int voiceloc[4]; // SATB voice spine locations /////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { HumdrumFile infile; // process the command-line options checkOptions(options, argc, argv); // figure out the number of input files to process int numinputs = options.getArgCount(); for (int i=0; i lastnotes; // keeping track of null record notes int i; if (chordinit) { chordinit = 0; lastnotes.setSize(aRecord.getFieldCount()); for (i=0; i currentNotes(lastnotes.getSize()); int count = 0; for (i=0; i B.line) { return 1; } else { int x, y; x = atoi(A.message); y = atoi(B.message); if (x < y) { return -1; } else if (x > y) { return 1; } else { return 0; } } } ////////////////////////////// // // errormessage -- insert an error message into the error buffer // for printing later // void errormessage(int errornumber, const char* voice1, const char* voice2, int linenumber) { Error anError; anError.line = linenumber; switch (errornumber) { case 1: sprintf(anError.message, "1. Parallel 5th between %s and %s", voice1, voice2); break; case 2: sprintf(anError.message, "2. Parallel octave between %s and %s", voice1, voice2); break; case 3: sprintf(anError.message, "3. Contrary parallel 5th between %s and %s", voice1, voice2); break; case 4: sprintf(anError.message, "4. Unequal 5th between %s and %s", voice1, voice2); break; case 5: sprintf(anError.message, "5. Hidden 5th between %s and %s", voice1, voice2); break; case 6: sprintf(anError.message, "6. Hidden octave between %s and %s", voice1, voice2); break; case 7: sprintf(anError.message, "7. Voice crossing between %s and %s", voice1, voice2); break; case 8: sprintf(anError.message, "8. Open spacing between %s and %s", voice1, voice2); break; default: return; } errorList.append(anError); } ////////////////////////////// // // example -- example usage of the quality program // void example(void) { cout << " \n" "# example usage of the quality program. \n" "# analyze a Bach chorale for chord qualities: \n" " quality chor217.krn \n" " \n" "# display the chord analysis with original data: \n" " quality -a chor217.krn \n" " \n" "# display only the roots of chords: \n" " quality -r chor217.krn \n" " \n" << endl; } ////////////////////////////// // // initialize -- setup for a new file // void initialize(HumdrumFile& infile) { infile.clear(); for (int k=0; k<4; k++) { voices[k].setSize(100000); voices[k].setAllocSize(100000); voices[k].setSize(0); voices[k].allowGrowth(); } linenum.setSize(100000); linenum.setAllocSize(100000); linenum.setSize(0); linenum.allowGrowth(); errorList.setSize(100000); errorList.setAllocSize(100000); errorList.setSize(0); errorList.allowGrowth(); } ////////////////////////////// // // printRules -- print the requested rules // void printRules(const char* ruleString) { int length = strlen(ruleString); for (int i=0; i