//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon Apr 12 23:22:19 PDT 2004
// Last Modified: Mon Apr 12 23:22:22 PDT 2004
// Last Modified: Thu Feb 24 22:43:17 PST 2005 (added -k option)
// Last Modified: Wed Jun 24 15:39:58 PDT 2009 (updated for GCC 4.4)
// Last Modified: Sun Sep 13 12:34:51 PDT 2009 (added -s option)
// Last Modified: Wed Nov 18 14:01:20 PST 2009 (added *Tr markers)
// Last Modified: Thu Nov 19 14:08:32 PST 2009 (added -q, -d and -c options)
// Last Modified: Thu Nov 19 15:12:01 PST 2009 (added -I options and *ITr marks)
// Last Modified: Thu Nov 19 19:28:26 PST 2009 (added -W and -C options)
// Filename: ...sig/examples/all/transpose.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/transpose.cpp
// Syntax: C++; museinfo
//
// Description: Transpose **kern musical data.
//
#include "humdrum.h"
#include "PerlRegularExpression.h"
#include <string.h>
#include <stdio.h>
#include <math.h>
#ifndef OLDCPP
#include <iostream>
#include <fstream>
#else
#include <iostream.h>
#include <fstream.h>
#endif
#define STYLE_CONCERT 0
#define STYLE_WRITTEN 1
// function declarations:
void checkOptions(Options& opts, int argc, char** argv);
void example(void);
void usage(const char* command);
void printFile(HumdrumFile& infile);
void processFile(HumdrumFile& infile, Array<int>& spines);
void printNewKernString(const char* string, int transval);
void printHumdrumKernToken(HumdrumRecord& record, int index,
int transval);
void printHumdrumDataRecord(HumdrumRecord& record,
Array<int>& spineprocess);
int getBase40ValueFromInterval(const char* string);
void printNewKeyInterpretation(HumdrumRecord& aRecord, int index,
int transval);
void printNewKeySignature(const char* keysig, int trans);
void convertScore(HumdrumFile& infile, int style);
void printTransposedToken(HumdrumFile& infile, int row, int col,
int transval);
void processInterpretationLine(HumdrumFile& infile, int line,
Array<int>& tvals, int style);
int isKeyMarker(const char* string);
int getTransposeInfo(HumdrumFile& infile, int row, int col);
int hasTrMarkers(HumdrumFile& infile, int line);
void convertToWrittenPitches(HumdrumFile& infile, int line,
Array<int>& tvals);
void convertToConcertPitches(HumdrumFile& infile, int line,
Array<int>& tvals);
// auto transpose functions:
double pearsonCorrelation(int size, double* x, double* y);
void doAutoTransposeAnalysis(HumdrumFile& infile);
void addToHistogramDouble(Array<Array<double> >& histogram, int pc,
double start, double dur, double tdur,
int segments);
double storeHistogramForTrack(Array<Array<double> >& histogram,
HumdrumFile& infile, int track, int segments);
void printHistograms(int segments, Array<int> ktracks,
Array<Array<Array<double> > >& trackhist);
void doAutoKeyAnalysis(Array<Array<Array<double> > >& analysis,
int level, int hop, int count, int segments,
Array<int>& ktracks,
Array<Array<Array<double> > >& trackhist);
void doTrackKeyAnalysis(Array<Array<double> >& analysis, int level,
int hop, int count,
Array<Array<double> >& trackhist,
Array<double>& majorweights,
Array<double>& minorweights);
void identifyKeyDouble(Array<double>& correls,
Array<double>& histogram,
Array<double>& majorweights,
Array<double>& minorweights);
void fillWeightsWithKostkaPayne(Array<double>& maj, Array<double>& min);
void printRawTrackAnalysis(Array<Array<Array<double> > >& analysis,
Array<int>& ktracks);
void doSingleAnalysis(Array<double>& analysis, int startindex,
int length, Array<Array<double> >& trackhist,
Array<double>& majorweights,
Array<double>& minorweights);
void identifyKey(Array<double>& correls,
Array<double>& histogram,
Array<double>& majorweights,
Array<double>& minorweights);
void doTranspositionAnalysis(Array<Array<Array<double> > >& analysis);
int calculateTranspositionFromKey(int targetkey, HumdrumFile& infile);
// spine list parsing functions:
void processFieldEntry(Array<int>& field, const char* string,
HumdrumFile& infile);
void fillFieldData(Array<int>& field, const char* fieldstring,
HumdrumFile& infile);
void removeDollarsFromString(Array<char>& buffer, int maxtrack);
// User interface variables:
Options options;
int transval = 0; // used with -b option
int ssetkeyQ = 0; // used with -k option
int ssetkey = 0; // used with -k option
int currentkey = 0;
int autoQ = 0; // used with --auto option
int debugQ = 0; // used with --debug option
int spineQ = 0; // used with -s option
const char* spinestring = ""; // used with -s option
int octave = 0; // used with -o option
int concertQ = 0; // used with -C option
int writtenQ = 0; // used with -W option
int quietQ = 0; // used with -q option
int instrumentQ = 0; // used with -I option
//////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
// process the command-line options
checkOptions(options, argc, argv);
HumdrumFile infile;
if (options.getArgCount() > 0) {
infile.read(options.getArg(1));
} else {
infile.read(cin);
}
Array<int> spineprocess(infile.getMaxTracks());
spineprocess.setGrowth(0);
spineprocess.setAll(1);
if (spineQ) {
fillFieldData(spineprocess, spinestring, infile);
if (debugQ) {
int i;
cout << "!! SPINE INFORMATION: ";
for (i=0; i<spineprocess.getSize(); i++) {
cout << spineprocess[i] << " ";
}
cout << endl;
}
}
if (ssetkeyQ) {
transval = calculateTranspositionFromKey(ssetkey, infile);
transval = transval + octave * 40;
if (debugQ) {
cout << "!!Key TRANSVAL = " << transval;
}
}
if (options.getBoolean("rotation")) {
// returns the base-12 pitch transposition for use in conjunction
// with the mkeyscape --rotate option
int value = 60 - Convert::base40ToMidiNoteNumber(162 - transval);
cout << value << endl;
exit(0);
}
if (concertQ) {
convertScore(infile, STYLE_CONCERT);
} else if (writtenQ) {
convertScore(infile, STYLE_WRITTEN);
} else if (autoQ) {
doAutoTransposeAnalysis(infile);
} else {
processFile(infile, spineprocess);
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// convertScore -- create a concert pitch score from
// a written pitch score. The function will search for *Tr
// interpretations in spines, and convert them to *ITr interpretations
// as well as transposing notes, and transposing key signatures and
// key interpretations. Or create a written score from a
// concert pitch score based on the style parameter.
//
void convertScore(HumdrumFile& infile, int style) {
Array<int> tvals; // transposition values for each spine
tvals.setSize(infile.getMaxTracks() + 1);
tvals.setAll(0);
int ptrack;
int i, j;
for (i=0; i<infile.getNumLines(); i++) {
switch (infile[i].getType()) {
case E_humrec_interpretation:
// scan the line for transposition codes
// as well as key signatures and key markers
processInterpretationLine(infile, i, tvals, style);
break;
case E_humrec_data:
// transpose notes according to tvals data
for (j=0; j<infile[i].getFieldCount(); j++) {
ptrack = infile[i].getPrimaryTrack(j);
if (tvals[ptrack] == 0) {
cout << infile[i][j];
} else {
printTransposedToken(infile, i, j, tvals[ptrack]);
}
if (j < infile[i].getFieldCount() - 1) {
cout << "\t";
}
}
cout << "\n";
break;
case E_humrec_data_comment:
case E_humrec_data_kern_measure:
case E_humrec_none:
case E_humrec_empty:
case E_humrec_global_comment:
case E_humrec_bibliography:
default:
cout << infile[i] << "\n";
break;
}
}
}
//////////////////////////////
//
// processInterpretationLine -- Used in converting between
// concert pitch and written pitch scores.
//
void processInterpretationLine(HumdrumFile& infile, int line,
Array<int>& tvals, int style) {
PerlRegularExpression pre;
int j;
int ptrack;
if (hasTrMarkers(infile, line)) {
switch (style) {
case STYLE_CONCERT:
convertToConcertPitches(infile, line, tvals);
break;
case STYLE_WRITTEN:
convertToWrittenPitches(infile, line, tvals);
break;
default: cout << infile[line];
}
cout << "\n";
return;
}
for (j=0; j<infile[line].getFieldCount(); j++) {
ptrack = infile[line].getPrimaryTrack(j);
// check for *ITr or *Tr markers
// ignore *ITr markers when creating a Concert-pitch score
// ignore *Tr markers when creating a Written-pitch score
if (pre.search(infile[line][j], "^\\*k\\[([a-gA-G\\#-]*)\\]", "")) {
// transpose *k[] markers if necessary
if (tvals[ptrack] != 0) {
printNewKeySignature(pre.getSubmatch(1), tvals[ptrack]);
} else {
cout << infile[line][j];
}
} else if (isKeyMarker(infile[line][j])) {
// transpose *C: markers and like if necessary
if (tvals[ptrack] != 0) {
printNewKeyInterpretation(infile[line], j, tvals[ptrack]);
} else {
cout << infile[line][j];
}
} else {
// other interpretations just echoed to output:
cout << infile[line][j];
}
if (j<infile[line].getFieldCount()-1) {
cout << "\t";
}
}
cout << "\n";
}
//////////////////////////////
//
// convertToWrittenPitches --
//
void convertToWrittenPitches(HumdrumFile& infile, int line, Array& tvals) {
PerlRegularExpression pre;
int j;
int base;
int ptrack;
char buffer1[128] = {0};
char buffer2[128] = {0};
for (j=0; j<infile[line].getFieldCount(); j++) {
if (pre.search(infile[line][j], "^\\*ITrd[+-]?\\d+c[+-]?\\d+$", "")) {
base = Convert::transToBase40(infile[line][j]);
strcpy(buffer1, "*Tr");
strcat(buffer1, Convert::base40ToTrans(buffer2, base));
cout << buffer1;
ptrack = infile[line].getPrimaryTrack(j);
tvals[ptrack] = base;
} else {
cout << infile[line][j];
}
if (j < infile[line].getFieldCount() - 1) {
cout << "\t";
}
}
}
//////////////////////////////
//
// convertToConcertPitches --
//
void convertToConcertPitches(HumdrumFile& infile, int line, Array& tvals) {
PerlRegularExpression pre;
int j;
int base;
int ptrack;
char buffer1[128] = {0};
char buffer2[128] = {0};
for (j=0; j<infile[line].getFieldCount(); j++) {
if (pre.search(infile[line][j], "^\\*Trd[+-]?\\d+c[+-]?\\d+$", "")) {
base = Convert::transToBase40(infile[line][j]);
strcpy(buffer1, "*ITr");
strcat(buffer1, Convert::base40ToTrans(buffer2, base));
cout << buffer1;
ptrack = infile[line].getPrimaryTrack(j);
tvals[ptrack] = -base;
} else {
cout << infile[line][j];
}
if (j < infile[line].getFieldCount() - 1) {
cout << "\t";
}
}
}
//////////////////////////////
//
// hasTrMarkers -- returns true if there are any tokens
// which start with *ITr or *Tr and contains c and d
// with numbers after each of them.
//
int hasTrMarkers(HumdrumFile& infile, int line) {
PerlRegularExpression pre;
int j;
for (j=0; j<infile[line].getFieldCount(); j++) {
if (pre.search(infile[line][j], "^\\*I?Trd[+-]?\\d+c[+-]?\\d+$", "")) {
return 1;
}
}
return 0;
}
//////////////////////////////
//
// isKeyMarker -- returns true if the interpretation is
// a key description, such as *C: for C major, or *a:.
//
int isKeyMarker(const char* string) {
PerlRegularExpression pre;
return pre.search(string, "^\\*[a-g]?[\\#-]?:", "i");
}
//////////////////////////////
//
// printTransposedToken -- print a Humdrum token with the given
// base-40 transposition value applied. Only **kern data is
// know now to transpose, other data types are currently not
// allowed to be transposed (but could be added here later).
//
void printTransposedToken(HumdrumFile& infile, int row, int col, int transval) {
if (strcmp("**kern", infile[row].getExInterp(col)) != 0) {
// don't know how to transpose this type of data, so leave it as is
cout << infile[row][col];
return;
}
printHumdrumKernToken(infile[row], col, transval);
}
//////////////////////////////
//
// calculateTranspositionFromKey --
//
int calculateTranspositionFromKey(int targetkey, HumdrumFile& infile) {
int i, j;
PerlRegularExpression pre;
int base40 = 0;
int currentkey = 0;
int mode = 0;
int found = 0;
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].isData()) {
// no initial key label was found, so don't transpose.
// in the future, maybe allow an automatic key analysis
// to be performed on the data if there is not explicit
// key designation.
return 0;
}
if (!infile[i].isInterpretation()) {
continue;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
if (!pre.search(infile[i][j], "^\\*([A-G][#-]?):", "i")) {
continue;
}
mode = 0; // major key
if (islower(infile[i][j][1])) {
mode = 1; // minor key
}
base40 = Convert::kernToBase40(infile[i][j]);
// base40 = base40 + transval;
base40 = base40 + 4000;
base40 = base40 % 40;
base40 = base40 + (3 + mode) * 40;
currentkey = base40;
found = 1;
break;
}
if (found) {
break;
}
}
int trans = (targetkey%40 - currentkey%40);
// base40 = targetkey + (3 + mode) * 40;
if (trans > 40) {
trans -= 40;
}
if (trans > 20) {
trans = 40 - trans;
trans = -trans;
}
if (trans < -40) {
trans += 40;
}
if (trans < -20) {
trans = -40 - trans;
trans = -trans;
}
return trans;
}
//////////////////////////////
//
// printTransposeInformation -- collect and print *Tr interpretations
// at the start of the spine. Looks for *Tr markers at the start
// of the file before any data.
//
void printTransposeInformation(HumdrumFile& infile, Array<int>& spineprocess,
int line, int transval) {
int j;
int ptrack;
Array<int> startvalues;
startvalues.setSize(infile.getMaxTracks()+1);
startvalues.setAll(0);
Array<int> finalvalues;
finalvalues.setSize(infile.getMaxTracks()+1);
finalvalues.setAll(0);
for (j=0; j<infile[line].getFieldCount(); j++) {
ptrack = infile[line].getPrimaryTrack(j);
startvalues[ptrack] = getTransposeInfo(infile, line, j);
// cout << "Found transpose value " << startvalues[ptrack] << endl;
}
int entry = 0;
// check if any spine will be transposed after final processing
for (j=0; j<infile[line].getFieldCount(); j++) {
ptrack = infile[line].getPrimaryTrack(j);
if (spineprocess[ptrack-1]) {
finalvalues[ptrack] = transval;
if (!instrumentQ) {
finalvalues[ptrack] += startvalues[ptrack];
}
// cout << "New value is: " << finalvalues[ptrack] << endl;
if (finalvalues[ptrack] != 0) {
entry = 1;
}
} else {
finalvalues[ptrack] = startvalues[ptrack];
if (finalvalues[ptrack] != 0) {
entry = 1;
}
}
}
if (!entry) {
return;
}
char buffer[1024] = {0};
for (j=0; j<infile[line].getFieldCount(); j++) {
ptrack = infile[line].getPrimaryTrack(j);
if (finalvalues[ptrack] == 0) {
cout << "*";
} else {
if (instrumentQ) {
cout << "*ITr";
cout << Convert::base40ToTrans(buffer, -finalvalues[ptrack]);
} else {
cout << "*Tr";
cout << Convert::base40ToTrans(buffer, finalvalues[ptrack]);
}
}
if (j < infile[line].getFieldCount()-1) {
cout << "\t";
}
}
cout << "\n";
}
//////////////////////////////
//
// getTransposeInfo -- returns the Transpose information found in
// the specified spine starting at the current line, and searching
// until data is found (or a *- record is found). Return value is a
// base-40 number.
//
int getTransposeInfo(HumdrumFile& infile, int row, int col) {
int i, j;
int track = infile[row].getPrimaryTrack(col);
int ptrack;
PerlRegularExpression pre;
int base;
int output = 0;
for (i=row; i<infile.getNumLines(); i++) {
if (infile[i].isData()) {
break;
}
if (!infile[i].isInterpretation()) {
continue;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
ptrack = infile[i].getPrimaryTrack(j);
if (ptrack != track) {
continue;
}
if (pre.search(infile[i][j], "^\\*Trd[+-]?\\d+c[+-]?\\d+$", "")) {
base = Convert::transToBase40(infile[i][j]);
output += base;
// erase the *Tr value because it will be printed elsewhere
infile[i].changeField(j, "*deletedTr");
}
}
}
return output;
}
//////////////////////////////
//
// checkForDeletedLine -- check to see if a "*deletedTr
//
int checkForDeletedLine(HumdrumFile& infile, int line) {
int j;
if (!infile[line].isInterpretation()) {
return 0;
}
int present = 0;
int composite = 0;
for (j=0; j<infile[line].getFieldCount(); j++) {
if (strstr(infile[line][j], "deletedTr") != NULL) {
present = 1;
} else if (strcmp(infile[line][j], "*") == 0) {
// do nothing: not composite
} else {
// not a *deletedTr token or a * token, so have to print line later
composite = 1;
}
}
if (present == 0) {
// no *deletedTr records found on the currnet line, so process normally
return 0;
}
if (composite == 0) {
// *deletedTr found, but no other important data found on line.
return 1;
}
// print non-deleted elements in line.
for (j=0; j<infile[line].getFieldCount(); j++) {
if (strcmp(infile[line][j], "*deletedTr") == 0) {
cout << "*";
} else {
cout << infile[line][j];
}
if (j < infile[line].getFieldCount() - 1) {
cout << "\t";
}
}
cout << "\n";
return 1;
}
//////////////////////////////
//
// processFile --
//
void processFile(HumdrumFile& infile, Array& spineprocess) {
int i;
int length;
int diatonic;
int j;
PerlRegularExpression pre;
const char* ptr;
int interpstart = 0;
for (i=0; i<infile.getNumLines(); i++) {
if (!quietQ && (interpstart == 1)) {
interpstart = 2;
printTransposeInformation(infile, spineprocess, i, transval);
}
if (checkForDeletedLine(infile, i)) {
continue;
}
switch (infile[i].getType()) {
case E_humrec_data:
printHumdrumDataRecord(infile[i], spineprocess);
cout << "\n";
break;
case E_humrec_interpretation:
for (j=0; j<infile[i].getFieldCount(); j++) {
if (strncmp(infile[i][j], "**", 2) == 0) {
interpstart = 1;
}
// check for key signature in a spine which is being
// transposed, and adjust it.
if (spineprocess[infile[i].getPrimaryTrack(j)-1] &&
pre.search(infile[i][j], "^\\*k\\[([a-gA-G\\#-]*)\\]", "")) {
printNewKeySignature(pre.getSubmatch(1), transval);
if (j<infile[i].getFieldCount()-1) {
cout << "\t";
}
continue;
}
// check for key tandem interpretation and tranpose
// if the spine data is being transposed.
length = strlen(infile[i][j]);
ptr = strrchr(infile[i][j], ':');
if ((length < 3) || (ptr == NULL) || (ptr - infile[i][j] > 4)
|| !spineprocess[infile[i].getPrimaryTrack(j)-1]) {
cout << infile[i][j];
if (j<infile[i].getFieldCount()-1) {
cout << "\t";
}
continue;
}
diatonic = tolower(infile[i][j][1]) - 'a';
if (diatonic >= 0 && diatonic <= 6) {
printNewKeyInterpretation(infile[i], j, transval);
if (j<infile[i].getFieldCount()-1) {
cout << "\t";
}
continue;
}
cout << infile[i][j];
if (j<infile[i].getFieldCount()-1) {
cout << "\t";
}
}
cout << "\n";
break;
case E_humrec_none:
case E_humrec_empty:
case E_humrec_global_comment:
case E_humrec_bibliography:
case E_humrec_data_comment:
case E_humrec_data_kern_measure:
default:
cout << infile[i] << "\n";
}
}
}
//////////////////////////////
//
// printNewKeySignature --
//
void printNewKeySignature(const char* keysig, int trans) {
int counter = 0;
int len = strlen(keysig);
int i;
for (i=0; i<len; i++) {
switch(keysig[i]) {
case '-': counter--; break;
case '#': counter++; break;
}
}
int xxx = Convert::base40IntervalToLineOfFifths(trans);
int newkey = xxx + counter;
cout << Convert::keyNumberToKern(newkey);
}
//////////////////////////////
//
// printNewKeyInterpretation --
//
void printNewKeyInterpretation(HumdrumRecord& aRecord, int index,
int transval) {
int mode = 0;
if (islower(aRecord[index][1])) {
mode = 1;
}
int base40 = Convert::kernToBase40(aRecord[index]);
currentkey = base40;
base40 = base40 + transval;
base40 = base40 + 4000;
base40 = base40 % 40;
base40 = base40 + (3 + mode) * 40;
char buffer[128] = {0};
cout << "*" << Convert::base40ToKern(buffer, base40) << ":";
PerlRegularExpression pre;
if (pre.search(aRecord[index], ":(.+)$", "")) {
cout << pre.getSubmatch(1);
}
}
//////////////////////////////
//
// printHumdrumDataRecord --
//
void printHumdrumDataRecord(HumdrumRecord& record, Array& spineprocess) {
int i;
for (i=0; i<record.getFieldCount(); i++) {
if (!spineprocess[record.getPrimaryTrack(i)-1]) {
// don't try to transpose spines which were not indicated.
cout << record[i];
if (i<record.getFieldCount()-1) {
cout << "\t";
}
continue;
}
if (record.getExInterpNum(i) != E_KERN_EXINT) {
// don't try to transpose non-kern spines
cout << record[i];
if (i<record.getFieldCount()-1) {
cout << "\t";
}
continue;
}
printHumdrumKernToken(record, i, transval);
if (i<record.getFieldCount()-1) {
cout << "\t";
}
continue;
}
}
//////////////////////////////
//
// printHumdrumKernToken --
//
void printHumdrumKernToken(HumdrumRecord& record, int index, int transval) {
if (strcmp(record[index], ".") == 0) {
// null record element (no pitch).
cout << ".";
return;
}
int k;
static char buffer[1024] = {0};
int tokencount = record.getTokenCount(index);
for (k=0; k<tokencount; k++) {
record.getToken(buffer, index, k);
printNewKernString(buffer, transval);
if (k<tokencount-1) {
cout << " ";
}
}
}
//////////////////////////////
//
// printNewKernString --
//
void printNewKernString(const char* string, int transval) {
if (strchr(string, 'r') != NULL) {
// don't transpose rests...
cout << string;
return;
}
char buffer[1024] = {0};
char buffer2[1024] = {0};
strcpy(buffer, string);
int base40 = Convert::kernToBase40(string);
char* ptr1 = strtok(buffer, "ABCDEFGabcdefg#-n");
char* ptr2 = strtok(NULL, "ABCDEFGabcdefg#-n");
char* ptr3 = strtok(NULL, "ABCDEFGabcdefg#-n");
if (ptr3 != NULL) {
cout << "Error in **kern pitch token: " << string << endl;
exit(1);
}
if (ptr1 != NULL) {
cout << ptr1;
}
cout << Convert::base40ToKern(buffer2, base40 + transval);
if (ptr2 != NULL) {
cout << ptr2;
}
}
//////////////////////////////
//
// checkOptions --
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("b|base40=i:0", "the base-40 transposition value");
opts.define("d|diatonic=i:0", "the diatonic transposition value");
opts.define("c|chromatic=i:0", "the chromatic transposition value");
opts.define("o|octave=i:0", "the octave addition to tranpose value");
opts.define("t|transpose=s", "musical interval transposition value");
opts.define("k|setkey=s", "transpose to the given key");
opts.define("auto=b", "auto. trans. inst. parts to concert pitch");
opts.define("debug=b", "print debugging statements");
opts.define("s|spines=s", "transpose only specified spines");
opts.define("q|quiet=b", "suppress *Tr interpretations in output");
opts.define("I|instrument=b", "insert instrument code (*ITr) as well");
opts.define("C|concert=b", "transpose written score to concert pitch");
opts.define("W|written=b", "trans. concert pitch score to written score");
opts.define("rotation=b", "display transposition in half-steps");
opts.define("author=b", "author of program");
opts.define("version=b", "compilation info");
opts.define("example=b", "example usages");
opts.define("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, 12 Apr 2004" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 17 Nov 2009" << 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);
}
transval = opts.getInteger("base40");
ssetkeyQ = opts.getBoolean("setkey");
ssetkey = Convert::kernToBase40(opts.getString("setkey"));
autoQ = opts.getBoolean("auto");
debugQ = opts.getBoolean("debug");
spineQ = opts.getBoolean("spines");
spinestring = opts.getString("spines");
octave = opts.getInteger("octave");
concertQ = opts.getBoolean("concert");
writtenQ = opts.getBoolean("written");
quietQ = opts.getBoolean("quiet");
instrumentQ = opts.getBoolean("instrument");
switch (opts.getBoolean("diatonic") + opts.getBoolean("chromatic")) {
case 1:
cerr << "Error: both -d and -c options must be specified" << endl;
exit(1);
break;
case 2:
{
char buffer[128] = {0};
sprintf(buffer, "d%dc%d", opts.getInt("d"), opts.getInt("c"));
transval = Convert::transToBase40(buffer);
}
break;
}
ssetkey = ssetkey % 40;
if (opts.getBoolean("transpose")) {
transval = getBase40ValueFromInterval(opts.getString("transpose"));
}
transval += 40 * octave;
}
//////////////////////////////
//
// getBase40ValueFromInterval -- note: only ninth interval range allowed
//
int getBase40ValueFromInterval(const char* string) {
int sign = 1;
if (strchr(string, '-') != NULL) {
sign = -1;
}
int length = strlen(string);
char* buffer = new char[length+1];
strcpy(buffer, string);
int i;
for (i=0; i<length; i++) {
if (buffer[i] == 'p') { buffer[i] = 'P'; }
if (buffer[i] == 'a') { buffer[i] = 'A'; }
if (buffer[i] == 'D') { buffer[i] = 'd'; }
}
int output = 0;
if (strstr(buffer, "dd1") != NULL) { output = -2; }
else if (strstr(buffer, "d1") != NULL) { output = -1; }
else if (strstr(buffer, "P1") != NULL) { output = 0; }
else if (strstr(buffer, "AA1") != NULL) { output = 2; }
else if (strstr(buffer, "A1") != NULL) { output = 1; }
else if (strstr(buffer, "dd2") != NULL) { output = 3; }
else if (strstr(buffer, "d2") != NULL) { output = 4; }
else if (strstr(buffer, "m2") != NULL) { output = 5; }
else if (strstr(buffer, "M2") != NULL) { output = 6; }
else if (strstr(buffer, "AA2") != NULL) { output = 8; }
else if (strstr(buffer, "A2") != NULL) { output = 7; }
else if (strstr(buffer, "dd3") != NULL) { output = 9; }
else if (strstr(buffer, "d3") != NULL) { output = 10; }
else if (strstr(buffer, "m3") != NULL) { output = 11; }
else if (strstr(buffer, "M3") != NULL) { output = 12; }
else if (strstr(buffer, "AA3") != NULL) { output = 14; }
else if (strstr(buffer, "A3") != NULL) { output = 13; }
else if (strstr(buffer, "dd4") != NULL) { output = 15; }
else if (strstr(buffer, "d4") != NULL) { output = 16; }
else if (strstr(buffer, "P4") != NULL) { output = 17; }
else if (strstr(buffer, "AA4") != NULL) { output = 19; }
else if (strstr(buffer, "A4") != NULL) { output = 18; }
else if (strstr(buffer, "dd5") != NULL) { output = 21; }
else if (strstr(buffer, "d5") != NULL) { output = 22; }
else if (strstr(buffer, "P5") != NULL) { output = 23; }
else if (strstr(buffer, "AA5") != NULL) { output = 25; }
else if (strstr(buffer, "A5") != NULL) { output = 24; }
else if (strstr(buffer, "dd6") != NULL) { output = 26; }
else if (strstr(buffer, "d6") != NULL) { output = 27; }
else if (strstr(buffer, "m6") != NULL) { output = 28; }
else if (strstr(buffer, "M6") != NULL) { output = 29; }
else if (strstr(buffer, "AA6") != NULL) { output = 31; }
else if (strstr(buffer, "A6") != NULL) { output = 30; }
else if (strstr(buffer, "dd7") != NULL) { output = 32; }
else if (strstr(buffer, "d7") != NULL) { output = 33; }
else if (strstr(buffer, "m7") != NULL) { output = 34; }
else if (strstr(buffer, "M7") != NULL) { output = 35; }
else if (strstr(buffer, "AA7") != NULL) { output = 37; }
else if (strstr(buffer, "A7") != NULL) { output = 36; }
else if (strstr(buffer, "dd8") != NULL) { output = 38; }
else if (strstr(buffer, "d8") != NULL) { output = 39; }
else if (strstr(buffer, "P8") != NULL) { output = 40; }
else if (strstr(buffer, "AA8") != NULL) { output = 42; }
else if (strstr(buffer, "A8") != NULL) { output = 41; }
else if (strstr(buffer, "dd9") != NULL) { output = 43; }
else if (strstr(buffer, "d9") != NULL) { output = 44; }
else if (strstr(buffer, "m9") != NULL) { output = 45; }
else if (strstr(buffer, "M9") != NULL) { output = 46; }
else if (strstr(buffer, "AA9") != NULL) { output = 48; }
else if (strstr(buffer, "A9") != NULL) { output = 47; }
return output * sign;
}
//////////////////////////////
//
// example --
//
void example(void) {
}
//////////////////////////////
//
// usage --
//
void usage(const char* command) {
}
///////////////////////////////////////////////////////////////////////////
//
// Automatic transposition functions
//
//////////////////////////////
//
// doAutoTransposeAnalysis --
//
void doAutoTransposeAnalysis(HumdrumFile& infile) {
Array<int> ktracks;
ktracks.setSize(infile.getMaxTracks()+1);
ktracks.setGrowth(0);
ktracks.setAll(0);
int i;
for (i=1; i<=infile.getMaxTracks(); i++) {
if (strcmp(infile.getTrackExInterp(i), "**kern") == 0) {
ktracks[i] = 1 + i;
}
}
infile.analyzeRhythm("4");
int segments = int(infile.getTotalDuration()+0.5);
if (segments < 1) {
segments = 1;
}
Array<Array<Array<double> > > trackhist;
trackhist.setSize(ktracks.getSize());
trackhist.allowGrowth(0);
trackhist[0].setSize(0);
for (i=1; i<trackhist.getSize(); i++) {
trackhist[i].setSize(0);
if (ktracks[i]) {
storeHistogramForTrack(trackhist[i], infile, i, segments);
}
}
if (debugQ) {
cout << "Segment pitch histograms: " << endl;
printHistograms(segments, ktracks, trackhist);
}
int level = 16;
int hop = 8;
int count = segments / hop;
if (segments < count * level / (double)hop) {
level = level / 2;
hop = hop / 2;
}
if (segments < count * level / (double)hop) {
count = count / 2;
}
if (segments < count * level / (double)hop) {
level = level / 2;
hop = hop / 2;
}
if (segments < count * level / (double)hop) {
count = count / 2;
}
Array<Array<Array<double> > > analysis;
doAutoKeyAnalysis(analysis, level, hop, count, segments, ktracks, trackhist);
// print analyses raw results
cout << "Raw key analysis by track:" << endl;
printRawTrackAnalysis(analysis, ktracks);
doTranspositionAnalysis(analysis);
}
//////////////////////////////
//
// doTranspositionAnalysis --
//
void doTranspositionAnalysis(Array > >& analysis) {
int i, j, k;
int value1;
int value2;
int value;
for (i=0; i<1; i++) {
for (j=2; j<3; j++) {
for (k=0; k<analysis[i].getSize(); k++) {
if (analysis[i][k][24] >= 0 && analysis[j][k][24] >= 0) {
value1 = (int)analysis[i][k][25];
if (value1 >= 12) {
value1 = value1 - 12;
}
value2 = (int)analysis[j][k][25];
if (value2 >= 12) {
value2 = value2 - 12;
}
value = value1 - value2;
if (value < 0) {
value = value + 12;
}
if (value > 6) {
value = 12 - value;
}
cout << value << endl;
}
}
}
}
}
//////////////////////////////
//
// printRawTrackAnalysis --
//
void printRawTrackAnalysis(Array<Array<Array<double> > >& analysis,
Array<int>& ktracks) {
int i, j;
int value;
int value2;
for (i=0; i<analysis[0].getSize(); i++) {
cout << "Frame\t" << i << ":";
for (j=0; j<analysis.getSize(); j++) {
cout << "\t";
value = (int)analysis[j][i][24];
if (value >= 12) {
value = value - 12;
}
value2 = (int)analysis[j][i][25];
if (value2 >= 12) {
value2 = value2 - 12;
}
cout << value;
// if (value != value2) {
// cout << "," << value2;
// }
}
cout << "\n";
}
}
//////////////////////////////
//
// doAutoKeyAnalysis --
//
void doAutoKeyAnalysis(Array<Array<Array<double> > >& analysis, int level,
int hop, int count, int segments, Array<int>& ktracks,
Array<Array<Array<double> > >& trackhist) {
Array<double> majorweights;
Array<double> minorweights;
fillWeightsWithKostkaPayne(majorweights, minorweights);
int size = 0;
int i;
for (i=1; i<ktracks.getSize(); i++) {
if (ktracks[i]) {
size++;
}
}
analysis.setSize(size);
analysis.allowGrowth(0);
for (i=0; i<analysis.getSize(); i++) {
analysis[i].setSize(count);
analysis[i].setSize(0);
}
ktracks.allowGrowth(0);
int aindex = 0;
for (i=1; i<ktracks.getSize(); i++) {
if (!ktracks[i]) {
continue;
}
doTrackKeyAnalysis(analysis[aindex++], level, hop, count,
trackhist[i], majorweights, minorweights);
}
}
//////////////////////////////
//
// doTrackKeyAnalysis -- Do individual key analyses of sections of the
// given track.
//
void doTrackKeyAnalysis(Array<Array<double> >& analysis, int level, int hop,
int count, Array<Array<double> >& trackhist,
Array<double>& majorweights, Array<double>& minorweights) {
int i;
for (i=0; i<count; i++) {
if (i * hop + level > trackhist.getSize()) {
break;
}
analysis.setSize(i+1);
doSingleAnalysis(analysis[analysis.getSize()-1], i*hop+level, level,
trackhist, majorweights, minorweights);
}
}
//////////////////////////////
//
// doSingleAnalysis --
//
void doSingleAnalysis(Array<double>& analysis, int startindex, int length,
Array<Array<double> >& trackhist, Array<double>& majorweights,
Array<double>& minorweights) {
Array<double> histsum(12);
histsum.allowGrowth(0);
histsum.setAll(0);
int i, k;
for (i=0; (i<length) && (startindex+i+length<trackhist.getSize()); i++) {
for (k=0; k<12; k++) {
histsum[k] += trackhist[i+startindex][k];
}
}
identifyKey(analysis, histsum, majorweights, minorweights);
}
///////////////////////////////
//
// fillWeightsWithKostkaPayne --
//
void fillWeightsWithKostkaPayne(Array& maj, Array& min) {
maj.setSize(12);
maj.allowGrowth(0);
min.setSize(12);
min.allowGrowth(0);
// found in David Temperley: Music and Probability 2006
maj[0] = 0.748; // C major weights
maj[1] = 0.060; // C#
maj[2] = 0.488; // D
maj[3] = 0.082; // D#
maj[4] = 0.670; // E
maj[5] = 0.460; // F
maj[6] = 0.096; // F#
maj[7] = 0.715; // G
maj[8] = 0.104; // G#
maj[9] = 0.366; // A
maj[10] = 0.057; // A#
maj[11] = 0.400; // B
min[0] = 0.712; // c minor weights
min[1] = 0.084; // c#
min[2] = 0.474; // d
min[3] = 0.618; // d#
min[4] = 0.049; // e
min[5] = 0.460; // f
min[6] = 0.105; // f#
min[7] = 0.747; // g
min[8] = 0.404; // g#
min[9] = 0.067; // a
min[10] = 0.133; // a#
min[11] = 0.330; // b
}
////////////////////////////////////////
//
// identifyKey -- correls contains the 12 major key correlation
// values, then the 12 minor key correlation values, then two
// more values: index=24 is the best key, and index=25 is the
// second best key. If [24] or [25] is -1, then that means that
// all entries in the original histogram were zero (all rests).
//
void identifyKey(Array<double>& correls, Array<double>& histogram,
Array<double>& majorweights, Array<double>& minorweights) {
correls.setSize(26);
correls.allowGrowth(0);
correls.setAll(0);
int i;
double h[24];
for (i=0; i<12; i++) {
h[i] = histogram[i];
h[i+12] = h[i];
}
double testsum = 0.0;
for (i=0; i<12; i++) {
testsum += h[i];
}
if (testsum == 0.0) {
correls[24] = -1;
correls[25] = -1;
return;
}
for (i=0; i<12; i++) {
correls[i] = pearsonCorrelation(12, majorweights.getBase(), h+i);
correls[i+12] = pearsonCorrelation(12, minorweights.getBase(), h+i);
}
// find max value
int besti = 0;
for (i=1; i<24; i++) {
if (correls[i] > correls[besti]) {
besti = i;
}
}
// find second best key
int secondbesti = 0;
if (besti == 0) {
secondbesti = 1;
}
for (i=1; i<24; i++) {
if (i == besti) {
continue;
}
if (correls[i] > correls[secondbesti]) {
secondbesti = i;
}
}
correls[24] = besti;
correls[25] = secondbesti;
}
//////////////////////////////
//
// pearsonCorrelation --
//
double pearsonCorrelation(int size, double* x, double* y) {
double sumx = 0.0;
double sumy = 0.0;
double sumco = 0.0;
double meanx = x[0];
double meany = y[0];
double sweep;
double deltax;
double deltay;
int i;
for (i=2; i<=size; i++) {
sweep = (i-1.0) / i;
deltax = x[i-1] - meanx;
deltay = y[i-1] - meany;
sumx += deltax * deltax * sweep;
sumy += deltay * deltay * sweep;
sumco += deltax * deltay * sweep;
meanx += deltax / i;
meany += deltay / i;
}
double popsdx = sqrt(sumx / size);
double popsdy = sqrt(sumy / size);
double covxy = sumco / size;
return covxy / (popsdx * popsdy);
}
//////////////////////////////
//
// printHistograms --
//
void printHistograms(int segments, Array<int> ktracks,
Array<Array<Array<double> > >& trackhist) {
int i, j, k;
int start;
for (i=0; i<segments; i++) {
//cout << "i=" << i << endl;
cout << "segment " << i
<< " ==========================================\n";
for (j=0; j<12; j++) {
start = 0;
//cout << "j=" << i << endl;
for (k=1; k<ktracks.getSize(); k++) {
//cout << "k=" << i << endl;
if (!ktracks[k]) {
continue;
}
if (!start) {
cout << j;
start = 1;
}
cout << "\t";
cout << trackhist[k][i][j];
}
if (start) {
cout << "\n";
}
}
}
cout << "==========================================\n";
}
//////////////////////////////
//
// storeHistogramForTrack --
//
double storeHistogramForTrack(Array<Array<double> >& histogram,
HumdrumFile& infile, int track, int segments) {
histogram.setSize(segments);
histogram.allowGrowth(0);
int i;
int j;
int k;
for (i=0; i<histogram.getSize(); i++) {
histogram[i].setSize(12);
histogram[i].setGrowth(0);
histogram[i].setAll(0);
}
infile.analyzeRhythm("4");
double totalduration = infile.getTotalDuration();
double duration;
char buffer[10000] = {0};
int pitch;
double start;
int tokencount;
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].getType() != E_humrec_data) {
continue;
}
start = infile[i].getAbsBeat();
for (j=0; j<infile[i].getFieldCount(); j++) {
if (infile[i].getPrimaryTrack(j) != track) {
continue;
}
if (strcmp(infile[i].getExInterp(j), "**kern") != 0) {
continue;
}
if (strcmp(infile[i][j], ".") == 0) {
continue; // ignore null tokens
}
tokencount = infile[i].getTokenCount(j);
for (k=0; k<tokencount; k++) {
infile[i].getToken(buffer, j, k);
if (strcmp(buffer, ".") == 0) {
continue; // ignore illegal inline null tokens
}
pitch = Convert::kernToMidiNoteNumber(buffer);
if (pitch < 0) {
continue; // ignore rests or strange objects
}
pitch = pitch % 12; // convert to chromatic pitch-class
duration = Convert::kernToDuration(buffer);
if (duration <= 0.0) {
continue; // ignore grace notes and strange objects
}
addToHistogramDouble(histogram, pitch,
start, duration, totalduration, segments);
}
}
}
return totalduration;
}
//////////////////////////////
//
// addToHistogramDouble -- fill the pitch histogram in the right spots.
//
void addToHistogramDouble(Array<Array<double> >& histogram, int pc,
double start, double dur, double tdur, int segments) {
pc = (pc + 12) % 12;
double startseg = start / tdur * segments;
double startfrac = startseg - (int)startseg;
double segdur = dur / tdur * segments;
if (segdur <= 1.0 - startfrac) {
histogram[(int)startseg][pc] += segdur;
return;
} else if (1.0 - startfrac > 0.0) {
histogram[(int)startseg][pc] += (1.0 - startfrac);
segdur -= (1.0 - startfrac);
}
int i = (int)(startseg + 1);
while (segdur > 0.0 && i < histogram.getSize()) {
if (segdur < 1.0) {
histogram[i][pc] += segdur;
segdur = 0.0;
} else {
histogram[i][pc] += 1.0;
segdur -= 1.0;
}
i++;
}
}
//
// Automatic transposition functions
//
///////////////////////////////////////////////////////////////////////////
//
// Spine field list extraction functions
//
//////////////////////////////
//
// fillFieldData --
//
void fillFieldData(Array<int>& field, const char* fieldstring,
HumdrumFile& infile) {
int maxtrack = infile.getMaxTracks();
field.setSize(maxtrack+1);
field.setGrowth(0);
field.setAll(0);
Array<int> tempfield;
tempfield.setSize(maxtrack);
tempfield.setSize(0);
PerlRegularExpression pre;
Array<char> buffer;
buffer.setSize(strlen(fieldstring)+1);
strcpy(buffer.getBase(), fieldstring);
pre.sar(buffer, "\\s", "", "gs");
int start = 0;
int value = 0;
value = pre.search(buffer.getBase(), "^([^,]+,?)");
while (value != 0) {
start += value - 1;
start += strlen(pre.getSubmatch(1));
processFieldEntry(tempfield, pre.getSubmatch(), infile);
value = pre.search(buffer.getBase() + start, "^([^,]+,?)");
}
int i;
for (i=0; i<tempfield.getSize(); i++) {
field[tempfield[i]-1] = 1;
}
}
//////////////////////////////
//
// processFieldEntry --
// 3-6 expands to 3 4 5 6
// $ expands to maximum spine track
// $-1 expands to maximum spine track minus 1, etc.
//
void processFieldEntry(Array<int>& field, const char* string,
HumdrumFile& infile) {
int maxtrack = infile.getMaxTracks();
PerlRegularExpression pre;
Array<char> buffer;
buffer.setSize(strlen(string)+1);
strcpy(buffer.getBase(), string);
// remove any comma left at end of input string (or anywhere else)
pre.sar(buffer, ",", "", "g");
// first remove $ symbols and replace with the correct values
removeDollarsFromString(buffer, infile.getMaxTracks());
if (pre.search(buffer.getBase(), "^(\\d+)-(\\d+)$")) {
int firstone = strtol(pre.getSubmatch(1), NULL, 10);
int lastone = strtol(pre.getSubmatch(2), NULL, 10);
if ((firstone < 1) && (firstone != 0)) {
cerr << "Error: range token: \"" << string << "\""
<< " contains too small a number at start: " << firstone << endl;
cerr << "Minimum number allowed is " << 1 << endl;
exit(1);
}
if ((lastone < 1) && (lastone != 0)) {
cerr << "Error: range token: \"" << string << "\""
<< " contains too small a number at end: " << lastone << endl;
cerr << "Minimum number allowed is " << 1 << endl;
exit(1);
}
if (firstone > maxtrack) {
cerr << "Error: range token: \"" << string << "\""
<< " contains number too large at start: " << firstone << endl;
cerr << "Maximum number allowed is " << maxtrack << endl;
exit(1);
}
if (lastone > maxtrack) {
cerr << "Error: range token: \"" << string << "\""
<< " contains number too large at end: " << lastone << endl;
cerr << "Maximum number allowed is " << maxtrack << endl;
exit(1);
}
int i;
if (firstone > lastone) {
for (i=firstone; i>=lastone; i--) {
field.append(i);
}
} else {
for (i=firstone; i<=lastone; i++) {
field.append(i);
}
}
} else if (pre.search(buffer.getBase(), "^(\\d+)")) {
int value = strtol(pre.getSubmatch(1), NULL, 10);
if ((value < 1) && (value != 0)) {
cerr << "Error: range token: \"" << string << "\""
<< " contains too small a number at end: " << value << endl;
cerr << "Minimum number allowed is " << 1 << endl;
exit(1);
}
if (value > maxtrack) {
cerr << "Error: range token: \"" << string << "\""
<< " contains number too large at start: " << value << endl;
cerr << "Maximum number allowed is " << maxtrack << endl;
exit(1);
}
field.append(value);
}
}
//////////////////////////////
//
// removeDollarsFromString -- substitute $ sign for maximum track count.
//
void removeDollarsFromString(Array& buffer, int maxtrack) {
PerlRegularExpression pre;
char buf2[128] = {0};
int value2;
if (pre.search(buffer.getBase(), "\\$$")) {
sprintf(buf2, "%d", maxtrack);
pre.sar(buffer, "\\$$", buf2);
}
if (pre.search(buffer.getBase(), "\\$(?![\\d-])")) {
// don't know how this case could happen, however...
sprintf(buf2, "%d", maxtrack);
pre.sar(buffer, "\\$(?![\\d-])", buf2, "g");
}
if (pre.search(buffer.getBase(), "\\$0")) {
// replace $0 with maxtrack (used for reverse orderings)
sprintf(buf2, "%d", maxtrack);
pre.sar(buffer, "\\$0", buf2, "g");
}
while (pre.search(buffer.getBase(), "\\$(-?\\d+)")) {
value2 = maxtrack - (int)fabs(strtol(pre.getSubmatch(1), NULL, 10));
sprintf(buf2, "%d", value2);
pre.sar(buffer, "\\$-?\\d+", buf2);
}
}
//
// Spine field list extraction functions
//
///////////////////////////////////////////////////////////////////////////
/* BRIEF DOCUMENTATION
transpose options:
-t interval = transpose music by the specified interval, where
interval is of the form:
P1 = perfect unison(no transposition)
m2 = up a minor second
-m2 = down a minor second
M3 = up a major third
-A4 = down an augmented fourth
d5 = up a diminished fifth
-b interval = transpose by the base-40 equivalent to the -t option interval
0 = perfect unison(no transposition)
6 = up a minor second
-6 = down a minor second
12 = up a major third
-18 = down an augmented fourth
22 = up a diminished fifth
-o octave = transpose(additionally by given octave)
transpose -t m3 -o 1 = transpose up by an octave and a minor
third.
-s fieldstring = transpose only the given list of data spines.
example:
transpose -f1-2,4 -t P4 fourpart.krn
transpose -f3 -t P4 fourpart.krn
Note: does not work yet with the -k option
##########################################################################
##
## EXAMPLES
##
input: file.krn -- an example with the key being a minor:
**kern
*a:
4A
4B
4c
4d
4e
4f
4g
4a
*-
#####################################################################
#
# Transpose the file up a minor third(so that it is in C Minor):
#
tranpose -t m3 file.krn
**kern
*c:
4c
4d
4e-
4f
4g
4a-
4b-
4cc
*-
#####################################################################
#
# Transpose the file down a minor third(so that it is in F# Minor):
#
tranpose -t -m3 file.krn
**kern
*f#:
4F#
4G#
4A
4B
4c#
4d
4e
4f#
*-
#####################################################################
#
# Transpose the file up a perfect octave:
#
tranpose -t P8 file.krn
**kern
*a:
4A
4B
4cc
4dd
4ee
4ff
4gg
4aa
*-
#####################################################################
#
# Force the file to a tonic on C rather than a:
#
transpose -k c file.krn
**kern
*c:
4c
4d
4e-
4f
4g
4a-
4b-
4cc
*-
# case -k option value is irrelevant:
transpose -k C file.krn
**kern
*c:
4c
4d
4e-
4f
4g
4a-
4b-
4cc
*-
# Transpose from A Minor to G# Minor:
transpose -k G# file.krn
**kern
*g#:
4G#
4A#
4B
4c#
4d#
4e
4f#
4g#
*-
# Changing input files to:
**kern
*C:
4c
4d
4e
4f
*G:
4g
4a
4b
4cc
*-
# Using -k option will convert all keys to same in output:
transpose -k e file.krn
**kern
*E:
4e
4f#
4g#
4a
*E:
4e
4f#
4g#
4a
*-
##############################
##
## octave transpositions
##
# Back to original data file, transposing up a minor tenth:
transpose -o 1 -t m3 file.krn
**kern
*E-:
8ee-
8ff
8gg
8aa-
8bb-
8ccc
8ddd
8eee-
*-
# transpose down two octaves:
transpose -o -2 file.krn
**kern
*a:
4AAA
4BBB
4CC
4DD
4EE
4FF
4GG
4AA
*-
####################
##
## base 40 -b option instead of -t option
##
# Transpose down two octaves:
transpose -b -80 file.krn
**kern
*a:
4AAA
4BBB
4CC
4DD
4EE
4FF
4GG
4AA
*-
*/
// md5sum: e92e0977d1f92f5fa48d3d3dc2bcacfb transpose.cpp [20100602]