Goto: [ Program Documentation ]

//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Thu Jun 22 17:16:28 PDT 2000
// Last Modified: Thu Jun 22 17:16:32 PDT 2000
// Last Modified: Thu Jun 29 21:21:31 PDT 2000
// Filename:      ...sig/exmaples/all/vofun.cpp
// Web Page:      http://sig.sapp.org/examples/museinfo/humdrum/vofun.cpp
// Syntax:        C++; museinfo
// Reference:     http://dactyl.som.ohio-state.edu/Humdrum/representations/embel.rep.html
//
// Description:   Analyzes **kern data for voice function analyses.
//
//

#include "humdrum.h"

#include <string.h>
#include <stdlib.h>
#include <ctype.h>


#ifndef OLDCPP
   #include <iostream>
#else
   #include <iostream.h>
#endif


// function declarations
void         checkOptions(Options& opts, int argc, char* argv[]);
void         determineChordQuality(ChordQuality& cq, 
		                         const HumdrumRecord& aRecord);
void         example(void);
void         prepareOutput(void);
void         processRecords(HumdrumFile& infile);
void         usage(const char* command);
void         doAnalysis(void);


// 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

EnumerationEmbellish embellish;

int          spinecount = 0;     // the number of **kern spines in the input

Array<int>   Line;               // the translation between elements and 
                                 //    file line numbers.
typedef Array<int> arrayint;
Array<arrayint> Notes;           // the input notes to be analyzed
Array<arrayint> Functions;       // the output functions

Array<int>   Inversion;          // the inversion value for the harmony
Array<int>   Root;               // the root value for the harmony in Base 40
Array<int>   Quality;            // the quality for the harmony


HumdrumFile infile, outfile;


///////////////////////////////////////////////////////////////////////////


int main(int argc, char* argv[]) {

   // initialize harmony tracking arrays
   Inversion.setSize(10000);
   Root.setSize(10000);
   Quality.setSize(10000);
   Line.setSize(10000);
   Notes.setSize(100);
   Functions.setSize(100);
   int k;
   for (k=0; k<100; k++) {
      Notes[k].setSize(10000);
      Notes[k].setSize(0);
      Notes[k].allowGrowth();
     
      Functions[k].setSize(10000);
      Functions[k].setSize(0);
      Functions[k].allowGrowth();
   }

   Inversion.setSize(0);
   Root.setSize(0);
   Quality.setSize(0);
   Line.setSize(0);
   Notes.setSize(0);
   Functions.setSize(0);

   Inversion.allowGrowth();
   Root.allowGrowth();
   Quality.allowGrowth();
   Line.allowGrowth();
   Notes.allowGrowth();
   Functions.allowGrowth();

   // 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<numinputs || i==0; i++) {
      chordinit = 1;
      infile.clear();
      outfile.clear();

      // if no command-line arguments read data file from standard input
      if (numinputs < 1) {
         infile.read(cin);
      } else {
         infile.read(options.getArg(i+1));
      }

      // analyze the input file according to command-line options
      processRecords(infile);
      doAnalysis();
      
      prepareOutput();

      outfile.write(cout);
   }

   return 0;
}


///////////////////////////////////////////////////////////////////////////


//////////////////////////////
//
// doAnalysis -- generate the voice function analysis
//

void doAnalysis(void) {
   // first mark the chord tones
   int i, j;
   for (i=0; i<Quality.getSize(); i++) {
      if (Quality[i] < 10000) {
         for (j=0; j<Functions.getSize(); j++) {
            Functions[j][i] = E_embel_ct;
         }
      }
   }

}






//////////////////////////////
//
// 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("i|inversion=b");                // show only chord inversion
   opts.define("r|root=b");                     // show only chord root
   opts.define("a|assemble=b");                 // assemble analysis with input
   opts.define("f|format=s:t:i:r");             // control display style
   opts.define("u|unknown=s:X");                // control display of unknowns
   opts.define("d|debug=b");                    // determine bad input line num
   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, June 2000" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 22 June 2000" << endl;
      cout << "compiled: " << __DATE__ << endl;
      cout << MUSEINFO_VERSION << endl;
      exit(0);
   } else if (opts.getBoolean("help")) {
      usage(opts.getCommand());
      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"));
   }

   Convert::chordType.setNullName(opts.getString("unknown"));
   Convert::chordInversion.setNullName(opts.getString("unknown"));
   Convert::kernPitchClass.setNullName(opts.getString("unknown"));

   strncpy(unknown, opts.getString("unknown"), 64);
   strcat(unknown, ":");
   strncat(unknown, opts.getString("unknown"), 64);
   strcat(unknown, ":");
   strncat(unknown, opts.getString("unknown"), 64);

}



//////////////////////////////
//
// determineChordQuality -- extracts notes from humdrum data record
//	and sends them to a chord identifier.  Notes from previous
//	records are remembered, and replace any null records in the
//	data.  Rests are represented by some negative value 
//	and will be ignored by the chord identifier.
//

void determineChordQuality(ChordQuality& cq, const HumdrumRecord& aRecord) {
   static SigCollection<int> lastnotes;  // for keeping track of null record notes
   int i;
   if (chordinit) {
      chordinit = 0;
      lastnotes.setSize(aRecord.getFieldCount());
      for (i=0; i<aRecord.getFieldCount(); i++) {
         lastnotes[i] = E_root_rest;
      }
      // remove non-kern spines from note list
      for (i=0; i<aRecord.getFieldCount(); i++) {
         if (aRecord.getExInterpNum(i) != E_KERN_EXINT) {
            lastnotes.setSize(lastnotes.getSize() - 1);
         }
      }
   }

   // determine chord notes and send to chord identifier
   SigCollection<int> currentNotes(lastnotes.getSize());
   int count = 0;
   for (i=0; i<aRecord.getFieldCount(); i++) {
      if (aRecord.getExInterpNum(i) == E_KERN_EXINT) {
         if (strcmp(aRecord[i], ".") == 0) {
            currentNotes[count] = lastnotes[count];
         } else {
            currentNotes[count] = Convert::kernToBase40(aRecord[i]);
            lastnotes[count] = currentNotes[count];
         }
         count++;
      }
   }

   Convert::noteSetToChordQuality(cq, currentNotes); 
}



//////////////////////////////
//
// 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;
}

//////////////////////////////
//
// prepareOutput -- generates the final output file which will be
//      sent to standard output.
//

void prepareOutput(void) {
   int i, j;
   for (i=0; i<Line.getSize(); i++) {
      cout << Line[i] 
           << ":\t";

      cout << "{";
      for (j=0; j<Notes.getSize(); j++) {
           cout << Notes[j][i] << ":";
      }
      cout << "}";

      cout << "<";
      for (j=0; j<Functions.getSize(); j++) {
           cout << Functions[j][i] << ":";
      }
      cout << ">";

      cout << "\ti=" << Inversion[i] 
           << "\tr="  << Root[i] 
           << "\tq="  << Quality[i] 
           << "\n";
   }

   char buffer[1024] = {0};
   int k;
   j = 0;
   for (int i=0; i<infile.getNumLines(); i++) {
      switch (infile[i].getType()) {
         case E_humrec_data:
            buffer[0] = '\0';
            for (k=0; k<spinecount; k++) {
               if (Notes[k][j] == -1) {
                  strcat(buffer, ".");
               } else if (Functions[k][j] >= 0) {
                  strcat(buffer, embellish.getName(Functions[k][j]));
               } else {
                  strcat(buffer, "?");
               }
               if (k < spinecount - 1) {
                  strcat(buffer, "\t");
               }
            }
            outfile.appendLine(buffer);
            j++;
            break;
         default:
            outfile.appendLine(infile[i]);
      }
   }

}



//////////////////////////////
//
// processRecords -- looks at humdrum records and determines chord
//	quality
//

void processRecords(HumdrumFile& infile) {
   ChordQuality quality;

   HumdrumRecord currRecord;
   int i, j;
   for (i=0; i<infile.getNumLines(); i++) {
      if (options.getBoolean("debug")) {
         cout << "processing line " << (i+1) << " of input ..." << endl;
      }
      currRecord = infile[i]; 
      switch (currRecord.getType()) {
         case E_humrec_none:
         case E_humrec_empty:
         case E_humrec_bibliography:
         case E_humrec_global_comment:
            break;
         case E_humrec_data_comment:
            if (currRecord.equalFieldsQ("**kern")) {
               currRecord.appendField(currRecord[0]);
            } else {
               currRecord.appendField("!");
            }
            break;
         case E_humrec_data_interpretation:
            if (strncmp(currRecord[0], "**", 2) == 0) {
               spinecount = currRecord.getFieldCount();
               Notes.setSize(spinecount);
               Functions.setSize(spinecount);
            } else if (currRecord.equalFieldsQ("**kern")) {
               currRecord.appendField(currRecord[0]);
            } else {
               currRecord.appendField("*");
            }
            break;
         case E_humrec_data_kern_measure:
            if (currRecord.equalFieldsQ("**kern")) {
               currRecord.appendField(currRecord[0]);
            } else {
               currRecord.appendField("=");
            }
            break;
         case E_humrec_data:
            // handle null fields
            if (currRecord.equalFieldsQ("**kern", ".")) {
               currRecord.appendField(".");
               break;
            }
            determineChordQuality(quality, infile[i]);

            Line.append(i);
            for (j=0; j<infile[i].getFieldCount(); j++) {
               Notes[j].appendcopy(Convert::kernToBase40(infile[i][j]));
               Functions[j].appendcopy(-1);
            }
            Inversion.appendcopy(quality.getInversion());
            Root.appendcopy(quality.getRoot());
            Quality.appendcopy(quality.getType());

            break;
         default:
            cerr << "Error on line " << (i+1) << " of input" << endl;
            exit(1);
      }
   }
}



//////////////////////////////
//
// usage -- gives the usage statement for the quality program
//

void usage(const char* 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;
}



// md5sum: 7db20031dd986c32cb490eaa28e84c1f chordtone.cpp [20090626]