//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed May 16 14:14:47 PDT 2001
// Last Modified: Wed May 16 14:14:50 PDT 2001
// Filename: ...sig/examples/all/scrub.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/scrub.cpp
// Syntax: C++; museinfo
//
// Description: Remove chordal/non-chordal tones from input music.
//
// Things to do: Allow for pickup beats.
//
#include "humdrum.h"
#include "ctype.h"
#include "string.h"
// function declarations
void checkOptions(Options& opts, int argc, char* argv[]);
void example(void);
void usage(const char* command);
void processFile(HumdrumFile& infile);
int getDegree(int note, int root);
void erasenote(HumdrumFile& infile, NoteList& note);
// command line options:
Options options; // database for command-line arguments
int debugQ = 0; // for debugging
Array<int> filter(10); // notes to keep/remove.
double timebase = 1.0; // duration of each chord
///////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) {
HumdrumFile infile; // input Humdrum Format file
// 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));
}
infile.analyzeRhythm("4"); // force the beat to be quarter notes for now
processFile(infile);
cout << infile;
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// processFile --
//
void processFile(HumdrumFile& infile) {
double beatstart = 0.0;
Array<double> scores(40);
Array<int> roots(infile.getNumLines()*10);
Array<double> starttimes(infile.getNumLines()*10);
roots.setGrowth(infile.getNumLines()*10);
starttimes.setGrowth(infile.getNumLines()*10);
roots.setSize(0);
starttimes.setSize(0);
roots.allowGrowth(1);
starttimes.allowGrowth(1);
int root;
Array<double> p(3);
p[0] = 0.573;
p[1] = -4.0;
p[2] = -3.0;
while (beatstart+timebase-0.01 < infile.getTotalDuration()) {
root = infile.measureChordRoot(scores, p, beatstart,
beatstart+timebase-0.01, 0);
starttimes.append(beatstart);
roots.append(root);
beatstart += timebase;
}
NoteListArray notelist;
infile.generateNoteList(notelist, 0, infile.getNumLines() - 1);
char buffer[128] = {0};
int i;
int degree;
int k = 0;
for (i=0; i<notelist.getSize(); i++) {
while (k < starttimes.getSize() &&
notelist[i].getAbsBeat() >= starttimes[k]) {
if (debugQ) {
cout << "beat " << starttimes[k] << "\troot = "
<< Convert::base40ToKern(buffer, 2+roots[k]+3*40) << endl;
}
k++;
}
if(debugQ) {
cout << "\t" << Convert::base40ToKern(buffer, notelist[i].getPitch());
}
degree = getDegree(notelist[i].getPitch(), roots[k-1]+2);
if (debugQ) {
cout << "\t=\tdegree " << degree;
}
if (degree >= 0 && filter[degree] == 0) {
if (debugQ) {
cout << " (remove)";
}
notelist[i].setPitch(-1);
}
if (debugQ) {
cout << endl;
}
}
// list removed notes
for (i=0; i<notelist.getSize(); i++) {
if (notelist[i].getPitch() == -1) {
if (debugQ) {
cout << "Remove note at line="
<< notelist[i].getLine()
<< "\tspine=" << notelist[i].getSpine()
<< "\ttoken=" << notelist[i].getToken()
<< endl;
}
erasenote(infile, notelist[i]);
}
}
}
//////////////////////////////
//
// erasenote --
//
void erasenote(HumdrumFile& infile, NoteList& note) {
char buffer[64] = {0};
Convert::durationToKernRhythm(buffer, note.getFirstDur());
strcat(buffer, "r");
infile[note.getLine()].changeToken(note.getSpine(), note.getToken(),
buffer);
}
//////////////////////////////
//
// getDegree -- convert a note to a chord degree
// -1 = invalid
// 0 = root
// 1 = third
// 2 = fifth, etc.
//
int getDegree(int note, int root) {
int interval = (note - root + 40) % 40;
switch (interval) {
case 0: return 0;
case 1: return 7;
case 2: return 7;
case 3: return -1;
case 4: return 4;
case 5: return 4;
case 6: return 4;
case 7: return 4;
case 8: return 4;
case 9: return -1;
case 10: return 8;
case 11: return 8;
case 12: return 1;
case 13: return 1;
case 14: return 8;
case 15: return 5;
case 16: return 5;
case 17: return 5;
case 18: return 5;
case 19: return 5;
case 20: return -1;
case 21: return 9;
case 22: return 2;
case 23: return 2;
case 24: return 2;
case 25: return 9;
case 26: return -1;
case 27: return 6;
case 28: return 6;
case 29: return 6;
case 30: return 6;
case 31: return 6;
case 32: return -1;
case 33: return 10;
case 34: return 3;
case 35: return 3;
case 36: return 3;
case 37: return 3;
case 38: return 7;
case 39: return 7;
default: return -1;
}
}
//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("r|remove=s:", "scale degrees to remove");
opts.define("k|keep=s:13579AB", "scale degrees to keep");
opts.define("t|timebase=s:4", "chord analysis timebase");
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, May 2001" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: May 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);
}
int i;
const char* string;
int length;
for (i=0; i<filter.getSize(); i++) {
filter[i] = 1;
}
if (opts.getBoolean("remove")) {
for (i=0; i<filter.getSize(); i++) {
filter[i] = 1;
}
string = opts.getString("remove");
length = strlen(string);
for (i=0; i<=length; i++) {
switch (toupper(string[i])) {
case '1': filter[0] = 0; break;
case '3': filter[1] = 0; break;
case '5': filter[2] = 0; break;
case '7': filter[3] = 0; break;
case '9': filter[4] = 0; break;
case 'A': filter[5] = 0; break;
case 'B': filter[6] = 0; break;
case 'C': filter[7] = 0; break;
case 'D': filter[8] = 0; break;
case 'E': filter[9] = 0; break;
case '2': filter[4] = 0; break;
case '4': filter[5] = 0; break;
case '6': filter[6] = 0; break;
case '8': filter[0] = 0; break;
}
}
} else if (opts.getBoolean("keep")) {
for (i=0; i<filter.getSize(); i++) {
filter[i] = 0;
}
string = opts.getString("keep");
length = strlen(string);
for (i=0; i<=length; i++) {
switch (toupper(string[i])) {
case '1': filter[0] = 1; break;
case '3': filter[1] = 1; break;
case '5': filter[2] = 1; break;
case '7': filter[3] = 1; break;
case '9': filter[4] = 1; break;
case 'A': filter[5] = 1; break;
case 'B': filter[6] = 1; break;
case 'C': filter[7] = 1; break;
case 'D': filter[8] = 1; break;
case 'E': filter[9] = 1; break;
case '2': filter[4] = 1; break;
case '4': filter[5] = 1; break;
case '6': filter[6] = 1; break;
case '8': filter[0] = 1; break;
}
}
}
timebase = Convert::kernToDuration(opts.getString("timebase"));
debugQ = opts.getBoolean("debug");
}
//////////////////////////////
//
// example -- example usage of the scrub program
//
void example(void) {
cout <<
" \n"
<< endl;
}
//////////////////////////////
//
// usage -- gives the usage statement for the scrub program
//
void usage(const char* command) {
cout <<
" \n"
<< endl;
}
// md5sum: 6750cc5dc8228a3499322988390e0a8f scrub.cpp [20050403]