//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Tue Nov 2 18:28:33 PDT 2010
// Last Modified: Sun Nov 7 07:52:55 PST 2010
// Last Modified: Mon Apr 1 00:21:49 PDT 2013 Enabled multiple segment input
// Filename: ...sig/examples/all/serialize.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/serialize.cpp
// Syntax: C++; museinfo
//
// Description: Extract all spines from a multi-spine file into
// single-column output.
//
#include "humdrum.h"
#ifndef OLDCPP
using namespace std;
#endif
// function declarations
void checkOptions(Options& opts, int argc, char* argv[]);
void example(void);
void usage(const char* command);
int printHeader(HumdrumFile& infile);
void printFooter(HumdrumFile& infile);
void printTrack(HumdrumFile& infile, int track, int start);
void printOutput(HumdrumFile& infile);
int interpmatch(HumdrumFile& infile, int track, int start,
Array<char>& interp);
void printChordSeparator(HumdrumFile& infile, int row, int col,
char separator, int nth);
void analyzeMaxSubSpines(HumdrumFile& infile, Array<int>& parmax);
void printSubTracks(HumdrumFile& infile, int track, int start,
int submax);
void printSubSpine(HumdrumFile& infile, int track, int start,
int subspine);
void printSubToken(HumdrumFile& infile, int row, int col,
char separator, int nth);
// global variables
Options options; // database for command-line arguments
int mergeQ = 0; // used with -m option
Array<char> interp; // used with -i option
int chordQ = 0; // extract chord notes serially as well
int Exstart = 0; // used for -m -i option interaction
int Separator = ' '; // used with -c option and -s option
int subspineQ = 0; // used with -S option
const char* marker = "."; // used with -e option
int markerQ = 0; // used with -e option
int parsubQ = 0; // used with -p option
Array<int> parmax; // used with -p option
int nthQ = 0; // used with -n option
int nth = 0; // used with -n option
///////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) {
HumdrumFileSet infiles;
// process the command-line options
checkOptions(options, argc, argv);
// figure out the number of input files to process
int numinputs = options.getArgCount();
int i;
if (numinputs < 1) {
infiles.read(cin);
} else {
for (i=0; i<numinputs; i++) {
infiles.readAppend(options.getArg(i+1));
}
}
for (i=0; i<infiles.getCount(); i++) {
infiles[i].analyzeRhythm();
if (parsubQ) {
analyzeMaxSubSpines(infiles[i], parmax);
} else {
parmax.setSize(infiles[i].getMaxTracks()+1); // [0] is not used.
parmax.allowGrowth(0);
parmax.setAll(0);
}
Exstart = 0;
if (strcmp(infiles[i].getFilename(), "") != 0) {
cout << "!!!!SEGMENT: " << infiles[i].getFilename() << endl;
}
printOutput(infiles[i]);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// analyzeMaxSubSpines -- find the largest number of sub-spines in each
// spine.
//
void analyzeMaxSubSpines(HumdrumFile& infile, Array& parmax) {
parmax.setSize(infile.getMaxTracks()+1); // [0] is not used.
parmax.allowGrowth(0);
parmax.setAll(0);
Array<int> linemax;
linemax.setSize(infile.getMaxTracks()+1); // [0] is not used.
linemax.allowGrowth(0);
linemax.setAll(0);
int i, j;
for (i=0; i<infile.getNumLines(); i++) {
if (!infile[i].isData()) {
continue;
}
linemax.setAll(0);
for (j=0; j<infile[i].getFieldCount(); j++) {
linemax[infile[i].getPrimaryTrack(j)]++;
}
for (j=0; j<linemax.getSize(); j++) {
if (parmax[j] < linemax[j]) {
parmax[j] = linemax[j];
}
}
}
}
//////////////////////////////
//
// printOutput --
//
void printOutput(HumdrumFile& infile) {
int start = printHeader(infile);
int maxtrack = infile.getMaxTracks();
int i;
for (i=1; i<=maxtrack; i++) {
printTrack(infile, i, start);
if (parsubQ) {
printSubTracks(infile, i, start, parmax[i]);
}
if (parsubQ && !mergeQ) {
cout << "*-" << endl;
}
}
if (mergeQ) {
cout << "*-" << endl;
}
printFooter(infile);
}
//////////////////////////////
//
// printSubTracks --
//
void printSubTracks(HumdrumFile& infile, int track, int start, int submax) {
if (submax <= 1) {
// nothing to do...
return;
}
int i;
for (i=2; i<=submax; i++) {
printSubSpine(infile, track, start, i);
}
}
//////////////////////////////
//
// printSubSpine -- print a specific sub-spine sequence. Similar to
// printTrack but too messy to merge functionality.
//
void printSubSpine(HumdrumFile& infile, int track, int start, int subspine) {
int i, j;
int taber;
int counter = 0;
if (interp.getSize() > 1) {
if (!interpmatch(infile, track, start, interp)) {
return;
}
}
Array<int> currsub;
currsub.setSize(infile.getMaxTracks()+1);
currsub.allowGrowth(0);
Array<int> lastsub;
lastsub.setSize(infile.getMaxTracks()+1);
lastsub.allowGrowth(0);
int markerCounter = 0;
for (i=start; i<infile.getNumLines(); i++) {
if ((lastsub[track] >= subspine) && (currsub[track] < subspine)) {
if (markerQ && markerCounter) {
cout << "ZZZ" << marker << endl;
}
markerCounter++;
}
lastsub = currsub;
currsub.setAll(0);
switch (infile[i].getType()) {
case E_humrec_data_comment:
case E_humrec_data_kern_measure:
taber = 0;
for (j=0; j<infile[i].getFieldCount(); j++) {
currsub[infile[i].getPrimaryTrack(j)]++;
if (currsub[infile[i].getPrimaryTrack(j)] != subspine) {
break;
}
if (track == infile[i].getPrimaryTrack(j)) {
if (taber > 0) {
cout << "\t";
}
cout << infile[i][j];
counter++;
if (subspineQ) {
cout << "\n";
} else {
taber++;
}
}
}
if (taber > 0) {
cout << endl;
}
break;
case E_humrec_interpretation:
taber = 0;
for (j=0; j<infile[i].getFieldCount(); j++) {
currsub[infile[i].getPrimaryTrack(j)]++;
if (currsub[infile[i].getPrimaryTrack(j)] != subspine) {
break;
}
if (markerQ && (strcmp(infile[i][j], "*-") == 0)) {
// put marker at the end of the data for the spine so that
// the context command will not mix data from
// different spines. (grep -v out after context is run).
if (track == infile[i].getPrimaryTrack(j)) {
cout << "ZZZ2" << marker << "\n";
}
}
if ((strcmp(infile[i][0], "*-") == 0) && mergeQ) {
// suppress all spine terminators
// before the last one if merging
if (track != infile.getMaxTracks()) {
continue;
}
}
if (track == infile[i].getPrimaryTrack(j)) {
if (taber > 0) {
cout << "\t";
}
if (subspineQ || parsubQ) {
if (infile[i].isSpineManipulator(j)) {
// suppress spine manipulator characters
// so that output Humdrum file remains
// valid.
cout << "*" << "\n";
} else {
if (parsubQ && (strcmp(infile[i][j], "*-") == 0)) {
continue;
}
cout << infile[i][j] << "\n";
}
} else {
if (parsubQ && (strcmp(infile[i][j], "*-") == 0)) {
continue;
} else {
cout << infile[i][j];
}
taber++;
}
}
}
if (taber > 0) {
cout << endl;
}
break;
case E_humrec_data:
taber = 0;
for (j=0; j<infile[i].getFieldCount(); j++) {
currsub[infile[i].getPrimaryTrack(j)]++;
if (currsub[infile[i].getPrimaryTrack(j)] != subspine) {
continue;
}
if (track == infile[i].getPrimaryTrack(j)) {
if (taber > 0) {
cout << "\t";
}
if (chordQ) {
printChordSeparator(infile, i, j, Separator, nth);
counter++;
} else {
cout << infile[i][j];
counter++;
}
if (subspineQ) {
cout << "\n";
} else {
taber++;
}
}
}
if (taber > 0) {
cout << endl;
}
break;
case E_humrec_global_comment:
case E_humrec_empty:
case E_humrec_bibliography:
case E_humrec_none:
default:
cout << infile[i] << endl;
break;
}
if (infile[i].isInterpretation() && (strcmp(infile[i][0], "*-") == 0)) {
break;
}
}
// if ((counter > 0) && markerQ) {
// cout << marker << endl;
// }
}
//////////////////////////////
//
// interpmatch --
//
int interpmatch(HumdrumFile& infile, int track, int start,
Array<char>& interp) {
int i;
for (i=0; i<infile[start].getFieldCount(); i++) {
if (track == infile[start].getPrimaryTrack(i)) {
if (strcmp(infile[start].getExInterp(i), interp.getBase()) == 0) {
return 1;
} else {
return 0;
}
}
}
return 0;
}
//////////////////////////////
//
// printTrack -- print all data in a particular track.
//
void printTrack(HumdrumFile& infile, int track, int start) {
int i, j;
int taber;
if (interp.getSize() > 1) {
if (!interpmatch(infile, track, start, interp)) {
return;
}
}
Array<int> currsub;
currsub.setSize(infile.getMaxTracks()+1);
currsub.allowGrowth(0);
for (i=start; i<infile.getNumLines(); i++) {
currsub.setAll(0);
switch (infile[i].getType()) {
case E_humrec_data_comment:
case E_humrec_data_kern_measure:
taber = 0;
for (j=0; j<infile[i].getFieldCount(); j++) {
currsub[infile[i].getPrimaryTrack(j)]++;
if (parsubQ && (currsub[infile[i].getPrimaryTrack(j)] > 1)) {
break;
}
if (track == infile[i].getPrimaryTrack(j)) {
if (taber > 0) {
cout << "\t";
}
cout << infile[i][j];
if (subspineQ) {
cout << "\n";
} else {
taber++;
}
}
}
if (taber > 0) {
cout << endl;
}
break;
case E_humrec_interpretation:
taber = 0;
for (j=0; j<infile[i].getFieldCount(); j++) {
currsub[infile[i].getPrimaryTrack(j)]++;
if (parsubQ && (currsub[infile[i].getPrimaryTrack(j)] > 1)) {
break;
}
if ((strncmp(infile[i][0], "**", 2) == 0) && mergeQ) {
// suppress all exclusive interpretations
// after the first one if merging
if (Exstart == 0) {
Exstart++;
cout << infile[i][j];
taber++;
continue;
}
if (track != 1) {
continue;
}
}
if (markerQ && (strcmp(infile[i][j], "*-") == 0)) {
// put marker at the end of the data for the spine so that
// the context command will not mix data from
// different spines. (grep -v out after context is run).
if (track == infile[i].getPrimaryTrack(j)) {
cout << "ZZZ3" << marker << "\n";
}
}
if ((strcmp(infile[i][0], "*-") == 0) && (mergeQ || parsubQ)) {
// suppress all spine terminators
// before the last one if merging
if (track != infile.getMaxTracks()) {
continue;
}
}
if (track == infile[i].getPrimaryTrack(j)) {
if (taber > 0) {
cout << "\t";
}
if (subspineQ || parsubQ) {
if (infile[i].isSpineManipulator(j)) {
// suppress spine manipulator characters
// so that output Humdrum file remains
// valid.
cout << "*" << "\n";
} else {
if ((mergeQ || parsubQ) &&
(strcmp(infile[i][j], "*-") == 0)) {
continue;
}
cout << infile[i][j] << "\n";
}
} else {
if (mergeQ && (strcmp(infile[i][j], "*-") == 0)) {
continue;
}
cout << infile[i][j];
taber++;
}
}
}
if (taber > 0) {
cout << endl;
}
break;
case E_humrec_data:
taber = 0;
for (j=0; j<infile[i].getFieldCount(); j++) {
currsub[infile[i].getPrimaryTrack(j)]++;
if (parsubQ && (currsub[infile[i].getPrimaryTrack(j)] > 1)) {
break;
}
if (track == infile[i].getPrimaryTrack(j)) {
if (taber > 0) {
cout << "\t";
}
if (chordQ) {
printChordSeparator(infile, i, j, Separator, nth);
} else {
cout << infile[i][j];
}
if (subspineQ) {
cout << "\n";
} else {
taber++;
}
}
}
if (taber > 0) {
cout << endl;
}
break;
case E_humrec_global_comment:
case E_humrec_empty:
case E_humrec_bibliography:
case E_humrec_none:
default:
cout << infile[i] << endl;
break;
}
if (infile[i].isInterpretation() && (strcmp(infile[i][0], "*-") == 0)) {
return;
}
}
}
//////////////////////////////
//
// printChordSeparator --
//
void printChordSeparator(HumdrumFile& infile, int row, int col,
char separator, int nth) {
int count = infile[row].getTokenCount(col, separator);
int len = strlen(infile[row][col]);
Array<char> buf;
buf.setSize(len+1);
char* buffer = buf.getBase();
if (nth == 0) {
if (count <= 1) {
cout << infile[row][col];
return;
}
int i;
for (i=0; i<count; i++) {
infile[row].getToken(buffer, col, i, len+1, separator);
cout << buffer;
if (i < count - 1) {
cout << endl;
}
}
return;
}
// print only the specified sub-token, or a null token if the
// subtoken does not exist.
int index = nth - 1;
if (nth < 0) {
index = count + nth;
}
if ((index < 0) || (index >= count)) {
cout << ".";
return;
}
infile[row].getToken(buffer, col, index, len+1, separator);
cout << buffer;
}
//////////////////////////////
//
// printFooter -- print every line before the last *- in the file.
//
void printFooter(HumdrumFile& infile) {
int i;
int start = -1;
for (i=infile.getNumLines()-1; i>=0; i--) {
if (infile[i].isInterpretation()) {
start = i+1;
break;
}
}
if (start < 0) {
return;
}
for (i=start; i<infile.getNumLines(); i++) {
cout << infile[i] << endl;
}
}
//////////////////////////////
//
// printHeader -- print lines in the Humdrum file before the first
// exclusive interpretaion.
//
int printHeader(HumdrumFile& infile) {
int output = 0;
int i;
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].isInterpretation()) {
output = i;
break;
}
cout << infile[i] << endl;
}
return output;
}
//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//
void checkOptions(Options& opts, int argc, char* argv[]) {
// merging will not handle spine count changes between tracks.
opts.define("m|merge=b", "Merge data into single data spine");
opts.define("i|interp=s", "Serialize only specified exclusive interps.");
opts.define("c|chord=b", "Serialize notes in chords.");
opts.define("f|first=b", "Output only the first sub-token of data.");
opts.define("l|last=b", "Output only the last sub-token of data.");
opts.define("n|number=i:0", "Output only the nth sub-token of data.");
opts.define("s|separator=s: ", "Separator character");
opts.define("S|subspine=b", "Serialize subspines");
opts.define("t|marker=s:xxx", "end marker for each spine");
opts.define("p|parsub=b", "Serialize sub-spines in a different manner");
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, Nov 2010" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 2 Nov 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);
}
chordQ = opts.getBoolean("chord");
mergeQ = opts.getBoolean("merge");
subspineQ = opts.getBoolean("subspine");
nthQ = opts.getBoolean("number");
nth = opts.getInteger("number");
if (opts.getBoolean("first")) {
nthQ = 1;
nth = 1;
}
if (opts.getBoolean("last")) {
nthQ = 1;
nth = -1;
}
if (nthQ) {
chordQ = 1;
}
const char* ptr = opts.getString("interp");
int length = strlen(ptr);
interp.setSize(length+1);
if (length == 0) {
interp[0] = '\0';
} else if (strncmp(ptr, "**", 2) != 0) {
interp.setSize(length+3);
strcpy(interp.getBase(), "**");
strcat(interp.getBase(), ptr);
} else {
strcpy(interp.getBase(), ptr);
}
if (strlen(opts.getString("separator")) >= 1) {
Separator = opts.getString("separator")[0];
}
if (opts.getBoolean("marker")) {
markerQ = 1;
marker = opts.getString("marker");
}
parsubQ = opts.getBoolean("parsub");
}
//////////////////////////////
//
// example -- example usage of the quality program
//
void example(void) {
cout <<
" \n"
" \n"
<< endl;
}
//////////////////////////////
//
// usage -- gives the usage statement for the meter program
//
void usage(const char* command) {
cout <<
" \n"
" \n"
<< endl;
}
// md5sum: 54fdf298b2e78d54794163cea553bafe serialize.cpp [20130404]