//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed Dec 13 13:35:37 PST 2000
// Last Modified: Sun Oct 9 02:06:07 PDT 2005 (converted from kern2melisma)
// Last Modified: Tue Dec 13 22:16:14 PST 2005 (small fixes)
// Filename: ...sig/examples/all/time2matlab.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/time2matlab.cpp
// Syntax: C++; museinfo
//
// Description: Extracts performance times of Humdrum **kern data according
// to information found in a **time spine on the line.
//
#include "humdrum.h"
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#ifndef OLDCPP
#include <iostream>
#else
#include <iostream.h>
#endif
// function declarations:
void checkOptions(Options& opts, int argc, char** argv);
void example(void);
void preparePitch(char* buffer2, const char* buffer1);
void printOutput(HumdrumFile& hfile);
void usage(const char* command);
double getEndTime(HumdrumFile& hfile, int startindex,
double duration);
int getMetricLevel(HumdrumFile& hfile, int index);
int getMeasureNum(HumdrumFile& hfile, int index);
double getTimeToEnd(HumdrumFile& infile, double starttime,
int startindex);
void printSaccid(ostream& out, const char* string);
void comment(ostream& out, int count, int style);
// User interface variables:
Options options;
int debugQ = 0; // used with --debug option
int barlinesQ = 1; // used with -B option
int midinoteQ = 0; // used with -m option
int classQ = 0; // used with -c option
int auxdataQ = 0; // used with -a option
double tdefault = 60.0; // used with -t option
int humdrumQ = 0; // used with --humdrum option
char humComment = '!';
char matComment = '%';
//////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
// process the command-line options
checkOptions(options, argc, argv);
HumdrumFile hfile(options.getArg(1));
hfile.analyzeRhythm("4");
printOutput(hfile);
return 0;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// checkOptions --
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("a|auxiliary-data=b", "output extra notation info");
opts.define("B|nobarlines=b", "don't display barlines");
opts.define("m|midi|MIDI=b", "display pitches as MIDI note numbers");
opts.define("c|class=b", "display pitches in pitch class notation");
opts.define("t|tempo|default-tempo=d:60.0", "tempo if none specified");
opts.define("humdrum=b", "print data in Humdrum file format");
opts.define("debug=b", "Debugging flag");
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, Oct 2005" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 8 Oct 2005" << 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");
if (opts.getBoolean("nobarlines")) {
barlinesQ = 0;
} else {
barlinesQ = 1;
}
if (opts.getBoolean("class")) {
classQ = 1;
} else {
classQ = 0;
}
tdefault = opts.getDouble("default-tempo");
if (tdefault == 0.0) {
cout << "Error default tempo cannot be zero." << endl;
exit(1);
}
if (tdefault < 0.0) {
cerr << "Warning: converting tempo to be positive" << endl;
tdefault = -tdefault;
}
auxdataQ = opts.getBoolean("auxiliary-data");
humdrumQ = opts.getBoolean("humdrum");
}
//////////////////////////////
//
// example --
//
void example(void) {
// someone is lazy and has not put an example here.
}
//////////////////////////////
//
// comment --
//
void comment(ostream& out, int count, int style) {
int i;
for (i=0; i<count; i++) {
switch (style) {
case 0: out << matComment; break;
default: out << humComment;
}
}
}
//////////////////////////////
//
// printOutput --
//
void printOutput(HumdrumFile& hfile) {
Array<double> tempo;
hfile.analyzeTempoMarkings(tempo, tdefault);
double currentmillisecond = 0.0;
double lastduration = 0.0;
double linestarttime = 0.0;
double lineendtime = 0.0;
int founddata = 0;
int metlev;
int i, j, k;
char buffer1[1024] = {0};
char buffer2[1024] = {0};
double duration;
for (i=0; i<hfile.getNumLines(); i++) {
currentmillisecond = currentmillisecond +
lastduration * 60000.0 / tempo[i];
lastduration = hfile[i].getDuration();
if (hfile[i].getType() == E_humrec_global_comment) {
cout << "%% " << &(hfile[i].getLine()[3]) << endl;
}
if (hfile[i].getType() == E_humrec_bibliography) {
cout << "%%%" << &(hfile[i].getLine()[3]) << endl;
}
if (hfile[i].getType() != E_humrec_data) {
continue;
}
if (debugQ) {
cout << "RECORD: " << hfile[i] << endl;
}
if (founddata == 0) {
founddata = 1;
comment(cout, 2, humdrumQ);
cout << " Data column information:\n";
comment(cout, 3, humdrumQ);
cout << "col01: abstime\t" <<
"(average absolute time in milliseconds of human "
<< "beats)\n";
comment(cout, 3, humdrumQ);
cout << "col02: duration\t" <<
"(expected duration in ms based "
<< "on score duration)\n";
comment(cout, 3, humdrumQ);
cout << "col03: note\t\t" <<
"(MIDI note number of pitch)\n";
comment(cout, 3, humdrumQ);
cout << "col04: metlev\t" <<
"(metric level: 1 = downbeat; "
<< "0 = beat; -1 = offbeat)\n";
comment(cout, 3, humdrumQ);
cout << "col05: measure\t" <<
"(measure number in which note occurs)\n";
comment(cout, 3, humdrumQ);
cout << "col06: absbeat\t" <<
"(absolute beat from starting beat at 0)\n";
comment(cout, 3, humdrumQ);
cout << "col07: trackno\t" <<
"(hand which plays the note: 1 = left; 2=right)\n";
if (!auxdataQ) {
comment(cout, 3, humdrumQ);
cout << "col08: mintime\t" <<
"(minimum absolute time of human beat for this note)\n";
comment(cout, 3, humdrumQ);
cout << "col09: maxtime\t" <<
"(maximum absolute time of human beat for this note)\n";
comment(cout, 3, humdrumQ);
cout << "col10: sd\t\t" <<
"(standard deviation of human beat time in ms.)\n";
} else {
//comment(cout, 3, humdrumQ);
//cout << "col08: paccid\t" <<
// "(printed accidental 0=none, -1=flat, +1=sharp, 10=nat)\n";
comment(cout, 3, humdrumQ);
cout << "col08: saccid\t" <<
"(sounding accidental 0=natural, -1=flat, +1=sharp)\n";
}
if (humdrumQ) {
cout << "**start\t**dur\t**key\t**metr\t**meas\t**absb\t**track\t**saccid\n";
}
}
linestarttime = -1.0;
// find current time value and save
for (j=0; j<hfile[i].getFieldCount(); j++) {
if (strcmp(hfile[i].getExInterp(j), "**time") == 0) {
sscanf(hfile[i][j], "%lf", &linestarttime);
break;
}
}
for (j=0; j<hfile[i].getFieldCount(); j++) {
if (hfile[i].getExInterpNum(j) != E_KERN_EXINT) {
continue;
}
for (k=0; k<hfile[i].getTokenCount(j); k++) {
if (strcmp(hfile[i][j], ".") == 0) {
continue;
}
hfile[i].getToken(buffer1, j, k);
if (strchr(buffer1, '_') != NULL) {
// ignore notes which are tied
continue;
}
if (strchr(buffer1, ']') != NULL) {
// ignore notes which are tied
continue;
}
preparePitch(buffer2, buffer1);
duration = hfile.getTiedDuration(i, j, k);
lineendtime = getEndTime(hfile, i, duration);
int note = Convert::kernToMidiNoteNumber(buffer2);
if (classQ) {
note = note % 12;
}
if (note < 0) {
// don't display rests.
continue;
}
if (note < 0 || note > 127) {
cerr << "Error reading MIDI pitch number from string: "
<< buffer2 << endl;
exit(1);
}
//cout << "Note\t";
// cout << hfile.getAbsBeat(i) << "\t";
// cout << (int)(currentmillisecond+0.5) << "\t";
cout << linestarttime << "\t";
if (debugQ && (lineendtime - linestarttime < 0)) {
cerr << "Error duration of note on line: " << hfile[i] << endl;
cerr << "Starttime: " << linestarttime << endl;
cerr << "Endtime: " << lineendtime << endl;
cerr << "Line Index: " << i << endl;
exit(1);
}
if (lineendtime != -1) {
cout << lineendtime - linestarttime << "\t";
} else {
cout << (int)(getTimeToEnd(hfile, linestarttime, i) + 0.5)
<< "\t";
}
cout << note;
cout << "\t";
metlev = getMetricLevel(hfile, i);
cout << metlev;
cout << "\t" << getMeasureNum(hfile, i);
cout << "\t" << hfile[i].getAbsBeat();
// you must make sure that the spine order is
// correct or this data will be bad
cout << "\t" << hfile[i].getPrimaryTrack(j)-1;
cout << "\t";
printSaccid(cout, buffer2);
cout << "\n";
}
}
}
if (humdrumQ) {
cout << "*-\t*-\t*-\t*-\t*-\t*-\t*-\t*-\n";
}
}
//////////////////////////////
//
// printSaccid -- print the souding accidental of the note.
// 0 = natural without an accidental sign, 10 = natural with
// a natural sign, -1 is flat, -2 double flat, +1 = sharp,
// +2 = double sharp. 10 is added (subtracted) to sharps (flats)
// values if the sharp or flat is required to be printed as
// a cautionary sign.
//
void printSaccid(ostream& out, const char* string) {
if (strchr(string, 'n') != NULL) {
out << 10;
return;
}
int i;
int accid = 0;
int length = strlen(string);
int literal = 0;
for (i=0; i<length; i++) {
if (string[i] == '-') {
accid--;
}
if (string[i] == '#') {
accid++;
}
if (string[i] == 'X') {
literal = 1;
}
}
if (literal) {
if (accid > 0) {
accid = accid+10;
} else if (accid < 0) {
accid = accid-10;
}
}
out << accid;
return;
}
//////////////////////////////
//
// getTimeToEnd --
//
double getTimeToEnd(HumdrumFile& infile, double starttime, int startindex) {
double ctime = starttime;
double cbeat = infile[startindex].getAbsBeat();
double nbeat = infile[infile.getNumLines()-1].getAbsBeat();
int preindex = -1;
int i;
for (i=startindex-1; i>=0; i--) {
if (infile[i].getType() != E_humrec_data) {
continue;
}
preindex = i;
break;
}
if (preindex < 0) {
return -1;
}
double pbeat = infile[preindex].getAbsBeat();
double ptime = -1.0;
for (i=0; i<infile[preindex].getFieldCount(); i++) {
if (strcmp("**time", infile[preindex].getExInterp(i)) != 0) {
continue;
}
sscanf(infile[preindex][i], "%lf", &ptime);
break;
}
if (ptime < 0.0) {
return -1;
}
double db2 = nbeat - cbeat;
double db1 = cbeat - pbeat;
double dt1 = ctime - ptime;
// cout << "<< DB1 = " << db1 << " >> ";
// cout << "<< DB2 = " << db2 << " >> ";
// cout << "<< DT1 = " << dt1 << " >> ";
return db2 * dt1 / db1;
}
//////////////////////////////
//
// getMeasureNum --
//
int getMeasureNum(HumdrumFile& hfile, int index) {
int output = -1;
int i;
for (i=index; i>=0; i--) {
if (hfile[i][0][0] == '=' && isdigit(hfile[i][0][1])) {
sscanf(hfile[i][0], "=%d", &output);
break;
}
}
if (i <= 0) {
output = 0;
}
return output;
}
//////////////////////////////
//
// getEndTime -- return the **time value at the given duration point after
// the given index.
//
double getEndTime(HumdrumFile& hfile, int startindex, double duration) {
double stopbeat = duration + hfile[startindex].getAbsBeat();
int i, j;
double output = -1.0;
for (i=startindex+1; i<hfile.getNumLines(); i++) {
if (hfile[i].getType() != E_humrec_data) {
continue;
}
if (hfile[i].getAbsBeat() >= (stopbeat-0.0002)) {
for (j=0; j<hfile[i].getFieldCount(); j++) {
if (strcmp(hfile[i].getExInterp(j), "**time") == 0) {
sscanf(hfile[i][j], "%lf", &output);
break;
}
}
break;
}
}
return output;
}
//////////////////////////////
//
// preparePitch --
//
void preparePitch(char* buffer2, const char* buffer1) {
strcpy(buffer2, buffer1);
}
//////////////////////////////
//
// usage --
//
void usage(const char* command) {
}
//////////////////////////////
//
// getMetricLevel -- only checked with quarter-note beats.
//
int getMetricLevel(HumdrumFile& hfile, int index) {
double fraction = hfile[index].getAbsBeat();
fraction = fraction - (int)fraction;
if (fraction < 0.0001 || fraction > 0.9999) {
fraction = hfile[index].getAbsBeat() / 3.0;
fraction = fraction - (int)fraction;
if (fraction < 0.0001 || fraction > 0.9999) {
return 1; // on a measure downbeat
} else {
return 0; // on the (quarter note) beat
}
} else {
return -1; // on an offbeat
}
}
// md5sum: edad163cf9797b125c2fd80500199c0f time2matlab.cpp [20090406]