//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed Jun 16 20:02:43 PDT 2004
// Last Modified: Wed Jun 16 20:02:46 PDT 2004
// Filename: ...sig/examples/all/transfix.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/transfix.cpp
// Syntax: C++; museinfo
//
// Description: Identify and transpose **kern musical data in parts
// which need to be transposed. Test algorithm for future
// application to identify MuseData parts that need
// transposition.
//
#include "humdrum.h"
#include <string.h>
#include <stdio.h>
#ifndef OLDCPP
#include <iostream>
#include <fstream>
#else
#include <iostream.h>
#include <fstream.h>
#endif
// function declarations:
void checkOptions(Options& opts, int argc, char** argv);
void example(void);
void usage(const char* command);
void printFile(HumdrumFile& infile);
int generateFieldList(Array<int>& fieldlist,const char* fieldstring);
void processFile(HumdrumFile& infile);
void printNewKernString(const char* string);
void printHumdrumKernToken(HumdrumRecord& record, int index);
void printHumdrumDataRecord(HumdrumRecord& record,
Array<int>& spineprocess);
int getBase40ValueFromInterval(const char* string);
void printAnalysis(HumdrumFile& infile, Array<int>& transamount);
void determineTransposition(Array<int>& transamount,
Array<Array<int> >& keyarray);
void analyzeKeys(HumdrumFile& infile,
Array<Array<int> >& keyarray);
void analyzeKeyPart(HumdrumFile& infile, Array<int>& keyarray,
int tracknum, double hop, double window);
int findStartBeatIndex(HumdrumFile& infile, double startbeat);
int findStopBeatIndex(HumdrumFile& infile, double endbeat);
// User interface variables:
Options options;
int debugQ = 0; // used with --debug option
int transval = 0; // used with -b option
const char* fieldstring = ""; // used with -f option
int fieldQ = 0; // used with -f option
double hop = 16.0; // used with -h option
double window = 16.0; // used with -w option
//////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
// process the command-line options
checkOptions(options, argc, argv);
HumdrumFile infile;
infile.read(options.getArg(1));
infile.analyzeRhythm("4");
Array<Array<int> > keyarray;
analyzeKeys(infile, keyarray);
Array<int> transamount;
determineTransposition(transamount, keyarray);
printAnalysis(infile, transamount);
return 0;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// analyzeKeys -- check the key of the music at various regular intervals.
//
void analyzeKeys(HumdrumFile& infile, Array >& keyarray) {
int count = infile.getMaxTracks();
if (count <= 0) {
cout << "Error: not enough spines in Humdrum file: " << count << endl;
exit(1);
}
keyarray.setSize(count);
int i;
for (i=0; i<count; i++) {
analyzeKeyPart(infile, keyarray[i], i+1, hop, window);
}
}
//////////////////////////////
//
// analyzeKeyPart -- extract the key analysis for one part
// -1 keyanalysis means not enough music in the region to analyze the key.
// 0-11 = Major keys 0=C 1=C#/Db 2=D, etc.
// 12-13 = Minor keys on same tonic: 0=c 1=c#/db, 2=d, etc.
//
void analyzeKeyPart(HumdrumFile& infile, Array<int>& keyarray, int tracknum,
double hop, double window) {
int framecount = (int)(infile[infile.getNumLines()-1].getAbsBeat() / hop);
if (debugQ) {
cout << "!!! Frame count = " << framecount << endl;
}
keyarray.setSize(framecount);
keyarray.setSize(0);
int keyanalysis = 0;
Array<double> scores(24);
scores.setAll(0);
int startindex;
int stopindex;
double startbeat;
double endbeat;
int i;
for (i=0; i<framecount; i++) {
startbeat = i * hop;
endbeat = startbeat + window;
startindex = findStartBeatIndex(infile, startbeat);
stopindex = findStartBeatIndex(infile, endbeat);
keyanalysis = infile.analyzeKeyKS(scores, startindex, stopindex,
1, 0, tracknum);
keyarray.append(keyanalysis);
}
}
//////////////////////////////
//
// findStartBeatIndex -- figure out which index into the Humdrum file
// starts at the given absolute beat position (or the first line after
// that beat position). Very simple algorithm with is not efficient
// at all.
//
int findStartBeatIndex(HumdrumFile& infile, double startbeat) {
int i;
int output = -1;
for (i=0; i<infile.getNumLines(); i++) {
// compensate for any rounding errors
if ((infile[i].getAbsBeat()+0.0001) >= startbeat) {
output = i;
break;
}
}
if (output == -1) {
return infile.getNumLines()-1;
} else {
return output;
}
}
//////////////////////////////
//
// findStopBeatIndex -- figure out which index into the Humdrum file
// stops at the given absolute beat position. Very simple algorithm
// which is not efficient at all.
//
int findStopBeatIndex(HumdrumFile& infile, double endbeat) {
int i;
int output = -1;
for (i=infile.getNumLines()-1; i>=0; i++) {
// compensate for any rounding errors
if ((infile[i].getAbsBeat()+0.0001) < endbeat) {
output = i+1;
break;
}
}
if (output >= infile.getNumLines()) {
output = infile.getNumLines()-1;
}
if (output == -1) {
return infile.getNumLines()-1;
} else {
return output;
}
}
//////////////////////////////
//
// determineTransposition --
//
void determineTransposition(Array<int>& transamount,
Array<Array<int> >& keyarray) {
cout << "GOT TO DETERMINE_TRANSPOSITION" << endl;
transamount.setSize(keyarray.getSize());
transamount.setAll(0);
int i;
int j;
Array<int> histogram(48);
histogram.setAll(0);
int value = 0;
if (debugQ) {
cout << "!!! Key analyses for each part:\n";
for (j=0; j<keyarray[0].getSize(); j++) {
for (i=0; i<keyarray.getSize(); i++) {
cout << keyarray[i][j];
if (i < keyarray.getSize()-1) {
cout << "\t";
} else if (keyarray.getSize() >= 2) {
cout << "\t";
value = keyarray[0][j] - keyarray[1][j];
cout << value;
histogram[value+24]++;
}
}
cout << "\n";
}
cout << "Histogram of difference between parts in spines 1 and 2:" <<endl;
int max = 24;
for (i=0; i<histogram.getSize(); i++) {
cout << "delta " << i - 24 << "\t=\t" << histogram[i] << endl;
// search for max in reasonable range
if (i >= 12 && i <= 32) {
if (histogram[i] > histogram[max]) {
max = i;
}
}
}
cout << "Most likely transposition between parts is: " << max-24 << endl;
}
}
//////////////////////////////
//
// printAnalysis --
//
void printAnalysis(HumdrumFile& infile, Array& transamount) {
// cout << "GOT TO PRINTANALYSIS" << endl;
}
//////////////////////////////
//
// processFile -- From old transposition function
//
void processFile(HumdrumFile& infile) {
int i;
Array<int> fieldlist;
Array<int> spineprocess(infile.getMaxTracks());
if (fieldQ) {
spineprocess.setAll(0);
generateFieldList(fieldlist, fieldstring);
for (i=0; i<fieldlist.getSize(); i++) {
if (fieldlist[i] < 1) {
continue;
}
if (fieldlist[i]-1 < spineprocess.getSize()) {
spineprocess[fieldlist[i]-1] = 1;
}
}
} else {
spineprocess.setAll(1);
}
for (i=0; i<infile.getNumLines(); i++) {
switch (infile[i].getType()) {
case E_humrec_data:
printHumdrumDataRecord(infile[i], spineprocess);
cout << "\n";
break;
case E_humrec_interpretation:
// adjust *k[] values here.
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";
}
}
}
//////////////////////////////
//
// 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);
if (i<record.getFieldCount()-1) {
cout << "\t";
}
continue;
}
}
//////////////////////////////
//
// printHumdrumKernToken --
//
void printHumdrumKernToken(HumdrumRecord& record, int index) {
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);
if (k<tokencount-1) {
cout << " ";
}
}
}
//////////////////////////////
//
// printNewKernString --
//
void printNewKernString(const char* string) {
if (strchr(string, 'r') != NULL) {
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("o|octave=i:0", "The octave addition to tranpose value");
opts.define("f|field=s", "The base-40 transposition value");
opts.define("t|transpose=s", "musical interval transposition value");
opts.define("h|hop=d:16", "The window hop size for key analysis in beats");
opts.define("w|window=d:16", "The window size for key analysis in beats");
opts.define("debug=b", "print debugging statements");
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, 16 June 2004" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 16 June 2004" << 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");
fieldstring = opts.getString("field");
fieldQ = opts.getBoolean("field");
hop = opts.getDouble("hop");
window = opts.getDouble("window");
debugQ = opts.getBoolean("debug");
if (opts.getBoolean("transpose")) {
transval = getBase40ValueFromInterval(opts.getString("transpose"));
}
transval += 40 * opts.getInteger("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) {
}
//////////////////////////////
//
// generateFieldList --
//
int generateFieldList(Array& fieldlist, const char* fieldstring) {
cout << "ENTERING GENERATEFIELD LIST" << endl;
int maxfield = 0;
int length = strlen(fieldstring);
char* buffer = new char[length+1];
strcpy(buffer, fieldstring);
int starti, stopi;
int temp;
int num;
char* ptr = strtok(buffer, " ,;:");
while (ptr != NULL) {
if (strchr(ptr, '-') != NULL) {
sscanf(ptr, "%d-%d", &starti, &stopi);
if (starti > stopi) {
temp = starti;
starti=stopi;
stopi = temp;
}
for (num=starti; num<=stopi; num++) {
fieldlist.append(num);
}
if (num > maxfield) {
maxfield = num;
}
} else {
sscanf(ptr, "%d", &num);
fieldlist.append(num);
if (num > maxfield) {
maxfield = num;
}
}
ptr = strtok(NULL, " ,;:");
}
cout << "LEAVING GENERATEFIELD LIST" << endl;
return maxfield;
}
// md5sum: 45db19294a1d4f87b78ec94dcfa937c8 transfix.cpp [20050403]