// Creation Date: Mon Jan 26 08:53:14 PST 2004
// Last Modified: Fri Apr 9 19:02:18 PDT 2004 rhythmic feature extraction
// Last Modified: Mon Apr 12 14:05:52 PDT 2004 adding more rhythm features
// Last Modified: Fri Apr 16 18:01:03 PDT 2004 added directory processing
// Last Modified: Sun Aug 8 18:44:36 PDT 2004 added more rhythm features
// Last Modified: Thu Oct 30 10:38:19 PST 2008 closedir fix for OSX
// Last Modified: Wed Jul 1 16:11:07 PDT 2009 added polyphonic extraction
// Last Modified: Mon Aug 30 13:57:51 PDT 2010 3... rhythm disallowed in dur
// Last Modified: Mon Aug 30 13:57:51 PDT 2010 -2147483648 rhythm --> X
// Last Modified: Wed Sep 1 13:37:32 PDT 2010 added metric position
// Last Modified: Sun Sep 12 21:01:37 PDT 2010 added bibliographic indexing
// Last Modified: Fri Nov 12 07:22:40 PST 2010 added instrument name tag
// Last Modified: Sun Nov 21 16:15:48 PST 2010 add rest boundary marker
// Last Modified: Sat Nov 27 17:23:58 PST 2010 added -D and -d options
// Last Modified: Thu Jan 13 03:15:14 PST 2011 renamed themebuilderx -> tindex
// Last Modified: Sun Jan 16 06:36:42 PST 2011 chaned default behavior to -a
// Last Modified: Tue Jan 18 08:36:29 PST 2011 added --verbose option
// Last Modified: Thu Feb 24 17:35:31 PST 2011 added --file option
// Last Modified: Sat Apr 2 18:04:05 PDT 2011 added L=Long & B=Breve for durs.
// Last Modified: Mon Apr 9 17:27:58 PDT 2012 NBC changes to features
// Last Modified: Thu May 24 12:28:08 PDT 2012 added -u and -I options
// Last Modified: Mon Nov 12 13:56:29 PST 2012 added !noff: processing
// Last Modified: Sun Apr 7 00:38:49 PDT 2013 Enabled multiple segment input
// Filename: ...museinfo/examples/all/tindex.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/tindex.cpp
// Syntax: C++; museinfo
//
// Description: Converts Humdrum files into themefinder search index records.
//
// Classical Thema index order:
// is: [Zz] { # : % } j J M
//
// Meaning of the tracer symbols:
// [Zz] = major/minor key
// { = 12-tone interval
// # = pitch refined contour
// : = pitch gross contour
// % = scale degree
// } = musical interval
// j = 12-tone pitch
// J = absolute pitch
// M = metric description
// Added rhythmic markers:
// ~ = duration gross contour
// ^ = duration refined contour
// ; = duration (IOI)
// & = beat level
// @ = metric gross contour
// ` = metric refined contour
// ' = metric level
// = = metric position
//
// Todo: When a medial or final tie does not match to
// an opening tie, that tied note should be indexed.
// This case occurs at multiple repeat endings in scores.
// character markers in index file:
#define P_PITCH_CLASS_MARKER 'J'
#define P_DIATONIC_INTERVAL_MARKER '}'
#define P_SCALE_DEGREE_MARKER '%'
#define P_12TONE_INTERVAL_MARKER '{'
#define P_PITCH_REFINED_CONTOUR_MARKER '#'
#define P_GROSS_CONTOUR_MARKER ':'
#define P_12TONE_PITCH_CLASS_MARKER 'j'
#define R_DURATION_GROSS_CONTOUR_MARKER '~'
#define R_DURATION_REFINED_CONTOUR_MARKER '^'
#define R_DURATION_MARKER ';'
#define R_BEAT_LEVEL_MARKER '&'
#define R_METRIC_POSITION_MARKER '='
#define R_METRIC_LEVEL_MARKER '\''
#define R_METRIC_GROSS_CONTOUR_MARKER '@'
#define R_METRIC_REFINED_CONTOUR_MARKER '`'
#define RESTDUR -1000
#include "humdrum.h"
#include "PerlRegularExpression.h"
#ifndef OLDCPP
#include <iostream>
#include <fstream>
#include <sstream>
#define SSTREAM stringstream
#define CSTRING str().c_str()
using namespace std;
#else
#include <iostream.h>
#include <fstream.h>
#ifdef VISUAL
#include <strstrea.h> /* for windows 95 */
#else
#include <strstream.h>
#endif
#define SSTREAM strstream
#define CSTRING str()
#endif
// includes needed for file/directory processing:
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
class ISTN {
public:
char *istn;
char *filename;
ISTN(void) { istn = NULL; filename = NULL; }
ISTN(ISTN& anISTN) {
int len;
if (anISTN.istn == NULL) {
istn = NULL;
} else {
len = strlen(anISTN.istn);
istn = new char[len+1];
strcpy(istn, anISTN.istn);
}
if (anISTN.filename == NULL) {
filename = NULL;
} else {
len = strlen(anISTN.filename);
filename = new char[len+1];
strcpy(filename, anISTN.filename);
}
}
void print(void) {
if (istn != NULL) {
cout << istn;
} else {
cout << ".";
}
cout << "\t";
if (filename != NULL) {
cout << filename;
} else {
cout << ".";
}
cout << "\n";
}
int is_valid(void) {
if (filename == NULL) return 0;
if (istn == NULL) return 0;
return 1;
}
void setFilename(const char* string) {
if (filename != NULL) {
delete [] filename;
}
filename = new char[strlen(string)+1];
strcpy(filename, string);
}
void setIstn(const char* string) {
if (istn != NULL) {
delete [] istn;
}
istn = new char[strlen(string)+1];
strcpy(istn, string);
}
ISTN& operator=(ISTN& anISTN) {
if (this == &anISTN) return *this;
clear();
int len;
if (anISTN.istn == NULL) {
istn = NULL;
} else {
len = strlen(anISTN.istn);
istn = new char[len+1];
strcpy(istn, anISTN.istn);
}
if (anISTN.filename == NULL) {
filename = NULL;
} else {
len = strlen(anISTN.filename);
filename = new char[len+1];
strcpy(filename, anISTN.filename);
}
return *this;
}
~ISTN() { clear(); }
void clear(void) {
if (istn != NULL) delete [] istn;
if (filename != NULL) delete [] filename;
istn = NULL;
filename = NULL;
}
};
// function declarations:
void checkOptions(Options& opts, int argc, char** argv);
void example(void);
void usage(const char* command);
void createIndex(HumdrumFile& infile, const char* filename);
void createIndexEnding(HumdrumFile& infile, int track, int layer);
void extractPitchSequence(Array<int>& pitches, HumdrumFile& infile,
int track, int layer);
void extractDurationSequence(Array<double>& durations, HumdrumFile& infile,
int track, int layer);
void extractMetricSequence(Array<double>& metriclevels,
Array<RationalNumber>& metricpositions,
HumdrumFile& infile, int track, int layer);
void getKey(HumdrumFile& infile, int& mode, int& tonic);
void printKey(int mode, int tonic);
void printMeter(HumdrumFile& infile);
int getMaxLayer(HumdrumFile& infile, int track);
// pitch sequence printing:
void printPitch(Array<int>& pitches);
void printGrossContour(Array<int>& pitches);
void printRefinedContour(Array<int>& pitches);
void print12toneInterval(Array<int>& pitches);
void print12tonePitch(Array<int>& pitches);
void printScaleDegree(Array<int>& pitches, int tonic);
void printMusicalInterval(Array<int>& pitches);
// rhythm sequence printing:
void printGrossContourRhythm(Array<double>& durations);
void printRefinedContourRhythm(Array<double>& durations);
void printMetricLevel(Array<double>& levels);
void printMetricRefinedContour(Array<double>& levels);
void printMetricGrossContour(Array<double>& levels);
void printBeatLevel(Array<double>& levels);
void printDuration(Array<double>& levels);
void printMetricPosition(Array<RationalNumber>& positions);
void extractFeatureSet(const char* features);
int is_directory (const char* path);
int is_file (const char* path);
void processArgument(const char* path);
void fillIstnDatabase(Array<ISTN>& istndatabase,
const char* istnfile);
const char* getIstn(const char* filename);
int bibsort(const void* a, const void* b);
void processBibRecords(ostream& out, HumdrumFile &infile,
const char* bibfilter);
void printInstrument(HumdrumFile& infile, int track);
char identifyLongMarker(HumdrumFile& infile);
void printSpineNoteInfo(HumdrumFile& infile, int track, int subtrack);
char* getOriginalFileName(char* buffer, HumdrumFile& infile,
const char* filename);
// User interface variables:
Options options;
int debugQ = 0; // used with --debug option
int polyQ = 0; // used with --moly option
int monoQ = 0; // used with --mono option
int poly2Q = 0; // used with --poly2 option
int dirQ = 1; // used with -D option
int endQ = 0; // used with --end option
int graceQ = 1; // used with -G option
int quietQ = 0; // used with -q option
int restQ = 0; // used with --rest option
int phraseQ = 0; // used with --phrase option
int fermataQ = 0; // used with --fermata option
int rhythmQ = 1; // used with -r option
int verboseQ = 0; // used with --verbose option
int pitchQ = 1; // used with -p option
int extraQ = 1; // used with -E option
int limitQ = 0; // used with -l option
int limit = 20; // used with -l option
int istnQ = 0; // used with --istn option
int bibQ = 0; // used with -b option
int fileQ = 0; // used with --file option
const char* Filename = ""; // used with --file option
char FileBuffer[1024] = {0}; // used with !!original-filename:
int instrumentQ = 0; // used with -i option
int dirprefixQ = 0; // used with -d option
Array<char> dirprefix; // used with -d option
const char* bibfilter = ""; // used with -B option
const char* istnfile= ""; // used with --istn option
Array<ISTN> istndatabase; // used with --istn option
#define PSTATESIZE 128
int pstate[PSTATESIZE] = {0}; // true for printing of particular feature
#define pHumdrumFormat 0
//pitch printing states:
#define pPitch 1
#define pGrossContour 2
#define pRefinedContour 3
#define p12toneInterval 4
#define p12tonePitch 5
#define pScaleDegree 6
#define pMusicalInterval 7
// rhythm printing states:
#define pDurationGrossContour 8
#define pDurationRefinedContour 9
#define pDuration 10
#define pBeat 11
#define pMetricLevel 12
#define pMetricRefinedContour 13
#define pMetricGrossContour 14
#define pMetricPosition 15
//////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
checkOptions(options, argc, argv); // process the command-line options
int i, j;
int numinputs = options.getArgCount();
HumdrumFileSet infiles;
// use --verbose to print default settings.
if (!quietQ) {
if (!graceQ) {
cout << "#NOGRACE" << endl;
} else {
if (verboseQ) {
cout << "#GRACE" << endl;
}
}
if (restQ) {
cout << "#REST" << endl;
} else {
if (verboseQ) {
cout << "#NOREST" << endl;
}
}
if (fermataQ) {
cout << "#FERMATA" << endl;
} else {
if (verboseQ) {
cout << "#NOFERMATA" << endl;
}
}
if (phraseQ) {
cout << "#PHRASE" << endl;
} else {
if (verboseQ) {
cout << "#NOPHRASE" << endl;
}
}
}
for (i=0; i<numinputs || i==0; i++) {
// if no command-line arguments read data file from standard input
if (numinputs < 1) {
infiles.read(cin);
for (j=0; j<infiles.getCount(); j++) {
createIndex(infiles[j], infiles[j].getFilename());
}
} else {
processArgument(options.getArg(i+1));
}
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// processArgument -- check if the argument is a file or a directory.
// if a directory, then process all files/subdirectories in it.
//
void processArgument(const char* path) {
HumdrumFileSet infiles;
DIR* dir = NULL;
char* fullname;
struct dirent* entry;
int namelen = 0;
int valid = 0;
const char* filename;
if (is_file(path)) {
namelen = strlen(path);
valid = 0;
if (strcmp(&(path[namelen-4]), ".thm") == 0) {
valid = 1;
} else if (strcmp(&(path[namelen-4]), ".krn") == 0) {
valid = 1;
} else if (strcmp(&(path[namelen-4]), ".THM") == 0) {
valid = 1;
} else if (strcmp(&(path[namelen-4]), ".KRN") == 0) {
valid = 1;
}
if (!valid) {
return;
}
infiles.read(path);
int i;
for (i=0; i<infiles.getCount(); i++) {
filename = infiles[i].getFilename();
if (strlen(filename) == 0) {
filename = path;
}
createIndex(infiles[i], filename);
}
} else if (is_directory(path)) {
dir = opendir(path);
if (dir == NULL) {
return;
}
entry = readdir(dir);
while (entry != NULL) {
if (strncmp(entry->d_name, ".", 1) == 0) {
entry = readdir(dir);
continue;
}
fullname = new char[strlen(path) + 1 + strlen(entry->d_name) + 1];
strcpy(fullname, path);
strcat(fullname, "/");
strcat(fullname, entry->d_name);
processArgument(fullname);
entry = readdir(dir);
}
}
if (dir != NULL) {
// can't close a NULL dir in OS X or program will crash.
closedir(dir);
}
}
//////////////////////////////
//
// is_file -- returns true if the string is a file.
//
int is_file(const char* path) {
struct stat filestat;
stat(path, &filestat);
return S_ISREG(filestat.st_mode);
}
//////////////////////////////
//
// is_directory -- returns true if the string is a directory.
//
int is_directory(const char* path) {
struct stat filestat;
stat(path, &filestat);
return S_ISDIR(filestat.st_mode);
}
//////////////////////////////
//
// getIstn --
//
const char* getIstn(const char* filename) {
int i;
const char* output = NULL;
for (i=0; i<istndatabase.getSize(); i++) {
if (strcmp(istndatabase[i].filename, filename) == 0) {
output = istndatabase[i].istn;
break;
}
}
if (output == NULL) {
output = filename;
}
return output;
}
//////////////////////////////
//
// createIndex -- The classical fixed order for the thema command
// is: [Zz] { # : % } j J M
//
void createIndex(HumdrumFile& infile, const char* filename) {
int i;
int maxtracks = infile.getMaxTracks();
if (fileQ) {
// used to spoof filename for standard input
filename = Filename;
} else {
filename = getOriginalFileName(FileBuffer, infile, filename);
}
PerlRegularExpression pre;
Array<char> printname;
if (dirprefixQ) {
// remove the directory given with the filename
pre.search(filename, "([^\\/]*)$", "");
printname.setSize(strlen(dirprefix.getBase()) +
strlen(pre.getSubmatch(1)) + 1);
strcpy(printname.getBase(), dirprefix.getBase());
strcat(printname.getBase(), pre.getSubmatch());
} else if (!dirQ) {
pre.search(filename, "([^\\/]*)$", "");
printname.setSize(strlen(pre.getSubmatch(1)) + 1);
strcpy(printname.getBase(), pre.getSubmatch());
} else {
printname.setSize(strlen(filename) + 1);
strcpy(printname.getBase(), filename);
}
pre.sar(printname, ":", ":", "g");
if (polyQ) {
for (i=1; i<=maxtracks; i++) {
if (strcmp("**kern", infile.getTrackExInterp(i)) != 0) {
continue;
}
if (istnQ) {
cout << getIstn(filename);
} else {
cout << printname;
}
cout << ":";
if (instrumentQ) {
printInstrument(infile, i);
}
// cout << ":" << i;
cout << ":";
printSpineNoteInfo(infile, i, 1);
if (strcmp("**kern", infile.getTrackExInterp(i)) == 0) {
createIndexEnding(infile, i, 1);
cout << "\n";
}
}
} else if (poly2Q) {
for (i=1; i<=maxtracks; i++) {
if (strcmp("**kern", infile.getTrackExInterp(i)) != 0) {
continue;
}
if (istnQ) {
cout << getIstn(filename);
} else {
cout << printname;
}
// print voice label
cout << ":";
if (instrumentQ) {
printInstrument(infile, i);
}
// print spine, subspine and note offset values
// cout << ":" << i;
cout << ":";
printSpineNoteInfo(infile, i, 1);
createIndexEnding(infile, i, 1);
cout << "\n";
int maxlayer = getMaxLayer(infile, i);
int j;
for (j=2; j<=maxlayer; j++) {
if (istnQ) {
cout << getIstn(filename);
} else {
cout << printname;
}
cout << ":";
if (instrumentQ) {
printInstrument(infile, i);
}
//cout << ":" << i << "." << j;
cout << ":";
printSpineNoteInfo(infile, i, j);
createIndexEnding(infile, i, j);
cout << "\n";
}
}
} else if (monoQ) {
if (istnQ) {
cout << getIstn(filename);
} else {
cout << printname;
}
for (i=1; i<=maxtracks; i++) {
if (strcmp("**kern", infile.getTrackExInterp(i)) == 0) {
createIndexEnding(infile, i, 1);
cout << "\n";
break;
}
}
} else {
cerr << "Strange error: no extraction model" << endl;
exit(1);
}
}
//////////////////////////////
//
// getOriginalFileName --
//
char* getOriginalFileName(char* buffer, HumdrumFile& infile,
const char* filename) {
int i;
for (i=0; i<infile.getNumLines(); i++) {
if (!infile[i].isGlobalComment()) {
continue;
}
if (strncmp(infile[i][0], "!!original-filename:", 20) == 0) {
PerlRegularExpression pre;
pre.search(infile[i][0], "!!original-filename:\\s*(.*?)\\s*$");
strcpy(buffer, pre.getSubmatch(1));
return buffer;
}
}
strcpy(buffer, filename);
return buffer;
}
//////////////////////////////
//
// printSpineNoteInfo -- print the track, subtrack and note offset.
// Note offsets are for indexes of extractions of pieces which
// will need to be aligned to the original data later on.
// !noff:1.2;23
// before any data in a (sub)spine would indicate the revised data.
// In this case, track = 1, subtrack = 2, note offset = 23
// Or using default track/subtrack found in data:
// !noff:23
// Would mean for that track/subtrack, the note offset value is 23.
//
void printSpineNoteInfo(HumdrumFile& infile, int track, int subtrack) {
int i, j;
int t, st;
int newt = -1;
int newst = -1;
int newoffset = -1;
PerlRegularExpression pre;
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].isData()) {
break;
}
if (!infile[i].isLocalComment()) {
continue;
}
st = 0;
for (j=0; j<infile[i].getFieldCount(); j++) {
t = infile[i].getPrimaryTrack(j);
if (t != track) {
continue;
}
st++;
if (st != subtrack) {
continue;
}
if (pre.search(infile[i][j], "^\\!noff:(\\d+)\\.?(\\d+)?;(\\d+)")) {
newt = atoi(pre.getSubmatch(1));
newst = atoi(pre.getSubmatch(2));
newoffset = atoi(pre.getSubmatch(3));
} else if (pre.search(infile[i][j], "^\\!noff:(\\d+)")) {
newoffset = atoi(pre.getSubmatch(1));
}
}
}
// print the track number
if (newt >= 0) {
cout << newt;
} else {
cout << track;
}
if (newst > 1) {
cout << newt;
} else if (subtrack > 1) {
cout << "." << subtrack;
}
if (newoffset > 0) {
cout << ';' << newoffset;
}
}
//////////////////////////////
//
// getMaxLayer -- return the maximum number of subspines for the
// given spine on any line.
//
int getMaxLayer(HumdrumFile& infile, int track) {
int i, j;
int maxlayer = 0;
int linelayer = 0;
for (i=0; i<infile.getNumLines(); i++) {
if (!infile[i].isData()) {
continue;
}
linelayer = 0;
for (j=0; j<infile[i].getFieldCount(); j++) {
if (infile[i].getPrimaryTrack(j) == track) {
linelayer++;
}
}
if (linelayer > maxlayer) {
maxlayer = linelayer;
}
}
return maxlayer;
}
///////////////////////////////
//
// printInstrument -- print the instrument name for the track
// which is stored in a *I" record before the first data line
// int that track.
//
void printInstrument(HumdrumFile& infile, int track) {
PerlRegularExpression pre;
int i, j;
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].isData()) {
return;
}
if (!infile[i].isInterpretation()) {
continue;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
if (track != infile[i].getPrimaryTrack(j)) {
continue;
}
if (pre.search(infile[i][j], "^\\*I\"(.*)$", "")) {
Array<char> iname;
iname.setSize(strlen(pre.getSubmatch(1)) + 1);
strcpy(iname.getBase(), pre.getSubmatch());
pre.sar(iname, ":", "", "g");
cout << iname;
return;
}
}
}
}
//////////////////////////////
//
// createIndexEnding -- The classical fixed order for the thema command
// is: [Zz] { # : % } j J M
//
// Returns the maximum number of layers occuring at any point in the file.
//
void createIndexEnding(HumdrumFile& infile, int track, int layer) {
Array<int> pitches;
Array<double> durations;
Array<double> metriclevels;
Array<RationalNumber> metricpositions;
extractPitchSequence(pitches, infile, track, layer);
int mode = 0;
int tonic = 2;
getKey(infile, mode, tonic);
if (extraQ) {
cout << '\t'; printKey(mode, tonic);
}
if (pstate[p12toneInterval]) {
cout << '\t'; print12toneInterval(pitches);
}
if (pstate[pRefinedContour]) {
cout << '\t'; printRefinedContour(pitches);
}
if (pstate[pGrossContour]) {
cout << '\t'; printGrossContour(pitches);
}
if (pstate[pScaleDegree]) {
cout << '\t'; printScaleDegree(pitches, tonic);
}
if (pstate[pMusicalInterval]) {
cout << '\t'; printMusicalInterval(pitches);
}
if (pstate[p12tonePitch]) {
cout << '\t'; print12tonePitch(pitches);
}
if (pstate[pPitch]) {
cout << '\t'; printPitch(pitches);
}
if (extraQ) {
cout << '\t'; printMeter(infile);
}
if (rhythmQ) {
extractDurationSequence(durations, infile, track, layer);
extractMetricSequence(metriclevels, metricpositions, infile, track, layer);
}
if (pstate[pDurationGrossContour]) {
cout << '\t'; printGrossContourRhythm(durations);
}
if (pstate[pDurationRefinedContour]) {
cout << '\t'; printRefinedContourRhythm(durations);
}
if (pstate[pDuration]) {
cout << '\t'; printDuration(durations);
}
if (pstate[pBeat]) {
cout << '\t'; printBeatLevel(metriclevels);
}
if (pstate[pMetricLevel]) {
cout << '\t'; printMetricLevel(metriclevels);
}
if (pstate[pMetricRefinedContour]) {
cout << '\t'; printMetricRefinedContour(metriclevels);
}
if (pstate[pMetricGrossContour]) {
cout << '\t'; printMetricGrossContour(metriclevels);
}
if (pstate[pMetricPosition]) {
cout << '\t'; printMetricPosition(metricpositions);
}
if (bibQ) {
processBibRecords(cout, infile, bibfilter);
}
}
//////////////////////////////
//
// processBibRecords -- print bibliographic records sorted into
// alphabetical order
//
void processBibRecords(ostream& out, HumdrumFile &infile,
const char* bibfilter) {
Array<HumdrumRecord*> bibs;
bibs.setSize(infile.getNumLines());
bibs.setSize(0);
int i, j;
PerlRegularExpression pre;
Array<Array<char> > bfilt;
bfilt.setSize(100);
bfilt.setGrowth(10000);
bfilt.setSize(0);
if (strcmp(bibfilter, "") != 0) {
PerlRegularExpression::getTokens(bfilt, "[:,\\s]+", bibfilter);
}
int valid;
char buffer[1024] = {0};
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].isBibliographic()) {
if (bfilt.getSize() > 0) {
valid = 0;
infile[i].getBibKey(buffer, 1000);
for (j=0; j<bfilt.getSize(); j++) {
if (pre.search(buffer, bfilt[j].getBase(), "")) {
valid = 1;
break;
}
}
if (valid == 0) {
continue;
}
}
bibs.increase(1);
bibs.last() = &(infile[i]);
}
}
qsort(bibs.getBase(), bibs.getSize(), sizeof(void*), bibsort);
Array<char> record;
for (i=0; i<bibs.getSize(); i++) {
record.setSize(strlen((*(bibs[i]))[0]) + 1);
strcpy(record.getBase(), (*(bibs[i]))[0]);
pre.sar(record, "\\t", " ", "g");
pre.sar(record, "\\s\\s+", " ", "g");
out << '\t' << record.getBase();
}
}
//////////////////////////////
//
// bibsort -- for sorting the tracks
//
int bibsort(const void* a, const void* b) {
HumdrumRecord& abib = **((HumdrumRecord**)a);
HumdrumRecord& bbib = **((HumdrumRecord**)b);
return strcmp(abib[0], bbib[0]);
}
//////////////////////////////
//
// printGrossContourRhythm --
//
void printGrossContourRhythm(Array& durations) {
int i;
cout << R_DURATION_GROSS_CONTOUR_MARKER;
for (i=1; i<durations.getSize(); i++) {
if (durations[i-1] < 0.0) {
cout << "R";
continue;
}
if (durations[i] < 0.0) {
// ignore rest, will be printed in next loop.
continue;
}
if (durations[i] - durations[i-1] > 0) {
cout << '>';
} else if (durations[i] - durations[i-1] < 0) {
cout << '<';
} else {
// what is this line?
cout << R_METRIC_POSITION_MARKER;
}
}
}
//////////////////////////////
//
// printRefinedContourRhythm --
//
void printRefinedContourRhythm(Array& durations) {
int i;
cout << R_DURATION_REFINED_CONTOUR_MARKER;
double value;
for (i=1; i<durations.getSize(); i++) {
if (durations[i-1] < 0.0) {
cout << "R";
continue;
}
if (durations[i] < 0.0) {
// ignore rest, will be printed in next loop.
continue;
}
if (durations[i-1] == 0.0) {
if (durations[i] == 0.0) {
cout << "=";
} else {
cout << "]";
}
} else {
value = durations[i]/durations[i-1];
if (value > 2.0) { cout << "]"; }
else if (value > 1.0) { cout << ">"; }
else if (value == 1.0) { cout << "="; }
else if (value >= 0.5) { cout << "<"; }
else if (value < 0.5) { cout << "["; }
else { cout << "X"; }
}
}
}
/* Old definition(a ratio between adjacent notes)
void printRefinedContourRhythm(Array& durations) {
int i;
cout << "^";
double value;
int ivalue;
for (i=1; i<durations.getSize(); i++) {
if (durations[i-1] != 0.0) {
value = durations[i]/durations[i-1];
ivalue = (int)(value * 1000.0 + 0.5);
} else {
ivalue = 100000;
}
cout << ivalue << " ";
}
}
*/
//////////////////////////////
//
// printDuration -- [2011/04/02] change code for Longa from 00 to L.
// and Breve from 0 to B so that grace notes do not interact with
// breve duration.
//
void printDuration(Array& durations) {
int i;
int j;
int k;
int len;
char buffer[128] = {0};
cout << R_DURATION_MARKER;
SSTREAM temps;
int count;
for (i=0; i<durations.getSize(); i++) {
if (durations[i] < 0) {
temps << "R ";
continue;
}
if (durations[i] == 16.0) {
strcpy(buffer, "L");
} else if (durations[i] == 8.0) {
strcpy(buffer, "B");
} else if (durations[i] == 12.0) {
strcpy(buffer, "B.");
} else {
Convert::durationToKernRhythm(buffer, durations[i]);
}
if (durations[i] > 0 && (buffer[0] == 'q')) {
count = (int)durations[i];
for (j=0; j<count; j++) {
temps << "4";
}
if (durations[i] - count > 0) {
Convert::durationToKernRhythm(buffer, durations[i]-count);
}
len = strlen(buffer);
for (k=0; k<len; k++) {
if (buffer[k] == '.') {
temps << "d";
} else {
temps << buffer[k];
}
}
temps << " ";
} else {
len = strlen(buffer);
for (k=0; k<len; k++) {
if (buffer[k] == '.') {
temps << "d";
} else {
temps << buffer[k];
}
}
temps << " ";
}
}
temps << ends;
Array<char> temps2;
temps2.setSize(strlen(temps.CSTRING)+1);
strcpy(temps2.getBase(), temps.CSTRING);
PerlRegularExpression pre;
// disallow 3.., and 3... rhythms
pre.sar(temps2, "3\\.\\.+", "X", "g");
// convert unknown rhythms into X:
pre.sar(temps2, "-2147483648", "X", "g");
pre.sar(temps2, "444448", "X", "g"); // wholenote tied to dotted quarter
cout << temps2.getBase() << flush;
}
//////////////////////////////
//
// printMetricLevel --
//
void printMetricLevel(Array& levels) {
int i;
cout << R_METRIC_LEVEL_MARKER;
double value;
int ivalue;
for (i=0; i<levels.getSize(); i++) {
value = levels[i];
if (value < -1000.0) {
// print rest marker.
cout << "R ";
continue;
}
ivalue = (int)value;
if (ivalue > 0) {
cout << "p";
} else if (ivalue < 0) {
cout << "m";
ivalue = -ivalue;
}
cout << ivalue << " ";
}
}
//////////////////////////////
//
// printMetricPosition --
//
void printMetricPosition(Array& positions) {
int i;
cout << R_METRIC_POSITION_MARKER;
RationalNumber value;
for (i=0; i<positions.getSize(); i++) {
if (positions[i] < -1000) {
cout << "R ";
continue;
}
value = positions[i];
cout << "x";
value.printTwoPart(cout, "_");
cout << ' ';
}
}
//////////////////////////////
//
// printMetricRefinedContour --
//
void printMetricRefinedContour(Array& levels) {
int i;
cout << R_METRIC_REFINED_CONTOUR_MARKER;
double value;
int ivalue;
double bvalue;
int bivalue;
int zvalue;
for (i=1; i<levels.getSize(); i++) {
if (levels[i-1] < -1000.0) {
// print a rest marker
cout << "R";
continue;
}
if (levels[i] < -1000.0) {
// ignore, rest printed in next loop
continue;
}
value = levels[i];
ivalue = (int)value;
bvalue = levels[i-1];
bivalue = (int)bvalue;
zvalue = ivalue - bivalue;
if (zvalue > 1) {
cout << "U";
} else if (zvalue == 1) {
cout << "u";
} else if (zvalue == 0) {
cout << "S";
} else if (zvalue == -1) {
cout << "d";
} else if (zvalue < -1) {
cout << "D";
} else {
cout << "x";
}
}
}
//////////////////////////////
//
// printMetricGrossContour --
//
void printMetricGrossContour(Array& levels) {
int i;
cout << R_METRIC_GROSS_CONTOUR_MARKER;
double value;
int ivalue;
double bvalue;
int bivalue;
int zvalue;
for (i=1; i<levels.getSize(); i++) {
if (levels[i-1] < -1000.0) {
// print a rest marker
cout << "R";
continue;
}
if (levels[i] < -1000.0) {
// ignore, rest printed in next loop
continue;
}
value = levels[i];
ivalue = (int)value;
bvalue = levels[i-1];
bivalue = (int)bvalue;
zvalue = ivalue - bivalue;
if (zvalue > 0) {
cout << "U";
} else if (zvalue < 0) {
cout << "D";
} else {
cout << "S";
}
}
}
//////////////////////////////
//
// printBeatLevel --
//
void printBeatLevel(Array& levels) {
int i;
cout << R_BEAT_LEVEL_MARKER;
double value;
int ivalue;
for (i=0; i<levels.getSize(); i++) {
if (levels[i] < -1000.0) {
// print rest
cout << "R";
continue;
}
value = levels[i];
ivalue = (int)value;
if (ivalue >= 0) {
cout << 1;
} else {
cout << 0;
}
}
}
//////////////////////////////
//
// printMusicalInterval --
//
void printMusicalInterval(Array& pitches) {
cout << P_DIATONIC_INTERVAL_MARKER;
int octave;
int interval;
int degree;
int direction;
int i;
for (i=1; i<pitches.getSize(); i++) {
if (pitches[i-1] < 0) {
// print a rest
cout << "R";
continue;
}
if (pitches[i] < 0) {
// skip, will be printed in next loop
continue;
}
interval = pitches[i] - pitches[i-1];
if (interval < 0) {
direction = -1;
interval = -interval;
} else {
direction = +1;
}
octave = interval / 40;
degree = (Convert::base40ToDiatonic(interval+2)%7)+1 + octave * 7;
// need the direction for augmented/diminished unisons...
//if (degree != 1) {
// if (direction < 0) {
// cout << 'x';
// } else {
// cout << 'X';
// }
//}
if (direction < 0) {
cout << 'x';
} else if (interval != 0) {
cout << 'X';
}
int accidental = Convert::base40ToAccidental(interval+2);
switch ((degree-1) % 7) {
case 0: // 1st
switch (direction * abs(accidental)) {
case -2: cout << "dd"; break;
case -1: cout << "d"; break;
case 0: cout << "P"; break;
case +1: cout << "A"; break;
case +2: cout << "AA"; break;
}
break;
case 3: // 4th
case 4: // 5th
switch (accidental) {
case -2: cout << "dd"; break;
case -1: cout << "d"; break;
case 0: cout << "P"; break;
case +1: cout << "A"; break;
case +2: cout << "AA"; break;
}
break;
case 1: // 2nd
case 2: // 3rd
case 5: // 6th
case 6: // 7th
switch (accidental) {
case -3: cout << "dd"; break;
case -2: cout << "d"; break;
case -1: cout << "m"; break;
case 0: cout << "M"; break;
case +1: cout << "A"; break;
case +2: cout << "AA"; break;
}
}
cout << degree;
}
}
//////////////////////////////
//
// printScaleDegree --
//
void printScaleDegree(Array& pitches, int tonic) {
cout << P_SCALE_DEGREE_MARKER;
int i;
for (i=0; i<pitches.getSize(); i++) {
if (pitches[i] < 0) {
cout << "R";
continue;
}
cout << (Convert::base40ToDiatonic(pitches[i]-tonic+2+40)%7)+1;
}
}
//////////////////////////////
//
// printMeter --
//
void printMeter(HumdrumFile& infile) {
int i;
int top;
int bottom;
int count = 0;
cout << "M";
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].isInterpretation()) {
if (infile[i][0][1] != 'M') {
continue;
}
if (!isdigit(infile[i][0][2])) {
if (strcmp("*MX", infile[i][0]) == 0) {
cout << "irregular";
return;
}
continue;
}
count = sscanf(infile[i][0], "*M%d/%d", &top, &bottom);
if (count != 2) {
continue;
}
cout << &(infile[i][0][2]);
switch (top) {
case 4:
case 12:
cout << "quadruple";
break;
case 3:
case 9:
cout << "triple";
break;
case 2:
case 6:
cout << "duple";
break;
default: cout << "irregular";
}
switch (top) {
case 6:
case 9:
case 12:
case 16:
cout << "compound";
break;
case 1:
case 2:
case 3:
case 4:
case 5:
cout << "simple";
break;
}
break;
}
}
}
//////////////////////////////
//
// printKey --
//
void printKey(int mode, int tonic) {
char buffer[128] = {0};
if (tonic < 0) {
// unknown key
cout << "ZX=";
return;
}
if (mode) {
cout << 'z'; // minor
} else {
cout << 'Z'; // major
}
cout << Convert::base40ToKern(buffer, tonic + 3*40);
cout << '=';
}
//////////////////////////////
//
// getKey --
//
void getKey(HumdrumFile& infile, int& mode, int& tonic) {
tonic = 2; // C
mode = 0; // major
int i;
int length;
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].isInterpretation()) {
length = strlen(infile[i][0]);
if (length < 2 || length > 5) {
continue;
}
if (infile[i][0][length-1] != ':') {
continue;
}
if (!isalpha(infile[i][0][1])) {
continue;
}
if (islower(infile[i][0][1])) {
mode = 1; // minor
} else {
mode = 0; // major
}
tonic = Convert::kernToBase40(&(infile[i][0][1]));
if (tonic >= 0) {
tonic = tonic % 40;
}
if (infile[i][0][1] == 'x' || infile[i][0][1] == 'X') {
tonic = -1; // unknown key
}
break;
}
}
}
//////////////////////////////
//
// print12tonePitch --
//
void print12tonePitch(Array& pitches) {
cout << P_12TONE_PITCH_CLASS_MARKER;
int i;
int midi;
for (i=0; i<pitches.getSize(); i++) {
if (pitches[i] < 0) {
cout << "R";
continue;
}
midi = Convert::base40ToMidiNoteNumber(pitches[i]) % 12;
if (midi < 10) {
cout << midi;
} else if (midi == 10) {
cout << 'A';
} else if (midi == 11) {
cout << 'B';
} else {
cout << 'X';
}
}
}
//////////////////////////////
//
// print12toneInterval --
//
void print12toneInterval(Array& pitches) {
Array<int> midi(pitches.getSize());
cout << P_12TONE_INTERVAL_MARKER;
int i;
for (i=0; i<pitches.getSize(); i++) {
if (pitches[i] < 0) {
midi[i] = -1000000;
} else {
midi[i] = Convert::base40ToMidiNoteNumber(pitches[i]);
}
}
for (i=1; i<midi.getSize(); i++) {
if (midi[i-1] < -1000) {
// print a rest marker
cout << "R";
continue;
}
if (midi[i] < -1000) {
// ignore rest, printed on next round
continue;
}
if (midi[i] > midi[i-1]) {
cout << 'p' << midi[i] - midi[i-1];
} else if (midi[i] < midi[i-1]) {
cout << 'm' << midi[i-1] - midi[i];
} else {
cout << "p0";
}
}
}
//////////////////////////////
//
// printRefinedContour -- augmented second is assigned to be a step
//
void printRefinedContour(Array& pitches) {
int i;
cout << P_PITCH_REFINED_CONTOUR_MARKER;
for (i=1; i<pitches.getSize(); i++) {
if (pitches[i-1] < 0) {
// process a rest
cout << "R";
continue;
}
if (pitches[i] < 0) {
continue;
}
if (pitches[i] < pitches[i-1]) {
if (pitches[i-1] - pitches[i] < 9) {
cout << 'd';
} else {
cout << 'D';
}
} else if (pitches[i] > pitches[i-1]) {
if (pitches[i] - pitches[i-1] < 9) {
cout << 'u';
} else {
cout << 'U';
}
} else {
cout << 's';
}
}
}
//////////////////////////////
//
// printGrossContour --
//
void printGrossContour(Array& pitches) {
int i;
cout << P_GROSS_CONTOUR_MARKER;
for (i=1; i<pitches.getSize(); i++) {
if (pitches[i-1] < 0) {
// process a rest
cout << "R";
continue;
}
if (pitches[i] < 0) {
continue;
}
if (pitches[i] < pitches[i-1]) {
cout << 'D';
} else if (pitches[i] > pitches[i-1]) {
cout << 'U';
} else {
cout << 'S';
}
}
}
//////////////////////////////
//
// printPitch --
//
void printPitch(Array& pitches) {
int i;
int j;
char buffer[128] = {0};
cout << P_PITCH_CLASS_MARKER;
for (i=0; i<pitches.getSize(); i++) {
if (pitches[i] < 0) {
// process a rest marker
cout << "R ";
continue;
}
Convert::base40ToKern(buffer, (pitches[i] % 40) + 3 * 40);
j = 0;
while (buffer[j] != '\0') {
if (buffer[j] == '-') {
cout << 'b';
} else {
cout << buffer[j];
}
j++;
}
cout << " ";
// when not printing a terminal " ":
//if (i < pitches.getSize()-1) {
// cout << " ";
//}
}
}
//////////////////////////////
//
// extractMetricSequence --
//
void extractMetricSequence(Array<double>& metriclevels,
Array<RationalNumber>& metricpositions, HumdrumFile& infile,
int track, int layer) {
PerlRegularExpression pre;
Array<int> metlev;
infile.analyzeMetricLevel(metlev);
infile.analyzeRhythm(); // should already be done
int i, j;
metriclevels.setSize(metlev.getSize());
metriclevels.setSize(1000);
metriclevels.setSize(0);
metriclevels.allowGrowth();
metricpositions.setSize(1000);
metricpositions.setSize(0);
metricpositions.allowGrowth();
RationalNumber aposition;
RationalNumber bignegative(-1000000,1);
int lastlayercount = 0;
int layercount = 0;
int pitch = 0;
double level;
for (i=0; i<infile.getNumLines(); i++) {
switch (infile[i].getType()) {
case E_humrec_data:
lastlayercount = layercount;
layercount = 0;
for (j=0; j<infile[i].getFieldCount(); j++) {
if (infile[i].getPrimaryTrack(j) != track) {
continue;
}
layercount++;
if (layer != layercount) {
continue;
}
if (strcmp(infile[i][j], ".") == 0) {
// ignore null tokens
break;
}
if ((lastlayercount != 0) && (lastlayercount < layer)) {
// insert segmentation marker into the data
// since this desired layer does not continue
// directly from the last occurance of the layer
if (metricpositions.last() != bignegative) {
// only append segmentation marker if a
// segmentation marker is not present in the
// pitch sequence already.
level = -1000000.0;
metriclevels.append(level);
metricpositions.append(bignegative);
}
}
// all notes in a chord should have the same duration
// so not bothering with adjusting for the --end option.
if ((!graceQ) && pre.search(infile[i][j], "q", "i")) {
// don't count grace notes if not wanted
continue;
}
if (strchr(infile[i][j], '_') != NULL) {
// ignore continuing ties
break;
}
if (strchr(infile[i][j], ']') != NULL) {
// ignore ending ties
break;
}
if (strchr(infile[i][j], 'r') != NULL) {
if (pitch < 0) {
// don't repeat segmentation markers
break;
}
if (phraseQ && pre.search(infile[i][j], "}", "")) {
pitch = -1;
level = -1000000.0;
metriclevels.append(level);
metricpositions.append(bignegative);
break;
}
if (!restQ) {
// ignore rests
break;
}
pitch = -1;
level = -1000000.0;
metriclevels.append(level);
metricpositions.append(bignegative);
break;
}
pitch = 0; // used to keep track of rests
level = -(double)metlev[i];
aposition = infile[i].getBeatR();
metriclevels.append(level);
metricpositions.append(aposition);
if (fermataQ && pre.search(infile[i][j], "^[^\\s]*;", "")) {
pitch = -1;
level = -1000000.0;
metriclevels.append(level);
metricpositions.append(bignegative);
} else if (phraseQ && pre.search(infile[i][j], "}", "")) {
pitch = -1;
level = -1000000.0;
metriclevels.append(level);
metricpositions.append(bignegative);
}
break;
}
break;
}
}
}
//////////////////////////////
//
// extractDurationSequence -- Disallow 3... rhythms from being stored
// (equivalent to dotted half tied to eighth note?)
// Translate special "l" markers for longs into Long durations.
//
void extractDurationSequence(Array<double>& durations, HumdrumFile& infile,
int track, int layer) {
PerlRegularExpression pre;
durations.setSize(10000);
durations.setSize(0);
durations.allowGrowth();
char longchar = identifyLongMarker(infile);
double dur = 0;
int i, j;
int pitch = 0;
int lastlayercount = 0;
int layercount = 0;
for(i=0; i<infile.getNumLines(); i++) {
switch (infile[i].getType()) {
case E_humrec_none:
case E_humrec_empty:
case E_humrec_bibliography:
case E_humrec_global_comment:
case E_humrec_data_comment:
case E_humrec_interpretation:
case E_humrec_data_kern_measure:
break;
case E_humrec_data:
lastlayercount = layercount;
layercount = 0;
for (j=0; j<infile[i].getFieldCount(); j++) {
if (infile[i].getPrimaryTrack(j) != track) {
continue;
}
layercount++;
if (layer != layercount) {
continue;
}
if (strcmp(infile[i][j], ".") == 0) {
// ignore null tokens
break;
}
if ((lastlayercount != 0) && (lastlayercount < layer)) {
// insert segmentation marker into the data
// since this desired layer does not continue
// directly from the last occurance of the layer
if (durations.last() != -1.0) {
// only append segmentation marker if a
// segmentation marker is not present in the
// pitch sequence already.
dur = -1.0;
durations.append(dur);
}
}
// all notes in a chord should have the same duration
// so not bothering with adjusting for the --end option.
if ((!graceQ) && pre.search(infile[i][j], "q", "i")) {
// don't count grace notes if not wanted
break;
}
if (strchr(infile[i][j], '_') != NULL) {
// ignore continuing ties
break;
}
if (strchr(infile[i][j], ']') != NULL) {
// ignore ending ties
break;
}
if (strchr(infile[i][j], 'r') != NULL) {
if (pitch < 0) {
// ignore repeated rests
break;
}
if (phraseQ && pre.search(infile[i][j], "}", "")) {
pitch = -1;
dur = -1.0;
durations.append(dur);
break;
}
if (!restQ) {
// ignore rests
break;
}
pitch = -1;
dur = RESTDUR;
durations.append(dur);
break;
}
pitch = Convert::kernToBase40(infile[i][j]);
if ((pitch < 0) || (pitch > 10000)) {
// ignore rests and other strange things
break;
}
dur = infile.getTiedDuration(i, j);
if (longchar && (strchr(infile[i][j], longchar) != NULL)) {
dur = 16.0;
}
if ((!graceQ) && (dur <= 0.0)) {
// for some reason grace note was not filtered before,
// so filter it now.
break;
}
durations.append(dur);
if (limitQ) {
if (durations.getSize() >= limit) {
return;
}
}
if (fermataQ && pre.search(infile[i][j], "^[^\\s]*;", "")) {
pitch = -1;
dur = -1.0;
durations.append(dur);
} else if (phraseQ && pre.search(infile[i][j], "}", "")) {
pitch = -1;
dur = -1.0;
durations.append(dur);
}
break;
}
break;
default:
break;
}
}
}
//////////////////////////////
//
// extractPitchSequence --
// restrictions:
// (1) **kern data expected is track being searched
// (2) chords will be ignored, only first note in chord will be processed.
//
void extractPitchSequence(Array<int>& pitches, HumdrumFile& infile,
int track, int layer) {
pitches.setSize(10000);
pitches.setGrowth(10000);
pitches.setSize(0);
pitches.allowGrowth();
int pitch = 0;
int i, j;
PerlRegularExpression pre;
char notebuf[1024] = {0};
int subtokens = 0;
int lastlayercount = 0;
int layercount = 0;
for (i=0; i<infile.getNumLines(); i++) {
switch (infile[i].getType()) {
case E_humrec_none:
case E_humrec_empty:
case E_humrec_bibliography:
case E_humrec_global_comment:
case E_humrec_data_comment:
case E_humrec_interpretation:
case E_humrec_data_kern_measure:
break;
case E_humrec_data:
lastlayercount = layercount;
layercount = 0;
for(j=0; j<infile[i].getFieldCount(); j++) {
if (infile[i].getPrimaryTrack(j) != track) {
continue;
}
layercount++;
if(layer != layercount) {
continue;
}
if (strcmp(infile[i][j], ".") == 0) {
// ignore null tokens
break;
}
if ((lastlayercount != 0) && (lastlayercount < layer)) {
// insert segmentation marker into the data
// since this desired layer does not continue
// directly from the last occurance of the layer
if (pitches.getSize() > 0 && pitches.last() >= 0) {
// only append segmentation marker if a
// segmentation marker is not present in the
// pitch sequence already.
pitch = -1;
pitches.append(pitch);
}
}
subtokens = infile[i].getTokenCount(j);
if(subtokens == 1) {
strcpy(notebuf, infile[i][j]);
} else if (endQ) {
infile[i].getToken(notebuf, j, subtokens-1, 1000);
} else {
infile[i].getToken(notebuf, j, 0, 1000);
}
if ((!graceQ) && pre.search(notebuf, "q", "i")) {
// don't count grace notes if not wanted
continue;
}
if (strchr(notebuf, '_') != NULL) {
// ignore continuing ties
break;
}
if (strchr(notebuf, ']') != NULL) {
// ignore ending ties
break;
}
if (strchr(notebuf, 'r') != NULL) {
if (pitches.getSize() > 0 && pitches.last() < 0) {
// already stored one rest, so ignore this one
break;
}
if (phraseQ && pre.search(notebuf, "}", "")) {
pitch = -1;
pitches.append(pitch);
break;
}
if(!restQ) {
// ignore rests
break;
}
pitch = -1;
pitches.append(pitch);
break;
}
pitch = Convert::kernToBase40(notebuf);
if((pitch < 0) || (pitch > 10000)) {
// ignore rests and other strange things
break;
}
pitches.append(pitch);
if(limitQ) {
if (pitches.getSize() >= limit) {
return;
}
}
if(fermataQ && pre.search(notebuf, "^[^\\s]*;", "")) {
pitch = -1;
pitches.append(pitch);
} else if (phraseQ && pre.search(infile[i][j], "}", "")) {
// observe that phrase marks only occur once
// in a multi-stop token, so have to search
// the entire multi-stop token for a phrase ending mark
// which is usually at the end of the token.
pitch = -1;
pitches.append(pitch);
break;
}
break;
}
break;
default:
break;
}
}
}
//////////////////////////////
//
// checkOptions --
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("debug=b", "print debug information");
opts.define("poly|moly=b", "create polyphonic");
opts.define("mono=b", "extract only the first column of data");
opts.define("poly2=b", "create polyphonic, all layers");
opts.define("end=b", "use last note in chord tokens");
opts.define("rest|rests=b", "encode rest boundaries");
opts.define("i|instrument=b", "encode instrument names in tag");
opts.define("b|bib|ref=b", "store bibliographic records");
opts.define("B|bibfilter=s", "bibliographic record filter string");
opts.define("D|no-dir=b", "don't include directory in filename tag");
opts.define("d|dir-prefix=s", "append directory to filename tag");
opts.define("E|no-extra=b", "do not print extra information");
opts.define("G|no-grace=b", "do not print extra information");
opts.define("r|rhythm=b", "extract rhythm information");
//opts.define("p|pitch=b", "extract pitch information");
opts.define("pitch-only=b", "extract pitch information");
opts.define("P|not-pitch=b", "do not extract pitch information");
opts.define("a|all=b", "extract all possible musical features");
opts.define("H|humdrum=b", "format output as a humdrum file");
opts.define("q|Q|quiet=b", "don't suppress ^# messages");
opts.define("fermata=b", "add R markers for fermatas in input");
opts.define("phrase=b", "add R markers for phrase endings in input");
opts.define("verbose=b", "Display all control settings");
opts.define("f|features=s", "extract the list of features");
// parameter matching to themax:
opts.define("u|duration=b", "duration (IOI)");
opts.define("I|MI|mi|DI|di|INT|int|pitch-musical-interval|interval=b",
"musical interval");
opts.define("p|PCH|pch|PC|pc|pitch-class|pitch=b", "pitch class");
opts.define("file=s", "filename to use for standard input data");
opts.define("t|istn|translate=s", "translation file which contains istn values");
opts.define("l|limit=i:20", "limit the number of extracted features");
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, Jan 2004" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 26 Jan 2004" << 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);
}
// poly2 is now the default behavior, use --moly or --mono to
// change
// poly2Q = opts.getBoolean("poly2");
poly2Q = 1;
monoQ = opts.getBoolean("mono");
polyQ = opts.getBoolean("poly");
if (monoQ || polyQ) {
poly2Q = 0;
}
instrumentQ = opts.getBoolean("instrument");
debugQ = opts.getBoolean("debug");
dirQ =!opts.getBoolean("no-dir");
endQ = opts.getBoolean("end");
restQ = opts.getBoolean("rest");
fermataQ = opts.getBoolean("fermata");
phraseQ = opts.getBoolean("phrase");
graceQ =!opts.getBoolean("no-grace");
quietQ = opts.getBoolean("quiet");
rhythmQ = opts.getBoolean("rhythm");
fileQ = opts.getBoolean("file");
if (fileQ) {
Filename = opts.getString("file");
}
pitchQ = 1;
rhythmQ = 1;
if (opts.getBoolean("rhythm")) {
pitchQ = 0; // if -r is given, then turn off -p
}
if (opts.getBoolean("pitch")) {
rhythmQ = 0; // if -p is given, then turn off -r
}
extraQ = !opts.getBoolean("no-extra");
limitQ = opts.getBoolean("limit");
limit = opts.getInteger("limit");
istnQ = opts.getBoolean("istn");
bibQ = opts.getBoolean("bib");
istnfile = opts.getString("istn");
dirprefixQ = opts.getBoolean("dir-prefix");
verboseQ = opts.getBoolean("verbose");
if (dirprefixQ) {
dirprefix.setSize(strlen(opts.getString("dir-prefix")) + 1);
strcpy(dirprefix.getBase(), opts.getString("dir-prefix"));
if (dirprefix[dirprefix.getSize()-2] != '/') {
dirprefix.increase(1);
strcat(dirprefix.getBase(), "/");
}
} else {
dirprefix.setSize(1);
dirprefix[0] = '\0';
}
if (opts.getBoolean("bibfilter")) {
bibfilter = opts.getString("bibfilter");
}
if (istnQ) {
fillIstnDatabase(istndatabase, istnfile);
}
if (opts.getBoolean("all")) {
rhythmQ = 1;
pitchQ = 1;
}
if (options.getBoolean("not-pitch")) {
pitchQ = 0;
}
if (!opts.getBoolean("humdrum")) {
pstate[pHumdrumFormat] = 1;
}
if (opts.getBoolean("features")) {
pitchQ = 0; // no need to turn off, but just in case
rhythmQ = 0; // no need to turn off, but just in case
extractFeatureSet(opts.getString("features"));
} else if (opts.getBoolean("duration")) {
pitchQ = 0; // no need to turn off, but just in case
rhythmQ = 0; // no need to turn off, but just in case
// do stuff later
} else if (opts.getBoolean("interval")) {
pitchQ = 0; // no need to turn off, but just in case
rhythmQ = 0; // no need to turn off, but just in case
// do stuff later
} else if (opts.getBoolean("pitch-class")) {
pitchQ = 0; // no need to turn off, but just in case
rhythmQ = 0; // no need to turn off, but just in case
// do stuff later
} else {
// set up the printing options.
pstate[pGrossContour] = pitchQ;
pstate[pRefinedContour] = pitchQ;
pstate[p12toneInterval] = pitchQ;
pstate[p12tonePitch] = pitchQ;
pstate[pScaleDegree] = pitchQ;
pstate[pMusicalInterval] = pitchQ;
pstate[pPitch] = pitchQ;
pstate[pDurationGrossContour] = rhythmQ;
pstate[pDurationRefinedContour] = rhythmQ;
pstate[pDuration] = rhythmQ;
pstate[pBeat] = rhythmQ;
pstate[pMetricLevel] = rhythmQ;
pstate[pMetricRefinedContour] = rhythmQ;
pstate[pMetricGrossContour] = rhythmQ;
pstate[pMetricPosition] = rhythmQ;
}
if (opts.getBoolean("duration")) {
pstate[pDuration] = 1;
rhythmQ = 1;
}
if (opts.getBoolean("interval")) {
pstate[pMusicalInterval] = 1;
pitchQ = 1;
}
if (opts.getBoolean("pitch-class")) {
pstate[pPitch] = 1;
pitchQ = 1;
}
}
//////////////////////////////
//
// extractFeatureSet --
//
// Pitch Abbreviations
// PCH, P, PC, DPC = Pitch
// GC, PGC, CON = GrossContour
// RC, PRC = RefinedContour
// 12I = 12toneInterval
// 12P = 12tonePitch
// SD, S, D = ScaleDegree
// MI, I = MusicalInterval
// Rhythm Abbreviations:
// RGC, DGC = RhythmGrossContour
// RRC, DRC = RhythmRefinedContour
// DUR, IOI = Duration
// BLV = BeatLevel
// MLV = Metric Level
// MLI = Metric Interval
//
void extractFeatureSet(const char* features) {
char *buffer;
int length = strlen(features);
buffer = new char[length+1];
int i;
for (i=0; i<length; i++) {
buffer[i] = toupper(features[i]);
}
buffer[length] = '\0';
char* ptr = strtok(buffer, " :;-\n\t");
while (ptr != NULL) {
if (strcmp(ptr, "GC") == 0) { pstate[pGrossContour] = 1; }
else if (strcmp(ptr, "PGC") == 0) { pstate[pGrossContour] = 1; }
else if (strcmp(ptr, "GC" ) == 0) { pstate[pGrossContour] = 1; }
else if (strcmp(ptr, "CON") == 0) { pstate[pGrossContour] = 1; }
else if (strcmp(ptr, "RC" ) == 0) { pstate[pRefinedContour] = 1; }
else if (strcmp(ptr, "PRC") == 0) { pstate[pRefinedContour] = 1; }
else if (strcmp(ptr, "12I") == 0) { pstate[p12toneInterval] = 1; }
else if (strcmp(ptr, "12P") == 0) { pstate[p12tonePitch] = 1; }
else if (strcmp(ptr, "SD" ) == 0) { pstate[pScaleDegree] = 1; }
else if (strcmp(ptr, "S" ) == 0) { pstate[pScaleDegree] = 1; }
else if (strcmp(ptr, "D" ) == 0) { pstate[pScaleDegree] = 1; }
else if (strcmp(ptr, "MI" ) == 0) { pstate[pMusicalInterval] = 1; }
else if (strcmp(ptr, "DI" ) == 0) { pstate[pMusicalInterval] = 1; }
else if (strcmp(ptr, "INT") == 0) { pstate[pMusicalInterval] = 1; }
else if (strcmp(ptr, "I" ) == 0) { pstate[pMusicalInterval] = 1; }
else if (strcmp(ptr, "RGC") == 0) { rhythmQ = 1; pstate[pDurationGrossContour] = 1; }
else if (strcmp(ptr, "RRC") == 0) { rhythmQ = 1; pstate[pDurationRefinedContour] = 1; }
else if (strcmp(ptr, "IOI") == 0) { rhythmQ = 1; pstate[pDuration] = 1; }
else if (strcmp(ptr, "DUR") == 0) { rhythmQ = 1; pstate[pDuration] = 1; }
else if (strcmp(ptr, "BLV") == 0) { rhythmQ = 1; pstate[pBeat] = 1; }
else if (strcmp(ptr, "MLV") == 0) { rhythmQ = 1; pstate[pMetricLevel] = 1; }
else if (strcmp(ptr, "MRC") == 0) { rhythmQ = 1; pstate[pMetricRefinedContour] = 1; }
else if (strcmp(ptr, "MGC") == 0) { rhythmQ = 1; pstate[pMetricGrossContour] = 1; }
else if (strcmp(ptr, "MPS") == 0) { rhythmQ = 1; pstate[pMetricPosition] = 1; }
else if (strcmp(ptr, "PCH") == 0) { pstate[pPitch] = 1; }
else if (strcmp(ptr, "PC" ) == 0) { pstate[pPitch] = 1; }
else if (strcmp(ptr, "DPC") == 0) { pstate[pPitch] = 1; }
else if (strcmp(ptr, "P" ) == 0) { pstate[pPitch] = 1; }
ptr = strtok(NULL, " :;-\n\t");
}
}
//////////////////////////////
//
// fillIstnDatabase --
//
void fillIstnDatabase(Array<ISTN>& istndatabase, const char* istnfile) {
HumdrumFile infile;
infile.read(istnfile);
int i, j;
ISTN entry;
istndatabase.setSize(infile.getNumLines());
istndatabase.setSize(0);
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].getType() != E_humrec_data) {
continue;
}
entry.clear();
for (j=0; j<infile[i].getFieldCount(); j++) {
if (strcmp(infile[i].getExInterp(j), "**theme") == 0) {
entry.setFilename(infile[i][j]);
} else if (strcmp(infile[i].getExInterp(j), "**istn") == 0) {
entry.setIstn(infile[i][j]);
}
}
if (entry.is_valid()) {
istndatabase.append(entry);
}
}
istndatabase.allowGrowth(0);
}
//////////////////////////////
//
// identifyLongMarker --
//
//
char identifyLongMarker(HumdrumFile& infile) {
int i;
PerlRegularExpression pre;
// int hasLongQ = 0;
int longchar = 0;
for (i=infile.getNumLines()-1; i>=0; i--) {
if (!infile[i].isBibliographic()) {
continue;
}
if (pre.search(infile[i][0],
"^!!!RDF\\*\\*kern\\s*:\\s*([^\\s=])\\s*=.*long", "i")) {
// hasLongQ = 1;
longchar = pre.getSubmatch(1)[0];
break;
}
}
return longchar;
}
//////////////////////////////
//
// example --
//
void example(void) {
}
//////////////////////////////
//
// usage --
//
void usage(const char* command) {
}
// md5sum: 056cbdaef4124ba46bbe6af758631d8c tindex.cpp [20130420]