//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Thu Oct 13 21:48:04 PDT 2011
// Last Modified: Thu Oct 13 21:48:07 PDT 2011
// Filename: ...sig/examples/all/addtempo.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/addtempo.cpp
// Syntax: C++; museinfo
//
// Description: Add tempo settings to a Humdrum file with **kern spines.
//
#ifndef OLDCPP
#include <iostream>
using namespace std;
#else
#include <iostream.h>
#endif
#include "humdrum.h"
#include "PerlRegularExpression.h"
//////////////////////////////////////////////////////////////////////////
// function declarations:
void checkOptions(Options& opts, int argc, char** argv);
void example(void);
void usage(const char* command);
void processFile(HumdrumFile& infile,
Array<Array<double> >& tempos);
int assignTempoLine(Array<double>& tempos, HumdrumFile& infile);
void printTempo(HumdrumFile& infile, int line,
Array<double>& tempo);
void setTempoValues(Array<Array<double> >& settings,
const char* string);
// User interface variables:
Options options;
int debugQ = 0; // used with --debug option
Array<Array<double> > TempoSettings; // used with -t option
//////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
HumdrumFile infile;
// initial processing of the command-line options
checkOptions(options, argc, argv);
if (options.getArgCount() < 1) {
infile.read(cin);
} else {
infile.read(options.getArg(1));
}
processFile(infile, TempoSettings);
return 0;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// processFile -- insert tempo marks into Humdrum file.
//
void processFile(HumdrumFile& infile, Array >& tempos) {
Array<int> tline;
tline.setSize(tempos.getSize());
tline.setGrowth(0);
tline.setAll(-1);
infile.analyzeRhythm("4");
int i;
for (i=0; i<tempos.getSize(); i++) {
tline[i] = assignTempoLine(tempos[i], infile);
if (debugQ) {
cout << "!! LINE " << i << "\t" << tline[i] << endl;
}
}
int ii = 0;
for (i=0; i<infile.getNumLines(); i++) {
cout << infile[i] << "\n";
if ((ii < tline.getSize()) && (tline[ii] == i)) {
printTempo(infile, tline[ii], tempos[ii]);
ii++;
}
}
}
//////////////////////////////
//
// printTempo --
//
void printTempo(HumdrumFile& infile, int line, Array& tempo) {
int& i = line;
int j;
if (infile[i].isGlobalComment()) {
cerr << "Global comment case not handled yet" << endl;
exit(1);
}
if (infile[i].isBibliographic()) {
cerr << "Reference record case not handled yet" << endl;
exit(1);
}
for (j=0; j<infile[i].getFieldCount(); j++) {
if (infile[i].isExInterp(j, "**kern")) {
cout << "*MM" << tempo[0];
} else {
cout << "*";
}
if (j < infile[i].getFieldCount() - 1) {
cout << "\t";
}
}
cout << "\n";
// cout << "!!TEMPO: " << tempo[0] << "\tM="
// << tempo[1] << "\tB=" << tempo[2] << endl;
}
//////////////////////////////
//
// assgignTempoLines --
//
int assignTempoLine(Array& tempos, HumdrumFile& infile) {
// first find the first data line at the specified position (or just
// before the next data line which occurs after the target position).
PerlRegularExpression pre;
int measure = (int)tempos[1];
double beat = tempos[2];
int i;
int exinterp = -1;
int dataline = -1;
int measureline = -1;
int meterline = -1;
int metline = -1;
if (measure == -1) {
// the tempo should go at the start of the music. First search for
// a *M%d/%d line (optionally followed by a *met() line) to place
// the tempo marking after. If not found, then place just before
// the first data or measure line found in the score. Also assuming
// that the meter, measure are in the first column of data.
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].isData()) {
dataline = i;
break;
}
if (infile[i].isMeasure()) {
measureline = i;
continue;
}
if (infile[i].isInterpretation()) {
if (strncmp(infile[i][0], "**", 2) == 0) {
exinterp = i;
} else if (pre.search(infile[i][0], "^\\*M(\\d+)/(\\d+)")) {
meterline = i;
continue;
} else if (pre.search(infile[i][0], "^\\*met(.*)")) {
metline = i;
continue;
}
}
}
if (metline > 0) {
return metline;
} else if (meterline > 0) {
return meterline;
} else if (measureline > 0) {
return measureline-1;
} else if (dataline > 0) {
return dataline-1;
} else {
return exinterp;
}
}
// place tempo with a particular measure
int curmeasure = -1;
double basebeat = -1;
double curbeat = -1;
int workingline = -1;
int j;
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].isMeasure()) {
if (pre.search(infile[i][0], "^=(\\d+)")) {
curmeasure = atoi(pre.getSubmatch(1));
}
}
if (curmeasure == measure) {
basebeat = infile[i].getAbsBeat();
for (j=i+1; j<infile.getNumLines(); j++) {
curbeat = infile[j].getAbsBeat() - basebeat + 1.0;
if (fabs(curbeat - beat) < 0.01) {
workingline = j-1;
break;
} else if (curbeat > beat) {
workingline = j-1;
break;
}
}
// refine working line later...
return workingline;
}
}
cerr << "GOT TO A FUNNY PLACE" << endl;
exit(1);
}
//////////////////////////////
//
// checkOptions --
//
void checkOptions(Options& opts, int argc, char** argv) {
opts.define("d|debug=b", "Debugging information");
opts.define("t|tempo=s:", "Tempo setting(s)");
opts.define("author=b", "Program author");
opts.define("version=b", "Program version");
opts.define("example=b", "Program examples");
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, Feb 2011" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 5 Feb 2011" << 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("tempo")) {
setTempoValues(TempoSettings, opts.getString("tempo"));
if (TempoSettings.getSize() == 0) {
usage(opts.getCommand());
}
} else {
usage(opts.getCommand());
}
}
//////////////////////////////
//
// setTempoValues -- read the tempo values from the
//
void setTempoValues(Array >& settings, const char* string) {
PerlRegularExpression pre;
Array<Array<char> > tokens;
pre.getTokens(tokens, "[\\s,:;]+", string);
settings.setSize(tokens.getSize());
settings.allowGrowth(0);
int i;
for (i=0; i<settings.getSize(); i++) {
settings[i].setSize(3);
settings[i].setAll(-1);
settings[i].allowGrowth(0);
}
double tempo;
double measure;
double beat;
for (i=0; i<tokens.getSize(); i++) {
if (pre.search(tokens[i], "([\\d.]+)@m(\\d+)b([\\d.]+)")) {
// read tempo for specific measure and beat
sscanf(pre.getSubmatch(1), "%lf", &tempo);
sscanf(pre.getSubmatch(2), "%lf", &measure);
sscanf(pre.getSubmatch(3), "%lf", &beat);
} else if (pre.search(tokens[i], "(\\d+)@m(\\d+)")) {
// read tempo for a measure, set beat to 1
sscanf(pre.getSubmatch(1), "%lf", &tempo);
sscanf(pre.getSubmatch(2), "%lf", &measure);
beat = 1;
} else if (pre.search(tokens[i], "(\\d+)")) {
// read default tempo, set measure to -1 and beat to -1
sscanf(pre.getSubmatch(1), "%lf", &tempo);
measure = -1;
beat = -1;
} else {
continue;
}
settings[i][0] = tempo;
settings[i][1] = measure;
settings[i][2] = beat;
}
if (debugQ) {
for (i=0; i<settings.getSize(); i++) {
cout << "!! " << i << "\t" << settings[i][0]
<< "\t" << settings[i][1]
<< "\t" << settings[i][2]
<< endl;
}
}
}
//////////////////////////////
//
// example -- example function calls to the program.
//
void example(void) {
}
//////////////////////////////
//
// usage -- command-line usage description and brief summary
//
void usage(const char* command) {
cout << "Usage: " << command << " -t tempostring file.in > file.out\n";
exit(1);
}
// md5sum: bc8e4e25dd5abf9b3b79bbaa0f4a69cd addtempo.cpp [20111020]