// // Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu> // Creation Date: Fri Jan 7 17:32:52 PST 2011 // Last Modified: Fri Jan 7 19:33:31 PST 2011 (added -o and -a options) // Filename: ...sig/examples/all/rscale.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/rscale.cpp // Syntax: C++; museinfo // // Description: Scale the duration of all **kern rhythms by a constant // factor. // // #include "PerlRegularExpression.h" #include "humdrum.h" #include <math.h> #include <string.h> #include <ctype.h> #ifndef OLDCPP #include <sstream> #define SSTREAM stringstream #define CSTRING str().c_str() using namespace std; #else #ifdef VISUAL #include <strstrea.h> /* for windows 95 */ #else #include <strstream.h> #endif #define SSTREAM strstream #define CSTRING str() #endif // function declarations void checkOptions(Options& opts, int argc, char* argv[]); void example(void); void usage(const char* command); void printOutput(HumdrumFile& infile, RationalNumber& rnum); void printKernToken(HumdrumFile& infile, int row, int col, RationalNumber& factor); void printSingleKernSubtoken(const char* buff, RationalNumber& factor); void printSpecialRational(RationalNumber& value); void processTimeSignature(HumdrumFile& infile, int row, RationalNumber& factor); void handleBibliographic(HumdrumFile& infile, int row, RationalNumber& num); void getOriginalFactor(HumdrumFile& infile, RationalNumber& factor); void getAlternateFactor(HumdrumFile& infile, RationalNumber& factor); void cleanUpBeams(char* prebuffer, char* postbuffer, int level); // global variables Options options; // database for command-line arguments int debugQ = 0; // used with --debug RationalNumber factor; // used with -f option int meterQ = 1; // used with -M option int FoundRef = 0; // used with !!!rscale: reference record int originalQ = 0; // used with --original int alternateQ = 0; // used with --alternate int longQ = 0; // used with -r option int rebeamQ = 0; // used with -B option /////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { HumdrumFile infile; // process the command-line options checkOptions(options, argc, argv); // figure out the number of input files to process int numinputs = options.getArgCount(); for (int i=0; i<numinputs || i==0; i++) { infile.clear(); // if no command-line arguments read data file from standard input if (numinputs < 1) { infile.read(cin); } else { infile.read(options.getArg(i+1)); } // analyze the input file according to command-line options infile.analyzeRhythm("4"); if (originalQ) { getOriginalFactor(infile, factor); } else if (alternateQ) { getAlternateFactor(infile, factor); } printOutput(infile, factor); } if ((!originalQ) && (!FoundRef) && (factor != 1)) { cout << "!!!rscale: " << factor << endl; } return 0; } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // printOutput -- // void printOutput(HumdrumFile& infile, RationalNumber& rnum) { int i, j; for (i=0; i<infile.getNumLines(); i++) { if (infile[i].isBibliographic()) { handleBibliographic(infile, i, rnum); continue; } if (!infile[i].isData()) { if (meterQ && infile[i].isInterpretation()) { processTimeSignature(infile, i, rnum); } else { cout << infile[i] << "\n"; } continue; } for (j=0; j<infile[i].getFieldCount(); j++) { if (!(infile[i].isExInterp(j, "**kern") || infile[i].isExInterp(j, "**recip"))) { cout << infile[i][j]; if (j < infile[i].getFieldCount() - 1) { cout << "\t"; } continue; } printKernToken(infile, i, j, rnum); if (j < infile[i].getFieldCount() - 1) { cout << "\t"; } } cout << "\n"; } } ////////////////////////////// // // getOriginalFactor -- return the inverse of the value in the ref record: // !!!rscale: 1/2 // Would return "2" in the above case. // void getOriginalFactor(HumdrumFile& infile, RationalNumber& factor) { int i; PerlRegularExpression pre; RationalNumber newvalue; int top; int bot; for (i=0; i<infile.getNumLines(); i++) { if (!infile[i].isBibliographic()) { continue; } if (!pre.search(infile[i][0], "^!!!rscale\\s*:\\s*(\\d+)(/?)(\\d*)", "")) { continue; } top = atoi(pre.getSubmatch(1)); bot = 1; if (strlen(pre.getSubmatch(2)) != 0) { if (strlen(pre.getSubmatch(3)) != 0) { bot = atoi(pre.getSubmatch()); } } if (top == 0) { top = 1; } if (bot == 0) { bot = 1; } newvalue.setValue(bot, top); factor = newvalue; return; } } /////////////////////////////// // // getAlternateFactor -- return the factor which will produce the preferred // alternate rhythmic values. // void getAlternateFactor(HumdrumFile& infile, RationalNumber& factor) { PerlRegularExpression pre; int i; int top; int bot; RationalNumber value; RationalNumber orig(1,1); getOriginalFactor(infile, orig); for (i=0; i<infile.getNumLines(); i++) { if (!infile[i].isBibliographic()) { continue; } if (!pre.search(infile[i][0], "^!!!rscale-alt\\s*:\\s*(\\d+)(/?)(\\d*)", "")) { continue; } top = atoi(pre.getSubmatch(1)); bot = 1; if (strlen(pre.getSubmatch(2)) != 0) { if (strlen(pre.getSubmatch(3)) != 0) { bot = atoi(pre.getSubmatch()); } } if (top == 0) { top = 1; } if (bot == 0) { bot = 1; } value.setValue(top, bot); factor = value * orig; return; } } ////////////////////////////// // // handleBibliographic -- print reference records. Scan for a reference // record in the form: // !!!rscale: 1/2 // which is the scaling factor applied to the file already. // Modify this value by the factor. Suppresss the record if // the output scaling is 1/1. // void handleBibliographic(HumdrumFile& infile, int row, RationalNumber& num) { char buffer[1024] = {0}; infile[row].getBibKey(buffer, 1000); if (strcmp(buffer, "rscale") != 0) { cout << infile[row] << endl; return; } RationalNumber onum; PerlRegularExpression pre; if (!pre.search(infile[row][0], "!!!rscale([^\\d]*)(\\d+)(/?)(\\d*)(.*)", "")) { cout << infile[row] << endl; return; } int top = atoi(pre.getSubmatch(2)); int bot = 1; if (strlen(pre.getSubmatch(3)) != 0) { if (strlen(pre.getSubmatch(4)) != 0) { bot = atoi(pre.getSubmatch()); } } if (bot == 0) { bot = 1; } onum.setValue(top, bot); onum *= num; FoundRef = 1; if (onum == 1) { // don't print !!!rscale: entry if scaling is 1. return; } if (originalQ) { // original rhythmic values are being generated, don't print ref rec. return; } cout << "!!!rscale"; cout << pre.getSubmatch(1); cout << onum; cout << pre.getSubmatch(5); cout << "\n"; } ////////////////////////////// // // processTimeSignature -- look for time signatures, and alter the // bottom of the time signature by the scaling factor. // void processTimeSignature(HumdrumFile& infile, int row, RationalNumber& factor) { int j; RationalNumber beat; int mtop; int top; int bot; PerlRegularExpression pre; for (j=0; j<infile[row].getFieldCount(); j++) { if (pre.search(infile[row][j], "^\\*tb(\\d+)(%?)(\\d*)$", "")) { // process a timebase interpretation top = atoi(pre.getSubmatch(1)); bot = 1; if (strlen(pre.getSubmatch(2)) != 0) { if (strlen(pre.getSubmatch(3)) != 0) { bot = atoi(pre.getSubmatch()); if (bot == 0) { bot = 1; } } } beat.setValue(top, bot); beat /= factor; cout << "*tb"; printSpecialRational(beat); if (j < infile[row].getFieldCount() - 1) { cout << "\t"; } continue; } if ((!meterQ) || (!pre.search(infile[row][j], "^\\*M(\\d+)/(\\d+)(%?)(\\d*)(.*)", ""))) { cout << infile[row][j]; if (j < infile[row].getFieldCount() - 1) { cout << "\t"; } continue; } mtop = atoi(pre.getSubmatch(1)); top = atoi(pre.getSubmatch(2)); bot = 1; if (strlen(pre.getSubmatch(3)) != 0) { if (strlen(pre.getSubmatch(4)) != 0) { bot = atoi(pre.getSubmatch()); if (bot == 0) { bot = 1; } } } beat.setValue(top, bot); beat /= factor; cout << "*M"; cout << mtop; cout << "/"; printSpecialRational(beat); cout << pre.getSubmatch(5); if (j < infile[row].getFieldCount() - 1) { cout << "\t"; } } cout << "\n"; } ///////////////////////////// // // printKernToken -- // void printKernToken(HumdrumFile& infile, int row, int col, RationalNumber& factor) { int k; static char buffer[1024] = {0}; if (strcmp(infile[row][col], ".") == 0) { // print null tokens and return cout << infile[row][col]; return; } if (strchr(infile[row][col], 'q') != NULL) { // print notes/chords which have grace notes (chord rhythms // should not mix and always be equal). cout << infile[row][col]; return; } PerlRegularExpression pre; int tcount = infile[row].getTokenCount(col); for (k=0; k<tcount; k++) { infile[row].getToken(buffer, col, k, 1000); printSingleKernSubtoken(buffer, factor); if (k < tcount - 1) { cout << " "; } } } ////////////////////////////// // // printSingleKernSubtoken -- // void printSingleKernSubtoken(const char* buff, RationalNumber& factor) { PerlRegularExpression pre; RationalNumber value; int level = 0; if (factor == 2) { level = 1; } else if (factor == 4) { level = 2; } if (!rebeamQ) { level = 0; } char prebuffer[1024] = {0}; char postbuffer[1024] = {0}; if (pre.search(buff, "([^\\d]*)(\\d+)(%?)(\\d*)(.*)", "")) { int top = atoi(pre.getSubmatch(2)); int bot; if (strlen(pre.getSubmatch(4)) != 0) { bot = atoi(pre.getSubmatch()); if (bot == 0) { bot = 1; } } else { bot = 1; } if (top == 0) { // handle cases where breve=0; long=00; maxima=000 int tcount = 0; int i; int len = strlen(buff); for (i=0; i<len; i++) { if (buff[i] == '0') { tcount++; } } top = 1; bot = int(pow(2.0,tcount)); } value.setValue(top, bot); value /= factor; // factor is duration so inverse strcpy(prebuffer, pre.getSubmatch(1)); strcpy(postbuffer, pre.getSubmatch(5)); cleanUpBeams(prebuffer, postbuffer, level); cout << prebuffer; printSpecialRational(value); // print stuff after numeric part of rhythm cout << postbuffer; } else { cout << buff; } } ////////////////////////////// // // cleanUpBeams -- remove k, K, L, J as needed from beam information. // void cleanUpBeams(char* prebuffer, char* postbuffer, int level) { if (level < 1) { return; } int kcount1 = 0; int Kcount1 = 0; int Lcount1 = 0; int Jcount1 = 0; int kcount2 = 0; int Kcount2 = 0; int Lcount2 = 0; int Jcount2 = 0; int len1 = strlen(prebuffer); int len2 = strlen(postbuffer); int i; for (i=0; i<len1; i++) { switch (prebuffer[i]) { case 'k': kcount1++; break; case 'K': Kcount1++; break; case 'L': Lcount1++; break; case 'J': Jcount1++; break; } } for (i=0; i<len2; i++) { switch (postbuffer[i]) { case 'k': kcount2++; break; case 'K': Kcount2++; break; case 'L': Lcount2++; break; case 'J': Jcount2++; break; } } Array<char> prestring; Array<char> poststring; prestring.setSize(len1+1); strcpy(prestring.getBase(), prebuffer); poststring.setSize(len1+1); strcpy(poststring.getBase(), postbuffer); PerlRegularExpression pre; if ((level == 1) && (Lcount1 > 0)) { pre.sar(prestring, "L", ""); level--; } else if ((level == 1) && (Jcount1 > 0)) { pre.sar(prestring, "J", ""); level--; } else if ((level == 1) && (Lcount2 > 0)) { pre.sar(prestring, "L", ""); level--; } else if ((level == 1) && (Jcount2 > 0)) { pre.sar(prestring, "J", ""); level--; } else if ((level == 2) && (Lcount1 > 1)) { pre.sar(prestring, "L", ""); pre.sar(prestring, "L", ""); level -= 2; } else if ((level == 2) && (Jcount1 > 1)) { pre.sar(prestring, "J", ""); pre.sar(prestring, "J", ""); level -= 2; } else if ((level == 2) && (Lcount2 > 1)) { pre.sar(prestring, "L", ""); pre.sar(prestring, "L", ""); level -= 2; } else if ((level == 2) && (Jcount2 > 1)) { pre.sar(prestring, "J", ""); pre.sar(prestring, "J", ""); level -= 2; } // deal with k and K later... strcpy(prebuffer, prestring.getBase()); strcpy(postbuffer, poststring.getBase()); } ////////////////////////////// // // printSpecialRational -- print rational number rhythm, but use 0 // for 1/2 (breve), 00 for 1/4 (long), or 000 for 1/8 (maxima). // void printSpecialRational(RationalNumber& value) { static RationalNumber half(1,2); // breve static RationalNumber quarter(1,4); // long static RationalNumber eighth(1,8); // maxima if (longQ) { // don't print 0 for breve, 00 for long or 000 for maxima. cout << value.getNumerator(); if (value.getDenominator() != 1) { cout << '%' << value.getDenominator(); } } else { if (value == half) { // breve alternate cout << "0"; } else if (value == quarter) { // long alternate cout << "00"; } else if (value == eighth) { // maxima alternate cout << "000"; } else { cout << value.getNumerator(); if (value.getDenominator() != 1) { cout << '%' << value.getDenominator(); } } } } ////////////////////////////// // // checkOptions -- validate and process command-line options. // void checkOptions(Options& opts, int argc, char* argv[]) { opts.define("d|f|factor=s:1/1", "Factor to multiply durations by"); opts.define("M|T|nometer=b", "Do not alter time signatures"); opts.define("o|original=b", "Revert to original rhythms"); opts.define("a|alternate=b", "Change to alternate rhythm set"); opts.define("r|long=b", "Do not use short form for breve,long,maxima"); opts.define("B|adjust-beams=b", "Adjust beaming for powers of 2 rscaling"); opts.define("debug=b"); // determine bad input line num opts.define("author=b"); // author of program opts.define("version=b"); // compilation info opts.define("example=b"); // example usages opts.define("h|help=b"); // short description opts.process(argc, argv); // handle basic options: if (opts.getBoolean("author")) { cout << "Written by Craig Stuart Sapp, " << "craig@ccrma.stanford.edu, Jan 2011" << endl; exit(0); } else if (opts.getBoolean("version")) { cout << argv[0] << ", version: 6 Jan 2011" << 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); } PerlRegularExpression pre; if (pre.search(opts.getString("factor"), "(\\d+)\\/?(\\d*)", "")) { int top = 1; int bot = 1; top = atoi(pre.getSubmatch(1)); if (strlen(pre.getSubmatch(2)) != 0) { bot = atoi(pre.getSubmatch()); if (bot == 0) { bot = 1; } } else { bot = 1; } factor.setValue(top, bot); } else { factor.setValue(1,1); cerr << "Scaling factor set to " << factor << endl; } debugQ = opts.getBoolean("debug"); if (debugQ) { cerr << "Scaling factor set to " << factor << endl; } meterQ = !opts.getBoolean("nometer"); originalQ = opts.getBoolean("original"); alternateQ = opts.getBoolean("alternate"); longQ = opts.getBoolean("long"); rebeamQ = opts.getBoolean("adjust-beams"); } ////////////////////////////// // // example -- example usage of the quality program // void example(void) { cout << " \n" << endl; } ////////////////////////////// // // usage -- gives the usage statement for the meter program // void usage(const char* command) { cout << " \n" << endl; } // md5sum: 2257c7799a9606009bf8cd5663342c1b rscale.cpp [20120727]