//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Tue Nov 14 09:18:07 PST 2000
// Last Modified: Fri Nov 17 12:02:24 PST 2000
// Filename: ...sig/examples/all/diametric.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/diametric.cpp
// Syntax: C++; museinfo
//
// Description: Analyzes **kern sonorities for diatonic compactness
// in various orderings of seconds, thirds, fifths
// and their inversions.
//
#include "humdrum.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <math.h>
#ifndef OLDCPP
#include <iostream>
#else
#include <iostream.h>
#endif
// function declarations
void checkOptions(Options& opts, int argc, char* argv[]);
int countAttacks(HumdrumRecord& record);
void displayAccentedBeatMetrics(Array<double>& metposition,
Array<double>& ascores,
const char* metermarker, int start, int stop);
int doublecompare(const void* a, const void* b);
void example(void);
void generateAnalysis(HumdrumFile& infile, Array<int>&analysis,
int metric, Array<int>& attacks);
void generateSummary(HumdrumFile& infile, Array<int>& metricana);
void generateSubSummary(HumdrumFile& infile, Array<int>& metricana,
const char* metricmarker, int start, int stop);
void getPitches(Array<int>& pitches, HumdrumFile& infile,
int line);
double makeBeatScore(int totalbeats, double width,
Array<double>& metposition,
Array<double>&ascores);
int makeMeasurement(Array<int>&pitches, int metric);
void printAnalysis(HumdrumFile& infile, Array<int>&metric);
void usage(const char* command);
// global variables
Options options; // database for command-line arguments
int debugQ = 0; // used with the --debug option
int metric = 1; // diatonic interleaving (1 = by thirds)
int appendQ = 0; // used with the -a option
int histogramQ = 0; // used with the -c option
int summaryQ = 0; // used with the -c option
int beatQ = 0; // used with the -b option
///////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) {
HumdrumFile infile;
Array<int> analysis;
Array<int> attacks;
// 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++) {
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, analysis, metric, attacks);
printAnalysis(infile, analysis);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("m|metric=i:3", "diatonic space: 3=thirds, 2=2nds, etc.");
opts.define("a|append=b", "append analysis to data in output");
opts.define("c|count|histogram=b", "diatonic histogram of sonority notes");
opts.define("s|sum|summary=b", "average value for each metrical position");
opts.define("b|beat=b", "display beat size");
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, Nov 2000" << 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());
exit(0);
} else if (opts.getBoolean("example")) {
example();
exit(0);
}
// metric == 0 --> ordered by seconds, sevenths
// metric == 1 --> ordered by thirds, sixths
// metric == 2 --> ordered by fourths, fifths
metric = opts.getInteger("metric") - 2;
if (metric < 0) {
metric = 1;
} else if (metric == 3) {
metric = 2;
} else if (metric == 4) {
metric = 1;
} else if (metric == 5) {
metric = 0;
} else if (metric > 5) {
metric = 1;
}
debugQ = opts.getBoolean("debug");
appendQ = opts.getBoolean("append");
histogramQ = opts.getBoolean("histogram");
if (opts.getBoolean("summary")) {
summaryQ = 1;
histogramQ = 0;
appendQ = 0;
} else {
summaryQ = 0;
}
if (opts.getBoolean("beat")) {
summaryQ = 1;
beatQ = 1;
histogramQ = 0;
appendQ = 0;
}
}
//////////////////////////////
//
// example -- example usage of the quality program
//
void example(void) {
cout <<
" \n"
<< endl;
}
//////////////////////////////
//
// generateAnalysis --
//
void generateAnalysis(HumdrumFile& infile, Array<int>& analysis, int metric,
Array<int>& attacks) {
Array<int> pitches;
pitches.setSize(100);
pitches.setSize(0);
pitches.allowGrowth(1);
analysis.setSize(infile.getNumLines());
attacks.setSize(infile.getNumLines());
for (int i=0; i<infile.getNumLines(); i++) {
if (options.getBoolean("debug")) {
cout << "processing line " << (i+1) << " of input ..." << endl;
}
if (infile[i].getType() != E_humrec_data) {
analysis[i] = -1;
attacks[i] = -1;
continue;
}
getPitches(pitches, infile, i);
analysis[i] = makeMeasurement(pitches, metric);
attacks[i] = countAttacks(infile[i]);
}
}
//////////////////////////////
//
// countAttacks -- count the number of notes that are struck
//
int countAttacks(HumdrumRecord& record) {
int i, j;
int count = 0;
char buffer[128] = {0};
for (i=0; i<record.getFieldCount(); i++) {
if (record.getExInterpNum(i) != E_KERN_EXINT) {
continue;
}
if (strcmp(record[i], ".") == 0) {
continue;
}
int tcount = record.getTokenCount(i);
for (j=0; j<tcount; j++) {
record.getToken(buffer, i, j);
if (strcmp(buffer, ".") == 0) {
continue;
}
if (strchr(buffer, '_') != NULL) {
continue;
}
if (strchr(buffer, ']') != NULL) {
continue;
}
count++;
}
}
return count;
}
//////////////////////////////
//
// getPitches -- get the diatonic pitches found on the current line.
//
void getPitches(Array& pitches, HumdrumFile& infile, int line) {
pitches.setSize(0);
int diatonic;
int j, k;
int tokencount;
char buffer[128] = {0};
for (j=0; j<infile[line].getFieldCount(); j++) {
if (infile[line].getExInterpNum(j) != E_KERN_EXINT) {
continue;
}
tokencount = infile[line].getTokenCount(j);
for (k=0; k<tokencount; k++) {
if (strcmp(infile[line][j], ".") == 0) {
tokencount = infile[infile[line].getDotLine(j)].getTokenCount(
infile[line].getDotSpine(j));
infile[infile[line].getDotLine(j)].getToken(buffer,
infile[line].getDotSpine(j), k);
} else {
infile[line].getToken(buffer, j, k);
}
diatonic = Convert::kernToDiatonicPitchClass(buffer);
pitches.append(diatonic);
}
}
}
//////////////////////////////
//
// makeMeasurement -- get the diatonic pitches found on the current line.
//
int makeMeasurement(Array&pitches, int metric) {
int compare[3][14] =
{{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'c', 'd', 'e', 'f', 'g'},
{'a', 'c', 'e', 'g', 'b', 'd', 'f', 'a', 'c', 'e', 'g', 'b', 'd', 'f'},
{'a', 'e', 'b', 'f', 'c', 'g', 'd', 'a', 'e', 'b', 'f', 'c', 'g', 'd'}};
int data[14] = {0};
int i;
//cout << "Comparing to : ";
//for (i=0; i<7; i++) {
// cout << (char)compare[metric][i] << " ";
//}
//cout << " ... : " << endl;
int j;
for (i=0; i<pitches.getSize(); i++) {
j = 0;
while (j<7) {
if (compare[metric][j] == pitches[i]) {
data[j]++;
data[j+7]++;
break;
}
j++;
}
}
int max = 0;
int count = 0;
for (i=0; i<14; i++) {
if (data[i] != 0) {
if (count > max) {
max = count;
}
count = 0;
} else {
count++;
}
}
if (count > max) {
max = count;
}
//for (i=0; i<pitches.getSize(); i++) {
//cout << (char)pitches[i] << " ";
//}
//cout << "\t";
//for (i=0; i<7; i++) {
//cout << data[i];
//}
//cout << " final result: " << 7 - max << endl;
int result = 7 - max;
if (result < 0) {
return 0;
} else {
return result;
}
}
//////////////////////////////
//
// generateSummary -- generate the average measurement value for
// each metrical position in the meter.
//
void generateSummary(HumdrumFile& infile, Array& metricana) {
int start = 0;
int stop = 0;
int i=0;
int m=0;
int datatrigger = 0;
int metertrigger = 0;
int summarytrigger = 0;
int length;
const char* metricmarker = "";
if (!beatQ) {
cout << "**takt\t**avg\t**fract\t**ascore\n";
}
while (i<infile.getNumLines()) {
if (infile[i].getType() == E_humrec_data) {
datatrigger = 1;
if (start == 0) {
start = i;
}
}
if (infile[i].getType() == E_humrec_interpretation) {
for (m=0; m<infile[i].getFieldCount(); m++) {
if ((infile[i].getExInterpNum(m) == E_KERN_EXINT) &&
(strncmp(infile[i][m], "*M", 2) == 0)) {
length = strlen(infile[i][m]);
if ((length > 2) && isdigit(infile[i][m][2]) &&
(strchr(infile[i][m], '/') != NULL)) {
metertrigger = 1;
metricmarker = infile[i][m];
if (datatrigger) {
summarytrigger = 1;
stop = i-1;
} else {
start = i;
}
break;
} else if (strncmp(infile[i][m], "*MM", 3) == 0) {
if (!beatQ) {
cout << infile[i][m] << "\t";
cout << infile[i][m] << "\t";
cout << infile[i][m] << "\t";
cout << infile[i][m] << "\n";
break;
}
}
}
}
}
if (strcmp(infile[i][0], "*-") == 0) {
summarytrigger = 1;
stop = i;
}
if (datatrigger == 1 && metertrigger == 0) {
generateSubSummary(infile, metricana, "*MX", 0, infile.getNumLines());
break;
}
if (summarytrigger) {
generateSubSummary(infile, metricana, metricmarker, start, stop);
summarytrigger = 0;
datatrigger = 0;
metricmarker = "";
start = stop + 1;
}
i++;
}
if (!beatQ) {
cout << "*-\t*-\t*-\t*-\n";
}
}
//////////////////////////////
//
// generateSubSummary --
//
void generateSubSummary(HumdrumFile& infile, Array<int>& metricana,
const char* metricmarker, int start, int stop) {
infile.analyzeRhythm();
Array<double> metposition;
metposition.setSize(0);
metposition.allowGrowth();
Array<int> count;
count.setSize(0);
count.allowGrowth();
Array<int> data;
data.setSize(0);
data.allowGrowth();
double current;
int one = 1;
const char* metermarker = "*";
int sumcount = 0;
int i;
int j;
int k;
int sindex;
for (i=start; i<stop; i++) {
if (infile[i].getType() == E_humrec_interpretation) {
for (j=0; j<infile[i].getFieldCount(); j++) {
if (strncmp(infile[i][j], "*M", 2) == 0) {
if (isdigit(infile[i][j][2]) &&
(strchr(infile[i][j], '/') != NULL)) {
metermarker = infile[i][j];
}
}
}
}
if (infile[i].getType() != E_humrec_data) {
continue;
}
sindex = -1;
if (metposition.getSize() == 0) {
current = infile[i].getBeat();
metposition.append(current);
count.append(one);
data.append(metricana[i]);
sumcount++;
} else {
k = 0;
while (k < metposition.getSize()) {
if (fabs(metposition[k] - infile[i].getBeat()) < 0.001) {
sindex = k;
break;
}
k++;
}
if (sindex >= 0) {
count[sindex]++;
data[sindex] += metricana[i];
sumcount++;
} else {
current = infile[i].getBeat();
metposition.append(current);
count.append(one);
data.append(metricana[i]);
sumcount++;
}
}
}
Array<double> sortmet = metposition;
qsort(sortmet.getBase(), metposition.getSize(), sizeof(double),
doublecompare);
Array<double> ascores;
Array<double> average;
Array<double> fraction;
ascores.setSize(metposition.getSize());
average.setSize(metposition.getSize());
fraction.setSize(metposition.getSize());
double min = 99999999.0;
for (i=0; i<ascores.getSize(); i++) {
average[i] = (double)data[i]/count[i];
fraction[i] = (double)count[i]/sumcount;
ascores[i] = average[i]/fraction[i];
if (i==0) {
min = ascores[i];
} else if (min > ascores[i]) {
min = ascores[i];
}
}
// normalize ascore values
if (min > 0.0) {
for (i=0; i<ascores.getSize(); i++) {
ascores[i] /= min;
}
}
if (!beatQ) {
cout << metermarker << "\t*lines=" << start+1 << ":" << stop+1
<< "\t*cnt=" << sumcount << "\t*norm=" << min << "\n";
for (i=0; i<metposition.getSize(); i++) {
k = 0;
while (k < metposition.getSize()) {
if (metposition[k] == sortmet[i]) {
break;
}
k++;
}
if (k > metposition.getSize()) {
continue;
}
cout << sortmet[i] << "\t"
<< average[k] << "\t"
<< fraction[k] << "\t"
<< 1.0/ascores[k]
<< "\n";
}
} else {
displayAccentedBeatMetrics(metposition, ascores, metermarker, start, stop);
}
}
//////////////////////////////
//
// diaplyAccentedBeatMetrics -- generate a score for each possible
// cyclical meter/beat combination.
//
void displayAccentedBeatMetrics(Array<double>& metposition,
Array<double>& ascores, const char* metermarker,
int start, int stop) {
int beatcount = -1;
int beatvalue = -1;
sscanf(metermarker, "*M%d/%d", &beatcount, &beatvalue);
int i;
cout << "Start = " << start << "\tStop = " << stop << endl;
cout << "Meter: " << metermarker << endl;
cout << "beats: " << beatcount << endl;
cout << "base: " << beatvalue << endl;
double difference;
double beatscore;
double width;
Array<double> beatscores;
beatscores.setSize(0);
beatscores.allowGrowth();
Array<double> widths;
widths.setSize(0);
widths.allowGrowth();
int candidate;
for (i=1; i<ascores.getSize(); i++) {
width = metposition[i] - 1.0;
if (fabs(width) < 0.001) {
width = beatcount;
}
difference = beatcount/width;
difference = difference - (int)difference;
if (difference < 0.001 || difference > 0.999) {
candidate = 1;
} else {
candidate = 0;
}
if (candidate) {
beatscore = makeBeatScore(beatcount, width, metposition, ascores);
beatscores.append(beatscore);
widths.append(width);
if (!summaryQ) {
cout << "beat duration: " << metposition[i] - 1
<< "\tscore: " << beatscore << endl;
}
}
}
if (summaryQ) {
double max = beatscores[0];
int index = 0;
for (i=1; i<beatscores.getSize(); i++) {
if (beatscores[i] > max) {
max = beatscores[i];
index = i;
}
}
cout << "Maximum Beat: " << widths[index]
<< "\tRating: " << beatscores[index] << endl;
}
}
//////////////////////////////
//
// makeBeatScore --
//
double makeBeatScore(int totalbeats, double width, Array<double>& metposition,
Array<double>&ascores) {
int i;
double product = 1.0;
double difference;
int count = 0;
for (i=0; i<metposition.getSize(); i++) {
difference = metposition[i]/width;
difference = difference - (int)difference;
if (difference < 0.001 || difference > 0.999) {
count++;
product *= 1.0/ascores[i];
}
}
int realcount = (int)(totalbeats/width + 0.5);
for (i=0; i<realcount-count; i++) {
product *= 0.01;
}
double output = pow(realcount, 1.0/2.0) * pow(product, 3.1415968/realcount);
return output;
}
//////////////////////////////
//
// printAnalysis --
//
void printAnalysis(HumdrumFile& infile, Array& metricana) {
int i;
if (summaryQ || beatQ) {
generateSummary(infile, metricana);
return;
}
if (appendQ) {
for (i=0; i<infile.getNumLines(); i++) {
switch (infile[i].getType()) {
case E_humrec_global_comment:
case E_humrec_bibliography:
case E_humrec_none:
case E_humrec_empty:
cout << infile[i].getLine() << "\n";
break;
case E_humrec_data:
cout << infile[i].getLine() << "\t"
<< metricana[i] << "\n";
break;
case E_humrec_data_comment:
if (infile[i].equalFieldsQ("**kern")) {
cout << infile[i].getLine() << "\t"
<< infile[i][0] << "\n";
} else {
cout << infile[i].getLine() << "\t!\n";
}
break;
case E_humrec_data_measure:
if (infile[i].equalFieldsQ("**kern")) {
cout << infile[i].getLine() << "\t"
<< infile[i][0] << "\n";
} else {
cout << infile[i].getLine() << "\t=\n";
}
break;
case E_humrec_data_interpretation:
if (strncmp(infile[i][0], "**", 2) == 0) {
cout << infile[i].getLine() << "\t"
<< "**diam" << "\n";
int counter = infile[i].getFieldCount();
for (int z=0; z<counter; z++) {
cout << "*\t";
}
cout << "*metric:" << metric+2 << "\n";
} else if (infile[i].equalFieldsQ("**kern")) {
cout << infile[i].getLine() << "\t"
<< infile[i][0] << "\n";
} else {
cout << infile[i].getLine() << "\t*\n";
}
break;
}
}
} else {
for (i=0; i<infile.getNumLines(); i++) {
switch (infile[i].getType()) {
case E_humrec_global_comment:
case E_humrec_bibliography:
case E_humrec_none:
case E_humrec_empty:
cout << infile[i].getLine() << "\n";
break;
case E_humrec_data:
cout << metricana[i] << "\n";
break;
case E_humrec_data_comment:
if (infile[i].equalFieldsQ("**kern")) {
cout << infile[i][0] << "\n";
} else {
// do nothing
}
break;
case E_humrec_data_measure:
if (infile[i].equalFieldsQ("**kern")) {
cout << infile[i][0] << "\n";
} else {
cout << "\t=\n";
}
break;
case E_humrec_data_interpretation:
if (strncmp(infile[i][0], "**", 2) == 0) {
cout << "**diam" << "\n";
cout << "*metric:" << metric+2 << "\n";
} else if (infile[i].equalFieldsQ("**kern")) {
cout << infile[i][0] << "\n";
} else {
// do nothing
}
break;
}
}
}
}
//////////////////////////////
//
// usage -- gives the usage statement for the quality program
//
void usage(const char* command) {
cout <<
" \n"
<< endl;
}
//////////////////////////////
//
// doublecompare -- compare two doubles for ordering
//
int doublecompare(const void* a, const void* b) {
if (*((double*)a) < *((double*)b)) {
return -1;
} else if (*((double*)a) > *((double*)b)) {
return 1;
} else {
return 0;
}
}
// md5sum: e62a7791feeb3e4522b94907300b65bf diametric.cpp [20050403]