//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Tue Mar 19 15:51:09 PDT 2013
// Last Modified: Tue Apr 23 17:32:43 PDT 2013 Added colored notes
// Filename: ...sig/examples/all/hum2enp.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/hum2enp.cpp
// Syntax: C++; museinfo
//
// Description: Converts Humdrum files into ENP (Expressive Notation Package)
// files.
//
#ifndef OLDCPP
#include <iostream>
#include <fstream>
#include <sstream>
#define SSTREAM stringstream
#define CSTRING str().c_str()
using namespace std;
#else
#include <iostream.h>
#include <fstream.h>
#ifdef VISUAL
#include <strstrea.h>
#else
#include <strstream.h>
#endif
#define SSTREAM strstream
#define CSTRING str()
#endif
#include "humdrum.h"
#include "PerlRegularExpression.h"
class Coordinate {
public:
int i, j;
};
//////////////////////////////////////////////////////////////////////////
// function declarations:
void checkOptions(Options& opts, int argc, char** argv);
void example(void);
void usage(const char* command);
void convertHumdrumToEnp(HumdrumFile& infile);
void getKernTracks(Array<int>& tracks, HumdrumFile& infile);
void getPartNames(HumdrumFile& infile,
Array<Array<char> >& PartNames);
void pline(int level, const char* string);
void indent(int level);
void printPart(HumdrumFile& infile, int spine,
Array<int>& barlines);
void getBarlines(Array<int>& barlines, HumdrumFile& infile);
void printMeasure(HumdrumFile& infile, int spine,
int voice, Array<int>& barlines, int index);
void printInitialStaff(HumdrumFile& infile, int spine);
void printKeySignature(HumdrumFile& infile, int spine, int line);
void printTimeSignature(HumdrumFile& infile, int spine, int line);
void extractVoiceItems(Array<Coordinate>& items, HumdrumFile& infile,
int spine, int voice, int startbar, int endbar);
int printSubBeatLevel(HumdrumFile& infile, Array<Coordinate>& items,
Array<int>& notes, int noteindex);
void printMeasureContent(HumdrumFile& infile, Array<Coordinate>& items);
void printMidiNotes(HumdrumFile& infile, int line, int field);
int getBeatGroupCount(HumdrumFile& infile, Array<Coordinate>& items,
Array<int>& notes, int noteindex);
RationalNumber getSmallestRhythm(HumdrumFile& infile, Array<Coordinate>& items,
Array<int>& notes, int noteindex,
int groupcount);
void printChordArticulations(HumdrumFile& infile, int line, int field);
void printHeaderComments(HumdrumFile& infile);
void printTrailerComments(HumdrumFile& infile);
void printDataComments(HumdrumFile& infile, Array<Coordinate>& items,
int index);
void printTieDot(HumdrumFile& infile, int line, int field);
void checkMarks(HumdrumFile& infile, Array<char>& marks,
Array<Array<char> >& markcolors);
void getNoteAttributes(SSTREAM& attributes, HumdrumFile& infile,
int line, int field, int subfield,
const char* buffer);
void getNoteExpressions(SSTREAM& expressions, HumdrumFile& infile,
int line, int field, int subfield,
const char* buffer);
// User interface variables:
Options options;
int debugQ = 0; // used with --debug option
int originalQ = 0; // used with --original option
int sidecommentQ = 1; // not hooked up to an option yet
const char* INDENT = "\t"; // indentation for each level
int LEVEL = 0; // used to indent the score
Array<char> marks; // used to color notes
Array<Array<char> > markcolors; // used to color notes
Array<int> markline; // used to search for circles
// The instance ID works similar to XML::id
int InstanceIdCounter = 0;
//////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
HumdrumFile infile;
// initial processing of the command-line options
checkOptions(options, argc, argv);
if (options.getArgCount() < 1) {
infile.read(cin);
} else {
infile.read(options.getArg(1));
}
convertHumdrumToEnp(infile);
return 0;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// convertHumdrumToEnp --
//
void convertHumdrumToEnp(HumdrumFile& infile) {
infile.analyzeRhythm("4");
Array<int> kerntracks;
getKernTracks(kerntracks, infile);
Array<Array<char> > partnames;
getPartNames(infile, partnames);
Array<int> barlines;
getBarlines(barlines, infile);
checkMarks(infile, marks, markcolors);
printHeaderComments(infile);
LEVEL = 0;
// Print the outer score parentheses
pline(LEVEL++, "(:begin :score"); // score-level parenthesis
int i;
int partnum = 0;
for (i=kerntracks.getSize()-1; i>=0; i--) {
partnum++;
indent(LEVEL++);
cout << "(:begin :part" << partnum << endl; // part-level parenthesis
printInitialStaff(infile, kerntracks[i]);
printKeySignature(infile, kerntracks[i], 0);
printTimeSignature(infile, kerntracks[i], 0);
printPart(infile, kerntracks[i], barlines);
pline(--LEVEL, ") ; end :part"); // part-level parenthesis
}
pline(--LEVEL, ") ; end :score"); // score level parenthesis
printTrailerComments(infile);
}
//////////////////////////////
//
// printHeaderComments --
//
void printHeaderComments(HumdrumFile& infile) {
int i;
for (i=0; i<infile.getNumLines(); i++ ) {
if (infile[i].isBibliographic()) {
cout << ";" << infile[i] << endl;
continue;
}
if (infile[i].isGlobalComment()) {
cout << ";" << infile[i] << endl;
continue;
}
if (infile[i].isInterpretation()) {
break;
}
}
}
//////////////////////////////
//
// printTrailerComments --
//
void printTrailerComments(HumdrumFile& infile) {
int i;
int endline = -1;
for (i=infile.getNumLines()-1; i>=0; i--) {
if (infile[i].isInterpretation()) {
break;
}
endline = i;
}
for (i=endline; i<infile.getNumLines(); i++ ) {
if (infile[i].isBibliographic()) {
cout << ";" << infile[i] << endl;
continue;
}
if (infile[i].isGlobalComment()) {
cout << ";" << infile[i] << endl;
continue;
}
}
}
//////////////////////////////
//
// printTimeSignature --
//
void printTimeSignature(HumdrumFile& infile, int spine, int line) {
Coordinate timesig;
timesig.i = -1;
timesig.j = -1;
int track;
PerlRegularExpression pre;
int i, j;
// first search backwards for time signature
for (i=line; i>0; i--) {
if (infile[i].isData()) {
// quit loop when a data line is found.
break;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
track = infile[i].getPrimaryTrack(j);
if (track != spine) {
continue;
};
if (!infile[i].isInterpretation()) {
continue;
}
if (pre.search(infile[i][j], "^\\*M\\d.*/\\d")) {
timesig.i = i;
timesig.j = j;
break;
}
}
}
// now search forwards for time signature
for (i=line+1; i<infile.getNumLines(); i++) {
if (infile[i].isData()) {
// quit loop when a data line is found.
break;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
track = infile[i].getPrimaryTrack(j);
if (track != spine) {
continue;
};
if (!infile[i].isInterpretation()) {
continue;
}
if (pre.search(infile[i][j], "^\\*M\\d.*/\\d")) {
timesig.i = i;
timesig.j = j;
break;
}
}
}
if (timesig.i < 0) {
// nothing to do
return;
}
// for the moment, do not allow % within time signature
pre.search(infile[timesig.i][timesig.j], "^\\*M(\\d+)/(\\d+)");
indent(LEVEL);
cout << ":time-signature ("
<< pre.getSubmatch(1)
<< " "
<< pre.getSubmatch(2);
if (line == 0) {
// printing initial time signature
// check to see if the first measure is a pickup beat
RationalNumber pickupdur;
pickupdur = infile.getPickupDurationR();
if (pickupdur > 0) {
cout << " :kind :pickup";
}
}
cout << ")" << endl;
}
//////////////////////////////
//
// printKeySignature -- Print a key signature for the given
// voice. Search anywhere within enclosing data lines for
// the *k[] (key signature) and *C: (key) markers.
//
void printKeySignature(HumdrumFile& infile, int spine, int line) {
Coordinate keysig;
Coordinate key;
keysig.i = -1;
keysig.j = -1;
key.i = -1;
key.j = -1;
int i, j;
int track;
PerlRegularExpression pre;
// first search backwards from current location:
for (i=line; i>0; i--) {
if (infile[i].isData()) {
// quit loop when a data line is found.
break;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
track = infile[i].getPrimaryTrack(j);
if (track != spine) {
continue;
};
if (!infile[i].isInterpretation()) {
continue;
}
if (pre.search(infile[i][j], "^\\*k\\[.*\\]")) {
keysig.i = i;
keysig.j = j;
} else if (pre.search(infile[i][j], "^\\*[A-G][n#-]*:", "i")) {
key.i = i;
key.j = j;
}
}
}
// now search forwards for key signature and key.
for (i=line+1; i<infile.getNumLines(); i++) {
if (infile[i].isData()) {
// quit loop when a data line is found.
break;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
track = infile[i].getPrimaryTrack(j);
if (track != spine) {
continue;
};
if (!infile[i].isInterpretation()) {
continue;
}
if (pre.search(infile[i][j], "^\\*k\\[.*\\]")) {
keysig.i = i;
keysig.j = j;
} else if (pre.search(infile[i][j], "^\\*[A-G][n#-]*:", "i")) {
key.i = i;
key.j = j;
}
}
}
int mode = 0; // major by default.
if (key.i > 0) {
if (islower(infile[key.i][key.j][1])) {
mode = 1;
}
}
int fifthskey = 0;
if (keysig.i > 0) {
fifthskey = Convert::kernKeyToNumber(infile[keysig.i][keysig.j]);
}
indent(LEVEL); cout << ":key-signature ";
if (mode == 0) { // major mode
switch (fifthskey) {
case -7: cout << ":c-flat-major"; break;
case -6: cout << ":g-flat-major"; break;
case -5: cout << ":d-flat-major"; break;
case -4: cout << ":a-flat-major"; break;
case -3: cout << ":e-flat-major"; break;
case -2: cout << ":b-flat-major"; break;
case -1: cout << ":f-major"; break;
case 0: cout << ":c-major"; break;
case +1: cout << ":g-major"; break;
case +2: cout << ":d-major"; break;
case +3: cout << ":a-major"; break;
case +4: cout << ":e-major"; break;
case +5: cout << ":b-major"; break;
case +6: cout << ":f-sharp-major"; break;
case +7: cout << ":c-sharp-major"; break;
}
} else if (mode == 1) { // minor modes
switch (fifthskey) {
case -7: cout << ":a-flat-minor"; break;
case -6: cout << ":e-flat-minor"; break;
case -5: cout << ":b-flat-minor"; break;
case -4: cout << ":f-minor"; break;
case -3: cout << ":c-minor"; break;
case -2: cout << ":g-minor"; break;
case -1: cout << ":d-minor"; break;
case 0: cout << ":a-minor"; break;
case +1: cout << ":e-minor"; break;
case +2: cout << ":b-minor"; break;
case +3: cout << ":f-sharp-minor"; break;
case +4: cout << ":c-sharp-minor"; break;
case +5: cout << ":g-sharp-minor"; break;
case +6: cout << ":d-sharp-minor"; break;
case +7: cout << ":a-sharp-minor"; break;
}
}
cout << endl;
}
//////////////////////////////
//
// printInitialStaff -- print the starting staff for a part (if any).
//
void printInitialStaff(HumdrumFile& infile, int spine) {
int i, j;
int track;
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].isData()) {
break;
}
for (j=0; j<infile[i].getFieldCount(); j++ ) {
track = infile[i].getPrimaryTrack(j);
if (track != spine) {
continue;
}
if (strncmp(infile[i][j], "*clef", 5) != 0) {
// only checking the first subspine for clef information:
break;
}
if (strcmp(infile[i][j], "*clefG2") == 0) {
pline(LEVEL, ":staff :treble-staff");
} else if (strcmp(infile[i][j], "*clefF4") == 0) {
pline(LEVEL, ":staff :bass-staff");
} else if (strcmp(infile[i][j], "*clefGv2") == 0) {
pline(LEVEL, ":staff :tenor-staff"); // vocal-tenor clef?
} else if (strcmp(infile[i][j], "*clefC3") == 0) {
pline(LEVEL, ":staff :alto-staff");
} else if (strcmp(infile[i][j], "*clefX") == 0) {
pline(LEVEL, ":staff :percussion-staff");
}
// only checking the first subspine for clef information:
break;
}
}
}
//////////////////////////////
//
// printPart -- print a particular part. Just the first one for now.
//
void printPart(HumdrumFile& infile, int spine, Array& barlines) {
int i;
int voice = 0;
indent(LEVEL++);
cout << "(:begin :voice" << voice+1 << endl;
for (i=0; i<barlines.getSize()-1; i++) {
printMeasure(infile, spine, voice, barlines, i);
}
pline(--LEVEL, ") ; end :voice");
}
//////////////////////////////
//
// printMeasure --
//
void printMeasure(HumdrumFile& infile, int spine, int voice,
Array<int>& barlines, int index) {
if (voice != 0) {
// multiple voices are not allowed at the moment.
exit(1);
}
int startbar = barlines[index];
int endbar = barlines[index+1];
int i;
Array<Coordinate> items;
extractVoiceItems(items, infile, spine, voice, startbar, endbar);
indent(LEVEL++);
int barnum = -1;
cout << "(";
if (sscanf(infile[items[0].i][items[0].j], "=%d", &barnum)) {
cout << ":begin :measure" << barnum;
if (sidecommentQ && infile[items[0].i].isMeasure()) {
cout << "\t; " << infile[items[0].i][0];
}
}
cout << endl;
for (i=0; i<items.getSize(); i++) {
if ((i == items.getSize()-1) && infile[items[i].i].isMeasure()) {
break;
}
indent(LEVEL); cout << "; " << infile[items[i].i][items[i].j] << endl;
}
printMeasureContent(infile, items);
pline(--LEVEL, ")");
}
//////////////////////////////
//
// printMeasureContent --
//
void printMeasureContent(HumdrumFile& infile, Array& items) {
int i;
Array<int> notes;
notes.setSize(items.getSize());
notes.setSize(0);
for (i=0; i<items.getSize(); i++) {
if (!infile[items[i].i].isData()) {
continue;
}
if (strcmp(infile[items[i].i][items[i].j], ".") != 0) {
notes.append(i);
}
}
RationalNumber start, dur;
int ii, jj;
for (i=0; i<notes.getSize(); i++) {
printDataComments(infile, items, notes[i]);
ii = items[notes[i]].i;
jj = items[notes[i]].j;
start = infile[ii].getBeatR();
dur = Convert::kernToDurationR(infile[ii][jj]);
if (!start.isInteger()) {
cout << "RHYTHM ERROR at token (" << ii << "," << jj << "): "
<< infile[ii][jj] << endl;
exit(1);
}
if (dur.isInteger()) {
// simple case where the note is an integer number of beats.
indent(LEVEL);
if (strchr(infile[ii][jj], 'r') != NULL) {
// this rest has no attributes so not adding an extra paren set
// otherwise it would be "((-".
cout << "(" << dur << " (-" << 1;
printTieDot(infile, ii, jj);
cout << "))";
if (sidecommentQ) {
cout << "\t; " << infile[ii][jj];
}
cout << endl;
} else {
cout << "(" << dur << " ((" << 1;
printTieDot(infile, ii, jj);
cout << " :notes (";
printMidiNotes(infile, ii, jj);
cout << ")"; // end of notes list
printChordArticulations(infile, ii, jj);
cout << "))"; // end of beat list
cout << ")"; // end of beat group
if (sidecommentQ) {
cout << "\t; " << infile[ii][jj];
}
cout << endl;
}
} else {
i = printSubBeatLevel(infile, items, notes, i);
}
}
}
//////////////////////////////
//
// printTieDot -- print a dot after a rhythm to indicate that it is
// tied to a previous note. Do not put a marker on the first note
// in a tied group. Ties are created by converting a rhythmic
// value into a floating-point number by adding ".0" to the end
// of the rhythm. Note that adding "." by itself does not work.
//
void printTieDot(HumdrumFile& infile, int line, int field) {
int tieQ = 0;
if (strchr(infile[line][field], ']') != NULL) {
tieQ = 1;
} else if (strchr(infile[line][field], '_') != NULL) {
tieQ = 1;
}
if (tieQ) {
cout << ".0";
}
}
/////////////////////////////
//
// printChordArticulations --
//
void printChordArticulations(HumdrumFile& infile, int line, int field) {
int fermataQ = 0;
if (strchr(infile[line][field], ';') != NULL) {
fermataQ = 1;
}
int expressionQ = 0;
expressionQ |= fermataQ;
int counter = 0;
if (expressionQ) {
cout << " :expressions (";
if (fermataQ) {
if (counter++ != 0) { cout << " "; }
cout << ":fermata";
}
cout << ")";
}
}
//////////////////////////////
//
// printDataComments -- Print any comments before the current
// line of data for the voice within the part's voice. Stop
// if a previous note/rest is found.
//
void printDataComments(HumdrumFile& infile, Array<Coordinate>& items,
int index) {
int start = -1;
int ii;
int i;
for (i=index-1; i>=0; i--) {
ii = items[i].i;
if (infile[ii].isComment()) {
start = i;
continue;
}
break;
}
if (start < 0) {
// nothing to do
return;
}
for (i=start; i<items.getSize(); i++) {
ii = items[i].i;
if (infile[ii].isComment()) {
indent(LEVEL);
cout << ";" << infile[ii] << endl;
continue;
}
break;
}
}
//////////////////////////////
//
// printMidiNotes --
//
void printMidiNotes(HumdrumFile& infile, int line, int field) {
int tokencount = infile[line].getTokenCount(field);
int k;
int base12;
char buffer[1024] = {0};
for (k=0; k<tokencount; k++) {
infile[line].getToken(buffer, field, k);
base12 = Convert::kernToMidiNoteNumber(buffer);
if (k > 0) {
cout << " ";
}
SSTREAM slots;
getNoteAttributes(slots, infile, line, field, k, buffer);
slots << ends;
if (strlen(slots.CSTRING) > 0) {
cout << "(";
cout << base12;
cout << slots.CSTRING;
cout << ")";
} else {
cout << base12;
}
}
}
//////////////////////////////
//
// getNoteAttributes -- returns a list of attributes for a note (if any)
//
void getNoteAttributes(SSTREAM& attributes, HumdrumFile& infile, int line,
int field, int subfield, const char* buffer) {
// if the note is supposed to be shows as a flatted note, then
// add an attribute which says to display it as a flat (otherwise
// ENP will always show accidentals as sharped notes).
if (strchr(buffer, '-') != NULL) {
// indicate the the MIDI pitch should be displayed as a diatonic flat
attributes << " :enharmonic :flat";
}
// check for colored notes based on !!!RDF: entries in the file.
int i;
for (i=0; i<marks.getSize(); i++) {
if (marks[i] == '\0') {
// ignore any null-character
continue;
}
if (strchr(buffer, marks[i]) != NULL) {
attributes << " :color :" << markcolors[i];
}
}
SSTREAM expressions;
getNoteExpressions(expressions, infile, line, field, subfield, buffer);
expressions << ends;
if (strlen(expressions.CSTRING) > 0) {
attributes << " :expressions (";
attributes << expressions.CSTRING;
attributes << ")";
}
}
//////////////////////////////
//
// getNoteExpressions --
//
void getNoteExpressions(SSTREAM& expressions, HumdrumFile& infile, int line,
int field, int subfield, const char* buffer) {
PerlRegularExpression pre;
int i;
for (i=0; i<marks.getSize(); i++) {
if (strchr(buffer, marks[i]) != NULL) {
if (pre.search(infile[markline[i]][0], "circle")) {
expressions << "(:score-expression/" << InstanceIdCounter++;
expressions << " :kind :circled" << ")";
}
}
}
}
//////////////////////////////
//
// printSubBeatLevel -- Print a list of notes which occur within a beat
// (or more than one beat, possibly). Noteindex is the index into
// the notes array for the first note of the group to process. Keep
// including notes until a beat boundary has been reached. Return the index
// of the next note to process after the sub-beat grouping (or the size
// of notes if there is no more notes to process).
//
int printSubBeatLevel(HumdrumFile& infile, Array<Coordinate>& items,
Array<int>& notes, int noteindex) {
// groupcount is the number of notes in an integer number
// of beats within the measure.
int groupcount = getBeatGroupCount(infile, items, notes, noteindex);
RationalNumber groupduration;
RationalNumber startpos;
RationalNumber endpos;
RationalNumber enddur;
int ii, jj;
ii = items[notes[noteindex]].i;
jj = items[notes[noteindex]].j;
startpos = infile[ii].getAbsBeatR();
ii = items[notes[noteindex+groupcount-1]].i;
jj = items[notes[noteindex+groupcount-1]].j;
endpos = infile[ii].getAbsBeatR();
enddur = Convert::kernToDurationR(infile[ii][jj]);
groupduration = (endpos - startpos) + enddur;
if (!groupduration.isInteger()) {
cout << "Funny error: group duration is not an integer" << endl;
exit(1);
}
RationalNumber minrhy;
minrhy.setValue(1,2);
// minrhy is smallest duration in terms of quarter notes.
minrhy = getSmallestRhythm(infile, items, notes, noteindex, groupcount);
indent(LEVEL++); cout << "(" << groupduration << " ("<< endl;
RationalNumber quarternote(1,4);
// print group notes
int i;
RationalNumber notediv;
for (i=noteindex; i<noteindex+groupcount; i++) {
ii = items[notes[i]].i;
jj = items[notes[i]].j;
indent(LEVEL);
notediv = Convert::kernToDurationR(infile[ii][jj]) / minrhy;
// cout << "(" << notediv.getNumerator();
cout << "(" << notediv;
cout << " :notes (";
printMidiNotes(infile, ii, jj);
cout << ")"; // end of MIDI pitch list
cout << ")"; // end of note
if (sidecommentQ) {
cout << "\t; " << infile[ii][jj];
}
cout << endl;
}
indent(--LEVEL); cout << "))" << endl; // end of beat group list
return noteindex + groupcount - 1;
}
//////////////////////////////
//
// getSmallestRhythm --
//
RationalNumber getSmallestRhythm(HumdrumFile& infile, Array<Coordinate>& items,
Array<int>& notes, int noteindex, int groupcount) {
int i;
RationalNumber minrhy;
RationalNumber testrhy;
minrhy.setValue(1,1);
int ii, jj;
for (i=noteindex; i<noteindex+groupcount; i++) {
ii = items[notes[i]].i;
jj = items[notes[i]].j;
testrhy = Convert::kernToDurationR(infile[ii][jj]);
if (testrhy.getNumerator() == 0) {
cout << "ERROR: grace notes are not yet handled by program" << endl;
exit(1);
}
if (minrhy > testrhy) {
minrhy = testrhy;
}
}
return minrhy;
}
//////////////////////////////
//
// getBeatGroupCount -- return the number of notes in an integer number
// of beats within the measure.
//
int getBeatGroupCount(HumdrumFile& infile, Array<Coordinate>& items,
Array<int>& notes, int noteindex) {
int output = 0;
int i;
RationalNumber dursum;
dursum.setValue(0,1);
int ii, jj;
for (i=noteindex; i<notes.getSize(); i++) {
ii = items[notes[i]].i;
jj = items[notes[i]].j;
dursum += Convert::kernToDurationR(infile[ii][jj]);
output++;
if (dursum.isInteger()) {
return output;
}
}
cout << "ERROR: measure does not sum to an integer amount of beats." << endl;
cout << "Instead the group duration is: " << dursum << endl;
exit(1);
return -1;
}
//////////////////////////////
//
// extractVoiceItems -- get a list of non-measure, non-null tokens for
// part/voice within the given line range.
//
void extractVoiceItems(Array<Coordinate>& items, HumdrumFile& infile,
int spine, int voice, int startbar, int endbar) {
items.setSize(endbar-startbar+1);
items.setSize(0);
int i, j;
int track;
int voicenum;
Coordinate loc;
for (i=startbar; i<=endbar; i++ ) {
voicenum = 0;
for (j=0; j<infile[i].getFieldCount(); j++) {
track = infile[i].getPrimaryTrack(j);
if (spine != track) {
continue;
}
if (voicenum++ != voice) {
continue;
}
if (strcmp(infile[i][j], ".") == 0) { // Null token
continue;
}
if (strcmp(infile[i][j], "*") == 0) { // Null interpretation
continue;
}
if (strcmp(infile[i][j], "!") == 0) { // Empty local comment
continue;
}
// found something, so store its location
loc.i = i;
loc.j = j;
items.append(loc);
}
}
}
//////////////////////////////
//
// getBarlines -- returns the line numbers in the score where there
// are barlines. The barlines cannot be intermingled with other
// data types.
//
void getBarlines(Array& barlines, HumdrumFile& infile) {
int i;
int zero = 0;
int foundstartdata = 0;
int founddata = 0;
barlines.setSize(infile.getNumLines());
barlines.setSize(0);
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].isMeasure()) {
if ((barlines.getSize() == 0) && foundstartdata) {
// pickup measure, so include start of file.
barlines.append(zero);
}
barlines.append(i);
founddata = 0;
} else if (infile[i].isData()) {
foundstartdata = 1;
founddata = 1;
}
}
if (founddata) {
// data after last barline, so include last line of file
int lastline = infile.getNumLines() - 1;
barlines.append(lastline);
}
}
//////////////////////////////
//
// pline -- Print a line of data.
//
void pline(int level, const char* string) {
indent(level);
cout << string;
cout << endl;
}
//////////////////////////////
//
// indent -- indent the line the specified level
//
void indent(int level) {
for (int i=0; i<level; i++) {
cout << INDENT;
}
}
//////////////////////////////
//
// getPartNames --
//
void getPartNames(HumdrumFile& infile, Array >& PartNames) {
int i, j;
PartNames.setSize(infile.getMaxTracks()+1); // 0 = unused
for (i=0; i<PartNames.getSize(); i++) {
PartNames[i].setSize(1);
PartNames[i][0]= '\0';
}
int abbreviationQ = 0;
Array<int> ignore;
ignore.setSize(infile.getMaxTracks()+1);
ignore.setAll(0);
PerlRegularExpression pre;
int track;
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].isData()) {
// stop looking when the first data line is found
break;
}
if (!infile[i].isInterpretation()) {
continue;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
if (strcmp(infile[i][j], "*^") == 0) {
// don't search for names after spine splits (there might
// be two names, and one of them will be ignored).
ignore[infile[i].getPrimaryTrack(j)] = 1;
}
if (ignore[infile[i].getPrimaryTrack(j)]) {
continue;
}
if (!abbreviationQ) {
if (pre.search(infile[i][j], "^\\*I\"\\s*(.*)\\s*$", "")) {
track = infile[i].getPrimaryTrack(j);
PartNames[track].setSize(strlen(pre.getSubmatch(1))+1);
strcpy(PartNames[track].getBase(), pre.getSubmatch());
}
} else {
if (pre.search(infile[i][j], "^\\*I\'\\s*(.*)\\s*$", "")) {
track = infile[i].getPrimaryTrack(j);
PartNames[track].setSize(strlen(pre.getSubmatch(1))+1);
strcpy(PartNames[track].getBase(), pre.getSubmatch());
}
}
}
}
// if no part name, set to "part name" (for debugging purposes):
//for (i=1; i<=infile.getMaxTracks(); i++) {
// if (strcmp(PartNames[i].getBase(), "") == 0) {
// PartNames[i].setSize(strlen("part name")+1);
// strcpy(PartNames[i].getBase(), "part name");
// }
// }
}
//////////////////////////////
//
// getKernTracks -- Return a list of the **kern primary tracks found
// in the Humdrum data. Currently all tracks are independent parts.
// No grand staff parts are considered if the staves are separated
// into two separate spines.
//
//
void getKernTracks(Array& tracks, HumdrumFile& infile) {
tracks.setSize(infile.getMaxTracks());
tracks.setSize(0);
int i;
for (i=1; i<=infile.getMaxTracks(); i++) {
if (strcmp(infile.getTrackExInterp(i), "**kern") == 0) {
tracks.append(i);
}
}
if (debugQ) {
cout << "\t**kern tracks:\n";
for (i=0; i<tracks.getSize(); i++) {
cout << "\t" << tracks[i] << endl;
}
}
}
//////////////////////////////
//
// checkMarks -- Check for notes which are marked with a particular
// color.
//
void checkMarks(HumdrumFile& infile, Array<char>& marks,
Array<Array<char> >& markcolors) {
int markQ = 1;
if (!markQ) {
marks.setSize(0);
markline.setSize(0);
markcolors.setSize(0);
return;
}
marks.setSize(0);
markline.setSize(0);
markcolors.setSize(0);
int i;
char target;
PerlRegularExpression pre;
for (i=0; i<infile.getNumLines(); i++) {
if (!infile[i].isBibliographic()) {
continue;
}
if (pre.search(infile[i][0],
"!!!RDF\\*\\*kern\\s*:\\s*([^=])\\s*=\\s*match", "i")) {
target = pre.getSubmatch(1)[0];
marks.append(target);
markline.append(i);
markcolors.setSize(markcolors.getSize()+1);
markcolors.last() = "red";
} else if (pre.search(infile[i][0],
"!!!RDF\\*\\*kern\\s*:\\s*([^=])\\s*=\\s*mark", "i")) {
target = pre.getSubmatch(1)[0];
marks.append(target);
markline.append(i);
markcolors.setSize(markcolors.getSize()+1);
markcolors.last() = "red";
}
}
if (debugQ) {
for (i=0; i<marks.getSize(); i++) {
cout << "MARK " << marks[i] << "\t" << markcolors[i] << endl;
}
}
}
//////////////////////////////
//
// checkOptions --
//
void checkOptions(Options& opts, int argc, char** argv) {
opts.define("debug=b", "Debugging flag");
opts.define("author=b", "Program author");
opts.define("version=b", "Program version");
opts.define("example=b", "Program examples");
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, March 2013" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 19 March 2013" << 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);
}
debugQ = opts.getBoolean("debug");
}
//////////////////////////////
//
// example -- example function calls to the program.
//
void example(void) {
}
//////////////////////////////
//
// usage -- command-line usage description and brief summary
//
void usage(const char* command) {
}
// md5sum: 22e60a81dd37bed86f7aa9e880f99dfe hum2enp.cpp [20130504]