// // Programmer: Craig Stuart Sapp // Creation Date: Thu Nov 25 00:40:56 PST 2004 // Last Modified: Tue Apr 14 13:41:35 PDT 2009 (fixed interpretations when // not appending to original data) // Last Modified: Wed Jun 24 15:41:09 PDT 2009 (updated for GCC 4.4) // // Filename: ...sig/examples/all/tsroot.cpp // Web Address: http://sig.sapp.org/examples/museinfo/humdrum/tsroot.cpp // Syntax: C++; museinfo // // Description: Analyze the chord root analysis from Temperley & // Sleator Melisma Music Analysis harmony program // (2003 version) and optionally append to original **kern data. // // Requires the following ancillary programs: // (1) meter -- Melisma meter program which adds Beat information to // note data. // (2) harmony -- Melisma root analysis program which adds chord // analysis data to meter program output. // (3) kern2melisma -- museinfo program which converts **kern data into // melisma Note data. // (4) harmony2humdrum -- PERL program which converts Melisma harmony // program output into a temporary Humdrum data file. // // For roman-numeral analysis, two more programs are required: // (5) key -- Melisma key analysis program // (6) key2humdrum -- PERL program which converts Melisma Roman Numeral // analysis into a temprary Humdrum data file. // // programs are expected to be in two directories: // // meldir = set with --meldir option. This is the location of the // melisma music analyzer command-line programs (meter, harmony, key). // midir = set with --midir option. This is the location of museinfo programs. // // key parameter file contents for roman numeral analysis: // // verbosity=2 <-- required for segment times // default_profile_value = 1.5 // npc_or_tpc_profile=1 // scoring_mode = 1 // segment_beat_level=3 // beat_printout_level=2 // romnums=1 <-- required for roman numeral analysis // romnum_type=0 // running=0 // #include "humdrum.h" #include "stdlib.h" #include "string.h" #include "time.h" #include "stdio.h" #ifndef OLDCPP #include using namespace std; #else #include #endif /////////////////////////////////////////////////////////////////////////// // function declarations void checkOptions (Options& opts, int argc, char* argv[]); void example (void); void usage (const char* command); void processFile (const char* filename, HumdrumFile& infile); void analyzeTiming (HumdrumFile& infile, Array& timings, Array& tempo); void getAnalysisTimes (Array& analysistimes, HumdrumFile& rootanalysis); void printAnalysis (HumdrumFile& rootanalysis, HumdrumFile& romananalysis, HumdrumFile& infile); int getClosestRootLine (Array& analysistimes, int targettime); void getDataLineTimings (Array& linetimes, HumdrumFile& infile); const char* getMatchRoot (int matchline, HumdrumFile& hfile); void createParameterFile(const char* parametertmpname); void getRomanAnalysisTimes(Array& romantimes, HumdrumFile& romananalysis); void getRomanAnalysisLines(Array& romanlines, Array& romantimes, Array& linetimes, HumdrumFile& romananalysis); const char* getRomanData (HumdrumFile& romananalysis, Array& romanlines, int target); void printRomanKey (HumdrumFile& romananalysis, Array& romanlines, int target, HumdrumRecord& aRecord); double dtempo = 120.0; // global variables Options options; // database for command-line arguments int debugQ = 0; // used with --debug option int appendQ = 0; // used with -a option int prependQ = 0; // used with -p option int verboseQ = 0; // used with -v option int harmonyQ = 0; // used with -h option const char* tmpdir = "/tmp"; // used with --tmpdir option const char* meldir = "/usr/ccarh/melisma/bin"; // used with --meldir option const char* midir = "/var/www/websites/museinfo/bin"; // used with --midir /////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { #ifndef VISUAL srand48(time(NULL)*12345); // seed rand num generator with current time #else srand(time(NULL)*12345); #endif HumdrumFile infile; // process the command-line options checkOptions(options, argc, argv); const char* filename; infile.clear(); // if no command-line arguments read data file from standard input int numinputs = options.getArgCount(); if (numinputs < 1) { cout << "Error: you must supply at least one input fileme" << endl; exit(1); } else { filename = options.getArg(1); infile.read(options.getArg(1)); } processFile(filename, infile); } /////////////////////////////////////////////////////////////////////////// ////////////////////////////// // // processFile -- // void processFile(const char* filename, HumdrumFile& infile) { // first convert the file to melisma format if (verboseQ) { cout << "The filename is: " << filename << endl; } char buffer[4096] = {0}; const char *ptr = NULL; ptr = strrchr(filename, '/'); if (ptr != NULL) { strcpy(buffer, ptr+1); } else { strcpy(buffer, filename); } if (verboseQ) { cout << "The filename base is: " << buffer << endl; } #ifndef VISUAL int randval = lrand48(); #else int randval = rand(); #endif if (verboseQ) { cout << "Random value is: " << randval << endl; cout << "Creating temporary Melisma file: " << endl; cout << "\t" << tmpdir << "/" << buffer << "." << randval << endl; } char commandbuffer[20000] = {0}; char tmpname[1024] = {0}; // output from the harmony2humdrum command char tmpname2[1024] = {0}; // output from the key2humdrum command char parametertmpname[1024] = {0}; // key parameter file char harmonytmpname[1024] = {0}; sprintf(tmpname, "%s/%s.%d", tmpdir, buffer, randval); sprintf(harmonytmpname, "%s/%s-%s.%d", tmpdir, buffer, "harmony", randval); sprintf(tmpname2, "%s/%s-%s.%d", tmpdir, buffer, "roman", randval); sprintf(parametertmpname, "%s/%s-%s.%d", tmpdir, buffer, "parameters", randval); if (harmonyQ) { createParameterFile(parametertmpname); sprintf(commandbuffer, "%s/kern2melisma %s | egrep -v '(Reference|Comment|Info)' | %s/meter | %s/harmony > %s; %s/harmony2humdrum %s > %s; %s/key -p %s %s | %s/key2humdrum > %s", midir, filename, meldir, meldir, harmonytmpname, meldir, harmonytmpname, tmpname, meldir, parametertmpname, harmonytmpname, meldir, tmpname2); } else { sprintf(commandbuffer, "%s/kern2melisma %s | egrep -v '(Reference|Comment|Info)' | %s/meter | %s/harmony | %s/harmony2humdrum > %s", midir, filename, meldir, meldir, meldir, tmpname); } if (verboseQ) { cout << "COMMAND: " << commandbuffer << endl; } int status = system(commandbuffer); if (status < 0) { cout << "ERROR: command not successful:\n" << commandbuffer << endl; exit(1); } // read in the newly created Humdrum file: HumdrumFile rootanalysis; rootanalysis.read(tmpname); HumdrumFile romananalysis; if (harmonyQ) { romananalysis.read(tmpname2); } printAnalysis(rootanalysis, romananalysis, infile); // delete temporary files if (harmonyQ) { sprintf(commandbuffer, "rm -f %s", tmpname); system(commandbuffer); sprintf(commandbuffer, "rm -f %s", tmpname2); system(commandbuffer); sprintf(commandbuffer, "rm -f %s", harmonytmpname); system(commandbuffer); sprintf(commandbuffer, "rm -f %s", parametertmpname); system(commandbuffer); } else { sprintf(commandbuffer, "rm -f %s", tmpname); system(commandbuffer); } } ////////////////////////////// // // createParameterFile -- // void createParameterFile(const char* parametertmpname) { ofstream pfile(parametertmpname); if (!pfile.is_open()) { cout << "Error: cannot write parameter file\n"; exit(1); } pfile << "verbosity=2\n" "default_profile_value = 1.5\n" "npc_or_tpc_profile=1\n" "scoring_mode = 1\n" "segment_beat_level=3\n" "beat_printout_level=2\n" "romnums=1\n" "romnum_type=0\n" "running=0\n" ; pfile.close(); } ////////////////////////////// // // printAnalysis -- match Melismla Music Analyzer output back to Humdrum data. // void printAnalysis(HumdrumFile& rootanalysis, HumdrumFile& romananalysis, HumdrumFile& infile) { const char* romandatum = "."; Array analysistimes; getAnalysisTimes (analysistimes, rootanalysis); Array linetimes; getDataLineTimings(linetimes, infile); Array romantimes; Array romanlines; if (harmonyQ) { getRomanAnalysisTimes(romantimes, romananalysis); getRomanAnalysisLines(romanlines, romantimes, linetimes, romananalysis); } const char* matchroot; int matchline; int i; for (i=0; i& romanlines, int target, HumdrumRecord& aRecord) { int i; int j; for (i=0; i romanlines[i]) { continue; } // target == romanlines[i]: assume first column contains data if (strchr(romananalysis[i][0], ':') == NULL) { continue; } // found a key interpretation which should be printed if (prependQ) { cout << romananalysis[i][0] << "\t"; cout << romananalysis[i][0] << "\t"; } if (prependQ || appendQ) { for (j=0; j& romanlines, int target) { int i; for (i=0; i romanlines[i]) { continue; } // target == romanlines[i]: assume first column contains data if (strcmp(romananalysis[i][0], "|") == 0) { continue; } return romananalysis[i][0]; } // most likely some sort of error if got to here return "."; } ////////////////////////////// // // getRomanAnalysisLines -- // void getRomanAnalysisLines(Array& romanlines, Array& romantimes, Array& linetimes, HumdrumFile& romananalysis) { romanlines.setSize(romantimes.getSize()); romanlines.setAll(-1); int i; for (i=0; i& romantimes, HumdrumFile& romananalysis) { romantimes.setSize(romananalysis.getNumLines()); romantimes.setAll(-1); int i, j; long timeval; for (i=0; i= 0) { count = 0; countother = 0; endingtime = -1; j = i+1; while (j < romantimes.getSize()) { if (romantimes[j] < -10) { count++; } else if (romantimes[j] < 0) { countother++; } else { endingtime = romantimes[j]; break; } j++; } if (count > 0) { increment = 1.0*(endingtime - romantimes[i])/count; } else { increment = 0.0; } count = 0; k = i+1; while (k= 0) { break; } k++; } i = j-1; } } for (i=romantimes.getSize()-2; i>=0; i--) { if (romantimes[i] == -1) { romantimes[i] = romantimes[i+1]; } } for (i=1; i& analysistimes, int targettime) { int i; int diff = 0; int mindiff = 100000; int mindiffline = -1; for (i=0; i& analysistimes, HumdrumFile& rootanalysis) { int i; analysistimes.setSize(rootanalysis.getNumLines()); analysistimes.allowGrowth(0); analysistimes.setAll(-10000); int j; int datavalue = 0; for (i=0; i& timings, Array& tempo) { infile.analyzeRhythm("4"); timings.setSize(infile.getNumLines()); timings.setAll(0.0); tempo.setSize(infile.getNumLines()); tempo.setAll(dtempo); tempo[0] = dtempo; double currtempo = dtempo; double input = 0.0; int count; int i; for (i=1; i 0) { tempo[i-1] = currtempo; } tempo[i] = currtempo; } } tempo[i] = currtempo; timings[i] = timings[i-1] + (infile[i].getAbsBeat() - infile[i-1].getAbsBeat()) * 60.0/currtempo; } } ////////////////////////////// // // getDataLineTimings -- borrowed and modified from addtime program. // void getDataLineTimings(Array& linetimes, HumdrumFile& infile) { Array timings; Array tempo; analyzeTiming(infile, timings, tempo); linetimes.setSize(infile.getNumLines()); linetimes.allowGrowth(0); linetimes.setAll(-5000); int i; int offset = 0; for (i=0; i