//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Thu Jan 29 16:16:33 PST 2009
// Last Modified: Thu Jan 29 16:16:38 PST 2009
// Filename: ...sig/examples/all/updown.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/updown.cpp
// Syntax: C++; museinfo
//
// Description: Identifies when there are m notes which rise in
// in pitch, followed by n notes falling in pitch.
//
// four types of outputs can be given:
// -u = specifies the number up expected ascending pitches
// -d = specifies the number up expected descending pitches
// -i = ignore repeated notes
//
#include "humdrum.h"
// function declarations
void checkOptions(Options& opts, int argc, char* argv[]);
void example(void);
void usage(const char* command);
void generateData(HumdrumFile& infile, Array<int>& pitches,
Array<int>& lines, Array<int>& measures,
int ignorerepeat);
void analyzeData(Array<int>& matchstart, Array<int>& pitches,
int upcount, int downcount);
void printAnalysis(Array<int>& matchstart, HumdrumFile& infile,
Array<int>& pitches, Array<int>& lines,
Array<int>& measures, int upcount,
int downcount);
int getFirstKernColumn(HumdrumFile& infile, int index);
void printMatchSequence(Array<int>& pitches, int startindex, int count);
// global variables
Options options; // database for command-line arguments
int debugQ = 0; // set with --debug
int risecount = 5; // set with -u option
int fallcount = 5; // set with -d option
int ignorerepeat = 0; // set with -i 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> pitches; // list of pitches in melody
Array<int> lines; // list of lines in input file for pitches
Array<int> measures; // list of measures for pitches
Array<int> matchstart; // list of measures for pitches
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));
}
generateData(infile, pitches, lines, measures, ignorerepeat);
analyzeData(matchstart, pitches, risecount, fallcount);
printAnalysis(matchstart, infile, pitches, lines, measures,
risecount, fallcount);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// generateData -- extract musical data from Humdrum File.
//
void generateData(HumdrumFile& infile, Array<int>& pitches,
Array<int>& lines, Array<int>& measures, int ignorerepeat) {
// pre-allocate maximum array sizes:
pitches.setSize(infile.getNumLines());
lines.setSize(infile.getNumLines());
measures.setSize(infile.getNumLines());
pitches.setSize(0);
lines.setSize(0);
measures.setSize(0);
int currmeas = 0;
int apitch = 0;
int col = 0;
int i;
for (i=0; i<infile.getNumLines(); i++) {
switch (infile[i].getType()) {
case E_humrec_data_kern_measure:
if (sscanf(infile[i][0], "=%d", &currmeas) != 1) {
currmeas++;
}
break;
case E_humrec_data:
col = getFirstKernColumn(infile, i);
if (strcmp(infile[i][col], ".") == 0) {
// ignore null tokens
continue;
} else if (strchr(infile[i][col], 'r') != NULL) {
// ignore rests
continue;
}
if (strchr(infile[i][col], ' ') != NULL) {
cout << "WARNING: chord found on line " << i+1
<< "(" << infile[i][col]
<< "): only using first note." << endl;
}
apitch = Convert::kernToMidiNoteNumber(infile[i][col]);
if (ignorerepeat && (pitches[pitches.getSize()-1] == apitch)) {
continue;
}
pitches.append(apitch);
lines.append(i);
measures.append(currmeas);
if (debugQ) {
cout << apitch << "\t" << i << "\t" << currmeas << endl;
}
break;
case E_humrec_none:
case E_humrec_empty:
case E_humrec_global_comment:
case E_humrec_bibliography:
case E_humrec_data_comment:
case E_humrec_interpretation:
default:
break;
}
}
}
//////////////////////////////
//
// getFirstKernColumn -- return the index of the first
// **kern data column on the row.
//
int getFirstKernColumn(HumdrumFile& infile, int index) {
int j;
for (j=0; j<infile[index].getFieldCount(); j++) {
if (strcmp(infile[index].getExInterp(j), "**kern") == 0) {
return j;
}
}
// did not find any **kern data on the line.
return -1;
}
//////////////////////////////
//
// analyzeData -- location required number of ascending and descending
// pitches in the melody.
//
void analyzeData(Array<int>& matchstart, Array<int>& pitches,
int upcount, int downcount) {
matchstart.setSize(pitches.getSize());
matchstart.setSize(0);
Array<int> intervals;
intervals.setSize(pitches.getSize()-1);
int i, j;
for (i=0; i<intervals.getSize(); i++) {
intervals[i] = pitches[i+1] - pitches[i];
if (debugQ) {
cout << "INTERVAL: " << intervals[i] << endl;
}
}
int success = 0;
int sum = upcount + downcount;
for (i=0; i<intervals.getSize()-sum; i++) {
success = 1;
for (j=0; j<upcount; j++) {
if (intervals[i+j] <= 0) {
success = 0;
break;
}
}
if (success != 0) {
for (j=0; j<downcount; j++) {
if (intervals[i+j+upcount] >= 0) {
success = 0;
break;
}
}
}
if (success) {
matchstart.append(i);
if (debugQ) {
cout << "MATCH at index " << i << endl;
}
}
}
}
//////////////////////////////
//
// printAnalysis -- print the Analysis results.
//
void printAnalysis(Array<int>& matchstart, HumdrumFile& infile,
Array<int>& pitches, Array<int>& lines, Array<int>& measures,
int upcount, int downcount) {
// analyze the input file according to command-line options
infile.analyzeRhythm();
int i;
for (i=0; i<matchstart.getSize(); i++) {
cout << measures[matchstart[i]] << ":";
cout << infile[lines[matchstart[i]]].getBeat() << " ";
printMatchSequence(pitches, matchstart[i], upcount+downcount+1);
cout << endl;
}
}
//////////////////////////////
//
// printMatchSequence --
//
void printMatchSequence(Array& pitches, int startindex, int count) {
int i;
char buffer[1024] = {0};
cout << "[";
for (i=0; i<count; i++) {
cout << Convert::base12ToKern(buffer, pitches[startindex+i]);
if (i < count-1) {
cout << ", ";
}
}
cout << "]";
}
//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("i|ignore-repeated-notes=b", "ignore repeated notes");
opts.define("u|up-count=i:5", "number of notes going upwards");
opts.define("d|down-count=i:5", "number of notes going downwards");
opts.define("debug=b", "pritn debugging statements");
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, 29 Jan 2008" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 29 Jan 2008" << 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");
risecount = opts.getInteger("up-count") - 1;
fallcount = opts.getInteger("down-count") - 1;
ignorerepeat = opts.getBoolean("ignore-repeated-notes");
debugQ = opts.getBoolean("debug");
}
//////////////////////////////
//
// example -- example usage of the quality program
//
void example(void) {
// add here
}
//////////////////////////////
//
// usage -- gives the usage statement for the meter program
//
void usage(const char* command) {
// add here
}
// md5sum: fedf5927e269a1ee1eb1b1e084e104e9 updown.cpp [20090323]