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]