//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Thu Nov 30 16:41:05 PST 2000
// Last Modified: Wed Dec 6 20:06:55 PST 2000
// Last Modified: Tue Dec 19 11:15:53 PST 2000 (arbitrary part #s (L values))
// Filename: ...sig/examples/all/mustran2kern.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/mustran2kern.cpp
// Syntax: C++; museinfo
//
// Description: Converts MUSTRAN musical data into Humdrum **kern data.
//
#include "humdrum.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#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> /* for Windows 95 */
#else
#include <strstream.h>
#endif
#define SSTREAM strstream
#define CSTRING str()
#endif
void parseLine(void);
void parseHeader(void);
void parseData(void);
void parseItem(char* data);
void parseNoteData(char* data, char* notebuffer);
void clearaccidentals(void);
int checkforpart(void);
void parseChord(char* data, char* chordbuffer);
void printKern(int pitch, int octave, int clef, char* buffer);
void printHfiles(Array<HumdrumFile*>& hfiles);
void combineLines(Array<HumdrumFile*>& hfiles);
void fixties(HumdrumFile& file);
void getTiedPitches(Array<int>& pitchcount, Array<int>& tiedPitches,
HumdrumRecord& record);
void setTies(Array<int>& pitchcount, Array<int>& tiedPitches,
HumdrumRecord& record);
#define ACCIDENTAL_SIZE 7
int accidental[ACCIDENTAL_SIZE] = {0};
int tupletQ = 0;
int tuplettype = 0;
int tupletmatch = 0;
int part = 0;
int clef = 0;
int chordQ = 0;
int suppressQ = 0;
char tbuffer[10000] = {0};
char notebuffer[10000] = {0};
char linebuffer[100000] = {0};
int length = 0;
Array<int> partindex;
// interface variables:
Options options;
int combineQ = 1; // used with the -s option
// function declarations:
void checkOptions(Options& opts, int argc, char* argv[]);
Array<HumdrumFile*> hfiles;
HumdrumFile header;
int main(int argc, char** argv) {
// process the command-line options
checkOptions(options, argc, argv);
if (options.getArgCount() != 1) {
cout << "Usage: " << options.getCommand() << " inputfile " << endl;
exit(1);
}
hfiles.setSize(1000);
hfiles.setSize(0);
partindex.setSize(1000);
partindex.setSize(0);
partindex.allowGrowth(1);
#ifndef OLDCPP
fstream infile(options.getArg(1), ios::in);
#else
fstream infile(options.getArg(1), ios::in | ios::nocreate);
#endif
if (!infile.is_open()) {
cout << "Error: cannot open file for reading: "
<< options.getArg(1) << endl;
exit(1);
}
char *buffer = linebuffer;
while (infile.getline(buffer, 100000, '\n')) {
if (buffer[0] != '\0') {
parseLine();
}
if (infile.eof()) {
break;
}
// cout << "Line " << line++ << ":\t" << buffer << endl;
}
int i;
for (i=0; i<hfiles.getSize(); i++) {
fixties(*(hfiles[i]));
}
if (hfiles.getSize() == 1 &&
strcmp((*hfiles[0])[hfiles[0]->getNumLines() - 1][0], "*-") != 0) {
hfiles[0]->appendLine("*-");
}
if (hfiles.getSize() > 1 && combineQ) {
combineLines(hfiles);
} else {
printHfiles(hfiles);
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("A|no-assemble=b"); // don't assemble analyses
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, December 2000" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 2 December 2000" << 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);
}
if (opts.getBoolean("no-assemble")) {
combineQ = 0;
} else {
combineQ = 1;
}
}
//////////////////////////////
//
// fixties --
//
void fixties(HumdrumFile& file) {
Array<int> pitchcount(1000);
pitchcount.zero();
int i;
int j = 0;
Array<int> tielines;
tielines.setSize(file.getNumLines());
tielines.zero();
tielines.allowGrowth(0);
// find the lines with ties, and how many there are on each line
for (i=file.getNumLines()-1; i>=0; i--) {
if (file[i].getType() != E_humrec_data) {
continue;
}
if (strstr(file[i][0], "]") != NULL) {
length = strlen(file[i][0]);
for (j=0; j<length; j++) {
if (file[i][0][j] == ']') {
tielines[i]++;
continue;
}
}
}
}
// now add opening ties to previous pitches
Array<int> tiedPitches;
int tieSearch = 0;
// int tieLine = -1;
for (i=file.getNumLines()-1; i>=0; i--) {
if (file[i].getType() != E_humrec_data) {
continue;
}
if (tieSearch) {
// looking for ties, and this line does not have any new ties
// there will be no continued ties.
// all previous ties should be accounted for on this line.
setTies(pitchcount, tiedPitches, file[i]);
tieSearch = 0;
}
if (tieSearch == 0 && tielines[i] != 0) {
// not previously looking for ties, but here is a line with them
tieSearch = 1;
// tieLine = i;
getTiedPitches(pitchcount, tiedPitches, file[i]);
continue;
}
if (tieSearch == 0 && tielines[i] == 0) {
// prevent bugs in the data from propagating
pitchcount.zero();
tiedPitches.zero();
}
}
}
//////////////////////////////
//
// setTies --
//
void setTies(Array<int>& pitchcount, Array<int>& tiedPitches,
HumdrumRecord& record) {
static char buffer[10000] = {0};
char tbuffer[128] = {0};
buffer[0] = '\0';
int tokenCount = record.getTokenCount(0);
int pitch;
int match = 0;
int m;
int j;
for (m=0; m<tokenCount; m++) {
record.getToken(tbuffer, 0, m);
pitch = Convert::kernToMidiNoteNumber(tbuffer);
match = 0;
for (j=0; j<tiedPitches.getSize(); j++) {
if (pitch == tiedPitches[j]) {
match = 1;
pitchcount[pitch]--;
if (pitchcount[pitch] == 0) {
tiedPitches[j] = 0;
}
}
}
if (match == 1) {
char* tptr;
tptr = strchr(tbuffer, ']');
if (tptr != NULL) {
tptr[0] = '_';
strcat(buffer, tbuffer);
} else {
strcat(buffer, "[");
strcat(buffer, tbuffer);
}
if (m < tokenCount - 1) {
strcat(buffer, " ");
}
} else {
strcat(buffer, tbuffer);
if (m < tokenCount - 1) {
strcat(buffer, " ");
}
}
}
record.changeField(0, buffer);
}
//////////////////////////////
//
// getTiedPitches --
//
void getTiedPitches(Array<int>& pitchcount, Array<int>& tiedPitches,
HumdrumRecord& record) {
tiedPitches.setSize(0);
tiedPitches.allowGrowth(1);
char buffer[128] = {0};
int j;
int pitch;
int tokenCount = record.getTokenCount(0);
for (j=0; j<tokenCount; j++) {
record.getToken(buffer, 0, j);
if ((strchr(buffer, ']') != 0) || (strchr(buffer, '_') != 0)) {
pitch = Convert::kernToMidiNoteNumber(buffer);
if (pitch < 0) {
cout << "Error: cannot have tied rests" << endl;
exit(1);
}
tiedPitches.append(pitch);
pitchcount[pitch]++;
}
}
}
//////////////////////////////
//
// combineLines --
//
void combineLines(Array& hfiles) {
HumdrumFile output;
int i;
for (i=0; i<hfiles.getSize(); i++) {
hfiles[i]->analyzeSpines();
}
HumdrumFile::assemble(output, hfiles.getSize(), hfiles.getBase());
cout << header;
cout << output;
}
//////////////////////////////
//
// printHfiles --
//
void printHfiles(Array& hfiles) {
cout << header;
for (int i=0; i<hfiles.getSize(); i++) {
cout << (*(hfiles[i]));
if (i < hfiles.getSize() - 1) {
cout << "\n";
}
}
}
//////////////////////////////
//
// parseline -- make commentary on the input data
//
void parseLine(void) {
int oldpart;
char* buffer = linebuffer;
length = strlen(buffer);
if (length < 13) {
// cout << " Not a valid data line" << endl;
return;
}
int measure;
int flag = sscanf(&buffer[11], "%d", &measure);
if (flag != 1) {
cout << "Error no measure number found" << endl;
}
if (measure == 0) {
// cout << " This is a header line giving title and composer" << endl;
parseHeader();
} else {
int partQ = checkforpart();
if (partQ) {
HumdrumFile* hfile = new HumdrumFile;
hfiles.append(hfile);
partindex.append(part);
oldpart = part;
part = partindex.getSize();
if (part > 1) {
// cout << "==\n*-\n\n";
hfiles[part-2]->appendLine("==");
hfiles[part-2]->appendLine("*-");
}
// cout << "**kern\n!part" << oldpart << endl;
hfiles[part-1]->appendLine("**kern");
sprintf(tbuffer, "!part%d", oldpart);
hfiles[part-1]->appendLine(tbuffer);
}
// cout << " measure = " << measure << "\tfor part " << part << endl;
if (measure > 1) {
// cout << "=" << measure << endl;
sprintf(tbuffer, "=%d", measure);
hfiles[part-1]->appendLine(tbuffer);
}
parseData();
}
}
//////////////////////////////
//
// checkforpart --
//
int checkforpart(void) {
int i;
for (i=0; i<length; i++) {
if (linebuffer[i] == 'L') {
i++;
int flag = sscanf(&linebuffer[i], "%d", &part);
if (flag != 1) {
cout << "Error no part number indicated" << endl;
exit(1);
}
return 1;
}
}
return 0;
}
//////////////////////////////
//
// parseHeader -- make commentary on the header information
//
void parseHeader(void) {
char hbuffer[1000] = {0};
char letter[2] = {0};
char* line = linebuffer;
char* title = strstr(line, "\'*T");
int i;
if (title != NULL) {
title += 3;
// cout << " Title\t=\t";
strcpy(hbuffer, "!!!OTL: ");
i = 0;
while ((title[i] != '\0') && (title[i] != '\'')) {
// cout << title[i++];
letter[0] = title[i++];
strcat(hbuffer, letter);
}
// cout << endl;
header.appendLine(hbuffer);
if (title[i] != '\'') {
cout << " There is an error in the title, no ending: \'" << endl;
}
} else {
cout << " No title information given" << endl;
}
char* name = strstr(line, "\'*C");
if (name != NULL) {
name += 3;
// cout << " Composer\t=\t";
strcpy(hbuffer, "!!!COM: ");
i = 0;
while ((name[i] != '\0') && (name[i] != '\'')) {
// cout << name[i++];
letter[0] = name[i++];
strcat(hbuffer, letter);
}
// cout << endl;
header.appendLine(hbuffer);
if (name[i] != '\'') {
cout << " There is an error in the name, no ending: \'" << endl;
}
} else {
cout << " No composer information given" << endl;
}
}
//////////////////////////////
//
// parseData -- make commentary on the input data line
//
void parseData(void) {
char* line = linebuffer;
char* data = strchr(line, ',');
data++;
char* buffer = linebuffer;
data = strtok(buffer, ",");
data = strtok(NULL, ",");
int count = 1;
// int tlen;
while (data != NULL) {
// tlen = strlen(data);
// cout << " item ";
// if (count < 10) {
// cout << ' ';
// }
// if (count < 100) {
// cout << ' ';
// }
// cout << count << ": _" << data << "_";
count++;
// if (tlen < 4) {
// cout << "\t";
// }
// cout << "\t==> ";
parseItem(data);
if (!suppressQ) {
// cout << endl;
}
suppressQ = 0;
data = strtok(NULL, ",");
}
}
//////////////////////////////
//
// parseItem --
//
void parseItem(char* data) {
static char chordbuffer[10000] = {0};
if (toupper(data[0]) == 'L') {
// sscanf(data, "L%d", &part);
// cout << "part number = " << partindex[part-1] << endl;
suppressQ = 1;
return;
}
if (strchr(data, '(') != 0) {
// cout << "start of tuplet";
tupletQ = 1;
int flag = sscanf(data, "%d", &tuplettype);
if (flag != 1) {
cout << "Error no starting tuplet indicator" << endl;
exit(1);
}
int i = data - linebuffer;;
while (linebuffer[i] != ')' && i < length) {
i++;
}
if (linebuffer[i] != ')') {
cout << "\nError: no ending to tuplet " << endl;
exit(1);
}
i--;
if (!isdigit(linebuffer[i])) {
cout << "\nError: no ending tuplet value" << endl;
exit(1);
}
while (isdigit(linebuffer[i])) {
i--;
}
i++;
sscanf(&linebuffer[i], "%d", &tupletmatch);
// cout << ": " << tuplettype << " in the time of " << tupletmatch;
suppressQ = 1;
return;
}
if (strchr(data, ')') != 0) {
// cout << "end of tuplet";
tupletQ = 0;
suppressQ = 1;
return;
}
if (strcmp(data, "GS") == 0) {
// cout << "treble clef";
// cout << "*clefG2";
hfiles[part-1]->appendLine("*clefG2");
clef = 0;
return;
}
if (strcmp(data, "FS") == 0) {
// cout << "bass clef";
// cout << "*clefF4";
hfiles[part-1]->appendLine("*clefF4");
clef = 1;
return;
}
if (strchr(data, '=') != NULL) {
int top;
int bottom;
sscanf(data, "%d=%d", &top, &bottom);
// cout << "Time signature: " << top << '/' << bottom;
// cout << "*M" << top << "/" << bottom;
sprintf(tbuffer, "*M%d/%d", top, bottom);
hfiles[part-1]->appendLine(tbuffer);
return;
}
if (strcmp(data, "/") == 0 || strncmp(data, "/ ", 2) == 0) {
// cout << "End of measure marker";
clearaccidentals();
suppressQ = 1;
return;
}
if (strncmp(data, "//", 2) == 0) {
// cout << "End of piece marker";
// cout << "==\n*-";
hfiles[part-1]->appendLine("==");
hfiles[part-1]->appendLine("*-");
clearaccidentals();
suppressQ = 1;
return;
}
if (strncmp(data, "END", 3) == 0) {
// cout << "End of piece assertion";
clearaccidentals();
suppressQ = 1;
return;
}
if (strchr(data, 'Y') != 0) {
chordQ = 1;
parseChord(data, chordbuffer);
hfiles[part-1]->appendLine(chordbuffer);
chordQ = 0;
return;
}
parseNoteData(data, notebuffer);
hfiles[part-1]->appendLine(notebuffer);
}
//////////////////////////////
//
// parseChord --
//
void parseChord(char* data, char* chordbuffer) {
char* note;
chordbuffer[0] = '\0';
int tlen = strlen(data);
int i = 0;
int count = 1;
int rcount = 0;
for(i=0; iif (data[i] == 'R') {
rcount++;
}
if (data[i] == 'Y') {
data[i] = '\0';
count++;
}
}
if(count-rcount == 1) {
// cout << "CHORD with " << count-rcount << " note";
} else {
// cout << "CHORD with " << count-rcount << " notes";
}
if(rcount > 0) {
if (rcount == 1) {
// cout << " (and " << rcount << " rest)";
} else {
// cout << " (and " << rcount << " rests)";
}
}
// cout << endl;
note = data;
int xcount = 0;
while(xcount < count) {
i = 0;
xcount++;
// cout << "\t _";
// cout << note;
// cout << "_\t chord ";
parseNoteData(note, notebuffer);
strcat(chordbuffer, notebuffer);
if (count != xcount) {
// cout << endl;
// cout << " ";
strcat(chordbuffer, " ");
}
while(note[i] != '\0') {
i++;
}
i++;
note += i;
}
}
//////////////////////////////
//
// clearaccidentals --
//
void clearaccidentals(void) {
for(int i=0; i//////////////////////////////
//
// parseNoteData --
//
void parseNoteData(char* data, char* notebuffer) {
int rhythm;
if (!isdigit(data[0])) {
cout << "ERROR: no rhythm";
return;
} else {
sscanf(data, "%d", &rhythm);
}
int forcedQ = 0;
int acc = 0;
if (strchr(data, '*') != NULL) {
acc = +1;
}
if (strchr(data, '$') != NULL) {
acc = -1;
}
if (strchr(data, 'N') != NULL) {
forcedQ = 1;
acc = +10;
}
if (strstr(data, "$$") != NULL) {
acc = -1;
}
if (strstr(data, "**") != NULL) {
acc = -1;
}
const char* pitch = "X";
if (strchr(data, 'A') != NULL) {
pitch = strchr(data, 'A');
} else if (strchr(data, 'B') != NULL) {
pitch = strchr(data, 'B');
} else if (strchr(data, 'C') != NULL) {
pitch = strchr(data, 'C');
} else if (strchr(data, 'D') != NULL) {
pitch = strchr(data, 'D');
} else if (strchr(data, 'E') != NULL) {
pitch = strchr(data, 'E');
} else if (strchr(data, 'F') != NULL) {
pitch = strchr(data, 'F');
} else if (strchr(data, 'G') != NULL) {
pitch = strchr(data, 'G');
} else if (strchr(data, 'R') != NULL) {
pitch = strchr(data, 'R');
} else {
cout << "Error: no pitch given";
}
if(pitch[0] != 'R') {
if (acc == 0) {
acc = accidental[pitch[0] - 'A'];
}
if (acc == 10) {
accidental[pitch[0] - 'A'] = 0;
acc = 0;
}
accidental[pitch[0] - 'A'] = acc;
}
int dotQ = 0;
if (strchr(data, '.')) {
dotQ = 1;
}
int octave = 0;
if (strchr(data, '+') != 0) {
octave = +1;
}
if (strchr(data, '-') != 0) {
octave = -1;
}
if (strstr(data, "++") != 0) {
octave = +2;
}
if (strstr(data, "--") != 0) {
octave = -2;
}
if (strstr(data, "+++") != 0) {
octave = +3;
}
if (strstr(data, "---") != 0) {
octave = -3;
}
if (strstr(data, "++++") != 0) {
octave = +4;
}
if (strstr(data, "----") != 0) {
octave = -4;
}
int tieQ = 0;
if (strchr(data, 'J') != 0) {
tieQ = 1;
}
//
// read to print analysis
//
//////////////////////////////
notebuffer[0] = '\0';
// cout << " rhythm=" << rhythm;
if(tupletQ) {
// cout << rhythm * tuplettype / tupletmatch;
sprintf(tbuffer, "%d", rhythm * tuplettype / tupletmatch);
strcat(notebuffer, tbuffer);
} else {
// cout << rhythm;
sprintf(tbuffer, "%d", rhythm);
strcat(notebuffer, tbuffer);
}
if(dotQ) {
// cout << ".";
strcat(notebuffer, ".");
}
// if (tupletQ) {
// cout << "T" << tuplettype << ":" << tupletmatch;
// }
if(pitch[0] == 'R') {
// cout << "rest";
// cout << "r";
strcat(notebuffer, "r");
} else {
printKern(pitch[0], octave, clef, notebuffer);
// cout << "note=" << pitch[0];
if (acc == -1) {
// cout << "-flat";
// cout << "-";
strcat(notebuffer, "-");
}
if (acc == -2) {
// cout << " double-flat";
// cout << "--";
strcat(notebuffer, "--");
}
if (acc == +1) {
// cout << "-sharp";
// cout << "#";
strcat(notebuffer, "#");
}
if (acc == +2) {
// cout << " double-sharp";
// cout << "##";
strcat(notebuffer, "##");
}
if (acc == 0 && forcedQ) {
// cout << "-natural";
// cout << "n";
strcat(notebuffer, "n");
}
// if (octave != 0) {
// cout << " octave=" << octave;
// }
}
if(clef) {
// cout << " clef=bass";
}
if(tieQ == 1) {
// cout << " tied-previous";
// cout << "]";
strcat(notebuffer, "]");
}
}
//////////////////////////////
//
// printKern --
//
void printKern(int pitch, int octave, int clef, char* buff) {
SSTREAM buffstream;
pitch = toupper(pitch);
if(clef == 0) {
// treble clef
if (octave == 0) {
buffstream << (char)tolower(pitch);
} else if (octave == -1) {
buffstream << (char)toupper(pitch);
} else if (octave == +1) {
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
} else if (octave == -2) {
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
} else if (octave == +2) {
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
} else if (octave == -3) {
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
} else if (octave == +3) {
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
} else if (octave == -4) {
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
} else if (octave == +4) {
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
} else if (octave == -5) {
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
} else {
buffstream << (char)tolower(pitch);
}
} else {
// bass clef
int newp = pitch - 'A';
newp += 2;
newp = newp % 7;
octave--;
if (newp == 2 || newp == 3) {
octave++;
}
pitch = newp + 'A';
// bass clef
if (octave == 0) {
buffstream << (char)toupper(pitch);
} else if (octave == -1) {
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
} else if (octave == +1) {
buffstream << (char)tolower(pitch);
} else if (octave == -2) {
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
} else if (octave == +2) {
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
} else if (octave == -3) {
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
} else if (octave == +3) {
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
} else if (octave == -4) {
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
buffstream << (char)toupper(pitch);
} else if (octave == +4) {
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
} else if (octave == +5) {
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
buffstream << (char)tolower(pitch);
} else {
buffstream << (char)toupper(pitch);
}
}
buffstream << ends;
strcat(buff, buffstream.CSTRING);
}
// md5sum: 817b14d888e18753d3e30a24e2246370 mustran2kern.cpp [20050403]