//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Tue Jan 16 11:25:31 PST 2001
// Last Modified: Mon Jan 29 14:02:45 PST 2001
// Last Modified: Fri Feb 2 12:05:51 PST 2001 (added weights display)
// Last Modified: Tue Feb 6 19:13:51 PST 2001 (fixed -r option for large dur)
// Last Modified: Wed Mar 21 13:31:19 PST 2001 (duration normalization added)
// Filename: ...sig/examples/all/croot.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/croot.cpp
// Syntax: C++; museinfo
//
// Description: determine the root of a chord in a given timespan.
//
#include "humdrum.h"
#include <string.h>
#include <math.h>
// function declarations
void checkOptions(Options& opts, int argc, char* argv[]);
void example(void);
void usage(const char* command);
void printAnalysis(HumdrumFile& infile, Array<int>& rootanalysis,
Array<double>& rootscores, double duration);
void generateAnalysis(HumdrumFile& infile, Array<int>& rootanalysis,
Array<double>& rootscores, double duration);
// global variables
Options options; // database for command-line arguments
int debugQ = 0; // used with the --debug option
int rawQ = 0; // used with the --raw option
int appendQ = 0; // used with the -a option
int compoundQ = 1; // used with the -c option
double rval = 1.0; // used with the -r option
double rlevel = 1.0; // used with the -r option
double duration = 1.0; // used with the -r option
int srhythm = 4; // used with the -r option
double empirical1 = -4; // used with the --e1 option
double empirical2 = -3; // used with the --e2 option
double sx = 0.578; // used with the --sx option
double sy = 1.0; // used with the --sx option
int octave = -1; // used with the -o option
int weightsQ = 0; // used with the -w option
///////////////////////////////////////////////////////////////////////////
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();
Array<int> rootanalysis; // roots analysed at various times
Array<double> rootbeat; // absolute start beat of root
Array<double> rootscores; // score of best root
for (int i=0; i<numinputs || i==0; i++) {
infile.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));
}
generateAnalysis(infile, rootanalysis, rootscores, duration);
printAnalysis(infile, rootanalysis, rootscores, duration);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// printAnalysis -- display the analysis results.
//
void printAnalysis(HumdrumFile& infile, Array<int>& rootanalysis,
Array<double>& rootscores, double duration) {
char buffer[1024] = {0};
int i;
double fraction;
int oldlocation = -1;
int location;
int n, m;
if (rawQ) {
for (i=0; i<rootanalysis.getSize(); i++) {
if (rootanalysis[i] > 0) {
cout << "beat: " << i * duration << "\t= "
<< Convert::base40ToKern(buffer, rootanalysis[i])
<< ",\tscore = " << rootscores[i] << endl;
} else {
cout << "beat: " << i * duration << "\t= "
<< "rest"
<< "\tscore = " << 0 << endl;
}
}
} else {
for (i=0; i<infile.getNumLines(); i++) {
switch (infile[i].getType()) {
case E_humrec_data:
fraction = fabs(infile[i].getAbsBeat()/duration);
location = (int)fraction;
fraction = fraction - (int)fraction;
if (fraction > 0.999) {
fraction = 0.0;
}
if (location - oldlocation > 1 && oldlocation > 0) {
for (n=1; n<location-oldlocation; n++) {
for (m=0; m<infile[i].getFieldCount(); m++) {
cout << ".\t";
}
cout << Convert::durationToKernRhythm(buffer, duration);
if (rootanalysis[oldlocation + 1] > 0) {
cout << Convert::base40ToKern(buffer,
rootanalysis[oldlocation + n]);
} else {
cout << "r";
}
if (weightsQ) {
cout << "\t" << rootscores[oldlocation + n];
}
cout << "\n";
}
}
oldlocation = location;
cout << infile[i].getLine();
cout << "\t";
if (fraction < 0.001) {
cout << Convert::durationToKernRhythm(buffer, duration);
if (rootanalysis[location] > 0) {
cout
<< Convert::base40ToKern(buffer, rootanalysis[location]);
} else {
cout << "r";
}
} else {
cout << ".";
}
if (weightsQ && fraction < 0.001) {
cout << "\t" << rootscores[location];
} else if (weightsQ) {
cout << "\t.";
}
cout << "\n";
break;
case E_humrec_data_measure:
fraction = fabs(infile[i].getAbsBeat()/duration);
location = (int)fraction;
fraction = fraction - (int)fraction;
if (fraction > 0.999) {
fraction = 0.0;
}
if (location - oldlocation > 1 && oldlocation > 0) {
for (n=1; n<location-oldlocation; n++) {
for (m=0; m<infile[i].getFieldCount(); m++) {
cout << ".\t";
}
cout << Convert::durationToKernRhythm(buffer, duration);
if (rootanalysis[oldlocation + 1] > 0) {
cout << Convert::base40ToKern(buffer,
rootanalysis[oldlocation + n]);
} else {
cout << "r";
}
if (weightsQ) {
cout << "\t" << rootscores[oldlocation + n];
}
cout << "\n";
}
}
oldlocation = location - 1;
cout << infile[i].getLine();
cout << "\t" << infile[i][0];
if (weightsQ) {
cout << "\t" << infile[i][0];
}
cout << "\n";
break;
case E_humrec_data_comment:
cout << infile[i].getLine();
if (infile[i].equalFieldsQ()) {
cout << "\t" << infile[i][0];
} else {
cout << "\t!";
}
if (weightsQ && infile[i].equalFieldsQ()) {
cout << "\t" << infile[i][0];
} else if (weightsQ) {
cout << "\t!";
}
cout << "\n";
break;
case E_humrec_data_interpretation:
cout << infile[i].getLine();
if (strncmp(infile[i][0], "**", 2) == 0) {
cout << "\t**kern";
if (weightsQ) {
cout << "\t**weight";
}
cout << "\n";
} else {
if (infile[i].equalFieldsQ()) {
cout << "\t" << infile[i][0];
} else {
cout << "\t*";
}
if (weightsQ && infile[i].equalFieldsQ()) {
cout << "\t" << infile[i][0];
} else if (weightsQ) {
cout << "\t*";
}
cout << "\n";
}
break;
case E_humrec_global_comment:
case E_humrec_bibliography:
case E_humrec_none:
case E_humrec_empty:
default:
fraction = fabs(infile[i].getAbsBeat()/duration);
location = (int)fraction;
fraction = fraction - (int)fraction;
if (fraction > 0.999) {
fraction = 0.0;
}
if (location - oldlocation > 1 && oldlocation > 0) {
for (n=1; n<location-oldlocation; n++) {
for (m=0; m<infile[i].getSpineWidth(); m++) {
cout << ".\t";
}
cout << Convert::durationToKernRhythm(buffer, duration);
if (rootanalysis[oldlocation + 1] > 0) {
cout << Convert::base40ToKern(buffer,
rootanalysis[oldlocation + n]);
} else {
cout << "r";
}
if (weightsQ) {
cout << "\t" << rootscores[oldlocation + n];
}
cout << "\n";
}
}
oldlocation = location - 1;
cout << infile[i].getLine() << "\n";
}
}
}
}
//////////////////////////////
//
// generateAnalysis -- analyze the roots of the chords
//
void generateAnalysis(HumdrumFile& infile, Array<int>& rootanalysis,
Array<double>& rootscores, double duration) {
infile.analyzeRhythm("4"); // force the beat to be quarter notes for now
Array<int> rhylev;
infile.analyzeMetricLevel(rhylev);
Array<double> tonicscores;
int most = 0;
Array<double> values;
values.setSize(0);
double startbeat = 0.0;
double endbeat = 0.0;
double totaldur = infile.getTotalDuration();
int size = (int)(totaldur/duration);
int nextroot = 0;
int lastroot = 0;
int i;
rootscores.setSize(size);
rootscores.setSize(0);
rootanalysis.setSize(size);
rootanalysis.setSize(0);
char buffer[128] = {0};
for (i=0; i<size; i++) {
startbeat = duration * i;
endbeat = startbeat + duration - 0.01;
// cout << "StartBeat = " << startbeat << "\tduration = " << duration << endl;
most = infile.analyzeChordProbabilityDur(values, startbeat,
endbeat, rhylev, empirical1, empirical2, sx, sy);
if (most >= 0) {
if (octave < 0) {
nextroot = (most + 2) % 40 + 80;
if (abs(nextroot + 40 - lastroot) < 13) {
nextroot += 40;
} else if (abs(nextroot - 40 - lastroot) < 13) {
nextroot -= 40;
}
} else {
nextroot = (most + 2) % 40 + 40 * octave;
}
Convert::base40ToKern(buffer, nextroot);
lastroot = nextroot;
} else {
nextroot = 0;
}
rootanalysis.append(nextroot);
if (most < 0) {
double zero = 0.0;
rootscores.append(zero);
} else {
rootscores.append(values[most]);
}
}
}
//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("a|append=b", "append analysis to data in output");
opts.define("raw=b", "raw display of output");
opts.define("C|compound=b", "don't try to use compound meters");
opts.define("r|rhythm=i:4", "rhythm to measure root at");
opts.define("e1|empirical1=d:-4.0", "empirical rhythm value 1");
opts.define("e2|empirical2=d:-3.0", "empirical rhythm value 2");
opts.define("sx=d:0.578", "scale factor for the x-pitch space axis");
opts.define("sy=d:1.0", "scale factor for the y-pitch space axis");
opts.define("o|octave=i:-1", "octave number of bass line");
opts.define("w|weights=b", "display parallel spine of scores next to roots");
opts.define("debug=b", "trace input parsing");
opts.define("author=b", "author of the program");
opts.define("version=b", "compilation information");
opts.define("example=b", "example usage");
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, Dec 2000" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 21 Mar 2001" << 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);
}
debugQ = opts.getBoolean("debug");
appendQ = opts.getBoolean("append");
rawQ = opts.getBoolean("raw");
if (opts.getBoolean("compound")) {
compoundQ = 0;
} else {
compoundQ = 0;
}
rval = opts.getInteger("rhythm")/4.0;
srhythm = (int)(rval * 4.0); // can't handle some cases yet
duration = 4.0 / srhythm;
rlevel = log10((double)srhythm)/log10(2.0) - 2.0;
empirical1 = opts.getDouble("empirical1");
empirical2 = opts.getDouble("empirical2");
sx = opts.getDouble("sx");
sy = opts.getDouble("sy");
octave = opts.getInteger("octave");
weightsQ = opts.getBoolean("weights");
}
//////////////////////////////
//
// example -- example usage of the maxent program
//
void example(void) {
cout <<
" \n"
<< endl;
}
//////////////////////////////
//
// usage -- gives the usage statement for the quality program
//
void usage(const char* command) {
cout <<
" \n"
<< endl;
}
// md5sum: e10b1da3b33076262ad45749fede939a croot.cpp [20050403]