//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sat Sep 20 06:35:45 PDT 2008
// Last Modified: Sat Sep 20 06:36:19 PDT 2008
// Filename: ...museinfo/examples/all/koto2xml.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/koto2xml.cpp
// Syntax: C++; museinfo
//
// Description: Convert **koto data into MEI XML and MusicXML.
//
#include "humdrum.h"
#define MEIXML 1
#define MUSICXML 2
// function declarations
void printTabs(ostream& outstream, int count);
void example(void);
void usage(const char* command);
void checkOptions(Options& opts, int argc, char* argv[]);
void processHumdrumFile(HumdrumFile& infile, int outputType);
// MEI XML functions:
void printMeiXml(ostream& outstream, HumdrumFile& infile);
void printMeiHeader(ostream& outstream, HumdrumFile& infile, int level);
void printMeiWork(ostream& outstream, HumdrumFile& infile, int level);
void printMeiKotoPart(ostream& outstream, HumdrumFile& infile, int spine,
int level);
void printMeiMeasure(ostream& outstream, HumdrumFile& infile, int spine,
int start, int level);
void printMeiNoteData(ostream& outstream, const char* string, int level);
void printMeiChordData(ostream& outstream, HumdrumFile& infile, int spine,
int line, int level);
void printMeiPitch(ostream& outstream, int pitch);
void printMeiRhythm(ostream& outstream, const char* data);
// MusicXML functions:
void printMusicXml(ostream& outstream, HumdrumFile& infile);
// Shared functions:
char* getTitle(char* buffer, int buffsize, HumdrumFile& infile);
void printEncodedString(ostream& outstream, char* buffer);
int checkForTune(int* mapping, HumdrumRecord& arecord, int spine);
int getKotoString(const char* data);
int getRhythm(const char* data);
int getDots(const char* data);
// User interface variables:
Options options;
int meiQ = 1; // default behavior
int musicxmlQ = 0; // convert to MusicXML
int STRINGMAPPING[21] = {0};
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));
}
processHumdrumFile(infile, MEIXML);
}
return 0;
}
////////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("debug=b", "trace input parsing");
opts.define("author=b", "author of the program");
opts.define("version=b", "compilation information");
opts.define("example=b", "example usage");
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, Sep 2008" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: Sep 2008" << 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);
}
}
//////////////////////////////
//
// processHumdrumFile --
//
void processHumdrumFile(HumdrumFile& infile, int outputType) {
switch (outputType) {
case MEIXML: printMeiXml(std::cout, infile); break;
case MUSICXML: printMusicXml(std::cout, infile); break;
}
}
//////////////////////////////
//
// example -- example usage of the scrub program
//
void example(void) {
cout <<
" \n"
<< endl;
}
//////////////////////////////
//
// usage -- gives the usage statement for the scrub program
//
void usage(const char* command) {
cout <<
" \n"
<< endl;
}
//////////////////////////////
//
// printTabs --
//
void printTabs(ostream& outstream, int count) {
for (int i=0; i<count && i < 1000; i++) {
// outstream << '\t';
outstream << " ";
}
}
///////////////////////////////////////////////////////////////////////////
//
// MEI XML functions
//
//////////////////////////////
//
// printMeiXml --
//
void printMeiXml(ostream& outstream, HumdrumFile& infile) {
outstream << "<?xml version='1.0' encoding='UTF-8'?>" << "\n";
outstream << "<!DOCTYPE mei SYSTEM 'mei18bFull.dtd'";
outstream << '>' << '\n';
outstream << "<mei version='1.8b'>\n";
int level = 1;
printMeiHeader(outstream, infile, level);
printMeiWork(outstream, infile, level);
outstream << "</mei>\n";
}
//////////////////////////////
//
// printMeiHeader --
//
void printMeiHeader(ostream& outstream, HumdrumFile& infile, int level) {
#define BUFFSIZE 10000
char buffer[BUFFSIZE];
printTabs(outstream, level); outstream << "<meihead>" << "\n";
printTabs(outstream, level+1); outstream << "<filedesc>" << "\n";
printTabs(outstream, level+2); outstream << "<titlestmt>" << "\n";
getTitle(buffer, BUFFSIZE, infile);
if (strlen(buffer) == 0) {
printTabs(outstream, level+3); outstream << "<title/>\n";
} else {
printTabs(outstream, level+3);
outstream << "<title>";
printEncodedString(outstream, buffer);
outstream << "</title>\n";
}
printTabs(outstream, level+2); outstream << "</titlestmt>" << "\n";
printTabs(outstream, level+2); outstream << "<pubstmt/>" << "\n";
printTabs(outstream, level+1); outstream << "</filedesc>" << "\n";
printTabs(outstream, level); outstream << "</meihead>" << "\n";
}
//////////////////////////////
//
// printMeiWork --
//
void printMeiWork(ostream& outstream, HumdrumFile& infile, int level) {
printTabs(outstream, level ); outstream << "<music>" << "\n";
printTabs(outstream, level+1); outstream << "<body>" << "\n";
printTabs(outstream, level+2); outstream << "<mdiv>" << "\n";
printTabs(outstream, level+3); outstream << "<score>" << "\n";
printTabs(outstream, level+4); outstream << "<staffgrp>" << "\n";
printTabs(outstream, level+5); outstream << "<staffdef n='1'/>" << "\n";
printTabs(outstream, level+4); outstream << "</staffgrp>" << "\n";
printMeiKotoPart(outstream, infile, 0, level+4);
printTabs(outstream, level+3); outstream << "</score>" << "\n";
printTabs(outstream, level+2); outstream << "</mdiv>" << "\n";
printTabs(outstream, level+1); outstream << "</body>" << "\n";
printTabs(outstream, level ); outstream << "</music>" << "\n";
}
//////////////////////////////
//
// printMeiKotoPart --
//
void printMeiKotoPart(ostream& outstream, HumdrumFile& infile, int spine,
int level) {
int i;
for (i=0; i<infile.getNumLines(); i++) {
switch (infile[i].getType()) {
case E_humrec_interpretation:
checkForTune(STRINGMAPPING, infile[i], spine);
break;
case E_humrec_data_measure:
if (strncmp(infile[i][spine], "==", 2) != 0) {
// online print measure if note a double-barline
// probably check later to see if there is an end of file
printMeiMeasure(outstream, infile, spine, i, level);
}
break;
//default:
// do nothing
}
}
}
//////////////////////////////
//
// printMeiMeasure --
//
void printMeiMeasure(ostream& outstream, HumdrumFile& infile, int spine,
int start, int level) {
int i;
if (infile[start][spine][0] != '=') {
std::cout << "ERROR: line does not have measure: "
<< infile[start][spine] << endl;
exit(1);
}
static int measure = -1;
int flag = sscanf(infile[start][spine], "=%d", &measure);
if (flag != 1) {
measure++;
}
printTabs(outstream, level);
outstream << "<measure n='" << measure << "'>" << "\n";
printTabs(outstream, level+1); outstream << "<staff n='1'>" << "\n";
start++;
for (i=start; i<infile.getNumLines(); i++) {
if (infile[i].getType() == E_humrec_interpretation) {
checkForTune(STRINGMAPPING, infile[i], spine);
} else if (infile[i].getType() == E_humrec_data_measure) {
break;
} else if (infile[i].getType() == E_humrec_data) {
printMeiChordData(outstream, infile, spine, i, level+2);
} else {
}
}
printTabs(outstream, level+1); outstream << "</staff>" << "\n";
printTabs(outstream, level); outstream << "</measure>" << "\n";
// perhaps return the new line here for processing next measure
}
//////////////////////////////
//
// printMeiChordData --
//
void printMeiChordData(ostream& outstream, HumdrumFile& infile, int spine,
int line, int level) {
int tokencount = infile[line].getTokenCount(spine);
if (tokencount < 1) {
return;
}
if (tokencount == 1) { // a single note
printMeiNoteData(outstream, infile[line][spine], level);
return;
}
// print chord
printTabs(outstream, level); outstream << "<chord>" << "\n";
char buffer[1024];
int i;
for (i=0; i<tokencount; i++) {
infile[line].getToken(buffer, spine, i, 1024);
printMeiNoteData(outstream, buffer, level+1);
}
printTabs(outstream, level); outstream << "</chord>" << "\n";
}
//////////////////////////////
//
// printMeiNoteData --
//
void printMeiNoteData(ostream& outstream, const char* data, int level) {
if (strcmp(data, ".") == 0) {
// nothing to print
return;
}
// WARNING: Have to deal with chords
int string = getKotoString(data);
printTabs(outstream, level);
if (string == 0) {
outstream << "<rest";
printMeiRhythm(outstream, data);
outstream << "/>\n";
} else if (string == 14) {
outstream << "<space";
printMeiRhythm(outstream, data);
outstream << "/>\n";
} else if (string > 0) {
outstream << "<note tab.string='" << string << "'";
printMeiPitch(outstream, STRINGMAPPING[string-1]);
printMeiRhythm(outstream, data);
outstream << "/>" << "\n";
}
}
//////////////////////////////
//
// printMeiRhythm --
//
void printMeiRhythm(ostream& outstream, const char* data) {
int rhythm = getRhythm(data);
int dots = getDots(data);
outstream << " dur='" << rhythm << "'";
if (dots > 0) {
outstream << " dots='" << dots << "'";
}
}
//////////////////////////////
//
// printMeiPitch --
//
void printMeiPitch(ostream& outstream, int pitch) {
int octave = pitch / 40;
int accidental = Convert::base40ToAccidental(pitch);
int diatonic = Convert::base40ToDiatonic(pitch) % 7;
if (diatonic < 0 || diatonic > 6) {
std::cerr << "ERROR: invalid diatonic number: "
<< diatonic << " (" << pitch << ")" << endl;
exit(1);
}
char Diatonic[7] = {'c', 'd', 'e', 'f', 'g', 'a', 'b'};
outstream << " pname='" << Diatonic[diatonic] << "'";
outstream << " oct='" << octave << "'";
if (accidental != 0) {
outstream << " accid.ges='";
switch (accidental) {
case 2: outstream << "ss"; break;
case 1: outstream << "s"; break;
case -1: outstream << "f"; break;
case -2: outstream << "ff"; break;
}
outstream << "'";
}
}
///////////////////////////////////////////////////////////////////////////
//
// MusicXML functions
//
void printMusicXml(ostream& outstream, HumdrumFile& infile) {
// do nothing for now
}
///////////////////////////////////////////////////////////////////////////
//
// Shared functions
//
//////////////////////////////
//
// printEncodedString -- escape quote output string
// < = <
// > = >
// & = &
// ' = '
// " = "
//
void printEncodedString(ostream& outstream, char* buffer) {
int i;
int length = strlen(buffer);
for (i=0; i<length; i++) {
switch (buffer[i]) {
case '<': outstream << "<"; break;
case '>': outstream << ">"; break;
case '&': outstream << "&"; break;
case '\'': outstream << "&"; break;
case '\"': outstream << "&"; break;
default: outstream << buffer[i];
}
}
}
//////////////////////////////
//
// getTitle -- Return the first !!!OTL record in the file.
//
char* getTitle(char* buffer, int buffsize, HumdrumFile& infile) {
int i, j;
int length;
int colon = -1;
buffer[0] = 0;
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].getType() == E_humrec_bibliography) {
if (strncmp(infile[i][0], "!!!OTL", 6) == 0) {
length = strlen(infile[i][0]);
for (j=6; j<length; j++) {
if (infile[i][0][j] == ':') {
colon = j+1;
break;
}
}
if (colon < 0) {
return buffer;
}
while (j < length && (infile[i][0][colon] == ' ' ||
infile[i][0][colon] == '\n')) {
colon++;
}
strncpy(buffer, &(infile[i][0][colon]), buffsize-1);
}
}
}
return buffer;
}
//////////////////////////////
//
// checkForTune -- Look for tuning directive in data
//
int checkForTune(int* mapping, HumdrumRecord& arecord, int spine) {
if (strncmp(arecord[spine], "*tune[", 6) != 0) {
return 0;
}
char buffer[1024];
strncpy(buffer, &(arecord[spine][6]), 1023);
char* ptr = strtok(buffer, ": \n\t]");
int counter = 0;
while (ptr != NULL && counter < 21) {
mapping[counter] = Convert::kernToBase40(ptr);
// std::cout << "FOUND " << counter << " to be " << ptr << endl;
counter++;
ptr = strtok(NULL, ": \n\t]");
}
// std::cout << "COUNTER = " << counter << endl;
return counter;
}
//////////////////////////////
//
// getKotoString --
//
int getKotoString(const char* data) {
int i;
int length = strlen(data);
for (i=0; i<length; i++) {
switch (data[i]) {
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'A': return 10;
case 'B': return 11;
case 'C': return 12;
case 'D': return 13;
case '0': return 0;
case '-': return 14;
}
}
return -1;
}
//////////////////////////////
//
// getRhythm -- return the numeric part of the rhythm (excluding
// augmentation dots)
//
int getRhythm(const char* data) {
double duration = Convert::kotoToDuration(data);
char buffer[1024];
Convert::durationToKernRhythm(buffer, duration);
int i;
int length = strlen(buffer);
int output = 0;
for (i=0; i<length; i++) {
if (isdigit(buffer[i])) {
sscanf(&(buffer[i]), "%d", &output);
break;
}
}
return output;
}
//////////////////////////////
//
// getDots -- return the number of augmentation dots in the rhythm.
//
int getDots(const char* data) {
int i;
int length = strlen(data);
int output = 0;
for (i=0; i<length; i++) {
if (data[i] == '.') {
output++;
}
}
return output;
}
// md5sum: 7ec5706a9a636a3405f6aa95317dce7d koto2xml.cpp [20101226]