//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon May 20 14:21:40 PDT 2002
// Last Modified: Mon May 20 14:21:43 PDT 2002
// Filename: ...sig/examples/all/kern2koto.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/kern2koto.cpp
// Syntax: C++; museinfo
//
// Description: Convert **kern representation to **koto representation
//
// Note: Not finished
//
#include "humdrum.h"
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
// function declarations
void checkOptions(Options& opts, int argc, char* argv[]);
void example(void);
void usage(const char* command);
void convertKernToKoto(HumdrumFile& infile);
void processKernData(HumdrumRecord& line);
void processKernDatum(const char* string);
void processInterpretation(HumdrumRecord& line);
void storeTuning(const char* tunestring);
void printRhythm(const char* string);
void printPitch(const char* string);
void printString(int stringno);
void getKeyAndRange(HumdrumFile& infile, int& key, int& lownote,
int& highnote, Array<double>& profile);
int getTranspose(int key, int lownote, int highnote,
Array<double>& profile);
// command line options:
Options options; // database for command-line arguments
int debugQ = 0; // for debugging options --debug
int appendQ = 0; // for use with -a option
int automaticQ = 1; // for use with -A option
int transpose = 0;
int lownote = -1;
int highnote = -1;
int key = -1;
Array<double> profile(128);
Array<int> tuning; // for koto tuning
// Hira choshi:
//const char* defaultTuning = "*tune[d:G:A:A#:d:d#:g:a:a#:dd:dd#:gg:aa]";
// Synthetic Chinese tuning:
// const char* defaultTuning = "*tune[C#:E:F#:A:B:c#:e:f#:a:b:cc#:ee:ff#]";
const char* majorTuningC = "*tune[c:d:e:f:g:a:b:cc:dd:ee:ff:gg:aa]";
const char* majorTuningG = "*tune[c:d:e:f#:g:a:b:cc:dd:ee:ff#:gg:aa]";
const char* defaultTuning = majorTuningC;
///////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) {
HumdrumFile infile; // input Humdrum Format file
tuning.setSize(13);
tuning.allowGrowth(0);
// process the command-line options
checkOptions(options, argc, argv);
// figure out the number of input files to process
int numinputs = options.getArgCount();
for (int i=0; i<numinputs || i==0; i++) {
infile.clear();
storeTuning(defaultTuning);
// if no command-line arguments read data file from standard input
if (numinputs < 1) {
infile.read(cin);
} else {
infile.read(options.getArg(i+1));
}
if (automaticQ) {
getKeyAndRange(infile, key, lownote, highnote, profile);
if (debugQ) {
cout << "Key is: " << key << endl;
cout << "Low note is: " << lownote << endl;
cout << "High note is: " << highnote << endl;
}
transpose = getTranspose(key, lownote, highnote, profile);
}
convertKernToKoto(infile);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// getTranspose --
//
int getTranspose(int key, int lownote, int highnote, Array& profile) {
double center = 0.0;
double norm = 0.0;
int i;
for (i=0; i<128; i++) {
center += i * profile[i];
norm += profile[i];
}
center = center / norm;
int notecenter = (int)(center + 0.5);
cout << "!! Key is: " << key << endl;
cout << "!! Center pitch is: " << center << endl;
cout << "!! Median center is: " << (highnote - lownote)/2.0 + lownote << endl;
// find the relationship between the key and the note center
int octave = notecenter / 12;
int diff0 = (octave-1) * 12 + key - notecenter;
int diff1 = octave * 12 + key - notecenter;
int diff2 = (octave+1) * 12 + key - notecenter;
int mode = 'G';
// cout << "diff0 = " << diff0 << "\t"
// << "diff1 = " << diff1 << "\t"
// << "diff2 = " << diff2 << endl;
if ((abs(diff0) <= abs(diff1)) && (abs(diff0) <= abs(diff2))) {
octave = octave - 1;
if (diff0 < 0) { // closest tonic below the center
mode = 'C';
} else { // closest tonic above the center
mode = 'G';
}
} else if ((abs(diff1) <= abs(diff0)) && (abs(diff1) <= abs(diff2))) {
if (diff1 < 0) { // closest tonic below the center
mode = 'C';
} else { // closest tonic above the center
mode = 'G';
}
} else if ((abs(diff2) <= abs(diff0)) && (abs(diff2) <= abs(diff1))) {
octave = octave + 1;
if (diff2 < 0) { // closest tonic below the center
mode = 'C';
} else { // closest tonic above the center
mode = 'G';
}
}
cout << "!! MODE = " << (char)mode << endl;
int transpose = 0;
if (mode == 'G') {
storeTuning(majorTuningG);
transpose = tuning[4] - (key + octave * 12);
defaultTuning = majorTuningG;
} else {
storeTuning(majorTuningC);
transpose = tuning[7] - (key + octave * 12);
defaultTuning = majorTuningC;
}
// 0 1 2 3 4 5 6 7 8 9 0 1 2
// majorTuningC = "*tune[c:d:e:f :g:a:b:cc:dd:ee:ff:gg:aa]";
// majorTuningG = "*tune[c:d:e:f#:g:a:b:cc:dd:ee:ff:gg:aa]";
int lowerrors = 0;
int hierrors = 0;
for (i=0; i<profile.getSize(); i++) {
if (profile[i] > 0) {
if (i+transpose < tuning[0]) {
lowerrors++;
} else if (i+transpose > tuning[12]) {
hierrors++;
}
}
}
if (lowerrors && mode == 'G') {
mode = 'C';
defaultTuning = majorTuningC;
storeTuning(majorTuningC);
transpose = tuning[7] - (key + octave * 12);
}
cout << "!! Transpose = " << transpose << endl;
return transpose;
}
//////////////////////////////
//
// getKeyAndRange --
//
void getKeyAndRange(HumdrumFile& infile, int& key, int& lownote, int& highnote,
Array<double>& profile) {
Array<int> pitches;
pitches.setSize(infile.getNumLines()*2);
pitches.setSize(0);
Array<double> durations;
durations.setSize(infile.getNumLines()*2);
durations.setSize(0);
profile.setSize(128);
profile.setAll(0.0);
int tokencount;
char buffer[1024] = {0};
int pitch = 0;
double duration = 0.0;
lownote = 127;
highnote = 0;
key = 0;
int i, j;
for (i=0; i<infile.getNumLines(); i++) {
if (!infile[i].isData()) {
continue;
}
if (strcmp(infile[i][0], ".") == 0) {
continue;
}
if (strchr(infile[i][0], '_') != NULL) {
continue;
}
if (strchr(infile[i][0], ']') != NULL) {
continue;
}
tokencount = infile[i].getTokenCount(0);
for (j=0; j<tokencount; j++) {
infile[i].getToken(buffer, 0, j);
pitch = Convert::kernToMidiNoteNumber(buffer);
if (pitch < 0) {
continue;
}
pitches.append(pitch);
duration = Convert::kernToDuration(buffer);
durations.append(duration);
profile[pitch] += duration;
if (pitch < lownote) lownote = pitch;
if (pitch > highnote) highnote = pitch;
}
}
Array<double> scores(24);
Array<double> distribution(12);
key = analyzeKeyKS(scores.getBase(), distribution.getBase(),
pitches.getBase(), durations.getBase(), pitches.getSize(), 1, 1);
// don't distinguish between major and minor for now:
if (key >= 12) key = (key + 3) % 12;
}
//////////////////////////////
//
// convertKernToKoto --
//
void convertKernToKoto(HumdrumFile& infile) {
int i;
for (i=0; i<infile.getNumLines(); i++) {
switch (infile[i].getType()) {
case E_humrec_none:
case E_humrec_empty:
case E_humrec_global_comment:
case E_humrec_bibliography:
cout << infile[i] << "\n";
break;
case E_humrec_data_comment:
if (appendQ) {
cout << infile[i] << "\t!\n";
} else {
cout << infile[i][0] << "\n";
}
break;
case E_humrec_data_kern_measure:
if (appendQ) {
cout << infile[i] << "\t" << infile[i][0] << "\n";
} else {
cout << infile[i][0] << "\n";
}
break;
case E_humrec_interpretation:
processInterpretation(infile[i]);
break;
case E_humrec_data:
processKernData(infile[i]);
break;
default:
cout << "!!" << infile[i] << "\n";
break;
}
}
}
//////////////////////////////
//
// processInterpretation --
//
void processInterpretation(HumdrumRecord& line) {
if (appendQ) {
cout << line << "\t";
}
if (strncmp(line[0], "**", 2) == 0) {
cout << "**koto\n";
cout << defaultTuning << endl;
return;
}
if (strcmp(line[0], "*-") == 0) {
cout << "*-\n";
return;
}
if (strncmp(line[0], "*M", 2) == 0) {
int top, bottom;
int count = sscanf(line[0], "*M%d/%d", &top, &bottom);
if (count == 2) {
cout << line[0] << "\n";
} else {
cout << "*\n";
}
return;
}
if (strncmp(line[0], "*tune[", 6) == 0) {
storeTuning(line[0]);
if (appendQ) {
cout << line[0] << "\n";
}
return;
}
if (appendQ) {
cout << line[0] << "\n";
}
}
///////////////////////////////
//
// storeTuning --
//
void storeTuning(const char* tunestring) {
int length = strlen(tunestring);
int i;
int stringnum = 0;
Array<int> info(13);
for (i=0; i<13; i++) {
info[i] = -1;
}
for (i=6; i<length-1; i++) {
if (tunestring[i] == ':') {
stringnum++;
} else {
if (info[stringnum] < 0) {
// info[stringnum] = Convert::kernToBase40(&tunestring[i]);
info[stringnum] = Convert::kernToMidiNoteNumber(&tunestring[i]);
}
}
}
for (i=0; i<13; i++) {
if (info[i] > 0) {
tuning[i] = info[i];
}
}
}
//////////////////////////////
//
// processKernData --
//
void processKernData(HumdrumRecord& line) {
static char buffer[1024] = {0};
if (appendQ) {
cout << line << "\t";
}
if (strncmp(line[0], "-", 1) == 0) {
cout << ".\n";
return;
}
int tokencount = line.getTokenCount(0);
int i;
for (i=0; i<tokencount; i++) {
line.getToken(buffer, 0, i);
processKernDatum(buffer);
if (i < tokencount - 1) {
cout << " ";
}
}
cout << "\n";
}
//////////////////////////////
//
// processKernDatum --
//
void processKernDatum(const char* string) {
if (strchr(string, '{') != NULL) {
cout << '{';
}
if (strchr(string, '(') != NULL) {
cout << '(';
}
if (strchr(string, '[') != NULL) {
cout << '[';
}
printPitch(string);
printRhythm(string);
int duration = (int)Convert::kernToDuration(string);
int i;
for (i=1; i<duration; i++) {
cout << endl << "-";
}
if (strchr(string, 'q') != NULL) {
cout << 'q';
}
if (strchr(string, ':') != NULL) {
cout << ':';
}
if (strchr(string, ';') != NULL) {
cout << ';';
}
if (strchr(string, ']') != NULL) {
cout << ']';
}
if (strchr(string, ')') != NULL) {
cout << ')';
}
if (strchr(string, '}') != NULL) {
cout << '}';
}
}
//////////////////////////////
//
// printRhythm -- given a **kern rhythm, convert to **koto rhythm
// not finalized, but good enough for now.
//
void printRhythm(const char* string) {
int dotcount = 0;
int i;
int length = strlen(string);
for (i=0; i<length; i++) {
if (string[i] == '.') {
dotcount++;
}
}
double duration = Convert::kernToDuration(string);
if (duration < 1.0) {
cout << "|";
}
if (duration < 0.5) {
cout << "|";
}
if (duration < 0.25) {
cout << "|";
}
if (duration < 0.125) {
cout << "|";
}
if (duration >= 2.0) {
cout << "+";
}
if (duration >= 3.0) {
cout << "+";
}
if (duration >= 4.0) {
cout << "+";
}
for (i=0; i<dotcount; i++) {
cout << ".";
}
}
//////////////////////////////
//
// printPitch --
//
void printPitch(const char* string) {
int pitch = Convert::kernToMidiNoteNumber(string);
if (pitch < 0) {
printString(0);
return;
}
pitch += transpose;
Array<int> difference(13);
difference.setAll(0);
int i;
for (i=0; i<13; i++) {
difference[i] = pitch - tuning[i];
if (difference[i] == 0) {
printString(i+1);
return;
}
}
for (i=0; i<13; i++) {
if (difference[i] == 1) {
printString(i+1);
cout << "#";
return;
}
}
for (i=0; i<13; i++) {
if (difference[i] == 2) {
printString(i+1);
cout << "##";
return;
}
}
for (i=0; i<13; i++) {
if (difference[i] == 3) {
printString(i+1);
cout << "###";
return;
}
}
cout << "X";
}
//////////////////////////////
//
// printString --
//
void printString(int stringno) {
if (stringno < 10) {
cout << stringno;
return;
}
if (stringno == 10) {
cout << "A";
return;
}
if (stringno == 11) {
cout << "B";
return;
}
if (stringno == 12) {
cout << "C";
return;
}
if (stringno == 13) {
cout << "D";
return;
}
cout << "X";
}
//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("a|append=b", "append analysis");
opts.define("A|no-automatic=b", "no automatic transposition");
opts.define("t|tuning=s:", "tuning to use");
opts.define("debug=b", "trace input parsing");
opts.define("author=b", "author of the program");
opts.define("version=b", "compilation information");
opts.define("example=b", "example usage");
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 2002" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 20 May 2002" << 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);
}
appendQ = opts.getBoolean("append");
debugQ = opts.getBoolean("debug");
automaticQ = !opts.getBoolean("no-automatic");
if (opts.getBoolean("tuning")) {
defaultTuning = opts.getString("tuning");
}
}
//////////////////////////////
//
// example -- example usage of the scrub program
//
void example(void) {
cout <<
" \n"
<< endl;
}
//////////////////////////////
//
// usage -- gives the usage statement for the scrub program
//
void usage(const char* command) {
cout <<
" \n"
<< endl;
}
// md5sum: 17930717fabc5d166decd3cf27940fea kern2koto.cpp [20050403]