//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon Oct 27 10:42:08 PST 2008
// Last Modified: Fri Jun 26 11:23:35 PDT 2009 converted from original version
// Last Modified: Thu Jul 2 15:59:04 PDT 2009 intial PCRE implementation
// Last Modified: Wed Aug 25 15:55:23 PDT 2010 made -t input case insensitive
// Last Modified: Thu Aug 26 14:32:01 PDT 2010 add cleaning for pitch queries
// Last Modified: Wed Sep 1 15:43:34 PDT 2010 added metric position
// Last Modified: Thu Sep 2 17:40:01 PDT 2010 added feature linking
// Last Modified: Fri Sep 3 13:31:22 PDT 2010 added --count feature
// Last Modified: Wed Sep 8 19:24:08 PDT 2010 added --limit
// Last Modified: Mon Nov 22 09:24:00 PST 2010 added -B
// Last Modified: Wed Nov 24 17:59:20 PST 2010 fixed -p/--location interaction
// Last Modified: Sun Nov 28 13:03:28 PST 2010 added -f option
// Last Modified: Tue Jan 11 19:32:58 PST 2011 added --location2 option
// Last Modified: Mon Jan 17 04:12:01 PST 2011 switched --loc and --loc2
// Last Midified: Tue Jan 18 08:16:26 PST 2011 added --overlap option
// Last Midified: Sat Apr 2 18:01:12 PDT 2011 added L=long B=breve durations
// Last Midified: Mon Nov 7 10:40:00 PST 2011 added + == # for pitch search
// Last Midified: Mon Nov 12 17:09:30 PST 2012 added note offsets
// Filename: ...museinfo/examples/all/themax.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/themax.cpp
// Syntax: C++; museinfo
//
// Description: searches an index created by tindex. Themax
// is a C++ implementation of the original thema command
// which was written by David Huron in 1996/1998, and
// modified with a few bug fixes during the implementation
// of Themefinder.org by Craig Sapp (1999-2001).
//
// Classical themebuilder (AWK version of tindex) entry order:
// fileid [Zz] { # : % } j J M
// Additional rhythmic marks:
// ~ ^ ! & @ ` '
//
// 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
//
// Options:
// -a anchor to start (implemented)
// -M major keys (implemented)
// -m minor keys (implemented)
// -t tonic (implemented)
// -T meter (implemented)
//
// PITCH FEATURES:
//
// -i { 12-tone interval (implemented)
// -c # refined contour (implemented)
// -C : gross contour (implemented)
// -d % scale degree (implemented)
// -I } musical interval (implemented)
// -P j 12-tone pitch class (implemented)
// -p J pitch-class name (implemented)
// -D diatonic pitch-class name(implemented)
//
// RHYTHM FEATURES:
//
// -u ; duration (IOI) (implemented)
// -R ~ duration gross contour (implemented)
// -r ^ duration refined contour (implemented)
// -b & beat level (implemented)
// -l = metric position (implemented)
// -E @ metric gross contour (implemented)
// -e ` metric refined contour (implemented)
// -L ' metric level (implemented)
//
// Todo: Add --repeat option which allows for any repeated notes
// between pitch features.
//
#include "humdrum.h"
#include "PerlRegularExpression.h"
#ifndef OLDCPP
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <sstream>
#define SSTREAM stringstream
#define CSTRING str().c_str()
using namespace std;
#else
#include <cstdlib.h>
#include <fstream.h>
#include <iostream.h>
#ifdef VISUAL
#include <strstrea.h> /* for windows 95 */
#else
#include <strstream.h>
#endif
#define SSTREAM strstream
#define CSTRING str()
#endif
// character markers in index file (as used in tindex)
#define P_PITCH_CLASS_MARKER 'J'
#define P_DIATONIC_INTERVAL_MARKER '}'
#define P_SCALE_DEGREE_MARKER '%'
#define P_12TONE_INTERVAL_MARKER '{'
#define P_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 '`'
// function declarations:
void checkOptions(Options& opts, int argc, char** argv);
void example(void);
void usage(const char* command);
void appendString(Array<char>& ss, const char* string);
void appendToSearchString(Array<char>& ss, const char* string,
char marker, int anchor);
void showCleanedParameters(void);
int searchForMatches(const char* filename, Array<char>& ss,
PerlRegularExpression& re, int mcount);
int searchForMatches(istream& inputfile, Array<char>& ss,
PerlRegularExpression& re, int mcount);
void prepareInterval(Array<char>& data);
int checkLink(string& line, int offset);
void getSimpleLocationINT(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor,
Array<int>& checklocs, int featurewidth = 1);
void getSimpleLocationFET(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor,
Array<int>& checklocs, int featurewidth = 1);
void getSeparatorLocationINT(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor,
Array<int>& checklocs, char separator = ' ');
void getSeparatorLocationFET(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor,
Array<int>& checklocs, char separator = ' ');
void getMusicalIntervalLocation(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor,
Array<int>& checklocs);
// void findIntersection (Array<int>& aa, Array<int>& bb);
void processKernString(const char* string);
void removeBoundaryCharacters(string& line);
void getTargetEnds(Array<int>& targetend, Array<int>& target,
string& line);
void adjustForInterleavedQuery(Options& opts);
void removeOverlappedMatches(Array<int>& target, Array<int>& targetend);
// location2Q functions
void getSeparatorLocationFETEnd(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor, Array<int>& startlocs,
char separator);
int countElementsA(const char* str, int ending, char separator);
void getSimpleLocationINTEnd(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor, Array<int>& checklocs,
int featurewidth = 1);
int countElementsB(const char* str, int ending, int width);
void getMusicalIntervalLocationEnd(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor, Array<int>& checklocs);
int countElementsC(const char* str, int ending);
// user input sanitation functions:
void cleanPpitchClass(Array<char>& data);
void cleanPmusicalInterval(Array<char>& data);
void cleanPscaleDegree(Array<char>& data);
void cleanPgrossContour(Array<char>& data);
void cleanPrefinedContour(Array<char>& data);
void cleanP12tonePitchClass(Array<char>& data);
void cleanP12toneInterval(Array<char>& data);
void cleanUpRangeSyntaxNoOutsideDigitsOrComma(Array<char>& data);
void cleanRbeatLevel(Array<char>& data);
void cleanRduration(Array<char>& data);
void cleanRgrossContour(Array<char>& data);
void cleanRrefinedContour(Array<char>& data);
void cleanRmgc(Array<char>& data);
void cleanRmrc(Array<char>& data);
void cleanRmetricLevel(Array<char>& data);
void cleanRmetricPosition(Array<char>& data);
// User interface variables:
Options options;
int showcleanQ = 0; // used with --cleaned
int verboseQ = 0; // used with --verbose option
int verbose2Q = 0; // used with --verbose2 option
int cleanQ = 1; // used with --no-clean option
int anchoredQ = 0; // used with -a option
int keyfilterQ = 0; // used with -m -M and -t options
int minorQ = 0; // used with -m option
int majorQ = 0; // used with -M option
int debugQ = 0; // used with --debug option
int regexQ = 0; // used with --regex option
int quietQ = 0; // used with -q option
int boundaryQ = 1; // used with -B option
int shortQ = 0; // used with --short option
int tonicQ = 0; // used with -t option
int diatonicQ = 0; // used with -D option
// const char* tonicstring = ""; // used with -t option
Array<char> tonicstring; // used with -t option
int meterQ = 0; // used with -T option
const char* meterstring = ""; // used with -T option
Array<char> meterss;
int totalQ = 0; // used with --total option
int countQ = 0; // used with --count option
int locationQ = 0; // used with --locstart option
int location2Q = 0; // used with --loc option
int printendQ = 0; // used with --overlap and --loc options
int overlapQ = 0; // used with --overlap
int notQ = 0; // used with --not option
int unlinkQ = 0; // used with --unlink option
int featureCount = 0; // used with --unlink option
int smartQ = 0; // used with --smart option
int kernQ = 0; // used with -k option
const char* kernstring = ""; // used with -k option
int limitQ = 0; // used with --limit option
int limitval = 0; // used with --limit option
Array<char> filetag; // used with -f option
int TOTALCOUNT = 0; // used for --total option, hack for some problem where count is
// returning file count instead of match count.
Array<char> tonicss; // tonic search string
// classical searches
int P12toneintervalQ = 0; // used with -i option
int PgrosscontourQ = 0; // used with -c option
int PrefinedcontourQ = 0; // used with -C option
int PscaledegreeQ = 0; // used with -d option
int PmusicalintervalQ = 0; // used with -I option
int P12tonepitchclassQ = 0; // used with -P option
int PpitchclassQ = 0; // used with -p option
Array<char> P12toneinterval; // used with -i option
Array<char> Pgrosscontour; // used with -c option
Array<char> Prefinedcontour; // used with -C option
Array<char> Pscaledegree; // used with -d option
Array<char> Pmusicalinterval; // used with -I option
Array<char> P12tonepitchclass; // used with -P option
Array<char> Ppitchclass; // used with -p option
// extended rhythm searches
int RgrosscontourQ = 0; // used with -R option
int RrefinedcontourQ = 0; // used with -r option
int RdurationQ = 0; // used with -u option
int RbeatlevelQ = 0; // used with -b option
int RmetriclevelQ = 0; // used with -L option
int RmetricpositionQ = 0; // used with -l option
int RmetricrefinedcontourQ = 0; // used with -e option
int RmetricgrosscontourQ = 0; // used with -E option
Array<char> Rgrosscontour; // used with -R option
Array<char> Rrefinedcontour; // used with -r option
Array<char> Rduration; // used with -u option
Array<char> Rbeatlevel; // used with -b option
Array<char> Rmetriclevel; // used with -L option
Array<char> Rmetricposition; // used with -l option
Array<char> Rmetricrefinedcontour; // used with -e option
Array<char> Rmetricgrosscontour; // used with -E option
// final search feature strings for use with link checking
Array<char> xP12toneinterval;
Array<char> xPgrosscontour;
Array<char> xPrefinedcontour;
Array<char> xPscaledegree;
Array<char> xPmusicalinterval;
Array<char> xP12tonepitchclass;
Array<char> xPpitchclass;
Array<char> xRgrosscontour;
Array<char> xRrefinedcontour;
Array<char> xRduration;
Array<char> xRbeatlevel;
Array<char> xRmetriclevel;
Array<char> xRmetricposition;
Array<char> xRmetricrefinedcontour;
Array<char> xRmetricgrosscontour;
//////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
tonicstring.setSize(1);
tonicstring[0] = '\0';
checkOptions(options, argc, argv); // process the command-line options
Array<char> searchstring;
Array<char>& ss = searchstring;
ss.setSize(10000);
ss.setGrowth(10000);
ss.setSize(0);
if (filetag.getSize() > 1) {
PerlRegularExpression fre;
// convert dots to literal dots (not regular expression dot)
fre.sar(filetag, "\\.", "\\.", "g");
// convert starts to match anything except tabs or colon
fre.sar(filetag, "\\*", "[^\\t:]*", "g");
appendString(ss, "^[^:\\t]*");
appendString(ss, filetag.getBase());
appendString(ss, "[:]?[^\\t]*\\t");
appendString(ss, ".*");
}
// order of data in index file:
// [Zz] = major/minor key //////////////////////////////////////////////////
tonicss.setSize(0);
if (keyfilterQ) {
tonicss.setSize(1);
tonicss[0] = '\0';
tonicss.setSize(0);
if (majorQ && !minorQ) {
appendString(tonicss, "Z");
} else if (minorQ && !majorQ) {
appendString(tonicss, "z");
} else {
appendString(tonicss, "[Zz]");
}
// place the tonic search next if given
if (tonicQ) {
appendString(tonicss, tonicstring.getBase());
} else {
appendString(tonicss, "[^=]*");
}
appendString(tonicss, "=");
// place the end of key information marker:
appendString(ss, tonicss.getBase());
appendString(ss, ".*");
}
// { = 12-tone interval //////////////////////////////////////////////////
if (P12toneintervalQ) {
if (cleanQ) { cleanP12toneInterval(P12toneinterval); }
appendToSearchString(ss, P12toneinterval.getBase(),
P_12TONE_INTERVAL_MARKER, anchoredQ);
}
// # = pitch refined contour /////////////////////////////////////////////
if (PrefinedcontourQ) {
if (cleanQ) { cleanPrefinedContour(Prefinedcontour); }
appendToSearchString(ss, Prefinedcontour.getBase(),
P_REFINED_CONTOUR_MARKER, anchoredQ);
}
// : = pitch gross contour ///////////////////////////////////////////////
if (PgrosscontourQ) {
if (cleanQ) { cleanPgrossContour(Pgrosscontour); }
appendToSearchString(ss, Pgrosscontour.getBase(),
P_GROSS_CONTOUR_MARKER, anchoredQ);
}
// % = scale degree //////////////////////////////////////////////////////
if (PscaledegreeQ) {
if (cleanQ) { cleanPscaleDegree(Pscaledegree); }
appendToSearchString(ss, Pscaledegree.getBase(),
P_SCALE_DEGREE_MARKER, anchoredQ);
}
// } = musical interval //////////////////////////////////////////////////
if (PmusicalintervalQ) {
if (cleanQ) { cleanPmusicalInterval(Pmusicalinterval); }
appendToSearchString(ss, Pmusicalinterval.getBase(),
P_DIATONIC_INTERVAL_MARKER, anchoredQ);
}
// j = 12-tone pitch class ///////////////////////////////////////////////
if (P12tonepitchclassQ) {
if (cleanQ) { cleanP12tonePitchClass(P12tonepitchclass); }
appendToSearchString(ss, P12tonepitchclass.getBase(),
P_12TONE_PITCH_CLASS_MARKER, anchoredQ);
}
// J = pitch class name //////////////////////////////////////////////////
if (PpitchclassQ) {
if (cleanQ) { cleanPpitchClass(Ppitchclass); }
appendToSearchString(ss, Ppitchclass.getBase(),
P_PITCH_CLASS_MARKER, anchoredQ);
}
// M = metric description ////////////////////////////////////////////////
meterss.setSize(0);
if (meterQ) {
appendString(meterss, "M");
if (!isdigit(meterstring[0])) {
appendString(meterss, "[^\\t]*?");
}
appendString(meterss, meterstring);
appendString(ss, meterss.getBase());
appendString(ss, ".*");
}
// Added rhythmic markers:
// ~ = duration gross contour ///////////////////////////////////////////
if (RgrosscontourQ) {
if (cleanQ) { cleanRgrossContour(Rgrosscontour); }
appendToSearchString(ss, Rgrosscontour.getBase(),
R_DURATION_GROSS_CONTOUR_MARKER, anchoredQ);
}
// ^ = duration refined contour /////////////////////////////////////////
if (RrefinedcontourQ) {
if (cleanQ) { cleanRrefinedContour(Rrefinedcontour); }
appendToSearchString(ss, Rrefinedcontour.getBase(),
R_DURATION_REFINED_CONTOUR_MARKER, anchoredQ);
}
// ! = duration (IOI) ///////////////////////////////////////////////////
if (RdurationQ) {
if (cleanQ) { cleanRduration(Rduration); }
appendToSearchString(ss, Rduration.getBase(),
R_DURATION_MARKER, anchoredQ);
}
// & = beat level ///////////////////////////////////////////////////////
if (RbeatlevelQ) {
if (cleanQ) { cleanRbeatLevel(Rbeatlevel); }
appendToSearchString(ss, Rbeatlevel.getBase(),
R_BEAT_LEVEL_MARKER, anchoredQ);
}
// ' = metric level /////////////////////////////////////////////////////
if (RmetriclevelQ) {
if (cleanQ) { cleanRmetricLevel(Rmetriclevel); }
appendToSearchString(ss, Rmetriclevel.getBase(),
R_METRIC_LEVEL_MARKER, anchoredQ);
}
// ` = metric refined contour ///////////////////////////////////////////
if (RmetricrefinedcontourQ) {
if (cleanQ) { cleanRmrc(Rmetricrefinedcontour); }
appendToSearchString(ss, Rmetricrefinedcontour.getBase(),
R_METRIC_REFINED_CONTOUR_MARKER, anchoredQ);
}
// @ = metric gross contour /////////////////////////////////////////////
if (RmetricgrosscontourQ) {
if (cleanQ) { cleanRmgc(Rmetricgrosscontour); }
appendToSearchString(ss, Rmetricgrosscontour.getBase(),
R_METRIC_GROSS_CONTOUR_MARKER, anchoredQ);
}
// = = metric position ///////////////////////////////////////////////////
if (RmetricpositionQ) {
if (cleanQ) { cleanRmetricPosition(Rmetricposition); }
appendToSearchString(ss, Rmetricposition.getBase(),
R_METRIC_POSITION_MARKER, anchoredQ);
}
//////////////////////////////////////////////////////////////////////////////
if ((!quietQ) && (overlapQ) && (locationQ || location2Q)) {
cout << "#OVERLAP" << endl;
}
// terminate the search string
char ch = '\0';
ss.append(ch);
ss.setSize(ss.getSize()-1);
PerlRegularExpression pre;
if (pre.search(ss, "\\.\\*$", "")) {
ss.setSize(ss.getSize()-2);
}
ss.append(ch);
ss.setSize(ss.getSize()-1);
ss.append(ch);
if (regexQ) {
cout << ss.getBase() << endl;
exit(0);
}
if (showcleanQ) {
showCleanedParameters();
cout << "Final Regular Expression: " << ss.getBase() << endl;
exit(0);
}
pre.initializeSearchAndStudy(ss.getBase());
int i;
int totalcount = 0;
if (options.getArgCount() == 0) {
// standard input
totalcount += searchForMatches(cin, ss, pre, totalcount);
} else {
for (i=1; i<=options.getArgCount(); i++) {
totalcount += searchForMatches(options.getArgument(i), ss, pre,
totalcount);
if (limitQ && (totalcount >= limitval)) {
break;
}
}
}
if (totalQ) {
if (TOTALCOUNT > 0) {
cout << TOTALCOUNT << endl;
} else {
cout << totalcount << endl;
}
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// searchForMatches --
//
int searchForMatches(const char* filename, Array<char>& ss,
PerlRegularExpression& pre, int mcount) {
ifstream inputfile;
inputfile.open(filename);
if (!inputfile.is_open()) {
return 0;
}
int count = searchForMatches(inputfile, ss, pre, mcount);
inputfile.close();
return count;
}
//////////////////////////////
//
// searchForMatches -- Should be merged with above function.
//
int searchForMatches(istream& inputfile, Array<char>& ss,
PerlRegularExpression& pre, int mcount) {
PerlRegularExpression blanktest;
PerlRegularExpression messagetest;
PerlRegularExpression noteoffsettest;
noteoffsettest.initializeSearchAndStudy("[^\\t]+;(\\d+)\\t");
string line;
int offset = 1;
int state;
int i;
int counter = 0;
while (!inputfile.eof()) {
getline(inputfile, line);
if (!boundaryQ) {
removeBoundaryCharacters(line);
}
if (blanktest.search(line.c_str(), "^\\s*$", "")) {
continue;
}
if (messagetest.search(line.c_str(), "^#", "")) {
if (!quietQ) {
// echo control messages in the index file.
cout << line << "\n";
}
continue;
}
state = pre.search(line.c_str());
if (noteoffsettest.search(line.c_str())) {
offset = atoi(noteoffsettest.getSubmatch(1));
} else {
offset = 1;
}
if (state && (!unlinkQ) && (!anchoredQ) && (featureCount > 1)) {
state = checkLink(line, offset);
} else if (state && (countQ || locationQ || location2Q)) {
counter += checkLink(line, offset);
mcount++;
continue;
}
if ((state && !notQ) || (notQ && !state)) {
counter++;
mcount++;
if (verboseQ) {
cout << "Matches in <STDIN>" << endl;
}
if (totalQ) {
continue;
}
if (shortQ && (!countQ && !locationQ && !location2Q)) {
i = 0;
while ((line.c_str()[i] != '\0') && (line.c_str()[i] != '\t')) {
cout << line.c_str()[i];
i++;
}
cout << "\n";
} else if (!countQ && !locationQ && !location2Q) {
cout << line << "\n";
}
}
if (limitQ && (mcount >= limitval)) {
break;
}
}
return counter;
}
//////////////////////////////
//
// removeBoundaryCharcters -- Remove "R", "r", "R ", or "r " after
// the first tab character in the string.
//
void removeBoundaryCharacters(string& line) {
int tabind = line.find_first_of('\t');
if (tabind < 0) {
// no tab character found
return;
}
PerlRegularExpression pre;
if (!pre.search(line.c_str(), "R ?", "gi")) {
// no boundary markers to remove
return;
}
Array<char> pretab;
pretab.setSize(tabind + 1);
strncpy(pretab.getBase(), line.c_str(), tabind);
pretab.last() = '\0';
Array<char> posttab;
int postlen = line.length() - tabind - 1;
posttab.setSize(postlen + 1);
strncpy(posttab.getBase(), line.c_str()+tabind+1, postlen);
posttab.last() = '\0';
pre.sar(posttab, "R ?", "", "gi");
line = pretab.getBase();
line += posttab.getBase();
}
//////////////////////////////
//
// checkLink -- make sure that every search feature starts at the same
// note number in the data. Returns the number of matches found
// on the input line.
//
int checkLink(string& line, int offset) {
Array<int> target;
target.setSize(0);
Array<int> temptarget;
if (P12toneintervalQ) {
getSimpleLocationINT(temptarget, line, P12toneinterval,
P_12TONE_INTERVAL_MARKER, target, 2);
if (temptarget.getSize() == 0) {
return 0;
} else {
target = temptarget;
}
}
if (PgrosscontourQ) {
getSimpleLocationINT(temptarget, line, Pgrosscontour,
P_GROSS_CONTOUR_MARKER, target);
if (temptarget.getSize() == 0) {
return 0;
} else {
target = temptarget;
}
}
if (PrefinedcontourQ) {
getSimpleLocationINT(temptarget, line, Prefinedcontour,
P_REFINED_CONTOUR_MARKER, target);
if (temptarget.getSize() == 0) {
return 0;
} else {
target = temptarget;
}
}
if (PscaledegreeQ) {
getSimpleLocationFET(temptarget, line, Pscaledegree,
P_SCALE_DEGREE_MARKER, target);
if (temptarget.getSize() == 0) {
return 0;
} else {
target = temptarget;
}
}
if (PmusicalintervalQ) {
getMusicalIntervalLocation(temptarget, line, Pmusicalinterval,
P_DIATONIC_INTERVAL_MARKER, target);
if (temptarget.getSize() == 0) {
return 0;
} else {
target = temptarget;
}
}
if (P12tonepitchclassQ) {
getSimpleLocationFET(temptarget, line, P12tonepitchclass,
P_12TONE_PITCH_CLASS_MARKER, target);
if (temptarget.getSize() == 0) {
return 0;
} else {
target = temptarget;
}
}
if (PpitchclassQ) {
getSeparatorLocationFET(temptarget, line, Ppitchclass,
P_PITCH_CLASS_MARKER, target, ' ');
if (temptarget.getSize() == 0) {
return 0;
} else {
target = temptarget;
}
}
if (RgrosscontourQ) {
getSimpleLocationINT(temptarget, line, Rgrosscontour,
R_DURATION_GROSS_CONTOUR_MARKER, target);
if (temptarget.getSize() == 0) {
return 0;
} else {
target = temptarget;
}
}
if (RrefinedcontourQ) {
getSimpleLocationINT(temptarget, line, Rrefinedcontour,
R_DURATION_REFINED_CONTOUR_MARKER, target);
if (temptarget.getSize() == 0) {
return 0;
} else {
target = temptarget;
}
}
if (RdurationQ) {
getSeparatorLocationFET(temptarget, line, Rduration,
R_DURATION_MARKER, target, ' ');
if (temptarget.getSize() == 0) {
return 0;
} else {
target = temptarget;
}
}
if (RbeatlevelQ) {
getSimpleLocationFET(temptarget, line, Rbeatlevel,
R_BEAT_LEVEL_MARKER, target);
if (temptarget.getSize() == 0) {
return 0;
} else {
target = temptarget;
}
}
if (RmetriclevelQ) {
getSeparatorLocationFET(temptarget, line, Rmetriclevel,
R_METRIC_LEVEL_MARKER, target, ' ');
if (temptarget.getSize() == 0) {
return 0;
} else {
target = temptarget;
}
}
if (RmetricpositionQ) {
getSeparatorLocationFET(temptarget, line, Rmetricposition,
R_METRIC_POSITION_MARKER, target, ' ');
if (temptarget.getSize() == 0) {
return 0;
} else {
target = temptarget;
}
}
if (RmetricrefinedcontourQ) {
getSimpleLocationINT(temptarget, line, Rmetricrefinedcontour,
R_METRIC_REFINED_CONTOUR_MARKER, target);
if (temptarget.getSize() == 0) {
return 0;
} else {
target = temptarget;
}
}
if (RmetricgrosscontourQ) {
getSimpleLocationINT(temptarget, line, Rmetricgrosscontour,
R_METRIC_GROSS_CONTOUR_MARKER, target);
if (temptarget.getSize() == 0) {
return 0;
} else {
target = temptarget;
}
}
// for use with --location
Array<int> targetend;
targetend.setSize(0);
if (location2Q) {
getTargetEnds(targetend, target, line);
} else {
// not necessary, but doing to anyway
targetend.setSize(target.getSize());
targetend.setAll(-1);
}
if (!overlapQ) {
removeOverlappedMatches(target, targetend);
}
int offsettoggle = 0;
int coloncount = 0;
if ((location2Q || locationQ || countQ) && (target.getSize() > 0)) {
int i = 0;
const char* ptr = line.c_str();
while ((ptr[i] != '\0') && (ptr[i] != '\t')) {
if (ptr[i] == ':') {
coloncount++;
}
if ((coloncount >= 2) && (ptr[i] == ';')) {
offsettoggle = 1;
}
if (offsettoggle == 0) {
cout << ptr[i];
}
i++;
}
if (countQ) {
cout << '\t' << target.getSize();
TOTALCOUNT += target.getSize();
} else {
// locationQ
cout << '\t';
for (i=0; i<target.getSize(); i++) {
cout << target[i] + offset;
if (printendQ && (targetend[i] != target[i])) {
cout << "-" << targetend[i] + offset;
}
if (i < target.getSize() -1) {
cout << " ";
}
}
}
cout << '\n';
}
// all position filters returned success
return target.getSize();
}
//////////////////////////////
//
// removeOverlappedMatches -- Input is presumed to be sorted by
// the target array.
//
void removeOverlappedMatches(Array& target, Array& targetend) {
Array<int> tstart;
Array<int> tend;
tstart.setSize(target.getSize());
tstart.setSize(0);
tend.setSize(targetend.getSize());
tend.setSize(0);
int i;
int emark = -1;
for (i=0; i<target.getSize(); i++) {
if (target[i] > emark) {
tstart.append(target[i]);
tend.append(targetend[i]);
if (targetend[i] > emark) {
emark = targetend[i];
}
}
}
if (target.getSize() == tstart.getSize()) {
// no overlaps, so just return
return;
}
// copied the unoverlapped matches
target = tstart;
targetend = tend;
}
//////////////////////////////
//
// getTargetEnds -- return the ending point of a match which is the
// highest note number from all searches which were done.
//
void getTargetEnds(Array& targetend, Array& target, string& line) {
targetend.setSize(target.getSize());
targetend.setAll(-1);
int i;
Array<int> temptarget;
temptarget.setSize(targetend.getSize());
targetend.setGrowth(0);
temptarget.setAll(-1);
// ggg
if (PgrosscontourQ) {
getSimpleLocationINTEnd(temptarget, line, Pgrosscontour,
P_GROSS_CONTOUR_MARKER, target, 1);
for (i=0; i<temptarget.getSize(); i++) {
if (temptarget[i]+1 > targetend[i]) {
targetend[i] = temptarget[i]+1; // +1 for interval to note
}
}
}
if (PrefinedcontourQ) {
getSimpleLocationINTEnd(temptarget, line, Prefinedcontour,
P_REFINED_CONTOUR_MARKER, target, 1);
for (i=0; i<temptarget.getSize(); i++) {
if (temptarget[i]+1 > targetend[i]) {
targetend[i] = temptarget[i]+1; // +1 for interval to note
}
}
}
if (PscaledegreeQ) {
// getSimpleLocationFETEnd -> INTEnd for now.
// check interaction with segmentation markers later...
getSimpleLocationINTEnd(temptarget, line, Pscaledegree,
P_SCALE_DEGREE_MARKER, target, 1);
for (i=0; i<temptarget.getSize(); i++) {
if (temptarget[i] > targetend[i]) {
targetend[i] = temptarget[i];
}
}
}
if (PmusicalintervalQ) {
getMusicalIntervalLocationEnd(temptarget, line, Pmusicalinterval,
P_DIATONIC_INTERVAL_MARKER, target);
for (i=0; i<temptarget.getSize(); i++) {
if (temptarget[i]+1 > targetend[i]) {
targetend[i] = temptarget[i]+1; // +1 for interval to note
}
}
}
if (P12tonepitchclassQ) {
// getSimpleLocationFETEnd -> INTEnd for now.
// check interaction with segmentation markers later...
getSimpleLocationINTEnd(temptarget, line, P12tonepitchclass,
P_12TONE_PITCH_CLASS_MARKER, target, 1);
for (i=0; i<temptarget.getSize(); i++) {
if (temptarget[i] > targetend[i]) {
targetend[i] = temptarget[i];
}
}
}
// (P12toneintervalQ) needs to be converted
if (PpitchclassQ) {
getSeparatorLocationFETEnd(temptarget, line, Ppitchclass,
P_PITCH_CLASS_MARKER, target, ' ');
for (i=0; i<temptarget.getSize(); i++) {
if (temptarget[i] > targetend[i]) {
targetend[i] = temptarget[i];
}
}
}
if (RgrosscontourQ) {
getSimpleLocationINTEnd(temptarget, line, Rgrosscontour,
R_DURATION_GROSS_CONTOUR_MARKER, target);
for (i=0; i<temptarget.getSize(); i++) {
if (temptarget[i]+1 > targetend[i]) {
targetend[i] = temptarget[i]+1; // +1 for interval to note
}
}
}
if (RrefinedcontourQ) {
getSimpleLocationINTEnd(temptarget, line, Rrefinedcontour,
R_DURATION_REFINED_CONTOUR_MARKER, target);
for (i=0; i<temptarget.getSize(); i++) {
if (temptarget[i]+1 > targetend[i]) {
targetend[i] = temptarget[i]+1; // +1 for interval to note
}
}
}
if (RdurationQ) {
getSeparatorLocationFETEnd(temptarget, line, Rduration,
R_DURATION_MARKER, target, ' ');
for (i=0; i<temptarget.getSize(); i++) {
if (temptarget[i] > targetend[i]) {
targetend[i] = temptarget[i];
}
}
}
if (RbeatlevelQ) {
// getSimpleLocationFETEnd -> INTEnd for now.
// check interaction with segmentation markers later...
getSimpleLocationINTEnd(temptarget, line, Rbeatlevel,
R_BEAT_LEVEL_MARKER, target);
for (i=0; i<temptarget.getSize(); i++) {
if (temptarget[i] > targetend[i]) {
targetend[i] = temptarget[i];
}
}
}
if (RmetriclevelQ) {
getSeparatorLocationFETEnd(temptarget, line, Rmetriclevel,
R_METRIC_LEVEL_MARKER, target, ' ');
for (i=0; i<temptarget.getSize(); i++) {
if (temptarget[i] > targetend[i]) {
targetend[i] = temptarget[i];
}
}
}
if (RmetricpositionQ) {
getSeparatorLocationFETEnd(temptarget, line, Rmetricposition,
R_METRIC_POSITION_MARKER, target, ' ');
for (i=0; i<temptarget.getSize(); i++) {
if (temptarget[i] > targetend[i]) {
targetend[i] = temptarget[i];
}
}
}
if (RmetricrefinedcontourQ) {
getSimpleLocationINTEnd(temptarget, line, Rmetricrefinedcontour,
R_METRIC_REFINED_CONTOUR_MARKER, target);
for (i=0; i<temptarget.getSize(); i++) {
if (temptarget[i]+1 > targetend[i]) {
targetend[i] = temptarget[i]+1; // +1 for interval to note
}
}
}
if (RmetricgrosscontourQ) {
getSimpleLocationINTEnd(temptarget, line, Rmetricgrosscontour,
R_METRIC_GROSS_CONTOUR_MARKER, target);
for (i=0; i<temptarget.getSize(); i++) {
if (temptarget[i]+1 > targetend[i]) {
targetend[i] = temptarget[i]+1; // +1 for interval to note
}
}
}
}
//////////////////////////////
//
// getMusicalIntervalLocationEnd --
//
void getMusicalIntervalLocationEnd(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor, Array<int>& startlocs) {
PerlRegularExpression pre;
Array<char> startmarker;
startmarker.setSize(0);
appendString(startmarker, "(\\t");
startmarker.append(searchanchor);
appendString(startmarker, ")");
pre.search(line.c_str(), startmarker.getBase(), "");
int startindex = pre.getSubmatchEnd(1);
int i;
Array<char> newfeature;
newfeature.setSize(feature.getSize() + 4);
newfeature.setSize(0);
appendString(newfeature, "(");
appendString(newfeature, feature.getBase());
appendString(newfeature, ")");
// Check only locations specified by startlocs.
// The positions in startlocs are presumed to be sorted.
// This code probably will no longer work if "R" segmentations are
// present in the data.
PerlRegularExpression pokey;
pokey.initializeSearchAndStudy(newfeature.getBase());
const char* str = line.c_str();
i = startindex;
int starting;
int ending;
int pos;
int tindex = 0;
int tsize = startlocs.getSize();
// Check only locations specified by startlocs.
// The positions in startlocs are presumed to be sorted.
while ((str[i] != '\0') && (str[i] != '\t') && (tindex < tsize)) {
// search for the ith start position, and then extract the end position
pos = pokey.search(str+i);
if (pos == 0) {
break;
}
starting = pokey.getSubmatchStart(1);
ending = pokey.getSubmatchEnd(1);
positions[tindex] = startlocs[tindex] +
countElementsC(str+i+starting, ending-starting);
tindex++;
i += pokey.getSubmatchStart(1);
i++;
}
}
//////////////////////////////
//
// countElementsC -- Count the number of character segments separated
// by the searchanchor, and stop when the ending point in the string
// has been found (or excceeded). Used with
// getMusicalIntervalLocationEnd().
//
int countElementsC(const char* str, int ending) {
int i;
int output = 0;
for (i=0; i<ending; i++) {
// If a separator character is found, then increment location.
// str is guarenteed to have valid data in the -1 index location.
if ((toupper(str[i]) == 'X') ||
((str[i] == 'P') && (toupper(str[i-1]) != 'X'))) {
output++;
}
}
return output - 1;
}
//////////////////////////////
//
// getSimpleLocationINTEnd -- return a list of the ending
// note postions for matches which have a separator character
// between notes.
//
void getSimpleLocationINTEnd(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor, Array<int>& startlocs,
int featurewidth) {
positions.setAll(-1);
if (startlocs.getSize() == 0) {
return;
}
PerlRegularExpression pre;
Array<char> startmarker;
startmarker.setSize(0);
appendString(startmarker, "(\\t");
startmarker.append(searchanchor);
appendString(startmarker, ")");
pre.search(line.c_str(), startmarker.getBase(), "");
int startindex = pre.getSubmatchEnd(1);
Array<char> newfeature;
newfeature.setSize(feature.getSize() + 4);
newfeature.setSize(0);
appendString(newfeature, "(");
appendString(newfeature, feature.getBase());
appendString(newfeature, ")");
PerlRegularExpression pokey;
pokey.initializeSearchAndStudy(newfeature.getBase());
const char* str = line.c_str();
int tsize = startlocs.getSize();
int i = startindex;
int pos; // start index position of match (plus 1)
int ending;
int starting;
int tindex = 0;
// Check only locations specified by startlocs.
// The positions in startlocs are presumed to be sorted.
while ((str[i] != '\0') && (str[i] != '\t') && (tindex < tsize)) {
// search for the ith start position, and then extract the end position
pos = pokey.search(str+i);
if (pos == 0) {
break;
}
starting = pokey.getSubmatchStart(1);
ending = pokey.getSubmatchEnd(1);
positions[tindex] = startlocs[tindex] +
countElementsB(str+i+starting, ending-starting, featurewidth);
tindex++;
i += pokey.getSubmatchStart(1);
i += featurewidth;
}
}
//////////////////////////////
//
// countElementsB -- Count the number of character segments separated
// by the searchanchor, and stop when the ending point in the string
// has been found (or excceeded). Used with getSimpleLocationINTEnd().
int countElementsB(const char* str, int ending, int width) {
int i;
int output = 0;
for (i=0; i<ending; i+=width) {
output++;
if (str[i] == '\t') {
break;
} else if (str[i] == '\0') {
cerr << "GOT TO A STRANGE LOCATION IN countElementsA\n";
break;
}
}
return output - 1;
}
//////////////////////////////
//
// getSeparatorLocationFETEnd -- return a list of the ending
// note postions for matches which have a separator character
// between notes.
//
void getSeparatorLocationFETEnd(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor, Array<int>& startlocs,
char separator) {
positions.setAll(-1);
if (startlocs.getSize() == 0) {
return;
}
PerlRegularExpression pre;
Array<char> startmarker;
startmarker.setSize(0);
appendString(startmarker, "(\\t");
startmarker.append(searchanchor);
appendString(startmarker, ")");
pre.search(line.c_str(), startmarker.getBase(), "");
int startindex = pre.getSubmatchEnd(1);
int i;
Array<char> newfeature;
newfeature.setSize(feature.getSize() + 4);
newfeature.setSize(0);
appendString(newfeature, "(");
appendString(newfeature, feature.getBase());
appendString(newfeature, ")");
// Check only locations specified by startlocs.
// The positions in startlocs are presumed to be sorted.
PerlRegularExpression pokey;
pokey.initializeSearchAndStudy(newfeature.getBase());
const char* str = line.c_str();
i = startindex;
int pos; // start index position of match (plus 1)
int ending;
int starting;
int tindex = 0;
int tsize = startlocs.getSize();
while ((str[i] != '\0') && (str[i] != '\t') && (tindex < tsize)) {
// search for the ith start position, and then extract the end position
pos = pokey.search(str+i);
if (pos == 0) {
break;
}
starting = pokey.getSubmatchStart(1);
ending = pokey.getSubmatchEnd(1);
positions[tindex] = startlocs[tindex] +
countElementsA(str+i+starting, ending-starting, separator);
tindex++;
i += pokey.getSubmatchStart(1);
i++;
}
}
//////////////////////////////
//
// countElementsA -- Count the number of character segments separated
// by the searchanchor, and stop when the ending point in the string
// has been found (or excceeded). Used with getSeparatorLocationFETEnd().
//
int countElementsA(const char* str, int ending, char separator) {
int i;
int output = 0;
for (i=0; i<ending; i++) {
if (str[i] == separator) {
output++;
} else if (str[i] == '\t') {
break;
} else if (str[i] == '\0') {
cerr << "GOT TO A STRANGE LOCATION IN countElementsA\n";
break;
}
}
return output - 1;
}
/* no longer needed, but keeping just in case:
//////////////////////////////
//
// findIntersection -- return the values which are present in both
// lists. The lists are presumed to be sorted. Output is stored
// in first array of input parameters.
//
void findIntersection(Array& aa, Array& bb) {
Array<int> output(aa.getSize());
output.setSize(0);
int aSize = aa.getSize();
int bSize = bb.getSize();
int aIndex = 0;
int bIndex = 0;
for (aIndex = 0; aIndex < aSize; aIndex++) {
if (bb[bIndex] < aa[aIndex]) {
do {
bIndex++;
if (bIndex >= bSize) {
break;
}
} while (bb[bIndex] < aa[aIndex]);
}
if (bIndex >= bSize) {
break;
}
if (bb[bIndex] == aa[aIndex]) {
output.append(aa[aIndex]);
}
}
aa = output;
}
*/
//////////////////////////////
//
// getMusicalIntervalLocation -- return the element number in the feature
// list (offset from 1) on the given line in the specified feature,
// when the features are sepearated by according to musical interval
// feature format, such as:
// }Xm2XM2P1xm2Xm2XM2xM2P1xM2P1P1P1P1xm3P1xm3xM3XM2XM2Xm2XP5P1XP4xm2xM2
// * * * * * * * * * * * * * * * * * * * * * * * * *
// (checking for segmentation when a digits is preceded by a non-digit.)
//
// Do NOT Ignore "R *" markers when counting notes. Add one for each
// occurence of a rest. Example test data to consider:
//
// -m2 F 1
// -M2 E 4
// +M2 D 4
// -M3 E 1.
// +M2 C 2
// -M2 D 1
// C 1
// R R R (measure 125 of Jos0802d -- bassus part)
// F L
// R R R
// -m3 F 1
// -M2 D 2
// +P4 C 2
// -m2 F 2.
// -M3 E 4
// C L
// R R R
// +M2 D 2.
// +m2 E 4
// +M2 F 2
// -P4 G 2
// +P4 D 2
// +M2 G 2.
//
void getMusicalIntervalLocation(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor, Array<int>& checklocs) {
positions.setSize(0);
PerlRegularExpression pre;
Array<char> startmarker;
startmarker.setSize(0);
appendString(startmarker, "(\\t");
startmarker.append(searchanchor);
appendString(startmarker, ")");
pre.search(line.c_str(), startmarker.getBase(), "");
int startindex = pre.getSubmatchEnd(1);
int i;
// If checklocs is non-zero, then only check the feature
// at the given locations; otherwise, check every position.
if (checklocs.getSize() == 0) {
// check every location in feature for a match (allowing overlaps)
PerlRegularExpression gumby;
gumby.setAnchor();
gumby.initializeSearchAndStudy(feature.getBase());
PerlRegularExpression rest;
rest.setAnchor();
rest.initializeSearchAndStudy("(R *)");
const char* str = line.c_str();
int location = -1; // need to start at -1 since first spot will increment
i = startindex;
while ((str[i] != '\0') && (str[i] != '\t')) {
if (rest.search(str+i)) {
i += rest.getSubmatchEnd(1) - rest.getSubmatchStart(1);
// do increment location
if (location >= 0) {
// but don't increment if the R marker is at the front
// of the sequence.
location++;
}
// continue; // maybe this is needed?
}
if ((toupper(str[i]) == 'X') ||
((str[i] == 'P') && (i>=1) && (toupper(str[i-1]) != 'X'))) {
location++;
}
if ((i > 1) && (toupper(str[i]) == 'R') && (toupper(str[i-1]) == 'R')) {
location++;
}
// cout << "I = " << i << "\t" << str[i] << str[i+1] << str[i+2] << str[i+3] << " LOCATION = " << location << endl;
if (gumby.search(str+i)) {
positions.append(location);
}
i++;
}
} else {
// Check only locations specified by checklocs.
// The positions in checklocs are presumed to be sorted.
// This code probably will no longer work if "R" segmentations are
// present in the data.
PerlRegularExpression pokey;
pokey.setAnchor();
pokey.initializeSearchAndStudy(feature.getBase());
const char* str = line.c_str();
int location = 0;
i = startindex;
int targetindex = 0;
int tsize = checklocs.getSize();
while ((str[i] != '\0') && (str[i] != '\t') && (targetindex < tsize)) {
// Increase the target location if it is smaller than
// the current search location.
if (checklocs[targetindex] < location) {
targetindex++;
continue;
}
// If a separator character is found, then increment location.
if ((toupper(str[i]) == 'X') ||
((str[i] == 'P') && (toupper(str[i-1]) != 'X'))) {
location++;
}
if (location == checklocs[targetindex]) {
if (pokey.search(str+i)) {
positions.append(location);
targetindex++;
}
}
i++;
}
}
if (verbose2Q) {
cout << "ORIGINAL LINE: " << line << endl;
cout << "\tCOUNT IS " << positions.getSize() << " FOR FEATURE SEARCH "
<< feature.getBase() << endl;
cout << "\tMATCHES AT: ";
int j;
for (j=0; j<positions.getSize(); j++) {
cout << positions[j] << " ";
}
cout << endl;
}
}
//////////////////////////////
//
// getSeparatorLocation -- return the element number in the feature
// list (offset from 0) on the given line in the specified feature,
// when the features are sepearated by spaces (or whatever the
// separator character is).
// Default value: separator = ' '
//
// INT version counts "R *" markers
// FET version does not count "R *" markers
//
void getSeparatorLocationINT(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor, Array<int>& checklocs,
char separator) {
positions.setSize(0);
PerlRegularExpression pre;
Array<char> startmarker;
startmarker.setSize(0);
appendString(startmarker, "(\\t");
startmarker.append(searchanchor);
appendString(startmarker, ")");
pre.search(line.c_str(), startmarker.getBase(), "");
int startindex = pre.getSubmatchEnd(1);
int i;
// If checklocs is non-zero, then only check the feature
// at the given locations; otherwise, check every position.
if (checklocs.getSize() == 0) {
// check every location in feature for a match (allowing overlaps)
PerlRegularExpression gumby;
gumby.setAnchor();
gumby.initializeSearchAndStudy(feature.getBase());
// currently rest separator is either nothing or a space
PerlRegularExpression rest;
rest.setAnchor();
rest.initializeSearchAndStudy("(R *)");
const char* str = line.c_str();
int location = 0;
i = startindex;
while ((str[i] != '\0') && (str[i] != '\t')) {
if (str[i] == separator) {
i++;
location++;
continue;
}
if (rest.search(str+i)) {
i += rest.getSubmatchEnd(1) - rest.getSubmatchStart(1);
location++;
continue;
}
if (gumby.search(str+i)) {
positions.append(location);
}
i++;
}
} else {
// Check only locations specified by checklocs.
// The positions in checklocs are presumed to be sorted.
PerlRegularExpression pokey;
pokey.setAnchor();
pokey.initializeSearchAndStudy(feature.getBase());
const char* str = line.c_str();
int location = 0;
i = startindex;
int targetindex = 0;
int tsize = checklocs.getSize();
while ((str[i] != '\0') && (str[i] != '\t') && (targetindex < tsize)) {
// Increase the target location if it is smaller than
// the current search location.
if (checklocs[targetindex] < location) {
targetindex++;
continue;
}
// If a separator character is found, then increment location.
if (str[i] == separator) {
i++;
location++;
continue;
}
if (location == checklocs[targetindex]) {
if (pokey.search(str+i)) {
positions.append(location);
targetindex++;
}
}
i++;
}
}
if (verbose2Q) {
cout << "ORIGINAL LINE: " << line << endl;
cout << "\tCOUNT IS " << positions.getSize() << " FOR FEATURE SEARCH "
<< feature.getBase() << endl;
cout << "\tMATCHES AT: ";
int j;
for (j=0; j<positions.getSize(); j++) {
cout << positions[j] << " ";
}
cout << endl;
}
}
void getSeparatorLocationFET(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor, Array<int>& checklocs,
char separator) {
positions.setSize(checklocs.getSize());
positions.setSize(0);
PerlRegularExpression pre;
Array<char> startmarker;
startmarker.setSize(256);
startmarker.setGrowth(10000);
startmarker.setSize(0);
appendString(startmarker, "(\\t");
startmarker.append(searchanchor);
appendString(startmarker, ")");
pre.search(line.c_str(), startmarker.getBase(), "");
// startindex is the first character in the search feature with
// the full search index. Matches will be counted from this
// position.
int startindex = pre.getSubmatchEnd(1);
int i, j;
Array<int> temppos(1000);
temppos.setGrowth(100000);
// If checklocs is non-zero, then only check the feature
// at the given locations; otherwise, check every position.
if (checklocs.getSize() == 0) {
// check every location in feature for a match (allowing overlaps)
PerlRegularExpression gumby;
gumby.setAnchor();
gumby.initializeSearchAndStudy(feature.getBase());
// currently rest separator is either nothing or a space
PerlRegularExpression rest;
rest.setAnchor();
rest.initializeSearchAndStudy("(R *)");
const char* str = line.c_str();
int location = 0;
i = startindex;
while ((str[i] != '\0') && (str[i] != '\t')) {
if (str[i] == separator) {
i++;
location++;
continue;
}
if (rest.search(str+i)) {
i += rest.getSubmatchEnd(1) - rest.getSubmatchStart(1);
continue;
}
if (gumby.search(str+i)) {
positions.append(location);
}
i++;
}
} else {
// Check only locations specified by checklocs.
// The positions in checklocs are presumed to be sorted.
PerlRegularExpression pokey;
pokey.setAnchor();
pokey.initializeSearchAndStudy(feature.getBase());
const char* str = line.c_str();
int location = 0;
int lastpos = 0;
i = startindex;
int targetindex = 0;
int tsize = checklocs.getSize();
// currently rest separator is either nothing or a space
PerlRegularExpression rest;
rest.setAnchor();
rest.initializeSearchAndStudy("(R *)");
while ((str[i] != '\0') && (str[i] != '\t') && (targetindex < tsize)) {
// Increase the target location if it is smaller than
// the current search location.
if (checklocs[targetindex] < location) {
targetindex++;
continue;
}
// If a separator character is found, then increment location.
if (str[i] == separator) {
i++;
location++;
continue;
}
if (rest.search(str+i)) {
i += rest.getSubmatchEnd(1) - rest.getSubmatchStart(1);
continue;
}
if (pokey.search(str+i)) {
//for (j=lastpos; j<checklocs.getSize(); j++) {
for (j=0; j<checklocs.getSize(); j++) {
if (location == checklocs[j]) {
positions.append(location);
lastpos = j;
break;
}
if (location < checklocs[j]) {
// not in checklocs (provided that checklocs is sorted)
break;
}
}
}
// The following line was causing a bug [fixed 20120607]
// targetindex++;
i++;
}
}
if (verbose2Q) {
cout << "ORIGINAL LINE: " << line << endl;
cout << "\tCOUNT IS " << positions.getSize() << " FOR FEATURE SEARCH "
<< feature.getBase() << endl;
cout << "\tMATCHES AT: ";
int j;
for (j=0; j<positions.getSize(); j++) {
cout << positions[j] << " ";
}
cout << endl;
}
}
//////////////////////////////
//
// getSimpleLocation -- return the element number in the feature list
// (offset from 0) on the given line in the specified feature
// Default value: featurewidth = 1
//
// INT version: intervallic features: count "R" as one note increment
// FET version: real features: ignore "R" markers when counting.
//
void getSimpleLocationINT(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor, Array<int>& checklocs,
int featurewidth) {
positions.setSize(0);
PerlRegularExpression pre;
Array<char> startmarker;
startmarker.setSize(0);
appendString(startmarker, "(\\t");
startmarker.append(searchanchor);
appendString(startmarker, ")");
pre.search(line.c_str(), startmarker.getBase(), "");
int startindex = pre.getSubmatchEnd(1);
int i;
// if checklocs is non-zero, then only check the feature
// at the given locations; otherwise, check every position.
if (checklocs.getSize() == 0) {
// check every location in feature for a match (allowing overlaps)
PerlRegularExpression gumby;
gumby.setAnchor();
gumby.initializeSearchAndStudy(feature.getBase());
const char* str = line.c_str();
int location;
int charloc = 0;
i = startindex;
while ((str[i] != '\0') && (str[i] != '\t')) {
if (gumby.search(str+i)) {
location = charloc / featurewidth;
positions.append(location);
}
charloc++;
i++;
}
} else {
// check only locations specified by checklocs
// The feature is presumed to be of equal length
// to other features, and an end of string problem
// will therefore not be checked. Only the start
// of a feature will be checked (no partial
// start features will be examined, but could be
// added).
PerlRegularExpression pokey;
pokey.setAnchor();
pokey.initializeSearchAndStudy(feature.getBase());
const char* str = line.c_str() + startindex;
for (i=0; i<checklocs.getSize(); i++) {
if (pokey.search(str+(checklocs[i] * featurewidth))) {
positions.append(checklocs[i]);
}
}
}
if (verbose2Q) {
cout << "ORIGINAL LINE: " << line << endl;
cout << "\tCOUNT IS " << positions.getSize() << " FOR FEATURE SEARCH "
<< feature.getBase() << endl;
cout << "\tMATCHES AT: ";
int j;
for (j=0; j<positions.getSize(); j++) {
cout << positions[j] << " ";
}
cout << endl;
}
}
void getSimpleLocationFET(Array<int>& positions, string& line,
Array<char>& feature, char searchanchor, Array<int>& checklocs,
int featurewidth) {
positions.setSize(0);
PerlRegularExpression pre;
Array<char> startmarker;
startmarker.setSize(0);
appendString(startmarker, "(\\t");
startmarker.append(searchanchor);
appendString(startmarker, ")");
pre.search(line.c_str(), startmarker.getBase(), "");
int startindex = pre.getSubmatchEnd(1);
int i;
// if checklocs is non-zero, then only check the feature
// at the given locations; otherwise, check every position.
if (checklocs.getSize() == 0) {
// check every location in feature for a match (allowing overlaps)
PerlRegularExpression gumby;
gumby.setAnchor();
gumby.initializeSearchAndStudy(feature.getBase());
// currently rest separator is either nothing or a space
PerlRegularExpression rest;
rest.setAnchor();
rest.initializeSearchAndStudy("R");
const char* str = line.c_str();
int location;
int charloc = 0;
i = startindex;
while ((str[i] != '\0') && (str[i] != '\t')) {
if (rest.search(str+i)) {
i++;
// don't increment charloc due to "R".
continue;
}
if (gumby.search(str+i)) {
location = charloc / featurewidth;
positions.append(location);
}
charloc++;
i++;
}
} else {
// check only locations specified by checklocs
// The feature is presumed to be of equal length
// to other features, and an end of string problem
// will therefore not be checked. Only the start
// of a feature will be checked (no partial
// start features will be examined, but could be
// added).
PerlRegularExpression pokey;
pokey.setAnchor();
pokey.initializeSearchAndStudy(feature.getBase());
// this code will not work with segmentation markers, so have
// to fix. Basically cannot speed up the search process, and
// have to step through data.
const char* str = line.c_str() + startindex;
for (i=0; i<checklocs.getSize(); i++) {
if (pokey.search(str+(checklocs[i] * featurewidth))) {
positions.append(checklocs[i]);
}
}
}
if (verbose2Q) {
cout << "ORIGINAL LINE: " << line << endl;
cout << "\tCOUNT IS " << positions.getSize() << " FOR FEATURE SEARCH "
<< feature.getBase() << endl;
cout << "\tMATCHES AT: ";
int j;
for (j=0; j<positions.getSize(); j++) {
cout << positions[j] << " ";
}
cout << endl;
}
}
//////////////////////////////
//
// showCleanedParameters -- Show what the post processed input search
// parameters look like for each search field (if it was used).
//
void showCleanedParameters(void) {
if (tonicss.getSize() > 1) {
cout << "tonic:\t\t" << tonicss.getBase() << endl;
}
if (P12toneinterval.getSize() > 1) {
cout << "twelve-tone interval:\t" << P12toneinterval.getBase() << endl;
}
if (Pgrosscontour.getSize() > 1) {
cout << "Pitch gross contour:\t" << Pgrosscontour.getBase() << endl;
}
if (Prefinedcontour.getSize() > 1) {
cout << "Pitch refined contour:\t" << Prefinedcontour.getBase() << endl;
}
if (Pscaledegree.getSize() > 1) {
cout << "Scale-degree:\t" << Pscaledegree.getBase() << endl;
}
if (Pmusicalinterval.getSize() > 1) {
cout << "Interval:\t" << Pmusicalinterval.getBase() << endl;
}
if (P12tonepitchclass.getSize() > 1) {
cout << "Twelve-tone pitch-class:\t" << P12tonepitchclass.getBase()
<< endl;
}
if (Ppitchclass.getSize() > 1) {
cout << "Pitch-class:\t" << Ppitchclass.getBase() << endl;
}
if (meterss.getSize() > 1) {
cout << "meter:\t\t" << meterss.getBase() << endl;
}
if (Rgrosscontour.getSize() > 1) {
cout << "Rhythm gross contour:\t" << Rgrosscontour.getBase() << endl;
}
if (Rrefinedcontour.getSize() > 1) {
cout << "Rhythm refined contour:\t" << Rrefinedcontour.getBase() << endl;
}
if (Rduration.getSize() > 1) {
cout << "Duration:\t" << Rduration.getBase() << endl;
}
if (Rbeatlevel.getSize() > 1) {
cout << "Beat level:\t" << Rbeatlevel.getBase() << endl;
}
if (Rmetriclevel.getSize() > 1) {
cout << "Metric level:\t" << Rmetriclevel.getBase() << endl;
}
if (Rmetricrefinedcontour.getSize() > 1) {
cout << "Metric refined contour:\t" << Rmetricrefinedcontour.getBase()
<< endl;
}
if (Rmetricgrosscontour.getSize() > 1) {
cout << "Metric gross contour:\t" << Rmetricgrosscontour.getBase()
<< endl;
}
if (Rmetricposition.getSize() > 1) {
cout << "Metric position:\t" << Rmetricposition.getBase() << endl;
}
}
//////////////////////////////
//
// appendToSearchString --
//
void appendToSearchString(Array<char>& ss, const char* string,
char marker, int anchor) {
char ch;
// add back quote for certain markers
switch (marker) {
case '{':
case '}':
case '^':
ch = '\\'; ss.append(ch);
}
ss.append(marker);
char nullchar = '\0';
ss.append(nullchar);
ss.setSize(ss.getSize()-1);
// add [^\t]* if not anchored:
//
//
if (!anchor) {
// The ? causes problems when searching for P1 in -I option:
// appendString(ss, "[^\\t]*?");
appendString(ss, "[^\\t]*");
}
appendString(ss, string);
// Moved to cleanPpitchClass:
// // for pitch-class names, the next character after the search
// // string must be a space or a tab to prevent accidentals
// // from matching on natural-note search endings.
// if (marker == P_PITCH_CLASS_MARKER) {
// appendString(ss, "[ \\t]");
// }
appendString(ss, ".*");
}
//////////////////////////////
//
// appendString --
//
void appendString(Array& ss, const char* string) {
int i;
char ch;
int length = strlen(string);
for (i=0; i<length; i++) {
ch = string[i];
ss.append(ch);
}
// place a dummy null character at end in case
// the string is printed directly from the data.
ch = '\0';
ss.append(ch);
ss.setSize(ss.getSize()-1);
}
//////////////////////////////
//
// checkOptions --
//
void checkOptions(Options& opts, int argc, char* argv[]) {
PerlRegularExpression pre;
opts.define("a|A|anchored=b", "anchored search at start");
opts.define("m|minor=b", "search only minor keys");
opts.define("M|major=b", "search only minor keys");
opts.define("t|tonic=s:", "search only given tonic");
opts.define("T|meter=s:", "search only given meters");
opts.define("i|12I|12i|pitch-12tone-interval=s:", "12-tone interval");
opts.define("C|PGC|pgc|GC|gc|CON|con|pitch-gross-contour=s:",
"pitch gross contour");
opts.define("c|PRC|prc|RC|rc|pitch-refined-contour=s:",
"pitch refined contour");
opts.define("d|SD|sd|pitch-scale-degree=s:", "pitch scale degree");
opts.define("I|MI|mi|DI|di|INT|int|pitch-musical-interval|interval=s:",
"musical interval");
opts.define("P|12P|12p|pitch-12tonepc=s:", "12-tone pitch class");
opts.define("p|PCH|pch|PC|pc|pitch-class|pitch=s:",
"pitch class");
opts.define("D|DPC|dpc|diatonic-pitch-class|diatonic=s:",
"diatonic pitch class");
opts.define("R|DGC|dgc|duration-gross-contour=s:",
"duration gross contour");
opts.define("r|DRC|drc|duration-refined-contour=s:",
"duration refined contour");
opts.define("u|duration=s:", "duration (IOI)");
opts.define("b|beat-level=s:", "beat level");
opts.define("L|metric-level=s:", "metric level");
opts.define("l|metric-position=s:", "metric position");
opts.define("e|MRC|mrc|rrc|RRC|metric-refined-contour=s:",
"metric refined contour");
opts.define("E|MGC|mgc|RGC|rgc|metric-gross-contour=s:",
"metric-gross contour");
//opts.define("i|pitch-twelvetone-interval=s:", "12-tone interval");
opts.define("q|query=s", "interleaved search query");
opts.define("B|no-boundary=b", "ignore boundary markers");
opts.define("k|kern=s", "kern-based search");
opts.define("f|file=s:", "filename tag filter");
opts.define("verbose=b", "verbose display");
opts.define("verbose2=b", "verbose display");
opts.define("raw|no-clean=b", "do not preprocess searches");
opts.define("cleaned|queries|features=b", "show cleaned input fields");
opts.define("short|trim=b", "display only filename in output");
opts.define("v|V|invert-match|not=b", "negate search query");
// output formatting options
opts.define("total=b", "return a count of the matches");
opts.define("count=b", "return count in matched lines");
opts.define("overlap=b", "allow overlapped matches");
opts.define("location|loc|locend=b", "return start/stop note of matchs");
opts.define("location2|startloc|locstart|start|startining|loc2=b",
"return only starting note number of match");
opts.define("limit=i:0", "exit if limit count of matches exceeded");
opts.define("debug=b", "debugging statements");
opts.define("regex=b", "print regular expression construct");
opts.define("unlink=b", "unlink search features");
opts.define("smart=b", "do a smart search");
opts.define("Q|no-messages=b", "do not echo control messages from input data");
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, Oct 2008" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 24 Nov 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);
}
////////////////////////////////////////////////
if (opts.getBoolean("query")) {
adjustForInterleavedQuery(opts);
}
quietQ = opts.getBoolean("no-messages");
shortQ = opts.getBoolean("short");
verboseQ = opts.getBoolean("verbose");
verbose2Q = opts.getBoolean("verbose2");
cleanQ = !opts.getBoolean("no-clean");
showcleanQ = opts.getBoolean("cleaned");
anchoredQ = opts.getBoolean("anchored");
debugQ = opts.getBoolean("debug");
regexQ = opts.getBoolean("regex");
boundaryQ = !opts.getBoolean("no-boundary");
smartQ = opts.getBoolean("smart");
majorQ = opts.getBoolean("major");
minorQ = opts.getBoolean("minor");
tonicQ = opts.getBoolean("tonic");
unlinkQ = opts.getBoolean("unlink");
notQ = opts.getBoolean("not");
overlapQ = opts.getBoolean("overlap");
tonicstring.setSize(strlen(opts.getString("tonic")) + 1);
strcpy(tonicstring.getBase(), opts.getString("tonic"));
pre.sar(tonicstring, "-sharp", "#", "g");
pre.sar(tonicstring, "-flat", "-", "g");
pre.tr(tonicstring, "abcdefg", "ABCDEFG");
totalQ = opts.getBoolean("total");
countQ = opts.getBoolean("count");
locationQ = opts.getBoolean("location2");
printendQ = opts.getBoolean("location");
overlapQ = opts.getBoolean("overlap");
kernQ = opts.getBoolean("kern");
kernstring = opts.getString("kern");
limitQ = opts.getBoolean("limit");
limitval = opts.getInteger("limit");
keyfilterQ = majorQ || minorQ || tonicQ;
meterQ = opts.getBoolean("meter");
meterstring = opts.getString("meter");
if (opts.getBoolean("file")) {
filetag.setSize(strlen(opts.getString("file")) + 1);
strcpy(filetag.getBase(), opts.getString("file"));
} else {
filetag.setSize(1);
filetag[0] = '\0';
}
P12toneintervalQ = opts.getBoolean("pitch-12tone-interval");
PgrosscontourQ = opts.getBoolean("pitch-gross-contour");
PrefinedcontourQ = opts.getBoolean("pitch-refined-contour");
PscaledegreeQ = opts.getBoolean("pitch-scale-degree");
PmusicalintervalQ = opts.getBoolean("pitch-musical-interval");
P12tonepitchclassQ = opts.getBoolean("pitch-12tonepc");
PpitchclassQ = opts.getBoolean("pitch-class");
diatonicQ = opts.getBoolean("diatonic-pitch-class");
if (diatonicQ) {
PpitchclassQ = 1;
}
P12toneinterval.setSize(strlen(opts.getString("pitch-12tone-interval"))+1);
strcpy(P12toneinterval.getBase(), opts.getString("pitch-12tone-interval"));
Pgrosscontour.setSize(strlen(opts.getString("pitch-gross-contour"))+1);
strcpy(Pgrosscontour.getBase(), opts.getString("pitch-gross-contour"));
Prefinedcontour.setSize(strlen(opts.getString("pitch-refined-contour"))+1);
strcpy(Prefinedcontour.getBase(), opts.getString("pitch-refined-contour"));
Pscaledegree.setSize(strlen(opts.getString("pitch-scale-degree"))+1);
strcpy(Pscaledegree.getBase(), opts.getString("pitch-scale-degree"));
Pmusicalinterval.setSize(strlen(opts.getString("pitch-musical-interval"))+1);
strcpy(Pmusicalinterval.getBase(), opts.getString("pitch-musical-interval"));
P12tonepitchclass.setSize(strlen(opts.getString("pitch-12tonepc"))+1);
strcpy(P12tonepitchclass.getBase(), opts.getString("pitch-12tonepc"));
if (diatonicQ) {
Ppitchclass.setSize(strlen(opts.getString("diatonic-pitch-class"))+1);
strcpy(Ppitchclass.getBase(), opts.getString("diatonic-pitch-class"));
} else {
Ppitchclass.setSize(strlen(opts.getString("pitch-class"))+1);
strcpy(Ppitchclass.getBase(), opts.getString("pitch-class"));
}
RgrosscontourQ = opts.getBoolean("duration-gross-contour");
RrefinedcontourQ = opts.getBoolean("duration-refined-contour");
RdurationQ = opts.getBoolean("duration");
RbeatlevelQ = opts.getBoolean("beat-level");
RmetriclevelQ = opts.getBoolean("metric-level");
RmetricpositionQ = opts.getBoolean("metric-position");
RmetricrefinedcontourQ = opts.getBoolean("metric-refined-contour");
RmetricgrosscontourQ = opts.getBoolean("metric-gross-contour");
Rgrosscontour.setSize(strlen(opts.getString("duration-gross-contour"))+1);
strcpy(Rgrosscontour.getBase(), opts.getString("duration-gross-contour"));
Rrefinedcontour.setSize(
strlen(opts.getString("duration-refined-contour"))+1);
strcpy(Rrefinedcontour.getBase(),
opts.getString("duration-refined-contour"));
Rduration.setSize(strlen(opts.getString("duration"))+1);
strcpy(Rduration.getBase(), opts.getString("duration"));
Rbeatlevel.setSize(strlen(opts.getString("beat-level"))+1);
strcpy(Rbeatlevel.getBase(), opts.getString("beat-level"));
Rmetriclevel.setSize(strlen(opts.getString("metric-level"))+1);
strcpy(Rmetriclevel.getBase(), opts.getString("metric-level"));
Rmetricposition.setSize(strlen(opts.getString("metric-position"))+1);
strcpy(Rmetricposition.getBase(), opts.getString("metric-position"));
Rmetricrefinedcontour.setSize(
strlen(opts.getString("metric-refined-contour"))+1);
strcpy(Rmetricrefinedcontour.getBase(),
opts.getString("metric-refined-contour"));
Rmetricgrosscontour.setSize(
strlen(opts.getString("metric-gross-contour"))+1);
strcpy(Rmetricgrosscontour.getBase(),
opts.getString("metric-gross-contour"));
if (kernQ) {
processKernString(kernstring);
}
// count the number of parallel features being searched
featureCount = 0;
if (P12toneintervalQ) { featureCount++; }
if (PgrosscontourQ) { featureCount++; }
if (PrefinedcontourQ) { featureCount++; }
if (PscaledegreeQ) { featureCount++; }
if (PmusicalintervalQ) { featureCount++; }
if (P12tonepitchclassQ) { featureCount++; }
if (PpitchclassQ) { featureCount++; }
if (RgrosscontourQ) { featureCount++; }
if (RrefinedcontourQ) { featureCount++; }
if (RdurationQ) { featureCount++; }
if (RbeatlevelQ) { featureCount++; }
if (RmetriclevelQ) { featureCount++; }
if (RmetricpositionQ) { featureCount++; }
if (RmetricrefinedcontourQ) { featureCount++; }
if (RmetricgrosscontourQ) { featureCount++; }
// note that overlapQ is only implemented for --location option
// and not in other cases (yet).
if (printendQ || !overlapQ) {
location2Q = 1;
}
}
//////////////////////////////
//
// adjustForInterleavedQuery -- parse the --query option for
// content, and fill in the appropriate serial feature strings.
//
void adjustForInterleavedQuery(Options& opts) {
if (!opts.getBoolean("query")) {
return;
}
Array<Array<char> > tokens;
PerlRegularExpression pre;
pre.getTokens(tokens, "\\s+", opts.getString("query"));
if (tokens.getSize() == 0) {
return;
}
Array<Array<char> > featuretype;
pre.getTokens(featuretype, ":", tokens[0].getBase());
if (featuretype.getSize() == 0) {
return;
}
int featurecount = featuretype.getSize();
int seqcount = tokens.getSize()-1;
SSTREAM *sptr[featurecount];
int i, j;
for (i=0; i<featurecount; i++) {
sptr[i] = new SSTREAM;
}
Array<Array<char> > feature;
int maxfet = featurecount;
for (i=0; i<seqcount; i++) {
pre.getTokens(feature, ":", tokens[i+1].getBase());
if (feature.getSize() != featurecount) {
break;
}
if (feature.getSize() < maxfet) {
// shrink feature length if it narrows.
maxfet = feature.getSize();
}
for (j=0; j<featurecount; j++) {
(*sptr[j]) << feature[j] << " ";
}
}
// store the separate feature strings.
for (i=0; i<featurecount; i++) {
(*sptr[i]) << ends;
opts.setModified(featuretype[i].getBase(), (*sptr[i]).CSTRING);
if (debugQ) {
cout << "SETTING " << featuretype[i].getBase() << " option to: " <<
(*sptr[i]).CSTRING << endl;
}
delete sptr[i];
sptr[i] = NULL;
}
}
//////////////////////////////
//
// example --
//
void example(void) {
}
//////////////////////////////
//
// usage --
//
void usage(const char* command) {
cout <<
"THEMA : Search thematic database; identify the composer and origin.\n"
"\n"
" This command searches a thematic database according to user-defined\n"
" search keys. Themes that match the search key are identified\n"
" by composer, title, instrumentation, opus, movement, etc.\n"
"\n"
" Search keys (\"incipits\") may be defined in several different ways, in-\n"
" cluding rough up/down contour (-C), up/down contour with step and leap\n"
" intervals distinguished (-c), by scale degree (-d), by semitone interval\n"
" (-i), by interval-class (-I), by pitch (-p), by relative rhythm (-r),\n"
" and by absolute rhythm (R). Several search keys can be combined in a\n"
" single search, and searches can be limited to structural tones only (-s).\n"
"\n"
"Syntax:\n"
"\n"
" thema [-m|M] [-s] [-C incipit] [-c incipit] [-d incipit] [-i incipit]\n"
" [-I incipit] [-p incipit] [-r incipit] [-R incipit] [-t tonic]\n"
"\n"
"Options:\n"
"\n"
"\n"
" -C incipit : identify according to up/down/unison(same) contour [UDS]\n"
" -c incipit : identify according to up/down/unison step/leap contour [UDuds]\n"
" -d incipit : identify according to scale degree [1234567]\n"
" -i incipit : identify according to semitone intervals [+-1234567890]\n"
" -m : limit search to themes in minor keys\n"
" -M : limit search to themes in major keys\n"
" -P incipit : identify according to pitch-class [#-abcdefg]\n"
" -p incipit : identify according to absolute pitch [#-ABCDEFGabcdefg]\n"
" -r incipit : identify according to longer/shorter/same rhythm [<>=]\n"
" -R incipit : identify according to long/short/medium rhythm [LSM]\n"
" -s : limit search to structural tones only\n"
" -t tonic : limit search to themes in key of \"tonic\" (major or minor)\n"
"\n"
" Refer to reference manual for further details."
<<endl;
}
///////////////////////////////////////////////////////////////////////////
//
// Cleaning functions -- these functions will filter user input to
// attempt to make it conformant to the index search
//
///////////////////////////////
//
// cleanRmgc -- metric gross contour
//
void cleanRmgc(Array& data) {
PerlRegularExpression pre;
// remove invalid characters
pre.sar(data, "[^UuDdRrEeSsHhwW=<>0-9,.{}()+*-]", "", "g");
pre.tr(data, "udHhWw=Eesr", "UDUUDDSSSSR");
}
///////////////////////////////
//
// cleanRmrc -- metric refined contour
//
void cleanRmrc(Array& data) {
PerlRegularExpression pre;
// remove invalid characters
pre.sar(data, "[^UuDdRrEeSsHhwW=<>0-9,.{}()+*-]", "", "g");
pre.tr(data, "HhWwEes=r", "UuDdSSSSR");
}
///////////////////////////////
//
// cleanRgrossContour --
//
void cleanRgrossContour(Array& data) {
PerlRegularExpression pre;
// remove invalid characters
pre.sar(data, "[^RrSsLl=<>0-9,.{}()+*-]", "", "g");
pre.tr(data, "rSsLl", "R<<>>");
}
///////////////////////////////
//
// cleanRrefinedContour --
//
void cleanRrefinedContour(Array& data) {
PerlRegularExpression pre;
// remove invalid characters
pre.sar(data, "[^\\]\\[RrSsLl=<>0-9,.{}()+*-]", "", "g");
pre.tr(data, "rsl", "R<>");
pre.sar(data, "\\]", "\\]", "g");
pre.sar(data, "\\[", "\\[", "g");
pre.sar(data, "S", "\\[", "g");
pre.sar(data, "L", "\\]", "g");
}
//////////////////////////////
//
// cleanRbeatLevel --
//
void cleanRbeatLevel(Array& data) {
PerlRegularExpression pre;
// remove invalid characters
pre.sar(data, "[^Rr0-9,.{}()|*+?BbSs]", "", "g");
// convert B to 1 and S to 0 if they are used (derived from metric level)
pre.tr(data, "rBbSs", "R1100");
// adjust meaning of dot so that it does not match to tab character:
pre.sar(data, "\\.", "[^\\t]", "g");
}
//////////////////////////////
//
// cleanRDuration --
// work on allowing more regular expressions.
//
void cleanRduration(Array& data) {
PerlRegularExpression pre;
// remove invalid characters
pre.sar(data, "[^LlBb?Rr0-9Dd,.{}()|*+\\sxX]", "", "g");
// change dots into "d", and "x" into "X":
pre.tr(data, "r.Dxlb", "RddXLB");
// convert "X" into the equivalent of regular-expression dot:
pre.sar(data, "X", " \\d+d* ", "g");
// change spaces to single, adding one at end, removing
// any from start
pre.sar(data, "\\s+", " ", "g");
pre.sar(data, "^\\s+", "", "");
pre.sar(data, "\\s*$", " ", "");
// adjust for ? wildcard (more added later...)
pre.sar(data, " ", " )(?:", "g");
pre.sar(data, "^", "(?:", "");
pre.sar(data, "$", ")", "");
pre.sar(data, "\\? \\)", " )?", "g");
pre.sar(data, "\\(\\?:\\)", "", "g"); // remove ending null case
}
//////////////////////////////
//
// cleanRmetricLevel --
//
void cleanRmetricLevel(Array& data) {
PerlRegularExpression pre;
// remove invalid characters
pre.sar(data, "[^Rr0-9,.{}()|*+\\s+-pmPM]", "", "g");
// change dots into "d":
pre.tr(data, "r+-PMbs", "RpmpmBS");
pre.sar(data, "B", " B ", "g");
pre.sar(data, "S", " S ", "g");
// change spaces to single, adding one at end, removing
// any from start
pre.sar(data, "\\s+", " ", "g");
pre.sar(data, "^\\s+", "", "");
pre.sar(data, "\\s*$", " ", "");
// expand B and S markers
pre.sar(data, "B", "(?:p\\d+|0)", "g");
pre.sar(data, "S", "m\\d+", "g");
}
//////////////////////////////
//
// cleanRmetricPosition --
//
void cleanRmetricPosition(Array& data) {
PerlRegularExpression pre;
// remove invalid characters
pre.sar(data, "[^Rr0-9,.{}()|*+\\~\\^\\s+_\\- \\/]", "", "g");
// change dashes into underscores
pre.tr(data, "r-", "R_");
// make sure underscores do not have any adjacent spaces:
pre.sar(data, "\\s*_\\s*", "_", "g");
// make dots surrounded by spaces:
pre.sar(data, "\\s*\\.\\s*", " . ", "g");
// change spaces to single space, adding one at end, removing
// any from start
pre.sar(data, "\\s+~\\s+", "~ ", "g"); // remove any spaces before ~
pre.sar(data, "\\s+\\^\\s+", "~ ", "g"); // remove any spaces before ^
pre.sar(data, "\\s+", " ", "g");
pre.sar(data, "^\\s+", "", "");
pre.sar(data, "\\s*$", " ", "");
// attach offbeat values to beats
pre.sar(data, " (?=\\d+\\/\\d+)", "_", "g");
// expand ~ marker (which means beat and any possible subbeat)
pre.sar(data, "\\~", "(?:_\\d+\\/\\d+)?", "g");
// expand ^ marker (which means any possible subbeat of a beat)
pre.sar(data, "\\^", "_(?:\\d+\\/\\d+)?", "g");
// add "x" markers to starts of beats
pre.sar(data, "\\s+", " x", "g");
pre.sar(data, "x$", "", "");
pre.sar(data, "^", "x", "");
// convert dots into form to match any beat position
pre.sar(data, "\\.", "\\d+(_\\d+\\/\\d+)?", "g");
}
//////////////////////////////
//
// cleanPgrossContour --
//
void cleanPgrossContour(Array& data) {
PerlRegularExpression pre;
// remove invalid characters
pre.sar(data, "[^?0-9/\\\\UuDdSsRr*+.{},()|=-]", "", "g");
// collapse alias chars
pre.tr(data, "s=-", "SSS");
pre.tr(data, "/udr\\\\", "UUDRD");
// convert "." into [^\t] since tab characters should not match "."
pre.sar(data, "\\.", "[^\\t]", "g");
}
//////////////////////////////
//
// cleanPrefinedContour --
//
void cleanPrefinedContour(Array& data) {
PerlRegularExpression pre;
// remove invalid characters
pre.sar(data, "[^?0-9UuDdSsRr*+.{},()|-]", "", "g");
// collapse alias chars
pre.tr(data, "-sr", "SSR");
// convert "." into [^\t] since tab characters should not match "."
pre.sar(data, "\\.", "[^\\t]", "g");
}
//////////////////////////////
//
// cleanPscaleDegree -- This function cleans user input so that it
// can be used to search the data which is formatted in a particular way.
//
void cleanPscaleDegree(Array& data) {
PerlRegularExpression pre;
if (pre.search(data, "[a-x]", "i")) {
// convert pitch names to scale degrees
// remove all accidentals
pre.sar(data, "( |-)?is", "", "gi"); // replace German sharp
pre.sar(data, "( |-)?es", "", "gi"); // replace German flat
pre.sar(data, "( |-)?sharp", "", "gi"); // replace acc names with syms
pre.sar(data, "( |-)?flat", "", "gi"); // replace acc names with syms
// convert solfege syllables into scale degrees.
int solfege = 0; // true if using solfege syllables
if (pre.sar(data, "ut|do", "1", "gi")) { solfege = 1; }
if (pre.sar(data, "re", "2", "gi")) { solfege = 1; }
if (pre.sar(data, "mi", "3", "gi")) { solfege = 1; }
if (pre.sar(data, "sol?", "5", "gi")) { solfege = 1; }
if (pre.sar(data, "la", "6", "gi")) { solfege = 1; }
if (pre.sar(data, "[ts]i", "7", "gi")) { solfege = 1; }
// if any other solfege syllables have been used, then convert
// "fa" into F; otherwise, assume that "fa" means "F A";
if (solfege) {
pre.sar(data, "fa", "4", "gi");
}
// remove any plain accidentals
pre.sar(data, "x", "", "gi");
pre.sar(data, "#", "", "gi");
pre.sar(data, "-", "", "gi");
// convert pitches to scale degrees as if they were in C major:
pre.sar(data, "c", "1", "gi");
pre.sar(data, "d", "2", "gi");
pre.sar(data, "e", "3", "gi");
pre.sar(data, "f", "4", "gi");
pre.sar(data, "g", "5", "gi");
pre.sar(data, "a", "6", "gi");
pre.sar(data, "b", "7", "gi");
}
// remove any characters which are not 1-7
pre.sar(data, "[^Rr1-7@+.]", "", "g");
pre.tr(data, "r@", "R+");
}
//////////////////////////////
//
// cleanP12toneInterval -- This function cleans user input so that it
// can be used to search the data which is formatted in a particular way.
//
void cleanP12toneInterval(Array& data) {
PerlRegularExpression pre;
// place plus in front of any intervals which don't have signs
pre.sar(data, "\\s(?=\\d)", " p", "g");
if (pre.search(data, "^\\d", "")) {
pre.sar(data, "^", "p", "");
}
// change plus and minus into "p" and "m"
pre.tr(data, "PM+\\-r", "pmpmR");
// change any m0 to p0:
pre.sar(data, "m0", "p0", "g");
// remove any disallowed characters
pre.sar(data, "[^Rr0-9pm~\\[\\]]", "", "g");
// change tilde sign into [pm] which means either up or down.
pre.sar(data, "~", "[pm]", "g");
}
//////////////////////////////
//
// cleanP12tonePitchClass -- This function cleans user input so that it
// can be used to search the data which is formatted in a particular way.
//
void cleanP12tonePitchClass(Array& data) {
PerlRegularExpression pre;
// change lowercase to upper case
// Y => A, Z => B. May use pitch classes in the future so
// minimizing use of A and B in raw search from user.
pre.tr(data, "yz", "YZ");
if (!pre.search(data, "[YZ]", "")) {
// convert "10" and "11" to "A" and "B" if they are present in query
pre.sar(data, "\\b10\\b", "Y", "g");
pre.sar(data, "\\b11\\b", "Z", "g");
}
if (pre.search(data, "[a-x]", "i")) {
// convert pitch names to 12-tone intervals, no triple sharps/flats
// or higher allowed.
pre.sar(data, "( |-)?is", "#", "gi"); // replace German sharp
pre.sar(data, "( |-)?es", "-", "gi"); // replace German flat
pre.sar(data, "( |-)?sharp", "#", "gi"); // replace acc names with syms
pre.sar(data, "( |-)?flat", "-", "gi"); // replace acc names with syms
// convert solfege syllables into English note names.
int solfege = 0; // true if using solfege syllables
if (pre.sar(data, "ut|do", "C", "gi")) { solfege = 1; }
if (pre.sar(data, "re", "D", "gi")) { solfege = 1; }
if (pre.sar(data, "mi", "E", "gi")) { solfege = 1; }
if (pre.sar(data, "sol?", "G", "gi")) { solfege = 1; }
if (pre.sar(data, "la", "A", "gi")) { solfege = 1; }
if (pre.sar(data, "[ts]i", "B", "gi")) { solfege = 1; }
// if any other solfege syllables have been used, then convert
// "fa" into F; otherwise, assume that "fa" means "F A";
if (solfege) {
pre.sar(data, "fa", "F", "gi");
}
pre.sar(data, "x", "##", "gi");
pre.sar(data, "c##", "2", "gi");
pre.sar(data, "d##", "4", "gi");
pre.sar(data, "e##", "6", "gi");
pre.sar(data, "f##", "7", "gi");
pre.sar(data, "g##", "9", "gi");
pre.sar(data, "a##", "Z", "gi");
pre.sar(data, "b##", "1", "gi");
pre.sar(data, "c#", "1", "gi");
pre.sar(data, "d#", "3", "gi");
pre.sar(data, "e#", "5", "gi");
pre.sar(data, "f#", "6", "gi");
pre.sar(data, "g#", "8", "gi");
pre.sar(data, "a#", "Y", "gi");
pre.sar(data, "b#", "0", "gi");
pre.sar(data, "c--", "Y", "gi");
pre.sar(data, "d--", "0", "gi");
pre.sar(data, "e--", "2", "gi");
pre.sar(data, "f--", "3", "gi");
pre.sar(data, "g--", "5", "gi");
pre.sar(data, "a--", "7", "gi");
pre.sar(data, "b--", "9", "gi");
pre.sar(data, "c-", "Z", "gi");
pre.sar(data, "d-", "1", "gi");
pre.sar(data, "e-", "3", "gi");
pre.sar(data, "f-", "4", "gi");
pre.sar(data, "g-", "6", "gi");
pre.sar(data, "a-", "8", "gi");
pre.sar(data, "b-", "Y", "gi");
pre.sar(data, "c", "0", "gi");
pre.sar(data, "d", "2", "gi");
pre.sar(data, "e", "4", "gi");
pre.sar(data, "f", "5", "gi");
pre.sar(data, "g", "7", "gi");
pre.sar(data, "a", "9", "gi");
pre.sar(data, "b", "Z", "gi");
}
// convert Y and Z symbols to A and B (pitches 10 and 11 in thema index
pre.tr(data, "rYZ", "RAB");
// remove any characters which are invalid
pre.sar(data, "[^RrAB0-9]", "", "g");
}
//////////////////////////////
//
// cleanPmusicalInterval -- based on processIntervalClassQuary in original
// PERL interface. This function cleans user input so that it can be
// used to search the data which is formatted in a particular way.
//
// Todo: add ^ and # wildcards.
//
void cleanPmusicalInterval(Array& data) {
PerlRegularExpression pre;
PerlRegularExpression pre2;
Array<Array<char> > pieces;
pieces.setSize(1000);
pieces.setSize(0);
pieces.setGrowth(100000);
// remove invalid characters
pre.sar(data, "[^Rr0-9\\+\\-MmPpAaDd\\.\\*\\? ]", "", "g");
// translate characters
pre.tr(data, "\\*\\+\\-Dapr", "SXxdAPR");
// extract individual intervals from data string:
const char* ptr;
int index;
// the following while loop should be improved, so that the second
// long regular expression is not needed (in other words, increment
// the pointer into the data for the start of the search so that
// the match does not need to be destroyed once it is found.
while (pre.search(data,
"(S|R|\\*\\s*|\\.\\s*\\??|[Xx]?[PdmMA]?\\d+\\s*\\??|[Xx]?[PdmMA]\\s*\\??|[Xx]\\s*\\?\?)",
"")) {
ptr = pre.getSubmatch(1);
if (pre2.search(ptr, "^\\s*$")) {
continue;
}
index = pieces.getSize();
pieces.setSize(index+1);
pieces[index].setSize(0);
appendString(pieces[index], ptr);
prepareInterval(pieces[index]);
pre.sar(data,
"(S|R|\\*\\s*|\\.\\s*\\??|[Xx]?[PdmMA]?\\d+\\s*\\??|[Xx]?[PdmMA]\\s*\\??|[Xx]\\s*\\?\?)",
"", "");
}
// prevent perfect unison from being mixed with P15, etc.
pre.sar(data, "1(?!\\d)", "1(?!\\d)", "gi");
data.setSize(0);
int i;
for (i=0; i<pieces.getSize(); i++) {
appendString(data, pieces[i].getBase());
}
// change * to any interval
pre.sar(data, "S", "(?:[Xx][mMPAd][1-9][0-9]?)*", "g");
}
//////////////////////////////
//
// prepareInterval -- Process a single interval item from the function
// cleanPmusicalInterval. Original function in PERL interface has
// the same name.
//
void prepareInterval(Array& data) {
PerlRegularExpression pre;
// rests are uninteresting to prepare
if (pre.search(data, "R", "")) {
return;
}
// * characters are handled later
if (pre.search(data, "S", "")) {
return;
}
// step 1: check to see which components are present
pre.search(data, "([Xx])?([MmPAd])?([0-9]+)?(.*)", "");
Array<char> direction;
Array<char> quality;
Array<char> ssize;
Array<char> other;
direction.setSize(1);
quality.setSize(1);
ssize.setSize(1);
other.setSize(1);
direction[0] = '\0';
quality[0] = '\0';
ssize[0] = '\0';
other[0] = '\0';
direction.setSize(0);
quality.setSize(0);
ssize.setSize(0);
other.setSize(0);
appendString(direction, pre.getSubmatch(1));
appendString(quality, pre.getSubmatch(2));
appendString(ssize, pre.getSubmatch(3));
appendString(other, pre.getSubmatch(4));
if (strcmp(other.getBase(), ".") == 0) {
data.setSize(0);
appendString(data, "([Xx]?[mMPAd][1-9][0-9]?)");
return;
}
if (strcmp(other.getBase(), "*") == 0) {
data.setSize(0);
appendString(data, "([Xx]?[mMPAd][1-9][0-9]?)*");
return;
}
if (strcmp(direction.getBase(), "") == 0) {
int isize = strtol(ssize.getBase(), NULL, 10);
if ((strcmp(quality.getBase(), "P") == 0) &&
(isize == 1)) {
// do not add an interval direction for P1
} else {
direction.setSize(0);
appendString(direction, "[Xx]?");
}
}
if (strcmp(quality.getBase(), "") == 0) {
quality.setSize(0);
appendString(quality, "[mMPAd]");
}
if (strcmp(ssize.getBase(), "") == 0) {
ssize.setSize(0);
appendString(ssize, "[1-9][0-9]?");
}
data.setSize(1);
data[0] = '\0';
data.setSize(0);
if (direction.getSize() > 0) {
appendString(data, direction.getBase());
}
if (quality.getSize() > 0) {
appendString(data, quality.getBase());
}
if (ssize.getSize() > 0) {
appendString(data, ssize.getBase());
}
PerlRegularExpression pre2;
// Perfect unisons do not have a direction so remove if present
pre2.sar(data, "[X|x]P1$", "P1", "");
if (other.getSize() > 0) {
if (pre2.search(other, "\\?", "")) {
Array<char> tempdata;
tempdata.setSize(0);
appendString(tempdata, "(");
appendString(tempdata, data.getBase());
appendString(tempdata, ")?");
data.setSize(0);
appendString(data, tempdata.getBase());
}
}
if (pre.search(data, "(?<!\\d)1$")) {
appendString(data, "\\t*");
}
// ggg
}
//////////////////////////////
//
// cleanPpitchClass -- based on processPitchQuery in original
// PERL interface. This function cleans user input so that it
// can be used to search the data which is formatted in a particular way.
//
void cleanPpitchClass(Array& data) {
PerlRegularExpression pre;
pre.sar(data, "\\+", "#", "g"); // allow for + to mean sharp
pre.sar(data, "( |-)?is", "#", "gi"); // replace German sharp
pre.sar(data, "( |-)?es", "-", "gi"); // replace German flat
pre.sar(data, "( |-)?sharp", "#", "gi"); // replace acc names with symbols
pre.sar(data, "( |-)?flat", "-", "gi"); // replace acc names with symbols
// convert solfege syllables into English note names.
int solfege = 0; // true if using solfege syllables
if (pre.sar(data, "ut|do", "C", "gi")) { solfege = 1; }
if (pre.sar(data, "re", "D", "gi")) { solfege = 1; }
if (pre.sar(data, "mi", "E", "gi")) { solfege = 1; }
if (pre.sar(data, "sol?", "G", "gi")) { solfege = 1; }
if (pre.sar(data, "la", "A", "gi")) { solfege = 1; }
if (pre.sar(data, "[ts]i", "B", "gi")) { solfege = 1; }
// if any other solfege syllables have been used, then convert
// "fa" into F; otherwise, assume that "fa" means "F A";
if (solfege) {
pre.sar(data, "fa", "F", "gi");
}
// remove duplicate spaces
pre.sar(data, "\\s+", " ", "g");
// adjust for aliases
pre.tr(data, "\n\txa-hsSrmM-", " XA-H##Rbbb");
pre.tr(data, "@", "+");
// expand double sharps
pre.sar(data, "X", "##", "gi");
// remove invalid chars
pre.sar(data, "[^nA-HRb# H(){},.?+^*0-9]", "", "g");
// make sure {} operator has valid syntax
cleanUpRangeSyntaxNoOutsideDigitsOrComma(data);
// convert parentheses temporarily into letters and store
// real regex operators in temporary letter values
pre.sar(data, "\\(", "Q", "g");
pre.sar(data, "\\)", "q", "g");
pre.sar(data, "q\\*", "qS", "g");
pre.sar(data, "\\*", "S", "g");
pre.sar(data, "q\\+", "qP", "g");
pre.sar(data, "\\+", "P", "g");
pre.sar(data, "q\\?", "qN", "g");
// if a German B-natural is presents, map B->Bb and H->B
if (pre.search(data.getBase(), "H")) {
pre.sar(data, "B", "Bb", "gi"); // transmute German B
pre.sar(data, "H", "B", "gi"); // transmute German H
}
// add spaces between notes
pre.sar(data, "(?<=[^ ])(?=[A-GRQq{.])", " ", "g");
// change meaning of * which means match to zero or more notes.
pre.sar(data, "\\*", "[YZ]*", "g");
// change meaning of "." which means any one pitch.
pre.sar(data, "\\.", "[A-G][#-]*", "g");
// remove duplicate spaces again
pre.sar(data, "\\s+", " ", "g");
// put parens around each note, then clean up:
// remove any leading spaces
pre.sar(data, "^\\s+", "");
// remove any trailing spaces
pre.sar(data, "\\s+$", "");
// if the -D option is specified, then than means the
// pitch names can contain any accidental (the search query
// should not have any accidentals listed).
if (diatonicQ) {
pre.sar(data, "(?<=[A-G])(?![#nb-])", "^", "g");
}
// remove natural sign (no longer needed after diatonic processing
pre.sar(data, "n", "", "g");
// put a parenthesis at very beginning and very end of string
pre.sar(data, "^", "(?:");
pre.sar(data, "$", ")");
// now put parentheses at each space separating a pitch.
pre.sar(data, " ", ") (?:", "g");
// move "?" outside of parens
pre.sar(data, "\\?\\) ", ")? ", "g");
pre.sar(data, "\\?\\)$", ")?" );
// fix the space/"?" association
pre.sar(data, "\\)\\?\\s", " )?", "g");
// fix the space/"+" association
pre.sar(data, "\\+\\)\\s", " )+", "g");
// change meaning of ^ which means any chromatic alteration of the note
// (including no alteration), in other words search for diatonic part
// with any chromatic alteration
pre.sar(data, "\\^", "(?:#+|b+)?", "g");
// finalize change of *
pre.sar(data, "Z", "\t", "g");
pre.sar(data, "Y", "^", "g");
pre.sar(data, "\\*\\) ", "*)", "g");
// change )( to ) (
pre.sar(data, "\\)\\(", ") (", "g");
// get rid of null notes
pre.sar(data, "\\(\\)", "", "g");
// get rid of parentheses around {} operator
pre.sar(data, "\\(\\?:\\{","{", "g");
pre.sar(data, "\\}\\)", "}", "g");
// get rid of space before {} operator
pre.sar(data, " \\{", "{", "g");
pre.sar(data, "\\( ", "(", "g");
pre.sar(data, " \\)", ")", "g");
// convert Q and q back into parentheses
pre.sar(data, "\\(\\?:Q\\) ", "(", "g");
pre.sar(data, "\\(\\?:q", "q", "g");
pre.sar(data, "(?<=q)\\) ?", "", "g");
pre.sar(data, "(?<=q[^ )])\\) ?","", "g");
pre.sar(data, "q", ")", "g");
// put regex operators back to normal:
pre.sar(data, "S\\) ", ") (?:[A-G][#b]* )*?", "g");
pre.sar(data, "P\\) ", " )+?", "g");
pre.sar(data, "P\\)$", " ?)+?", "g");
pre.sar(data, "S\\)$", ") (?:[A-G][#b]* ?)*?", "g");
pre.sar(data, "N", "?", "g");
// fix a bug related to * operator:
pre.sar(data, "\\(\\?:\\) ", "", "g");
// thema command adds the space later, so get rid of any at the end
pre.sar(data, " $", "");
// added 10 Dec 2000
pre.sar(data, "^ +", "");
// moved from appendToSearchString [20101123]
data.setSize(data.getSize() + strlen("[ \\t]"));
strcat(data.getBase(), "[ \\t]");
}
//////////////////////////////
//
// cleanUpRangeSyntax: disallow digits or commas to exist
// anywhere outside of the {} regular-expression operator.
//
void cleanUpRangeSyntaxNoOutsideDigitsOrComma(Array<char>& data) {
int i;
int inside = 0;
SSTREAM tempdata;
for (i=0; i<data.getSize()-1; i++) {
if ((inside == 0) && (data[i] == '{')) {
inside = 1;
tempdata << data[i];
continue;
}
if ((inside == 1) && (data[i] == '}')) {
inside = 0;
tempdata << data[i];
}
if (inside) {
if (isdigit(data[i]) || (data[i] == ',')) {
tempdata << data[i];
}
continue;
} else {
if (isdigit(data[i]) || (data[i] == ',') || (data[i] == '}') ||
(data[i] == '{')) {
continue;
} else {
tempdata << data[i];
continue;
}
}
}
tempdata << ends;
int len = strlen(tempdata.CSTRING);
data.setSize(len+1);
strcpy(data.getBase(), tempdata.CSTRING);
}
////////////////////////////////////////////////////////////////////////////
//
// smart search code ideas
//
// If --smart is added, then second argument is a **kern file which
// represents a search query as **kern data. (or if no second argument,
// then first argument is the **kern file, and standard input is the
// search index).
//
// * If there is a meter, then a search on pitches and metric position
// filtered for that meter will be done.
//
// * If there is a key designation, then that key will be used to filter
// the keys.
//
// For a smart search, if the most explicit search returns no results, then
// other less strict search features will be searched. For example if the
// exact pitches, duration and metric positions are not generating a match,
// then try pitches and durations without metric position. If that returns
// no matches, then try 12tone pitch classes (enharmonic spellings), then
// try pitch without durations, allowing repeated notes between pitches,
// allowing various chromatic alterations of the pitches, then musical
// intervals with durations, 12tone intervals with durations,
// musical intervals without durations, refined contour with durations,
// refined contour with refined duration contour, gross contour with refined
// duration contour, pitch gross contour with duration gross contour.
//
void processKernString(const char* astring) {
Array<Array<char> > tokens;
PerlRegularExpression::getTokens(tokens, "\\s+", astring);
PerlRegularExpression pre;
Array<char> tempc;
int noteQ;
Array<int> sequence;
sequence.setSize(tokens.getSize());
sequence.setSize(0);
int i;
for (i=0; i<tokens.getSize(); i++) {
tempc = tokens[i];
pre.sar(tempc, "([A-Ga-g])+", "", "");
pre.sar(tempc, "\\d+", "", "g");
pre.sar(tempc, "\\.+", "", "g");
pre.sar(tempc, "-+", "", "g");
pre.sar(tempc, "\\#+", "", "g");
if (pre.search(tempc, "^\\s*$")) {
noteQ = 1;
} else {
noteQ = 0;
}
if (noteQ) {
sequence.append(i);
// cout << tokens[i] << endl;
}
}
Array<int> pitches;
pitches.setSize(sequence.getSize());
Array<double> durations;
durations.setSize(sequence.getSize());
int pitchesq = 0;
int dursq = 0;
for (i=0; i<sequence.getSize(); i++) {
pitches[i] = Convert::kernToBase40(tokens[sequence[i]].getBase());
if ((pitches[i] > 1000) || (pitches[i] < 0)) {
pitches[i] = -1;
} else {
pitchesq = 1;
}
durations[i] = Convert::kernToDuration(tokens[sequence[i]].getBase());
if (durations[i] <= 0.0) {
} else {
dursq = 1;
}
}
if (pitchesq) {
PpitchclassQ = 1; // activate -p option
}
if (dursq) {
RdurationQ = 1; // activate -u option
}
SSTREAM pitchseq;
SSTREAM durseq;
Array<char> buffer(1024);
for (i=0; i<sequence.getSize(); i++) {
// cout << tokens[sequence[i]] << "\t" << pitches[i]
// << "\t" << durations[i] << endl;
if (pitchesq) {
if (pitches[i] > 0) {
Convert::base40ToKern(buffer.getBase(), pitches[i]%40 + 3*40);
pitchseq << buffer.getBase();
} else {
pitchseq << ".";
}
if (i < sequence.getSize()-1) {
pitchseq << ' ';
}
}
if (dursq) {
if (durations[i] > 0) {
Convert::durationToKernRhythm(buffer.getBase(), durations[i]);
if (pre.search(buffer.getBase(), "-")) {
durseq << "x";
} else {
durseq << buffer.getBase();
}
} else {
durseq << "x";
}
if (i < sequence.getSize()-1) {
durseq << ' ';
}
}
}
pitchseq << ends;
durseq << ends;
int plen = strlen(pitchseq.CSTRING);
int dlen = strlen(durseq.CSTRING);
Ppitchclass.setSize(plen+1);
Rduration.setSize(dlen+1);
strcpy(Ppitchclass.getBase(), pitchseq.CSTRING);
strcpy(Rduration.getBase(), durseq.CSTRING);
// cout << "PITCH SEQUENCE: " << Ppitchclass.getBase() << endl;
// cout << "DRUATION SEQUENCE: " << Rduration.getBase() << endl;
// exit(0);
}
// md5sum: 878e8c189062674f1d1eafb13c6f33df themax.cpp [20121112]