//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Tue Jun 12 23:17:41 PDT 2001
// Last Modified: Fri Jun 12 22:58:34 PDT 2009 (renamed SigCollection class)
// Filename: ...sig/examples/all/kern2skini.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/kern2skini.cpp
// Syntax: C++; museinfo
//
// Description: Generates a list of pitches in a Humdrum file
// according to the STK SKINI format.
//
#include "humdrum.h"
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <math.h>
#ifndef OLDCPP
#include <iostream>
#include <iomanip>
#else
#include <iostream.h>
#include <iomanip.h>
#endif
typedef long TEMP64BITFIX;
class SKINI {
public:
int message;
double time;
TEMP64BITFIX chan;
double note;
double vel;
double id;
};
#define UNKNOWN -1
#define MEASURE 0
#define NOTEON 1
#define NOTEOFF 2
#define COMMENT 3
#define TEMPO 4
#define KEYSIG 5
#define INFO 6
#define INSTRUMENT 7
// function declarations:
void checkOptions(Options& opts, int argc, char** argv);
void example(void);
void preparePitch(char* buffer2, const char* buffer1);
void generateSkini(HumdrumFile& hfile, SigCollection<SKINI>& skini);
void printSkini(SigCollection<SKINI>& skini);
void usage(const char* command);
ostream& operator<< (ostream& out, SKINI skini);
int skinicompare(const void* a, const void* b);
// User interface variables:
Options options;
int barlinesQ = 1; // used with the -B option
int midinoteQ = 0; // used with the -m option
int classQ = 0; // used with the -c option
double tdefault = 60.0; // used with the -t option
int idcounter = 0; // used to sort output data
//////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
// process the command-line options
checkOptions(options, argc, argv);
SigCollection<SKINI> skini;
skini.setGrowth(100000);
skini.setSize(100000);
skini.setSize(0);
skini.allowGrowth(1);
HumdrumFile hfile(options.getArg(1));
hfile.analyzeRhythm();
generateSkini(hfile, skini);
printSkini(skini);
return 0;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// printSkini -- sort SKINI messages and output
//
void printSkini(SigCollection& skini) {
int i;
qsort(skini.getBase(), skini.getSize(), sizeof(SKINI), skinicompare);
// convert to delta times:
for (i=skini.getSize()-1; i>0; i--) {
skini[i].time = skini[i].time - skini[i-1].time;
if (skini[i].time < 0.00001) {
skini[i].time = 0.0;
}
}
skini[0].time = 0.0;
for (i=0; i<skini.getSize(); i++) {
cout << skini[i] << endl;
}
}
//////////////////////////////
//
// checkOptions --
//
void checkOptions(Options& opts, int argc, char* argv[]) {
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("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, May 1998" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 3 July 1998" << 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("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;
}
}
//////////////////////////////
//
// example --
//
void example(void) {
}
//////////////////////////////
//
// generateSkini -- one small bug: the duration of the notes
// are calculated under constant tempo. A tempo change during
// a tied note will not give the currect ending point of the
// tied note. This bug is minor and the work to fix it is major.
//
void generateSkini(HumdrumFile& hfile, SigCollection& skini) {
Array<double> tempo;
hfile.analyzeTempoMarkings(tempo, tdefault);
double currentmillisecond = 0.0;
double lastduration = 0.0;
double endmillisecond;
SKINI tempskini;
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) {
tempskini.message = COMMENT;
tempskini.time = currentmillisecond/1000.0;
if (hfile[i][0][2] == ' ') {
tempskini.chan = (TEMP64BITFIX)(&hfile[i][0][3]);
} else {
tempskini.chan = (TEMP64BITFIX)(&hfile[i][0][2]);
}
tempskini.note = 0;
tempskini.vel = 0;
tempskini.id = idcounter;
skini.append(tempskini);
idcounter++;
// cout << "// " << &(hfile[i].getLine()[3]) << endl;
}
if (hfile[i].getType() == E_humrec_bibliography) {
tempskini.message = INFO;
tempskini.time = currentmillisecond/1000.0;
if (hfile[i][0][3] == ' ') {
tempskini.chan = (TEMP64BITFIX)(&hfile[i][0][4]);
} else {
tempskini.chan = (TEMP64BITFIX)(&hfile[i][0][3]);
}
tempskini.note = 0;
tempskini.vel = 0;
tempskini.id = idcounter;
skini.append(tempskini);
idcounter++;
// cout << "///" << &(hfile[i].getLine()[3]) << endl;
}
if (hfile[i].getType() == E_humrec_interpretation) {
for (j=0; j<hfile[i].getFieldCount(); j++) {
if (hfile[i].getExInterpNum(j) != E_KERN_EXINT) {
continue;
}
int direction = 0;
int acount = 0;
if (strncmp(hfile[i][j], "*k[", 3) == 0) {
if (strchr(hfile[i][j], '-') != NULL) {
direction = -1;
} else if (strchr(hfile[i][j], '#') != NULL) {
direction = 1;
}
acount = (strlen(hfile[i][j]) - 4)/2;
tempskini.message = KEYSIG;
tempskini.time = currentmillisecond/1000.0;
tempskini.chan = acount;
tempskini.note = 0;
tempskini.vel = 0;
tempskini.id = idcounter;
idcounter++;
if (direction == 0) {
skini.append(tempskini);
break;
} else {
if (direction < 0) {
tempskini.chan *= -1;
}
skini.append(tempskini);
break;
}
}
}
}
// look for key interpretations
/* if (hfile[i].getType() == E_humrec_interpretation) {
for (j=0; j<hfile[i].getFieldCount(); j++) {
if (hfile[i].getExInterpNum(j) != E_KERN_EXINT) {
continue;
}
int length = strlen(hfile[i][j]);
if (length < 3 || length > 4) {
continue;
}
if (hfile[i][j][length-1] == ':') {
cout << "// key ";
cout << (char)toupper(hfile[i][j][1]);
if (hfile[i][j][2] == '-') {
cout << "-flat";
}
if (hfile[i][j][2] == '#') {
cout << "-sharp";
}
if (islower(hfile[i][j][1])) {
cout << " Minor";
} else {
cout << " Major";
}
cout << endl;
break;
}
}
}
*/
// look for tempo markings
if (hfile[i].getType() == E_humrec_interpretation) {
for (j=0; j<hfile[i].getFieldCount(); j++) {
if (hfile[i].getExInterpNum(j) != E_KERN_EXINT) {
continue;
}
double tempomark = 0.0;
if (strncmp(hfile[i][j], "*MM", 3) == 0 &&
isdigit(hfile[i][j][3])) {
sscanf(hfile[i][j], "*MM%lf", &tempomark);
// cout << "// Tempo " << tempomark << " MM per quarter note"
// << endl;
tempskini.message = TEMPO;
tempskini.time = currentmillisecond/1000.0;
tempskini.chan = 0;
tempskini.note = 0;
tempskini.vel = tempomark;
tempskini.id = idcounter;
skini.append(tempskini);
idcounter++;
break;
}
}
}
// look for instrument names
if (hfile[i].getType() == E_humrec_interpretation) {
for (j=0; j<hfile[i].getFieldCount(); j++) {
if (hfile[i].getExInterpNum(j) != E_KERN_EXINT) {
continue;
}
if (strncmp(hfile[i][j], "*I", 2) == 0 &&
islower(hfile[i][j][2])) {
tempskini.message = INSTRUMENT;
tempskini.time = currentmillisecond/1000.0;
tempskini.chan = (TEMP64BITFIX)&hfile[i][j][2];
tempskini.note = hfile[i].getPrimaryTrack(j);
tempskini.vel = tempskini.time;
tempskini.id = idcounter;
skini.append(tempskini);
idcounter++;
}
}
}
// look for Barlines
if (hfile[i].getType() == E_humrec_data_measure) {
int mnum = -1;
int flag = 0;
flag = sscanf(hfile[i][0], "=%d", &mnum);
if (flag == 1) {
tempskini.message = MEASURE;
tempskini.time = currentmillisecond/1000.0;
tempskini.chan = mnum;
tempskini.note = 0;
tempskini.vel = tempskini.time;
tempskini.id = idcounter;
skini.append(tempskini);
idcounter++;
} else {
tempskini.message = MEASURE;
tempskini.time = currentmillisecond/1000.0;
tempskini.chan = -1;
tempskini.note = 0;
tempskini.vel = tempskini.time;
tempskini.id = idcounter;
skini.append(tempskini);
idcounter++;
}
}
if (hfile[i].getType() != E_humrec_data) {
continue;
}
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);
int note = Convert::kernToMidiNoteNumber(buffer2);
if (classQ) {
note = note % 12;
}
if (note < 0) {
// don't display rests.
continue;
}
tempskini.message = NOTEON;
tempskini.time = currentmillisecond/1000.0;
tempskini.chan = hfile[i].getPrimaryTrack(j);
tempskini.note = note;
tempskini.vel = 64;
tempskini.id = idcounter;
skini.append(tempskini);
endmillisecond = currentmillisecond + duration * 60000 / tempo[i];
tempskini.message = NOTEOFF;
tempskini.time = endmillisecond/1000.0;
tempskini.chan = hfile[i].getPrimaryTrack(j);
tempskini.note = note;
tempskini.vel = 64;
tempskini.id = idcounter;
skini.append(tempskini);
idcounter++;
}
}
}
}
//////////////////////////////
//
// preparePitch --
//
void preparePitch(char* buffer2, const char* buffer1) {
strcpy(buffer2, buffer1);
}
//////////////////////////////
//
// usage --
//
void usage(const char* command) {
}
//////////////////////////////
//
// operator<<SKINI --
//
ostream& operator<<(ostream& out, SKINI skini) {
if (skini.message == MEASURE) {
out << "\n";
out << "// Measure ";
if (skini.chan > 0) {
out << "number " << skini.chan;
}
out << "\t=" << skini.vel;
return out;
}
if (skini.message == COMMENT) {
out << "// " << (char*)(skini.chan);
return out;
}
if (skini.message == INFO) {
out << "///" << (char*)(skini.chan);
return out;
}
if (skini.message == TEMPO) {
out << "// Tempo " << skini.vel << " MM per quarter note";
return out;
}
if (skini.message == INSTRUMENT) {
out << "// Instrument <" << (char*)skini.chan << "> for channel "
<< skini.note;
return out;
}
if (skini.message == KEYSIG) {
out << "// Keysig " << labs(skini.chan);
if (skini.chan == 0) {
out << " accidentals";
} else if (skini.chan < 0) {
out << " flat";
} else {
out << " sharp";
}
if (labs(skini.chan) > 1) {
out << "s";
}
return out;
}
switch (skini.message) {
case NOTEON: out << "NoteOn \t"; break;
case NOTEOFF: out << "NoteOff \t"; break;
default: out << "UNKNOWN \t";
}
// send time value:
out.setf(ios::left, ios::adjustfield);
out << setw(10) << skini.time << " ";
// send channel:
out << skini.chan << "\t";
// send note number:
out << " " << skini.note << "\t";
// send attack/release velocity:
out << " " << skini.vel;
// out << "\t[" << skini.id << "]";
return out;
}
///////////////////////////////
//
// skinicompare -- for sorting the times of SKINI messages
//
int skinicompare(const void* a, const void* b) {
SKINI& aa = *((SKINI*)a);
SKINI& bb = *((SKINI*)b);
if (fabs(aa.time - bb.time) < 0.0001) {
// break ties by the id number
if (aa.id < bb.id) {
return -1;
} else if (aa.id > bb.id) {
return 1;
} else {
return 0;
}
} else if (aa.time < bb.time) {
return -1;
} else if (aa.time > bb.time) {
return 1;
}
return 1;
}
// md5sum: 810dfce9fe34da8fae775599337dfbb3 kern2skini.cpp [20100905]