//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sun Aug 5 12:58:51 PDT 2012
// Last Modified: Sun Aug 5 12:58:55 PDT 2012
// Filename: ...sig/examples/all/sverse.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/score/sverse.cpp
// Syntax: C++; museinfo
//
// Description: Display staff information for a SCORE page file.
//
#include "PerlRegularExpression.h"
#include "ScorePageSet.h"
#include "Options.h"
#include <string.h>
#include <stdio.h>
#include <math.h>
// function declarations:
void checkOptions(Options& opts, int argc, char* argv[]);
void example(void);
void usage(const char* command);
void printLyrics(ScorePageSet& work, Array<int>& staves);
void printLyricsByLineBreak(ScorePageSet& work, Array<int>& staves);
void printLyricsInfo(ScorePageSet& work, Array<int>& staves);
void printVerseWithBreaks(ScorePageSet& work, int staffidx,
int verseidx);
void printVerse(ScorePageSet& work, int staffidx,
int verseidx);
int getPrettyScoreText(Array<char>& textdata, ScoreRecord& arecord);
// block parsing functions:
void fillFieldData(Array<int>& field, const char* fieldstring,
int maxval);
void processFieldEntry(Array<int>& field, const char* string,
int maxval);
void removeDollarsFromString(Array<char>& buffer, int maxval);
void printInfo(ScorePage& page);
// interface variables:
Options options;
int verboseQ = 0; // used with -v option
int debugQ = 0; // used with --debug option
int breakQ = 0; // used with -b option
int infoQ = 0; // used with -i option
int staffQ = 0; // used with -s option
int plainQ = 0; // used with -p option
int htmlQ = 0; // used with -h option
//////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
checkOptions(options, argc, argv);
int i;
ScorePageSet work;
for (i=1; i<=options.getArgCount(); i++) {
work.appendRead(options.getArg(i), verboseQ);
}
work.analyzeContent();
// determine the extraction staves
Array<int> versestaves;
if (staffQ) {
fillFieldData(versestaves, options.getString("staff-list"),
work.getPartCount());
for (i=0; i<versestaves.getSize(); i++) {
versestaves[i]--;
}
if (debugQ) {
cout << "# SYSTEM LIST: " << versestaves << endl;
}
} else {
versestaves.setSize(work.getPartCount());
versestaves.setAll(0, 1);
}
if (infoQ) {
printLyricsInfo(work, versestaves);
} else if (breakQ) {
printLyricsByLineBreak(work, versestaves);
} else {
printLyrics(work, versestaves);
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// printLyricsInfo --
//
void printLyricsInfo(ScorePageSet& work, Array& versestaves) {
int count = 0;
int pcount = work.getPartCount();
int i;
for (i=0; i<pcount; i++) {
if (work.getVerseCount(i)) {
count++;
}
}
if (count == 0) {
cout << "Work has no lyrics." << endl;
return;
}
cout << count << " system ";
if (count == 1) {
cout << "staff";
} else {
cout << "staves";
}
cout << " has lyrics (from top to bottom):" << endl;
for (i=pcount-1; i>=0; i--) {
if (work.getVerseCount(i)) {
cout << "\t-- Staff " << pcount-i << " has " << work.getVerseCount(i);
if (work.getVerseCount(i) > 1) {
cout << " verses.";
} else {
cout << " verse.";
}
cout << endl;
}
}
}
//////////////////////////////
//
// printLyricsByLineBreak --
//
void printLyricsByLineBreak(ScorePageSet& work, Array& versestaves) {
int count = 0;
int pcount = work.getPartCount();
int i;
for (i=0; i<pcount; i++) {
if (work.getVerseCount(i)) {
count++;
}
}
if (count == 0) {
cout << "Work has no lyrics." << endl;
return;
}
int ii;
int j;
for (i=0; i<versestaves.getSize(); i++) {
ii = pcount - versestaves[i] - 1;
if (work.getVerseCount(ii)) {
cout << "@STAFF\t" << versestaves[i]+1 << " has "
<< work.getVerseCount(ii);
if (work.getVerseCount(ii) > 1) {
cout << " verses:";
} else {
cout << " verse:";
}
cout << endl;
for (j=0; j<work.getVerseCount(ii); j++) {
printVerseWithBreaks(work, ii, j);
}
}
}
}
/////////////////////////////
//
// printLyrics -- print on continuous line for each verse.
//
void printLyrics(ScorePageSet& work, Array& versestaves) {
int count = 0;
int pcount = work.getPartCount();
int i;
for (i=0; i<pcount; i++) {
if (work.getVerseCount(i)) {
count++;
}
}
if (count == 0) {
cout << "Work has no lyrics." << endl;
return;
}
int ii;
int j;
for (i=0; i<versestaves.getSize(); i++) {
ii = pcount - versestaves[i] - 1;
if (work.getVerseCount(ii)) {
cout << "@STAFF\t" << versestaves[i]+1 << " has "
<< work.getVerseCount(ii);
if (work.getVerseCount(ii) > 1) {
cout << " verses:";
} else {
cout << " verse:";
}
cout << endl;
for (j=0; j<work.getVerseCount(ii); j++) {
printVerse(work, ii, j);
cout << endl;
}
}
}
}
//////////////////////////////
//
// printVerseWithBreaks --
//
void printVerseWithBreaks(ScorePageSet& work, int staffidx, int verseidx) {
int i, j, k;
int tvindex;
ScoreRecord* srecord;
Array<char> textdata;
int endhyphen = 0;
cout << "\n@Verse: " << verseidx+1 << endl;
if (verseidx > 0) {
cout << endl;
}
int tcount;
int charcount = 0;
Array<char> syllable(100);
syllable.setSize(0);
int blankchecking = 0;
for (i=0; i<work.getPageCount(); i++) {
for (j=0; j<work.getPage(i).getSystemCount(); j++) {
tcount = 0;
blankchecking = 0;
for (k=0; k<work.getPage(i).getSystemStaffItemCount(j, staffidx); k++){
srecord = &(work.getPage(i).getSystemStaffItem(j, staffidx, k));
if (!srecord->isTextItem()) {
continue;
}
tvindex = srecord->getVerseIndex();
if (tvindex != verseidx) {
continue;
}
charcount = getPrettyScoreText(syllable, *srecord);
if (charcount == 0) {
continue;
}
if (tcount++ && srecord->isWordStart()) {
cout << " ";
}
cout << syllable;
if (charcount > 0) {
blankchecking++;
}
endhyphen = !(srecord->isWordEnd());
}
if (endhyphen) {
cout << "-";
}
if (blankchecking == 0) {
cout << "[blank]";
}
cout << endl;
}
cout << endl;
}
}
//////////////////////////////
//
// getPrettyScoreText -- returns the number of charcters printed
//
int getPrettyScoreText(Array& textdata, ScoreRecord& arecord) {
arecord.getTextDataWithoutFonts(textdata);
if (plainQ) {
ScoreRecord::convertScoreTextToPlainText(textdata);
} else if (htmlQ) {
ScoreRecord::convertScoreTextToHtmlText(textdata);
}
return strlen(textdata.getBase());
}
//////////////////////////////
//
// printVerse --
//
void printVerse(ScorePageSet& work, int staffidx, int verseidx) {
int i, j, k;
int tvindex;
ScoreRecord* srecord;
Array<char> textdata;
int endhyphen = 0;
cout << "\n@Verse: " << verseidx+1 << endl << endl;
int linebreak = 0;
Array<char> syllable(100);
syllable.setSize(0);
int tcount;
tcount = 0;
int lastsys = -1;
int system = -1;
for (i=0; i<work.getPageCount(); i++) {
for (j=0; j<work.getPage(i).getSystemCount(); j++) {
system++;
for (k=0; k<work.getPage(i).getSystemStaffItemCount(j, staffidx);
k++) {
srecord = &(work.getPage(i).getSystemStaffItem(j, staffidx, k));
if (!srecord->isTextItem()) {
continue;
}
tvindex = srecord->getVerseIndex();
if (tvindex != verseidx) {
continue;
}
if (!linebreak && tcount++ && srecord->isWordStart()) {
cout << " ";
}
if (lastsys < system-1) {
cout << "[blank] ";
}
// cout << srecord->getTextDataWithoutFonts(textdata);
getPrettyScoreText(syllable, *srecord);
lastsys = system;
cout << syllable;
linebreak = 0;
endhyphen = !(srecord->isWordEnd());
}
}
cout << endl;
linebreak = 1;
}
}
//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("i|info=b", "display lyric informatio");
opts.define("v|verbose=b", "verbose display of information");
opts.define("b|break|line-breaks=b",
"break lyric lines by staff line breaks");
opts.define("s|staff|staff-list=i",
"give a list system staves from which to extract");
opts.define("p|plain=b", "convert verse text to plain ASCII text");
opts.define("html=b", "convert verse text to HTML text");
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, Aug 2012" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 5 Aug 2012" << endl;
cout << "compiled: " << __DATE__ << endl;
exit(0);
} else if (opts.getBoolean("help")) {
usage(opts.getCommand());
exit(0);
} else if (opts.getBoolean("example")) {
example();
exit(0);
}
staffQ = opts.getBoolean("staff-list");
debugQ = opts.getBoolean("debug");
breakQ = opts.getBoolean("break");
infoQ = opts.getBoolean("info");
plainQ = opts.getBoolean("plain");
htmlQ = opts.getBoolean("html");
if (plainQ && htmlQ) {
cerr << "ERROR: plain and html text conversions not allowed at the same time."
<< endl;
exit(1);
}
}
//////////////////////////////
//
// 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;
}
///////////////////////////////////////////////////////////////////////////
//
// Spine field list extraction functions
//
//////////////////////////////
//
// fillFieldData --
//
void fillFieldData(Array& field, const char* fieldstring, int maxval) {
field.setSize(maxval);
field.setGrowth(0);
field.setAll(0);
Array<int> tempfield;
tempfield.setSize(maxval);
tempfield.setSize(0);
PerlRegularExpression pre;
Array<char> buffer;
buffer.setSize(strlen(fieldstring)+1);
strcpy(buffer.getBase(), fieldstring);
pre.sar(buffer, "\\s", "", "gs");
int start = 0;
int value = 0;
value = pre.search(buffer.getBase(), "^([^,]+,?)");
while (value != 0) {
start += value - 1;
start += strlen(pre.getSubmatch(1));
processFieldEntry(tempfield, pre.getSubmatch(), maxval);
value = pre.search(buffer.getBase() + start, "^([^,]+,?)");
}
if (debugQ) {
cout << "# tempfield: " << tempfield << endl;
}
field = tempfield;
//int i;
//for (i=0; i<tempfield.getSize(); i++) {
// field[tempfield[i]-1] = 1;
//}
}
//////////////////////////////
//
// processFieldEntry --
// 3-6 expands to 3 4 5 6
// $ expands to maximum block number
// $-1 expands to maximum spine track minus 1, etc.
//
void processFieldEntry(Array& field, const char* string, int maxval) {
PerlRegularExpression pre;
Array<char> buffer;
buffer.setSize(strlen(string)+1);
strcpy(buffer.getBase(), string);
// remove any comma left at end of input string (or anywhere else)
pre.sar(buffer, ",", " ", "g");
pre.sar(buffer, "\\s+$", "");
pre.sar(buffer, "^\\s+", "");
if (debugQ) {
cout << "MAXBLOCK = " << maxval << endl;
cout << "INPUT BLOCK STRING TO DOLLAR: " << buffer << endl;
}
// first remove $ symbols and replace with the correct values
removeDollarsFromString(buffer, maxval);
if (debugQ) {
cout << "OUTPUT BLOCK STRING FROM DOLLAR: " << buffer << endl;
}
if (pre.search(buffer.getBase(), "^(\\d+)-(\\d+)$")) {
int firstone = strtol(pre.getSubmatch(1), NULL, 10);
int lastone = strtol(pre.getSubmatch(2), NULL, 10);
if ((firstone < 1) && (firstone != 0)) {
cerr << "Error: range token: \"" << string << "\""
<< " contains too small a number at start: " << firstone << endl;
cerr << "Minimum number allowed is " << 1 << endl;
exit(1);
}
if ((lastone < 1) && (lastone != 0)) {
cerr << "Error: range token: \"" << string << "\""
<< " contains too small a number at end: " << lastone << endl;
cerr << "Minimum number allowed is " << 1 << endl;
exit(1);
}
if (firstone > maxval) {
cerr << "Error: range token: \"" << string << "\""
<< " contains number too large at start: " << firstone << endl;
cerr << "Maximum number allowed is " << maxval << endl;
exit(1);
}
if (lastone > maxval) {
cerr << "Error: range token: \"" << string << "\""
<< " contains number too large at end: " << lastone << endl;
cerr << "Maximum number allowed is " << maxval << endl;
exit(1);
}
int i;
if (firstone > lastone) {
for (i=firstone; i>=lastone; i--) {
field.append(i);
}
} else {
for (i=firstone; i<=lastone; i++) {
field.append(i);
}
}
} else if (pre.search(buffer.getBase(), "^(\\d+)")) {
int value = strtol(pre.getSubmatch(1), NULL, 10);
if ((value < 1) && (value != 0)) {
cerr << "Error: range token: \"" << string << "\""
<< " contains too small a number at end: " << value << endl;
cerr << "Minimum number allowed is " << 1 << endl;
exit(1);
}
if (value > maxval) {
cerr << "Error: range token: \"" << string << "\""
<< " contains number too large at start: " << value << endl;
cerr << "Maximum number allowed is " << maxval << endl;
exit(1);
}
field.append(value);
}
}
//////////////////////////////
//
// removeDollarsFromString -- substitute $ sign for maximum track count.
//
void removeDollarsFromString(Array& buffer, int maxval) {
PerlRegularExpression pre;
char buf2[128] = {0};
int value2;
if (debugQ) {
cout << "IN DOLLAR STRING MAXBLOCK = " << maxval << endl;
}
if (pre.search(buffer.getBase(), "\\$$")) {
sprintf(buf2, "%d", maxval);
pre.sar(buffer, "\\$$", buf2);
}
if (debugQ) {
cout << "IN DOLLAR STRING = " << buffer << endl;
}
if (pre.search(buffer.getBase(), "\\$(?![\\d-])")) {
// don't know how this case could happen, however...
sprintf(buf2, "%d", maxval);
pre.sar(buffer, "\\$(?![\\d-])", buf2, "g");
}
if (pre.search(buffer.getBase(), "\\$0")) {
// replace $0 with maxval (used for reverse orderings)
sprintf(buf2, "%d", maxval);
pre.sar(buffer, "\\$0", buf2, "g");
}
while (pre.search(buffer.getBase(), "\\$(-?\\d+)")) {
value2 = maxval - (int)fabs(strtol(pre.getSubmatch(1), NULL, 10));
sprintf(buf2, "%d", value2);
pre.sar(buffer, "\\$-?\\d+", buf2);
}
}
//
// Spine field list extraction functions
//
///////////////////////////////////////////////////////////////////////////
// md5sum: b3ffb1c66363946fb3bd519900302055 sverse.cpp [20120811]