//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sat Jun 9 14:48:41 PDT 2001
// Last Modified: Mon Apr 18 08:58:17 PDT 2011
// Last Modified: Tue Jul 17 12:42:32 PDT 2012 marked all tied notes
// Filename: ...museinfo/examples/all/parallel.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/parallel.cpp
// Syntax: C++; museinfo
//
// Description: calculates the minimum timebase which is the least common
// multiple of all rhythms in the file.
//
// Todo: some things to clean up in rhythmic representation due to conversions
// from ints to RationalNumber class for rhythms/durations...
#include "humdrum.h"
#include "SigString.h"
#include <stdlib.h>
#include "PerlRegularExpression.h"
class NoteNode {
public:
int b40; // base-40 pitch number or 0 if a rest, negative if tied0.
int b40next; // not used yet
int dia; // base-40 pitch number or 0 if a rest, negative if tied0.
int line; // line number in original score of note
int spine; // spine number in original score of note
int lastint; // interval to last note (or 0 if no last or last is rest)
int nextint; // interval to next note (or 0 if no next or last is rest)
int lastdint; // interval to last note (or 0 if no last or last is rest)
int nextdint; // interval to next note (or 0 if no next or last is rest)
int serial; // notes serial number
int measure; // measure number of note
NoteNode(void) { clear(); }
void clear(void) { serial = b40next = b40 = dia = 0; line = spine = -1;
lastdint = nextdint = lastint = nextint = 0; }
};
#define REST 0
#define RESTINT -1000000
///////////////////////////////////////////////////////////////////////////
// function declarations
void checkOptions(Options& opts, int argc, char* argv[]);
void example(void);
void usage(const char* command);
void processFile(HumdrumFile& infile, const char* filename);
void getKernTracks(Array<int>& ktracks, HumdrumFile& infile);
void calculateMelodicIntervals(Array<Array<NoteNode> >& notes);
void markParallels(Array<Array<NoteNode> >& notes,
HumdrumFile& infile, const char* filename,
Array<Array<char> >& names,
Array<int> reverselookup);
void addMark(HumdrumFile& infile, int line, int spine,
char marker);
void markParallel(HumdrumFile& infile,
Array<Array<NoteNode> >& notes,
int hint, int i, int j, int k);
int validateInterval(Array<Array<NoteNode> >& notes,
int i, int j, int k);
void markForward(HumdrumFile& infile, int line, int spine,
char marker);
void markBackward(HumdrumFile& infile, int line, int spine,
char marker);
int getNoteStart(HumdrumFile& infile, int line, int spine);
// global variables
Options options; // database for command-line arguments
int debugQ = 0; // used with --debug
int rawQ = 0; // used with -r option
int locQ = 0; // used with -l option
int raw2Q = 0; // used with --r2 option
int fileQ = 0; // used with -f option
int typeQ = 0; // used with -t option
int dataQ = 0; // used with -i option
int notiedQ = 0; // used with -T option (to be interfaced)
int diatonicQ= 0; // used with -d option
SigString Filename; // used if filename is "<STDIN>"
int OCTAVE = 0;
int FIFTH = 23;
///////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
checkOptions(options, argc, argv);
HumdrumFile infile;
int i;
// figure out the number of input files to process
int numinputs = options.getArgCount();
const char* filename = "";
PerlRegularExpression pre;
if (locQ) {
// print start of data
if (fileQ) {
cout << "**file\t";
}
cout << "**bar\t";
cout << "**description\t";
cout << "**voice1\t";
cout << "**voice2";
if (typeQ) {
cout << "\t**type";
}
if (dataQ) {
cout << "\t**data";
}
cout << "\n";
}
for (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);
filename = "<STDIN>";
} else {
filename = options.getArg(i+1);
infile.read(filename);
pre.search(filename, "([^/]+)(\\.[^\\.]+)$", "");
filename = pre.getSubmatch(1);
if (pre.search(filename, "^\\s*$")) {
filename = options.getArg(i+1);
}
}
processFile(infile, filename);
}
if (locQ) {
if (fileQ) {
cout << "*-\t";
}
cout << "*-\t*-\t*-\t*-";
if (typeQ) {
cout << "\t*-";
}
if (dataQ) {
cout << "\t*-";
}
cout << "\n";
}
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// getNames -- get the names of each column if they have one.
//
void getNames(Array<Array<char> >& names, Array<int>& reverselookup,
HumdrumFile& infile) {
names.setSize(reverselookup.getSize()-1);
names.allowGrowth(0);
char buffer[1024] = {0};
int value;
PerlRegularExpression pre;
int i;
int j;
int track;
for (i=0; i<names.getSize(); i++) {
value = reverselookup.getSize() - i;
sprintf(buffer, "%d", value);
names[i].setSize(strlen(buffer)+1);
strcpy(names[i].getBase(), buffer);
}
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].isData()) {
// stop looking for instrument name after the first data line
break;
}
if (!infile[i].isInterpretation()) {
continue;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
if (!infile[i].isExInterp(j, "**kern")) {
continue;
}
if (pre.search(infile[i][j], "^\\*I\"(.*)", "")) {
track = infile[i].getPrimaryTrack(j);
strcpy(buffer, pre.getSubmatch(1));
names[reverselookup[track]].setSize(strlen(buffer)+1);
strcpy(names[reverselookup[track]].getBase(), buffer);
}
}
}
if (debugQ) {
for (i=0; i<names.getSize(); i++) {
cout << i << ":\t" << names[i] << endl;
}
}
}
//////////////////////////////
//
// processFile --
//
void processFile(HumdrumFile& infile, const char* filename) {
int i, j;
Array<Array<NoteNode> > notes;
Array<Array<char> > names;
Array<int> ktracks;
Array<int> reverselookup;
int ii;
int jj;
getKernTracks(ktracks, infile);
notes.setSize(ktracks.getSize());
reverselookup.setSize(infile.getMaxTracks()+1);
reverselookup.setAll(-1);
for (i=0; i<ktracks.getSize(); i++) {
reverselookup[ktracks[i]] = i;
notes[i].setSize(infile.getNumLines());
notes[i].setSize(0);
}
getNames(names, reverselookup, infile);
PerlRegularExpression pre;
Array<NoteNode> current;
current.setSize(ktracks.getSize());
int sign;
int track;
int index;
int snum = 0;
int measurenumber = 0;
for (i=0; i<infile.getNumLines(); i++) {
if (debugQ) {
cout << "PROCESSING LINE: " << i << "\t" << infile[i] << endl;
}
if (infile[i].isMeasure()) {
if (pre.search(infile[i][0], "(\\d+)", "")) {
measurenumber = atoi(pre.getSubmatch(1));
}
}
for (j=0; j<current.getSize(); j++) {
current[j].clear();
current[j].measure = measurenumber;
}
if (infile[i].isMeasure() && (strstr(infile[i][0], "||") != NULL)) {
// double barline (terminal for Josquin project), so add a row
// of rests to prevent parallel interval identification between
// adjacent notes in different sections.
for (j=0; j<notes.getSize(); j++) {
notes[j].append(current[j]);
}
}
if (!infile[i].isData()) {
continue;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
sign = 1;
if (!infile[i].isExInterp(j, "**kern")) {
continue;
}
track = infile[i].getPrimaryTrack(j);
index = reverselookup[track];
current[index].line = i;
current[index].spine = j;
if (strcmp(infile[i][j], ".") == 0) {
sign = -1;
ii = infile[i].getDotLine(j);
jj = infile[i].getDotSpine(j);
} else {
ii = i;
jj = j;
}
if (strchr(infile[ii][jj], 'r') != NULL) {
current[index].b40 = 0;
current[index].dia = 0;
current[index].serial = ++snum;
continue;
}
if (strcmp(infile[ii][jj], ".") == 0) {
current[index].b40 = 0;
current[index].dia = 0;
current[index].serial = snum;
}
current[index].b40 = Convert::kernToBase40(infile[ii][jj]);
current[index].dia = Convert::base40ToDiatonic(current[index].b40);
if (strchr(infile[ii][jj], '_') != NULL) {
sign = -1;
current[index].serial = snum;
}
if (strchr(infile[ii][jj], ']') != NULL) {
sign = -1;
current[index].serial = snum;
}
current[index].b40 *= sign;
current[index].dia *= sign;
if (sign > 0) {
current[index].serial = ++snum;
}
}
if (debugQ) {
cout << "NOTES:";
}
for (j=0; j<notes.getSize(); j++) {
notes[j].append(current[j]);
if (debugQ) {
cout << "\t" << current[j].b40 << "(" << current[j].dia << ")";
}
}
if (debugQ) {
cout << endl;
}
}
int nextone = 0;
for (i=0; i<notes.getSize(); i++) {
notes[i].last().b40next = nextone;
for (j=notes[i].getSize()-1; j>= 0; j--) {
notes[i][j].b40next = nextone;
if (notes[i][j].b40 >= 0) {
nextone = notes[i][j].b40;
}
}
}
if (rawQ) {
for (i=0; i<notes[0].getSize(); i++) {
for (j=0; j<notes.getSize(); j++) {
cout << notes[j][i].b40;
cout << " to " << notes[j][i].b40next;
if (j < notes.getSize()-1) {
cout << "\t";
}
}
cout << endl;
}
exit(0);
}
calculateMelodicIntervals(notes);
if (raw2Q) {
for (i=0; i<notes[0].getSize(); i++) {
for (j=0; j<notes.getSize(); j++) {
cout << notes[j][i].b40;
cout << ",";
cout << notes[j][i].b40next;
cout << "(";
cout << notes[j][i].lastint;
cout << ";";
cout << notes[j][i].nextint;
cout << ")";
if (j < notes.getSize()-1) {
cout << "\t";
}
}
cout << endl;
}
exit(0);
}
markParallels(notes, infile, filename, names, reverselookup);
}
//////////////////////////////
//
// markParallels -- search for parts which are parallel fifth or octaves
//
// 0(REST;REST) 134(-5;REST) 162(11;-11) 174(6;28)
// 111(REST;-6) -134(-5;REST) -162(11;-11) 202(28;-5)
// -111(REST;-6) -134(-5;REST) 151(-11;6) -202(28;-5)
// 105(-6;6) -134(-5;REST) 157(6;5) 197(-5;-6)
// 111(6;23) -134(-5;REST) 162(5;-5) 191(-6;-6)
// 134(23;REST) -134(-5;REST) 157(-5;REST) 185(-6;REST)
//
void markParallels(Array<Array<NoteNode> >& notes, HumdrumFile& infile,
const char* filename, Array<Array<char> >& names,
Array<int> reverselookup) {
int i;
int j;
int k;
int hint;
int ii;
int jj;
int track;
int start1;
int start2;
int interval;
int dint;
int hasoctavemarks = 0;
int has5thmarks = 0;
char topstate = '?';
char bottomstate = '?';
int onote1;
int onote2;
int otherhint = 0;
int otherdint = 0;
int nextlower = 0;
int nextupper = 0;
int nextdlower = 0;
int nextdupper = 0;
for (j=0; j<notes[0].getSize(); j++) {
for (i=0; i<notes.getSize(); i++) {
if (notes[i][j].b40 == REST) {
// ignore rests
continue;
}
if (notes[i][j].nextint == RESTINT) {
// ignore rest intervals
continue;
}
if (notes[i][j].nextint == 0) {
// ignore repeated notes
continue;
}
for (k=i+1; k<notes.getSize(); k++) {
if (debugQ) {
cout << "comparing " << notes[i][j].b40
<< " to " << notes[k][j].b40 << endl;
}
if (notes[k][j].nextint == RESTINT) {
if (debugQ) {
cout << "SECOND NOTE, next note is rest, "
<< " int is " << notes[k][j].nextint << endl;
}
// ignore rest intervals
continue;
}
if (notes[k][j].b40 == 0) {
if (debugQ) {
cout << "SECOND NOTE is a rest" << endl;
}
// ignore rests
continue;
}
// disallow interval check from elided notes for now
//if (notes[i][j].b40 < 0) {
// continue;
//}
//if (notes[k][j].b40 < 0) {
// continue;
//}
if ((notes[k][j].b40 < 0) && (notes[i][j].b40 < 0)) {
if (debugQ) {
cout << "Both notes are not attacks " << endl;
}
continue;
}
char buf[1024];
if (abs(notes[k][j].b40) > abs(notes[i][j].b40)) {
hint = (abs(notes[k][j].b40) - abs(notes[i][j].b40) + 400) % 40;
dint = (abs(notes[k][j].dia) - abs(notes[i][j].dia) + 700) % 7;
onote1 = notes[k][j].b40next;
onote2 = notes[i][j].b40next;
if (debugQ) {
cout << "!! >>> " << Convert::base40ToKern(buf, abs(notes[k][j].b40));
cout << " is higher than " << Convert::base40ToKern(buf, abs(notes[i][j].b40));
cout << " INTERVAL " << hint << endl;
}
nextlower = notes[k][j].nextint;
nextupper = notes[i][j].nextint;
nextdupper = Convert::base40IntervalToDiatonic(nextupper);
if (nextdupper < 0) {
nextdupper--;
} else {
nextdupper++;
}
nextdlower = Convert::base40IntervalToDiatonic(nextlower);
if (nextdlower < 0) {
nextdlower--;
} else {
nextdlower++;
}
otherhint = hint + (nextlower - nextupper);
if (debugQ) {
cout << "!! " << Convert::base40ToKern(buf, onote1);
cout << " and " << Convert::base40ToKern(buf, onote2);
cout << " == OTHER HINT = " << otherhint << endl;
cout << "!! OR1 : " << hint + (nextupper - nextlower) << endl;
}
otherdint = Convert::base40IntervalToDiatonic(otherhint);
if (notes[k][j].b40 > 0) {
topstate = 'x';
} else {
topstate = 's';
}
if (notes[i][j].b40 > 0) {
bottomstate = 'x';
} else {
bottomstate = 's';
}
} else {
hint = (abs(notes[i][j].b40) - abs(notes[k][j].b40) + 400) % 40;
dint = (abs(notes[i][j].dia) - abs(notes[k][j].dia) + 700) % 7;
onote1 = notes[i][j].b40next;
onote2 = notes[k][j].b40next;
if (debugQ) {
cout << "!! >>X " << Convert::base40ToKern(buf, abs(notes[k][j].b40));
cout << " is lower than " << Convert::base40ToKern(buf, abs(notes[i][j].b40));
cout << " INTERVAL " << hint << endl;
}
nextupper = notes[i][j].nextint;
nextlower = notes[k][j].nextint;
nextdupper = Convert::base40IntervalToDiatonic(nextupper);
if (nextdupper < 0) {
nextdupper--;
} else {
nextdupper++;
}
nextdlower = Convert::base40IntervalToDiatonic(nextlower);
if (nextdlower < 0) {
nextdlower--;
} else {
nextdlower++;
}
otherhint = hint + (nextupper - nextlower);
if (debugQ) {
cout << "!! " << Convert::base40ToKern(buf, onote1);
cout << " and " << Convert::base40ToKern(buf, onote2);
cout << " == OTHER HINT = " << otherhint << endl;
cout << "!! OR2 : " << hint + (nextlower - nextupper) << endl;
}
otherdint = Convert::base40IntervalToDiatonic(otherhint);
if (notes[i][j].b40 > 0) {
topstate = 'x';
} else {
topstate = 's';
}
if (notes[k][j].b40 > 0) {
bottomstate = 'x';
} else {
bottomstate = 's';
}
}
if (debugQ) {
cout << "comparing " << notes[i][j].b40
<< " to " << notes[k][j].b40
<< "\tHINT = " << hint
<< "\tDINT = " << dint << endl;
}
if (diatonicQ) {
interval = dint;
} else {
interval = hint;
}
if (!((interval == OCTAVE) || (interval == FIFTH))) {
// not a fifth or an octave
continue;
}
if (diatonicQ) {
if (notes[k][j].nextdint != notes[i][j].nextdint) {
// note are not creating a parallel motion
continue;
}
if ((abs(notes[k][j].nextint) < 3) || (abs(notes[i][j].nextint) < 3)) {
// non-perfect unison/octave, ignore
continue;
}
} else {
if (notes[k][j].nextint != notes[i][j].nextint) {
// note are not creating a parallel motion
continue;
}
}
//cout << "Parallel " << hint << endl;
if (validateInterval(notes, i, j, k)) {
if (locQ) {
if (interval == OCTAVE) {
if (fileQ) {
if (strlen(Filename.getBase()) > 0) {
cout << Filename;
} else {
cout << filename;
}
cout << "\t";
}
cout << notes[i][j].measure;
cout << "\t" << "parallel octave/unison";
cout << "\t";
ii = notes[i][j].line;
jj = notes[i][j].spine;
track = infile[ii].getPrimaryTrack(jj);
cout << names[reverselookup[track]];
cout << "\t";
ii = notes[k][j].line;
jj = notes[k][j].spine;
track = infile[ii].getPrimaryTrack(jj);
cout << names[reverselookup[track]];
if (typeQ) {
start1 = getNoteStart(infile, notes[i][j].line,
notes[i][j].spine);
start2 = getNoteStart(infile, notes[k][j].line,
notes[k][j].spine);
if (start1 == start2) {
cout << "\t" << "hard";
} else {
cout << "\t" << "soft";
}
}
if (dataQ) {
cout << "\t" << topstate << bottomstate << ","
<< hint << "(" << dint+1 << ")"
<< ";xx," << otherhint << "(" << otherdint+1<< ")"
<< ";" << nextupper << "(" << nextdupper << ")"
<< ";" << nextlower << "(" << nextdlower << ")";
}
cout << endl;
} else if (interval == FIFTH) {
if (fileQ) {
if (strlen(Filename.getBase()) > 0) {
cout << Filename;
} else {
cout << filename;
}
cout << "\t";
}
cout << notes[i][j].measure;
cout << "\t" << "parallel fifth";
cout << "\t";
ii = notes[i][j].line;
jj = notes[i][j].spine;
track = infile[ii].getPrimaryTrack(jj);
cout << names[reverselookup[track]];
cout << "\t";
ii = notes[k][j].line;
jj = notes[k][j].spine;
track = infile[ii].getPrimaryTrack(jj);
cout << names[reverselookup[track]];
if (typeQ) {
start1 = getNoteStart(infile, notes[i][j].line,
notes[i][j].spine);
start2 = getNoteStart(infile, notes[k][j].line,
notes[k][j].spine);
if (start1 == start2) {
cout << "\t" << "hard";
} else {
cout << "\t" << "soft";
}
}
if (dataQ) {
cout << "\t" << topstate << bottomstate << ","
<< hint << "(" << dint+1 << ")"
<< ";xx," << otherhint << "(" << otherdint+1 << ")"
<< ";" << nextupper << "(" << nextdupper << ")"
<< ";" << nextlower << "(" << nextdlower << ")";
}
cout << endl;
}
}
markParallel(infile, notes, interval, i, j, k);
if (interval == OCTAVE) { hasoctavemarks = 1; }
if (interval == FIFTH) { has5thmarks = 1; }
}
}
}
}
/* // mark the ending notes in the parallel motion
for (i=0; i<notes.getSize(); i++) {
for (j=0; j<notes[i].getSize(); j++) {
if (notes[i][j].b40 == REST) {
// ignore rests
continue;
}
if (notes[i][j].lastint == RESTINT) {
// ignore rest intervals
continue;
}
if (notes[i][j].lastint == 0) {
// ignore repeated notes
continue;
}
for (k=i+1; k<notes.getSize(); k++) {
if (notes[i][k].lastint == RESTINT) {
// ignore rest intervals
continue;
}
if (notes[i][k].b40 == 0) {
// ignore rests
continue;
}
// disallow interval check from elided notes for now
if (notes[i][j].b40 < 0) {
continue;
}
if (notes[k][j].b40 < 0) {
continue;
}
if (abs(notes[k][j].b40) > abs(notes[i][j].b40)) {
hint = (abs(notes[k][j].b40) - abs(notes[i][j].b40) + 400) % 40;
} else {
hint = (abs(notes[i][j].b40) - abs(notes[k][j].b40) + 400) % 40;
}
if (!((hint == 0) || (hint == 23))) {
// not a fifth or an octave
continue;
}
if (notes[k][j].lastint != notes[i][j].lastint) {
// notes are not creating a parallel motion
continue;
}
// cout << "Parallel2 " << hint << endl;
//if (validateInterval(notes, i, j, k)) {
markParallel(infile, notes, hint, i, j, k);
if (hint == 0) { hasoctavemarks = 1; }
if (hint == 23) { has5thmarks = 1; }
//}
}
}
}
*/
if (!locQ) {
cout << infile;
if (hasoctavemarks) {
cout << "!!!RDF**kern: < = marked note, color=\"#00ff00\", ";
cout << "parallel octave" << endl;
}
if (has5thmarks) {
cout << "!!!RDF**kern: > = marked note, color=\"#ff0000\", ";
cout << "parallel fifth" << endl;
}
}
}
//////////////////////////////
//
// getNoteStart --
//
int getNoteStart(HumdrumFile& infile, int line, int spine) {
int isdot = 0;
if ((strchr(infile[line][spine], ']') != NULL) ||
(strchr(infile[line][spine], '_') != NULL) ) {
// do nothing
} else if (strcmp(infile[line][spine], ".") == 0) {
isdot = 1;
} else {
return line;
}
int track, ptrack;
int i, j;
// search for starting note of tied note group
track = infile[line].getPrimaryTrack(spine);
for (i=line-1; i>0; i--) {
if (!infile[i].isData()) {
continue;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
ptrack = infile[i].getPrimaryTrack(j);
if (ptrack != track) {
continue;
}
if (strcmp(infile[i][j], ".") == 0) {
continue;
}
if (strchr(infile[i][j], '_') != NULL) {
continue;
}
if (strchr(infile[i][j], '[') == NULL) {
// something probably went wrong: no opening to tie
if (isdot) {
return i;
} else {
// something probably went wrong: no opening to tie
return -1;
}
} else {
return i;
}
}
}
// something probably went wrong: no opening to tie
return -1;
}
//////////////////////////////
//
// validateInterval -- Currently only allow intervals where both notes
// in the second interval start at the same time.
//
int validateInterval(Array >& notes, int i, int j, int k) {
int nij = -1;
int nkj = -1;
int m;
for (m=j+1; m<notes[i].getSize(); m++) {
if (notes[i][m].b40 >= 0) {
nij = m;
break;
}
}
for (m=j+1; m<notes[k].getSize(); m++) {
if (notes[k][m].b40 >= 0) {
nkj = m;
break;
}
}
if (nij == nkj) {
return 1;
}
if (nij < 0) { // strange case if it occurs, but just mark as good
return 1;
}
if (nkj < 0) { // strage case if it occurs, but just mark as good
return 1;
}
return 0;
}
//////////////////////////////
//
// markParallel --
//
void markParallel(HumdrumFile& infile, Array<Array<NoteNode> >& notes,
int interval, int i, int j, int k) {
char marker = '<'; // parallel octaves/unisons
if (interval == FIFTH) {
marker = '>'; // parallel fifths
}
int ii;
int jj;
// mark first note in parallel motion
ii = notes[i][j].line;
jj = notes[i][j].spine;
if (strcmp(infile[ii][jj], ".") == 0) {
int ti;
int tj;
ti = infile[ii].getDotLine(jj);
tj = infile[ii].getDotSpine(jj);
ii = ti;
jj = tj;
}
if (strcmp(infile[ii][jj], ".") != 0) {
// if (strchr(infile[ii][jj], marker) == NULL) {
addMark(infile, ii, jj, marker);
// }
}
// mark second note in parallel motion
ii = notes[k][j].line;
jj = notes[k][j].spine;
if (strcmp(infile[ii][jj], ".") == 0) {
int ti;
int tj;
ti = infile[ii].getDotLine(jj);
tj = infile[ii].getDotSpine(jj);
ii = ti;
jj = tj;
}
if (strcmp(infile[ii][jj], ".") != 0) {
// if (strchr(infile[ii][jj], marker) == NULL) {
addMark(infile, ii, jj, marker);
// }
}
int m;
// mark third note in parallel motion
for (m=j+1; m<notes[i].getSize(); m++) {
if (notes[i][m].b40 < 0) {
continue;
}
ii = notes[i][m].line;
jj = notes[i][m].spine;
if (strcmp(infile[ii][jj], ".") == 0) {
int ti;
int tj;
ti = infile[ii].getDotLine(jj);
tj = infile[ii].getDotSpine(jj);
ii = ti;
jj = tj;
}
if (strcmp(infile[ii][jj], ".") != 0) {
// if (strchr(infile[ii][jj], marker) == NULL) {
addMark(infile, ii, jj, marker);
// }
}
break;
}
// mark third note in parallel motion
for (m=j+1; m<notes[k].getSize(); m++) {
if (notes[i][m].b40 < 0) {
continue;
}
ii = notes[k][m].line;
jj = notes[k][m].spine;
if (strcmp(infile[ii][jj], ".") == 0) {
int ti;
int tj;
ti = infile[ii].getDotLine(jj);
tj = infile[ii].getDotSpine(jj);
ii = ti;
jj = tj;
}
if (strcmp(infile[ii][jj], ".") != 0) {
// if (strchr(infile[ii][jj], marker) == NULL) {
addMark(infile, ii, jj, marker);
// }
}
break;
}
}
//////////////////////////////
//
// addMark --
//
void addMark(HumdrumFile& infile, int line, int spine, char marker) {
char buffer[1024];
char mstring[2] = {0};
mstring[0] = marker;
strcpy(buffer, infile[line][spine]);
strcat(buffer, mstring);
infile[line].changeField(spine, buffer);
// mark all notes which are tied to this note.
if (notiedQ) {
return;
}
if (strchr(buffer, '[') != NULL) {
markForward(infile, line, spine, marker);
} else if (strchr(buffer, ']') != NULL) {
markBackward(infile, line, spine, marker);
} else {
markForward(infile, line, spine, marker);
markBackward(infile, line, spine, marker);
}
}
//////////////////////////////
//
// markForward --
//
void markForward(HumdrumFile& infile, int line, int spine, char marker) {
int track = infile[line].getPrimaryTrack(spine);
int i, j;
char buffer[1024];
char mstring[2] = {0};
int ptrack;
mstring[0] = marker;
for (i=line+1; i<infile.getNumLines(); i++) {
if (!infile[i].isData()) {
continue;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
ptrack = infile[i].getPrimaryTrack(j);
if (ptrack != track) {
continue;
}
if (strcmp(infile[i][j], ".") == 0) {
continue;
}
if (strchr(infile[i][j], ']') != NULL) {
strcpy(buffer, infile[i][j]);
strcat(buffer, mstring);
infile[i].changeField(j, buffer);
return;
} else if (strchr(infile[i][j], '_') != NULL) {
strcpy(buffer, infile[i][j]);
strcat(buffer, mstring);
infile[i].changeField(j, buffer);
markForward(infile, i, j, marker);
} else {
// unterminated tie?
return;
}
}
}
}
//////////////////////////////
//
// markBackward --
//
void markBackward(HumdrumFile& infile, int line, int spine, char marker) {
int track = infile[line].getPrimaryTrack(spine);
int i, j;
char buffer[1024];
char mstring[2] = {0};
int ptrack;
mstring[0] = marker;
for (i=line-1; i>0; i--) {
if (!infile[i].isData()) {
continue;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
ptrack = infile[i].getPrimaryTrack(j);
if (ptrack != track) {
continue;
}
if (strcmp(infile[i][j], ".") == 0) {
continue;
}
if (strchr(infile[i][j], '[') != NULL) {
strcpy(buffer, infile[i][j]);
strcat(buffer, mstring);
infile[i].changeField(j, buffer);
return;
} else if (strchr(infile[i][j], '_') != NULL) {
strcpy(buffer, infile[i][j]);
strcat(buffer, mstring);
infile[i].changeField(j, buffer);
markBackward(infile, i, j, marker);
} else {
// unterminated tie?
return;
}
}
}
}
//////////////////////////////
//
// calculateMelodicIntervals --
//
void calculateMelodicIntervals(Array >& notes) {
int i, j;
int lastint = RESTINT;
int lastdint = RESTINT;
int lastnote = 0;
int lastdnote = 0;
// caluculate the intervals to previous notes:
for (i=0; i<notes.getSize(); i++) {
lastint = RESTINT;
lastdint = RESTINT;
lastnote = 0;
lastdnote = 0;
for (j=0; j<notes[i].getSize(); j++) {
if (notes[i][j].b40 < 0) {
// continuing note, just repeat previously calculate interval
notes[i][j].lastint = lastint;
notes[i][j].lastdint = lastdint;
continue;
}
if (notes[i][j].b40 == 0) {
// current note is a rest, to make the interval to the last
// note a RESTINT.
lastnote = 0;
lastdnote = 0;
notes[i][j].lastint = RESTINT;
notes[i][j].lastdint = RESTINT;
lastint = RESTINT;
lastdint = RESTINT;
continue;
}
if (lastnote == 0) {
// if the last note was a rest, then the interval to it is a RESTINT
lastnote = abs(notes[i][j].b40); // update last note
lastdnote = abs(notes[i][j].dia); // update last note
notes[i][j].lastint = RESTINT;
notes[i][j].lastdint = RESTINT;
lastint = RESTINT;
lastdint = RESTINT;
continue;
}
// have a note attack which does note follow a rest
lastint = notes[i][j].b40 - lastnote;
lastdint = notes[i][j].dia - lastdnote;
lastnote = notes[i][j].b40;
lastdnote = notes[i][j].dia;
notes[i][j].lastint = lastint;
notes[i][j].lastdint = lastdint;
}
}
// caluculate the intervales in the other direction
//128(-6) -174(0) -185(5) -197(-5)
//122(-6) -174(0) -185(5) 191(-6)
//128(6) 168(-6) 180(-5) -191(-6)
//-128(6) 162(-6) -180(-5) -191(-6)
//105(-23) 157(-5) 168(-12) 185(-6)
int nextint = RESTINT;
int nextdint = RESTINT;
int nextnote = 0;
// caluculate the intervals to next notes:
for (i=0; i<notes.getSize(); i++) {
nextint = RESTINT;
nextdint = RESTINT;
nextnote = 0;
for (j=notes[i].getSize()-1; j>=0; j--) {
notes[i][j].nextint = nextint;
notes[i][j].nextdint = nextdint;
if (notes[i][j].b40 >= 0) {
nextint = notes[i][j].lastint;
nextdint = notes[i][j].lastdint;
}
}
}
}
//////////////////////////////
//
// getKernTracks -- return a list of track number for **kern spines.
//
void getKernTracks(Array& ktracks, HumdrumFile& infile) {
int i, j;
ktracks.setSize(infile.getMaxTracks());
ktracks.setSize(0);
int track;
for (i=0; i<infile.getNumLines(); i++) {
if (!infile[i].isInterpretation()) {
continue;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
if (infile[i].isExInterp(j, "**kern")) {
track = infile[i].getPrimaryTrack(j);
ktracks.append(track);
}
}
break;
}
}
//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("d|diatonic=b", "do diatonic analysis");
opts.define("i|intervals|data=b", "display interval data");
opts.define("f|file=b", "display filenames with -l option");
opts.define("F|filename=s", "filenames to store with -f option (for stdin)");
opts.define("l|location|loc=b", "print locations of parallels");
opts.define("r|raw=b", "print raw data before analysis is done");
opts.define("t|type=b", "print type of interval (hard/soft)");
opts.define("raw2|r2=b", "print raw data 2 before analysis is done");
opts.define("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, April 2011" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 22 April 2010" << 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");
rawQ = opts.getBoolean("raw");
locQ = opts.getBoolean("location");
raw2Q = opts.getBoolean("raw2");
fileQ = opts.getBoolean("file");
typeQ = opts.getBoolean("type");
dataQ = opts.getBoolean("data");
diatonicQ = opts.getBoolean("diatonic");
if (diatonicQ) {
FIFTH = 4;
}
PerlRegularExpression pre;
pre.search(opts.getString("filename"), "([^/]+)(\\.[^\\.]+)$", "");
Filename = pre.getSubmatch(1);
if (pre.search(Filename.getBase(), "^\\s*$")) {
Filename = opts.getString("filename");
}
}
//////////////////////////////
//
// example -- example usage of the quality program
//
void example(void) {
cout <<
" \n"
<< endl;
}
//////////////////////////////
//
// usage -- gives the usage statement for the meter program
//
void usage(const char* command) {
cout <<
" \n"
<< endl;
}
// md5sum: 5714fd1abdf996f86687f03e1c0d1221 parallel.cpp [20120903]