//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed Apr 24 20:15:12 PDT 2002
// Last Modified: Wed Apr 24 20:15:15 PDT 2002
// Filename: ...sig/examples/all/rism2kern.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/rism2kern.cpp
// Syntax: C++; museinfo
//
// Description: Converts RISM II/A Plaine and Easie Code into **kern data.
//
// Todo: expand beam groupings
// identify fermatas
// interpret trills
// handle tuplets
// measure repeat indicator
// repeat barlines
// !,F repetitions
// natural sign indicator
// clef changes (%F-4 )
// full measure rests (single and multiple)
// time signature
// grace notes
// acciaccatura notes (q and qq..r types)
//
// Done: key signature
// ties
// beam groupings
// accidentals (except for naturals, and tied accidentals)
//
#include "humdrum.h"
#include <string.h>
#include <math.h>
#ifndef OLDCPP
#include <iostream>
#include <fstream>
using namespace std;
#else
#include <iostream.h>
#include <fstream.h>
#endif
typedef Array<char> ArrayChar;
//////////////////////////////////////////////////////////////////////////
class NoteObject {
public:
NoteObject(void) { clear(); };
void clear(void) {
pitch = tie = beam = clef = clefnum = 0;
duration = 0.0;
};
int pitch;
double duration;
int tie;
int beam;
int clef;
int clefnum;
};
//////////////////////////////////////////////////////////////////////////
// function declarations:
void checkOptions(Options& opts, int argc, char** argv);
void example(void);
void usage(const char* command);
void convertRism789ToKern(HumdrumFile& hfile, const char* line789);
void getPieces(Array<ArrayChar>& pieces, const char* line);
void getKeyInfo(Array<int>& key, Array<ArrayChar>& pieces);
void printKeyInfo(ostream& out, Array<int>& key, Array<ArrayChar>& pieces);
void generateMusicData(Array<NoteObject>& notes,
Array<ArrayChar>& pieces, Array<int>& key);
void printPitches(ostream& out, Array<NoteObject>& notes);
int getLineIndex(Array<ArrayChar>& pieces, const char* string);
// User interface variables:
Options options;
int debugQ = 0; // used with the --debug option
//////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
// process the command-line options
checkOptions(options, argc, argv);
HumdrumFile hfile;
#ifndef OLDCPP
ifstream infile(options.getArg(1));
#else
ifstream infile(options.getArg(1), ios::nocreate);
#endif
if (!infile.is_open()) {
cout << "Error: cannot open file: " << options.getArg(1) << endl;
exit(1);
}
char line[2048] = {0};
while (!infile.eof()) {
while (!infile.eof() && strncmp(line, "!!!marc789:", 11) != 0) {
infile.getline(line, 1024, '\n');
}
if (strncmp(line, "!!!marc789:", 11) == 0) {
convertRism789ToKern(hfile, line);
}
infile.getline(line, 1024, '\n');
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// convertRism789ToKern --
//
void convertRism789ToKern(HumdrumFile& hfile, const char* line789) {
// cout << "LINE: " << line789 << endl;
Array<ArrayChar> pieces;
getPieces(pieces, line789);
// cout << "There are " << pieces.getSize() << " subfields" << endl;
// for (int i=0; i<pieces.getSize(); i++) {
// cout << "\t" << pieces[i].getBase() << "\n";
// }
Array<NoteObject> notes;
Array<int> key(7);
key.setAll(0);
getKeyInfo(key, pieces);
generateMusicData(notes, pieces, key);
cout << "**kern\n";
printKeyInfo(cout, key, pieces);
printPitches(cout, notes);
cout << "*-\n\n";
}
//////////////////////////////
//
// generateMusicData --
//
void generateMusicData(Array<NoteObject>& notes, Array<ArrayChar>& pieces,
Array<int>& key) {
notes.setSize(0);
NoteObject tempnote;
int index = getLineIndex(pieces, "$h");
if (index == -1) {
return;
}
cout << "PAEDATA: " << pieces[index].getBase() << endl;
int length = strlen(pieces[index].getBase());
// measure octave at each position in the data string
Array<int> octave;
octave.setSize(length);
octave.setAll(0);
int i;
for (i=1; i<length; i++) {
switch (pieces[index][i]) {
case '\'':
if (pieces[index][i-1] == '\'') {
octave[i] = octave[i-1] + 1;
} else {
octave[i] = 1;
}
break;
case ',':
if (pieces[index][i-1] == ',') {
octave[i] = octave[i-1] - 1;
} else {
octave[i] = -1;
}
break;
default:
octave[i] = octave[i-1];
break;
}
}
for (i=0; i<length; i++) {
switch (octave[i]) {
case 0: octave[i] = 4; break;
case 1: octave[i] = 4; break;
case 2: octave[i] = 5; break;
case 3: octave[i] = 6; break;
case 4: octave[i] = 7; break;
case 5: octave[i] = 8; break;
case 6: octave[i] = 9; break;
case -1: octave[i] = 3; break;
case -2: octave[i] = 2; break;
case -3: octave[i] = 1; break;
case -4: octave[i] = 0; break;
default: octave[i] = 4;
}
}
Array<int> acc;
acc.setSize(length);
acc.setAll(0);
// measure accidentals at each position in the data string
for (i=0; i<length; i++) {
switch (pieces[index][i]) {
case 'x': acc[i] = acc[i-1] + 1; break;
case 'b': acc[i] = acc[i-1] - 1; break;
case 'n': acc[i] = 100; break;
}
}
int currentacc = 0;
for (i=1; i<length; i++) {
if (pieces[index][i] == 'x' || pieces[index][i] == 'b' ||
pieces[index][i] == 'n') {
currentacc = acc[i];
} else if ((pieces[index][i]-'A'>=0) && (pieces[index][i]-'A'<7)) {
acc[i] = currentacc;
currentacc = 0;
}
}
// calculate slur information
Array<int> tempslur;
tempslur.setSize(length);
tempslur.setAll(0);
int lasti = 0;
int ii;
for (i=0; i<length; i++) {
if ((pieces[index][i] - 'A' >= 0) && (pieces[index][i] - 'A' < 7)) {
lasti = i;
} else if (pieces[index][i] == '+') {
if (tempslur[lasti]) {
tempslur[lasti] = 3;
} else {
tempslur[lasti] = 1;
}
for (ii=i; ii<length; ii++) {
if ((pieces[index][ii]-'A'>= 0) && (pieces[index][ii]-'A'<7)) {
tempslur[ii] = 2;
i = ii;
break;
}
}
}
}
// calculate beam grouping information
Array<int> tempbeam;
tempbeam.setSize(length);
tempbeam.setAll(0);
lasti = 0;
for (i=0; i<length; i++) {
if ((pieces[index][i] - 'A' >= 0) && (pieces[index][i] - 'A' < 7)) {
lasti = i;
} else if (pieces[index][i] == '}') {
tempbeam[lasti] = 2;
} else if (pieces[index][i] == '{') {
for (ii=i; ii<length; ii++) {
if ((pieces[index][ii]-'A'>= 0) && (pieces[index][ii]-'A'<7)) {
tempbeam[ii] = 1;
i = ii;
lasti = i;
break;
}
}
}
}
// calculate durations for all positions in data string
Array<double> duration;
duration.setSize(length);
duration.setAll(0.0);
int dots = 0;
double basedur = 0.0;
for (i=1; i<length; i++) {
switch (pieces[index][i]) {
case '0': duration[i] = 16.0; basedur = duration[i]; dots = 0;
break;
case '1': duration[i] = 4.0; basedur = duration[i]; dots = 0;
break;
case '2': duration[i] = 2.0; basedur = duration[i]; dots = 0;
break;
case '3': duration[i] = 4.0/32.0; basedur = duration[i]; dots = 0;
break;
case '4': duration[i] = 1.0; basedur = duration[i]; dots = 0;
break;
case '5': duration[i] = 4.0/64.0; basedur = duration[i]; dots = 0;
break;
case '6': duration[i] = 0.25; basedur = duration[i]; dots = 0;
break;
case '7': duration[i] = 4.0/128.0; basedur = duration[i]; dots = 0;
break;
case '8': duration[i] = 0.5; basedur = duration[i]; dots = 0;
break;
case '9': duration[i] = 8.0; basedur = duration[i]; dots = 0;
break;
case '.':
dots++;
duration[i] = duration[i-1] + basedur * pow(2.0, -dots);
break;
default:
dots = 0;
duration[i] = duration[i-1];
break;
}
}
// adjust for tuplet durations here...
Array<int> kkey;
kkey.setSize(key.getSize());
for (i=0; i<kkey.getSize(); i++) {
if (key[i] < 0) {
kkey[i] = -1;
} else if (key[i] > 0) {
kkey[i] = +1;
} else {
kkey[i] = 0;
}
}
int accidental;
Array<int> mkey(7);
mkey = kkey;
int natural = 0;
int pitch;
for (i=0; i<length; i++) {
if (pieces[index][i] == '/') {
mkey = kkey;
}
accidental = acc[i];
if (accidental > 5) {
// accidental = 0;
natural = 1;
}
pitch = 1000;
switch (pieces[index][i]) {
case 'A':
if (accidental != 0) {
if (natural) {
pitch = 31 + 40 * octave[i];
mkey[4] = 0;
natural = 0;
} else {
pitch = 31 + 40 * octave[i] + accidental;
mkey[4] = accidental;
}
} else {
pitch = 31 + 40 * octave[i] + mkey[4];
}
break;
case 'B':
if (accidental != 0) {
if (natural) {
pitch = 37 + 40 * octave[i];
mkey[6] = 0;
natural = 0;
} else {
pitch = 37 + 40 * octave[i] + accidental;
mkey[6] = accidental;
}
} else {
pitch = 37 + 40 * octave[i] + mkey[6];
}
break;
case 'C':
if (accidental != 0) {
if (natural) {
pitch = 2 + 40 * octave[i];
mkey[1] = 0;
natural = 0;
} else {
pitch = 2 + 40 * octave[i] + accidental;
mkey[1] = accidental;
}
} else {
pitch = 2 + 40 * octave[i] + mkey[1];
}
break;
case 'D':
if (accidental != 0) {
if (natural) {
pitch = 8 + 40 * octave[i];
mkey[3] = 0;
natural = 0;
} else {
pitch = 8 + 40 * octave[i] + accidental;
mkey[3] = accidental;
}
} else {
pitch = 8 + 40 * octave[i] + mkey[3];
}
break;
case 'E':
if (accidental != 0) {
if (natural) {
pitch = 14 + 40 * octave[i];
mkey[5] = 0;
natural = 0;
} else {
pitch = 14 + 40 * octave[i] + accidental;
mkey[5] = accidental;
}
} else {
pitch = 14 + 40 * octave[i] + mkey[5];
}
break;
case 'F':
if (accidental != 0) {
if (natural) {
pitch = 19 + 40 * octave[i];
mkey[0] = 0;
natural = 0;
} else {
pitch = 19 + 40 * octave[i] + accidental;
mkey[0] = accidental;
}
} else {
pitch = 19 + 40 * octave[i] + mkey[0];
}
break;
case 'G':
if (accidental != 0) {
if (natural) {
pitch = 25 + 40 * octave[i];
mkey[2] = 0;
natural = 0;
} else {
pitch = 25 + 40 * octave[i] + accidental;
mkey[2] = accidental;
}
} else {
pitch = 25 + 40 * octave[i] + mkey[2];
}
break;
case '/': pitch = 0; break;
case '-': pitch = -1000; break;
case '=': pitch = -1000; break;
default:
break;
}
if (pitch < 1000) {
tempnote.pitch = pitch;
tempnote.duration = duration[i];
tempnote.tie = tempslur[i];
tempnote.beam = tempbeam[i];
notes.append(tempnote);
}
}
}
//////////////////////////////
//
// printPitches --
//
void printPitches(ostream& out, Array& notes) {
char buffer[1024] = {0};
int i;
for (i=0; i<notes.getSize(); i++) {
if (notes[i].pitch < 0) {
if (notes[i].tie == 1) {
out << "[";
}
out << Convert::durationToKernRhythm(buffer, notes[i].duration);
out << "r";
if (notes[i].tie == 2) {
out << "]";
} else if (notes[i].tie == 3) {
out << "_";
}
if (notes[i].beam == 1) {
out << "L";
} else if (notes[i].beam == 2) {
out << "J";
}
} else if (notes[i].pitch == 0) {
out << "=";
} else {
if (notes[i].tie == 1) {
out << "[";
}
out << Convert::durationToKernRhythm(buffer, notes[i].duration);
out << Convert::base40ToKern(buffer, notes[i].pitch);
if (notes[i].tie == 2) {
out << "]";
} else if (notes[i].tie == 3) {
out << "_";
}
if (notes[i].beam == 1) {
out << "L";
} else if (notes[i].beam == 2) {
out << "J";
}
}
out << "\n";
}
}
//////////////////////////////
//
// getKeyInfo -- read the key signature.
//
void getKeyInfo(Array& key, Array& pieces) {
int i;
for (i=0; i<pieces.getSize(); i++) {
if (strncmp(pieces[i].getBase(), "$f", 2) == 0) {
break;
}
}
if (i >= pieces.getSize()) {
return;
}
int line = i;
int length = strlen(pieces[line].getBase());
for (i=0; i<length; i++) {
if (pieces[line][i] == ':') {
break;
}
}
if (i >= length) {
return;
}
// cout << "Key: " << &pieces[line][i] << "\t=\t";
// at the key information line, extract data
int type = 1; // 1 = sharps, 2 = flats
int paren = 1; // 1 = normal, 2 = inside of square brackets
while (i < length) {
switch (pieces[line][i]) {
case 'b': type = -1; break;
case 'x': type = 1; break;
case '[': paren = 2; break;
case ']': paren = 1; break;
case 'F': key[0] = type * paren; break;
case 'C': key[1] = type * paren; break;
case 'G': key[2] = type * paren; break;
case 'D': key[3] = type * paren; break;
case 'A': key[4] = type * paren; break;
case 'E': key[5] = type * paren; break;
case 'B': key[6] = type * paren; break;
case ' ': break;
default:
if (debugQ) {
cout << "Error: unknown character: " << pieces[line][i]
<< endl;
exit(1);
}
break;
}
i++;
}
}
//////////////////////////////
//
// printKeyInfo -- not worrying about mixed sharp/flat key sigs
// for now...
//
void printKeyInfo(ostream& out, Array& key, Array& pieces) {
int i;
for (i=0; i<pieces.getSize(); i++) {
if (strncmp(pieces[i].getBase(), "$f", 2) == 0) {
break;
}
}
int pline = i;
char* ptr;
if (pline<pieces.getSize()) {
for (i=0; i<7; i++) {
if (abs(key[i]) > 1) {
out << "!! Key signature";
ptr = strchr(pieces[pline].getBase(), ':');
if (ptr != NULL) {
out << ptr;
}
out << "\n";
break;
}
}
}
out << "*k[";
if (key[0] > 0) {
if (key[0] > 0) out << "f#"; else if (key[0] < 0) out << "f-";
if (key[1] > 0) out << "c#"; else if (key[0] < 0) out << "c-";
if (key[2] > 0) out << "g#"; else if (key[0] < 0) out << "g-";
if (key[3] > 0) out << "d#"; else if (key[0] < 0) out << "d-";
if (key[4] > 0) out << "a#"; else if (key[0] < 0) out << "a-";
if (key[5] > 0) out << "e#"; else if (key[0] < 0) out << "e-";
if (key[6] > 0) out << "b#"; else if (key[0] < 0) out << "b-";
} else {
if (key[6] < 0) out << "b-"; else if (key[0] > 0) out << "b#";
if (key[5] < 0) out << "e-"; else if (key[0] > 0) out << "e#";
if (key[4] < 0) out << "a-"; else if (key[0] > 0) out << "a#";
if (key[3] < 0) out << "d-"; else if (key[0] > 0) out << "d#";
if (key[2] < 0) out << "g-"; else if (key[0] > 0) out << "g#";
if (key[1] < 0) out << "c-"; else if (key[0] > 0) out << "c#";
if (key[0] < 0) out << "f-"; else if (key[0] > 0) out << "g#";
}
out << "]" << endl;
}
//////////////////////////////
//
// getPieces --
//
void getPieces(Array& pieces, const char* line) {
// count the number of $ signs in line
int count = 0;
int i;
int length = strlen(line);
for (i=0; i<length; i++) {
if (line[i] == '$') {
count++;
}
}
pieces.setSize(count);
pieces.allowGrowth(0);
for (i=0; i<pieces.getSize(); i++) {
pieces[i].setSize(1024);
pieces[i].setAll(0);
}
while (i<length && line[i] != '$') {
i++;
}
if (i==length) {
pieces.setSize(0);
return;
}
int current = 0;
char character[2] = {0};
character[0] = line[i];
strcat(pieces[0].getBase(), "$");
i++;
while (i<length) {
if (line[i] == '$') {
current++;
}
character[0] = line[i];
strcat(pieces[current].getBase(), character);
i++;
}
current++;
pieces.setSize(current);
}
//////////////////////////////
//
// checkOptions --
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("debug=b", "print debug information");
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, April 2002" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 24 April 2002" << 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 --
//
void example(void) {
}
//////////////////////////////
//
// usage --
//
void usage(const char* command) {
}
//////////////////////////////
//
// getLineIndex -- get the index location of the given string.
//
int getLineIndex(Array& pieces, const char* string) {
int index = -1;
int i;
for (i=0; i<pieces.getSize(); i++) {
if (strstr(pieces[i].getBase(), string) != NULL) {
index = i;
break;
}
}
return index;
}
// md5sum: d1a6480ff58d1dbfc461df3ef8c82029 rism2kern.cpp [20050403]