//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Fri Apr 30 22:08:49 PDT 2010
// Last Modified: Fri Apr 30 22:08:55 PDT 2010
// Filename: ...sig/examples/all/mince.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/mince.cpp
// Syntax: C++; museinfo
//
// Description: Extract small segments of music from a **kern score.
//
#include "humdrum.h"
#include "PerlRegularExpression.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 processFile(HumdrumFile& infile, const char* filename);
void printSegmentSpine(Array<int>& segment, HumdrumFile& infile);
void generateMinceData(Array<int>& segment, HumdrumFile& infile,
const char* name);
void printSegment(ostream& out, int line, Array<int>& segment,
HumdrumFile& infile, int totalcount, int count);
int getPreviousSegment(int line, Array<int>& segment,
HumdrumFile& infile);
int getNextSegment(int line, Array<int>& segment,
HumdrumFile& infile);
int countSegments(Array<int>& segment);
void printInitialization(ostream& out, int line, HumdrumFile& infile);
void printTermination(ostream& out, int line, HumdrumFile& infile);
void printFilename(ostream& out, const char* name, int printpath);
void prepareMeterInfo(Array<int>& metertop, Array<int>& meterbot,
HumdrumFile& infile);
void prepareKeyInfo(Array<int>& key, HumdrumFile& infile);
void printPreviousPitches(ostream& out, int line, HumdrumFile& infile);
// global variables
Options options; // database for command-line arguments
int segQ = 0; // used with -s option
int measureQ = 1; // used with -b option
int meterQ = 1; // used with -M option
int baseQ = 0; // used with -B option
int keyQ = 1; // used with -K option
const char* dfilename = NULL; // used with -f option
Array<int> metertop;
Array<int> meterbot;
Array<int> key;
///////////////////////////////////////////////////////////////////////////
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();
const char* filename = "";
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);
filename = "";
} else {
filename = options.getArg(i+1);
infile.read(filename);
}
// analyze the input file according to command-line options
infile.analyzeRhythm();
if (dfilename != NULL) {
filename = dfilename;
}
processFile(infile, filename);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// processFile --
//
void processFile(HumdrumFile& infile, const char* filename) {
int i, j, k;
int tokencount;
char buffer[1024];
int state;
int ii, jj;
Array<int> segment;
segment.setSize(infile.getNumLines());
segment.allowGrowth(0);
segment.setAll(0);
for (i=0; i<infile.getNumLines(); i++) {
state = 1;
if (!infile[i].isData()) {
continue;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
if (!infile[i].isExInterp(j, "**kern")) {
continue;
}
if (strcmp(infile[i][j], ".") == 0) {
ii = infile[i].getDotLine(j);
jj = infile[i].getDotSpine(j);
if (strchr(infile[ii][jj], 'r') == NULL) {
state = 0;
break;
}
}
if (state == 0) {
break;
}
tokencount = infile[i].getTokenCount(j);
for (k=0; k<tokencount; k++) {
infile[i].getToken(buffer, j, k);
if (strcmp(buffer, ".") == 0) {
// don't know why you would get here...
continue;
}
if (strchr(buffer, ']') != NULL) {
state = 0;
break;
}
if (strchr(buffer, '_') != NULL) {
state = 0;
break;
}
}
}
segment[i] = state;
}
if (segQ) {
printSegmentSpine(segment, infile);
} else {
generateMinceData(segment, infile, filename);
}
}
//////////////////////////////
//
// generateMinceData --
//
void generateMinceData(Array<int>& segment, HumdrumFile& infile,
const char* name) {
int i;
int segmentcount = countSegments(segment);
int counter = 1;
if (meterQ) {
prepareMeterInfo(metertop, meterbot, infile);
}
if (keyQ) {
prepareKeyInfo(key, infile);
}
for (i=0; i<segment.getSize(); i++) {
if (segment[i]) {
if (counter > 1) {
cout << endl;
}
cout << "!!!filename:\t";
printFilename(cout, name, baseQ);
cout << endl;
cout << "!!!segment:\t" << counter << "/" << segmentcount << endl;
printSegment(cout, i, segment, infile, segmentcount, counter++);
}
}
}
//////////////////////////////
//
// prepareKeyInfo --
//
void prepareKeyInfo(Array& key, HumdrumFile& infile) {
key.setSize(infile.getNumLines());
key.allowGrowth(0);
key.setAll(-1);
int lkey = -1;
PerlRegularExpression pre;
int i, j;
for (i=0; i<infile.getNumLines(); i++) {
key[i] = lkey;
if (!infile[i].isInterpretation()) {
continue;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
if (!infile[i].isExInterp(j, "**kern")) {
continue;
}
if (pre.search(infile[i][j], "^\\*([A-Ga-g]):")) {
lkey = Convert::kernToBase40(pre.getSubmatch(1));
key[i] = lkey;
break;
}
}
}
}
//////////////////////////////
//
// prepareMeterInfo --
//
void prepareMeterInfo(Array<int>& metertop, Array<int>& meterbot,
HumdrumFile& infile) {
metertop.setSize(infile.getNumLines());
meterbot.setSize(infile.getNumLines());
metertop.allowGrowth(0);
meterbot.allowGrowth(0);
metertop.setAll(-1);
meterbot.setAll(-1);
int mettop = -1;
int metbot = -1;
PerlRegularExpression pre;
int i, j;
for (i=0; i<infile.getNumLines(); i++) {
metertop[i] = mettop;
meterbot[i] = metbot;
if (!infile[i].isInterpretation()) {
continue;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
if (!infile[i].isExInterp(j, "**kern")) {
continue;
}
if (pre.search(infile[i][j], "^\\*M(\\d+)/(\\d+)$")) {
mettop = int(atof(pre.getSubmatch(1))+0.5);
metbot = int(atof(pre.getSubmatch(2))+0.5);
metertop[i] = mettop;
meterbot[i] = metbot;
break;
}
}
}
}
//////////////////////////////
//
// printFilename --
//
void printFilename(ostream& out, const char* name, int printpath) {
if (printpath) {
out << name;
} else {
const char* ptr = strrchr(name, '/');
if (ptr != NULL) {
ptr++;
out << ptr;
} else {
out << name;
}
}
}
//////////////////////////////
//
// printSegment --
//
void printSegment(ostream& out, int line, Array<int>& segment,
HumdrumFile& infile, int totalcount, int count) {
int lastseg = getPreviousSegment(line, segment, infile);
int nextseg = getNextSegment(line, segment, infile);
char buffer[128] = {0};
double sbeat = infile[line].getBeat();
double nbeat = infile[nextseg].getBeat();
double abeat = infile[line].getAbsBeat();
out << "!!!sbeat:\t" << sbeat << endl;
out << "!!!nbeat:\t" << nbeat << endl;
out << "!!!abeat:\t" << abeat << endl;
out << "!!!duration:\t" << infile[nextseg].getAbsBeat() - abeat << endl;
if (meterQ && (metertop[line] > 0) && (meterbot[line] > 0)) {
out << "!!!meter:\t" << metertop[line] << "/" << meterbot[line] << endl;
}
if (keyQ && (key[line] > 0)) {
out << "!!!key:\t\t" << Convert::base40ToKern(buffer, key[line]) << endl;
}
printInitialization(out, lastseg, infile);
printPreviousPitches(out, line, infile);
// print spine adjustments if necessary to get to next segment here.
int i;
for (i=line; i<nextseg; i++) {
if (!measureQ && infile[i].isMeasure()){
continue;
}
out << infile[i] << endl;
}
printTermination(out, nextseg, infile);
}
//////////////////////////////
//
// printInitialization --
//
void printInitialization(ostream& out, int line, HumdrumFile& infile) {
// print data start markers. Deal with sub-spines later at this point.
int i;
for (i=0; i<infile[line].getFieldCount(); i++) {
out << infile[line].getExInterp(i);
if (i < infile[line].getFieldCount() - 1) {
out << "\t";
}
}
out << endl;
// deal with spine changes between previous chord segment and current one
// print pitches at the start of the previous segment:
int j, k;
int tokencount;
char buffer[128] = {0};
char buffer2[128] = {0};
if (infile[line].isData()) {
for (j=0; j<infile[line].getFieldCount(); j++) {
if (!infile[line].isExInterp(j, "**kern")) {
out << "!<<" << infile[line][j];
if (j < infile[line].getFieldCount()-1) {
out << "\t";
}
continue;
}
tokencount = infile[line].getTokenCount(j);
out << "!<<";
for (k=0; k<tokencount; k++) {
infile[line].getToken(buffer, j, k);
if (strchr(buffer, 'r') != NULL) {
out << "r";
} else {
Convert::base40ToKern(buffer2, Convert::kernToBase40(buffer));
out << buffer2;
}
if (k < tokencount - 1) {
out << " ";
}
}
if (j < infile[line].getFieldCount()-1) {
out << "\t";
}
}
out << "\n";
}
// print spine adjustments if necessary to get to next segment here.
}
//////////////////////////////
//
// printPreviousPitches --
//
void printPreviousPitches(ostream& out, int line, HumdrumFile& infile) {
int i;
for (i=line-1; i>0; i--) {
if (infile[i].isData()) {
break;
}
}
if (i >= line) {
return;
}
if (!infile[i].isData()) {
// no data lines found before current line;
return;
}
line = i;
int j, k;
int ii, jj;
int tokencount;
char buffer[128] = {0};
char buffer2[128] = {0};
int paren;
// print pitches at the end of the previous segment:
if (infile[line].isData()) {
for (j=0; j<infile[line].getFieldCount(); j++) {
if (!infile[line].isExInterp(j, "**kern")) {
out << "!<" << infile[line][j];
if (j < infile[line].getFieldCount()-1) {
out << "\t";
}
continue;
}
if (strcmp(infile[line][j], ".") == 0) {
ii = infile[line].getDotLine(j);
jj = infile[line].getDotSpine(j);
paren = 1;
} else {
ii = line;
jj = j;
paren = 0;
}
tokencount = infile[ii].getTokenCount(jj);
out << "!<";
for (k=0; k<tokencount; k++) {
infile[ii].getToken(buffer, jj, k);
if (strchr(buffer, 'r') != NULL) {
out << "r";
} else {
Convert::base40ToKern(buffer2, Convert::kernToBase40(buffer));
if (paren) {
out << "(";
}
out << buffer2;
if (paren) {
out << ")";
}
}
if (k < tokencount - 1) {
out << " ";
}
}
if (j < infile[line].getFieldCount()-1) {
out << "\t";
}
}
out << "\n";
}
}
//////////////////////////////
//
// printTermination -- print end of data marker for particular segment.
//
void printTermination(ostream& out, int line, HumdrumFile& infile) {
if (strcmp(infile[line][0], "*-") == 0) {
out << infile[line] << endl;
return;
}
// print pitches which follow in next segment:
int j, k;
int tokencount;
char buffer[128] = {0};
char buffer2[128] = {0};
if (infile[line].isData()) {
for (j=0; j<infile[line].getFieldCount(); j++) {
if (!infile[line].isExInterp(j, "**kern")) {
out << "!>" << infile[line][j];
if (j < infile[line].getFieldCount()-1) {
out << "\t";
}
continue;
}
tokencount = infile[line].getTokenCount(j);
out << "!>";
for (k=0; k<tokencount; k++) {
infile[line].getToken(buffer, j, k);
if (strchr(buffer, 'r') != NULL) {
out << "r";
} else {
Convert::base40ToKern(buffer2, Convert::kernToBase40(buffer));
out << buffer2;
}
if (k < tokencount - 1) {
out << " ";
}
}
if (j < infile[line].getFieldCount()-1) {
out << "\t";
}
}
}
out << "\n";
// just terminate data lines for now, fix split spines later...
int i;
for (i=0; i<infile[line].getFieldCount(); i++) {
out << "*-";
if (i < infile[line].getFieldCount()-1) {
out << "\t";
}
}
out << endl;
}
//////////////////////////////
//
// countSegments --
//
int countSegments(Array& segment) {
int counter = 0;
int i;
for (i=0; i<segment.getSize(); i++) {
if (segment[i]) {
counter++;
}
}
return counter;
}
//////////////////////////////
//
// getPreviousSegment -- return the line of the previous segment, or the
// start of data line if no previous segment point.
//
int getPreviousSegment(int line, Array& segment, HumdrumFile& infile) {
int i;
for (i=line-1; i>0; i--) {
if (infile[i].isInterpretation() &&
(strncmp(infile[i][0], "**", 2) == 0)) {
return i;
}
if (segment[i] != 0) {
return i;
}
}
// this line should not be reached.
return 0;
}
//////////////////////////////
//
// getNextSegment --
//
int getNextSegment(int line, Array& segment, HumdrumFile& infile) {
int i;
for (i=line+1; i<infile.getNumLines(); i++) {
if (strcmp(infile[i][0], "*-") == 0) {
return i;
}
if (segment[i]) {
return i;
}
}
// shouldn't get to this point.
return infile.getNumLines()-1;
}
//////////////////////////////
//
// printSegmentSpine --
//
void printSegmentSpine(Array& segment, HumdrumFile& infile) {
int i;
int counter = 1;
for (i=0; i<infile.getNumLines(); i++) {
switch (infile[i].getType()) {
case E_humrec_data_comment:
cout << "!\t" << infile[i] << endl;
break;
case E_humrec_data_kern_measure:
cout << infile[i][0] << "\t" << infile[i] << endl;
break;
case E_humrec_interpretation:
if (strncmp(infile[i][0], "**", 2) == 0) {
cout << "**segment";
} else if (strcmp(infile[i][0], "*-") == 0) {
cout << "*-";
} else if (strncmp(infile[i][0], "*>", 2) == 0) {
cout << infile[i][0];
} else {
cout << "*";
}
cout << "\t" << infile[i] << endl;
break;
case E_humrec_data:
if (segment[i]) {
cout << counter++;
} else {
cout << ".";
}
cout << "\t" << infile[i] << endl;
break;
case E_humrec_none:
case E_humrec_empty:
case E_humrec_global_comment:
case E_humrec_bibliography:
default:
cout << infile[i] << endl;
}
}
}
//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("K|no-key=b", "don't list current key");
opts.define("B|no-directory=b", "don't list file pathname");
opts.define("s|segment=b", "output original data with segment spine");
opts.define("f|filename=s:", "text to place in !!!filename: field");
opts.define("b|no-barlines=b", "don't preserve barlines in output data");
opts.define("M|no-meter=b", "don't display current meter");
opts.define("debug=b"); // determine bad input line num
opts.define("author=b"); // author of program
opts.define("version=b"); // compilation info
opts.define("example=b"); // example usages
opts.define("h|help=b"); // short description
opts.process(argc, argv);
// handle basic options:
if (opts.getBoolean("author")) {
cout << "Written by Craig Stuart Sapp, "
<< "craig@ccrma.stanford.edu, April 2010" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 30 April 2010" << endl;
cout << "compiled: " << __DATE__ << endl;
cout << MUSEINFO_VERSION << endl;
exit(0);
} else if (opts.getBoolean("help")) {
usage(opts.getCommand());
exit(0);
} else if (opts.getBoolean("example")) {
example();
exit(0);
}
baseQ = !opts.getBoolean("no-directory");
keyQ = !opts.getBoolean("no-key");
segQ = opts.getBoolean("segment");
measureQ = !opts.getBoolean("no-barlines");
meterQ = !opts.getBoolean("no-meter");
if (opts.getBoolean("filename")) {
dfilename = opts.getString("filename");
}
}
//////////////////////////////
//
// example -- example usage of the quality program
//
void example(void) {
cout <<
" \n"
<< endl;
}
//////////////////////////////
//
// usage -- gives the usage statement for the meter program
//
void usage(const char* command) {
cout <<
" \n"
<< endl;
}
// md5sum: 075feba4c6cbb42049565f87c6cfb3e2 mince.cpp [20100505]