//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon Apr 9 14:01:16 PDT 2001
// Last Modified: Mon Apr 9 14:01:20 PDT 2001
// Last Modified: Sun May 13 16:15:27 PDT 2001 (added KS algorithm)
// Last Modified: Mon Dec 1 19:07:40 PST 2003 (misc bug fixes)
// Last Modified: Wed Dec 22 16:28:02 PST 2004 (efficiency upgrade)
// Filename: ...sig/examples/all/keyscape2.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/keyscape2.cpp
// Syntax: C++; museinfo
//
// Description: generate a continuous key landscape picture or numeric data.
//
#include "humdrum.h"
#include <string.h>
#include <stdio.h>
#include <math.h>
typedef Array<float> ArrayFloat;
typedef Array<double> ArrayDouble;
class MusicWindow {
int startline;
int stopline;
};
double majorKeyAarden[12] = {
17.7661, // C
0.145624, // C#
14.9265, // D
0.160186, // D#
19.8049, // E
11.3587, // F
0.291248, // F#
22.062, // G
0.145624, // G#
8.15494, // A
0.232998, // A#
4.95122}; // B
double minorKeyAarden[12] = {
18.2648, // C
0.737619, // C#
14.0499, // D
16.8599, // D#
0.702494, // E
14.4362, // F
0.702494, // F#
18.6161, // G
4.56621, // G#
1.93186, // A
7.37619, // A#
1.75623 }; // B
// function declarations
void checkOptions(Options& opts, int argc, char* argv[]);
void example(void);
void usage(const char* command);
void makeGrayscale(void);
void printMatrix(Array<ArrayInt>& matrix);
void printTriangle(Array<ArrayInt>& matrix);
void printRoots(Array<ArrayInt>& matrix);
void printPPM(Array<ArrayInt>& matrix);
void printCAPPM(Array<ArrayInt>& matrix,
Array<ArrayFloat>& claritymatrix,
Array<ArrayFloat>& ambigmatrix);
void printCPPM(Array<ArrayInt>& matrix,
Array<ArrayFloat>& claritymatrix);
void printAPPM(Array<ArrayInt>& matrix,
Array<ArrayFloat>& ambigmatrix);
void printIAPPM(Array<ArrayInt>& matrix,
Array<ArrayFloat>& ambigmatrix,
Array<ArrayInt>& secondmatrix);
void printAveragePPM(Array<ArrayInt>& matrix);
int getStartIndex(HumdrumFile& infile, double startbeat);
int getStopIndex(HumdrumFile& infile, double stopbeat);
double difference(Array<double>& tonicscores, int max);
void scaleColor(int& R, int& G, int& B, double cfraction,
double afraction);
void scaleColorInterpolate
(int& R, int& G, int& B, int sec, double afraction);
void rgb2hsi(double *hsi, double *rgb);
void hsi2rgb(double* rgb, double* hsi);
int secondbest(Array<double>& tonicscores, int max);
int secondbestmax(Array<double>& tonicscores, int max);
void generateLineCoefficients(HumdrumFile& infile);
void generatePictureKS(HumdrumFile& infile);
void generatePictureCS(HumdrumFile& infile);
// command line options:
Options options; // database for command-line arguments
int debugQ = 0; // used with --debug option
int compoundQ = 1; // used with -c option
double empirical1 = -4; // used with --e1 option
double empirical2 = -3; // used with --e2 option
double sx = 0.578; // used with --sx option
double sy = 1.0; // used with --sx option
int ppmQ = 0; // used with --ppm option
int imageWidth = 1000; // used with -m option
int imageHeight = 500; // used with -t option
int linearQ = 0; // used with -l option
int monochromeQ = 0; // used with -b option
int matrixQ = 0; // used with -x option
int mirrorQ = 0; // used with --mirror option
int maxdivisions = -1; // used with -d option
int averageQ = 0; // used with -a option
int algorithm = 0; // used with --algorithm option
int clarityQ = 0; // used with -c option
int ambigQ = 0; // used with -a option
int secondQ = 0; // used with --second option
int ksalgorithmQ = 0; // used with -k option
int rhythmQ = 1; // used with -q option
int rootQ = 0; // used with --root option
int interpolateQ = 0; // used with -i option
int gradientQ = 0; // used with -g option
int lineQ = 0; // used with --line option
int linenum = 0; // used with --line option
int aardenQ = 0; // used with --aarden option
double minorscale = 0.75; // used with --minor option
double sfactor = 5.0; // range scaling factor
const char* background = "255 255 255"; // used with --bg option
double majorKey[12] = {0}; // used with -w option
double minorKey[12] = {0}; // used with -w option
int weightsQ = 0; // used with -w option
///////////////////////////////////////////////////////////////////////////
// Pitch to Color translations
int red[40] = {
0, 9, 18, 0, 63, 63, 63, 73, 82, 0, 218, 237, 255, 255, 255,
255, 255, 255, 218, 182, 0, 45, 54, 63, 63, 63, 0, 109, 118,
127, 145, 164, 0, 255, 255, 255, 255, 255, 73, 36
};
int green[40] = {
255, 246, 237, 0, 123, 109, 95, 86, 77, 0, 9, 4, 0, 18, 36, 218,
237, 255, 255, 255, 0, 209, 200, 191, 177, 164, 0, 50, 41, 31,
27, 22, 0, 91, 109, 127, 145, 164, 255, 255
};
int blue[40] = {
0, 36, 73, 0, 255, 255, 255, 255, 255, 0, 73, 36, 0, 0, 0, 0,
0, 0, 0, 0, 0, 182, 218, 255, 255, 255, 0, 255, 255, 255, 219,
182, 0, 0, 0, 0, 0, 0, 0, 0
};
///////////////////////////////////////////////////////////////////////////
double duration = 0.0;
int totalrows = 0;
Array<ArrayInt> matrix; // storage for root analysis results
Array<ArrayFloat> claritymatrix; // storage for root clarity
Array<ArrayFloat> ambigmatrix; // storage for root ambiguity
Array<ArrayInt> secondmatrix; // storage for second best root
Array<double> parameters(3);
int main(int argc, char* argv[]) {
HumdrumFile infile; // input Humdrum Format file
// 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();
// if no command-line arguments read data file from standard input
if (numinputs < 1) {
infile.read(cin);
} else {
infile.read(options.getArg(i+1));
}
infile.analyzeRhythm("4"); // force the beat to be quarter notes for now
duration = infile.getTotalDuration();
if (debugQ) {
cout << "Duration of input file is " << duration
<< " quarter notes" << endl;
}
totalrows = imageHeight;
if (lineQ) {
generateLineCoefficients(infile);
continue;
} else if (ksalgorithmQ) {
generatePictureKS(infile);
} else {
generatePictureCS(infile);
}
if (rootQ) {
printRoots(matrix);
} else if (clarityQ) {
printCPPM(matrix, claritymatrix);
} else if (ambigQ) {
printAPPM(matrix, ambigmatrix);
} else if (interpolateQ) {
printIAPPM(matrix, ambigmatrix, secondmatrix);
} else if (gradientQ) {
printCAPPM(matrix, claritymatrix, ambigmatrix);
} else if (secondQ) {
printPPM(secondmatrix);
} else {
printPPM(matrix);
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// initializeMatrices -- set all values in the matrices to -1.
//
void initializeMatrices(void) {
int row, column;
matrix.setSize(totalrows);
secondmatrix.setSize(totalrows);
claritymatrix.setSize(totalrows);
ambigmatrix.setSize(totalrows);
for (row=0; row<totalrows; row++) {
matrix[row].setSize(imageWidth);
matrix[row].allowGrowth(0);
secondmatrix[row].setSize(imageWidth);
secondmatrix[row].allowGrowth(0);
claritymatrix[row].setSize(imageWidth);
claritymatrix[row].allowGrowth(0);
ambigmatrix[row].setSize(imageWidth);
ambigmatrix[row].allowGrowth(0);
for (column=0; column<totalrows; column++) {
matrix[row][column] = -1;
secondmatrix[row][column] = -1;
claritymatrix[row][column] = -1;
ambigmatrix[row][column] = -1;
}
}
}
//////////////////////////////
//
// generatePictureCS --
//
void generatePictureCS(HumdrumFile& infile) {
int row, column;
int rrow = -1;
int lastrrow = -2;
initializeMatrices();
Array<double> tonicscores(40); // the scores for each possible key
tonicscores.zero(); // initialize the scores to zero
double startbeat; // absolute start beat of analysis window
double stopbeat; // absolute ending beat of analysis window
double centerbeat; // absolute center of the analysis window
double windowhalfwidth; // 1/2 of the analysis window duration
for (row=0; row<imageHeight; row++) {
if (linearQ) {
rrow = imageHeight - row;
} else {
rrow = (int)(imageHeight *
(1.0-log10((double)(imageHeight-row))/log10(imageHeight+1.0)));
rrow = imageHeight - rrow + 1;
}
if (lastrrow == rrow) {
matrix[row] = matrix[row-1];
secondmatrix[row] = secondmatrix[row-1];
ambigmatrix[row] = ambigmatrix[row-1];
claritymatrix[row] = claritymatrix[row-1];
continue;
}
for (column=0; column<imageWidth; column++) {
centerbeat = duration * (1 + 2 * column) / (2.0 * imageWidth);
windowhalfwidth = duration * (1 - (double)(rrow-1)/imageHeight)/2.0;
startbeat = centerbeat - windowhalfwidth;
stopbeat = centerbeat + windowhalfwidth;
if (fabs(startbeat) < 0.001) {
startbeat = 0.0;
}
if (fabs(stopbeat-duration) < 0.001) {
startbeat = duration;
}
if (startbeat < 0.0 || stopbeat > duration) {
// if the window falls anywhere outside the music put empty pixel
continue;
}
matrix[row][column] = infile.measureChordRoot(tonicscores,
parameters, startbeat, stopbeat, algorithm);
if (matrix[row][column] != -1) {
int value = matrix[row][column] % 40;
claritymatrix[row][column] = tonicscores[value];
cout << "CL=" << claritymatrix[row][column] << "\n";
ambigmatrix[row][column] = difference(tonicscores, value);
secondmatrix[row][column] = secondbest(tonicscores, value);
}
}
lastrrow = rrow;
}
}
//////////////////////////////
//
// generatePictureKS -- generate Krumhansl-Schmuckler key picture
//
void generatePictureKS(HumdrumFile& infile) {
int row, column;
int rrow = -1;
int lastrrow = -2;
initializeMatrices();
Array<double> scores(24); // the scores for each possible key
scores.zero(); // initialize the scores to zero
int startindex;
int stopindex;
double startbeat; // absolute start beat of analysis window
double stopbeat; // absolute ending beat of analysis window
double centerbeat; // absolute center of the analysis window
double windowhalfwidth; // 1/2 of the analysis window duration
for (row=0; row<imageHeight; row++) {
if (linearQ) {
rrow = imageHeight - row;
} else {
rrow = (int)(imageHeight *
(1.0-log10((double)(imageHeight-row))/log10(imageHeight+1.0)));
rrow = imageHeight - rrow + 1;
}
if (lastrrow == rrow) {
matrix[row] = matrix[row-1];
secondmatrix[row] = secondmatrix[row-1];
ambigmatrix[row] = ambigmatrix[row-1];
claritymatrix[row] = claritymatrix[row-1];
continue;
}
for (column=0; column<imageWidth; column++) {
centerbeat = duration * (1 + 2 * column) / (2.0 * imageWidth);
windowhalfwidth = duration * (1 - (double)(rrow-1)/imageHeight)/2.0;
startbeat = centerbeat - windowhalfwidth;
stopbeat = centerbeat + windowhalfwidth;
if (fabs(startbeat) < 0.001) {
startbeat = 0.0;
}
if (fabs(stopbeat-duration) < 0.001) {
startbeat = duration;
}
if (startbeat < 0.0 || stopbeat > duration) {
// if the window falls anywhere outside the music put empty pixel
continue;
}
startindex = getStartIndex(infile, startbeat);
stopindex = getStopIndex(infile, stopbeat);
if (aardenQ) {
matrix[row][column] = infile.analyzeKeyKS2(scores, startindex,
stopindex, majorKeyAarden, minorKeyAarden, rhythmQ);
} else if (weightsQ) {
cout << "GOT HERE in keyscape2" << endl;
matrix[row][column] = infile.analyzeKeyKS2(scores, startindex,
stopindex, majorKey, minorKey, rhythmQ);
} else {
matrix[row][column] = infile.analyzeKeyKS(scores, startindex,
stopindex, rhythmQ);
}
int value = matrix[row][column];
if (value >= 0) {
// -1 means invalid analysis data. preserve it.
matrix[row][column] = (Convert::base12ToBase40(value)-2+40)% 40;
if (value >= 12) {
matrix[row][column] += 40;
}
}
if (matrix[row][column] != -1) {
// claritymatrix[row][column] = tonicscores[matrix[row][column]];
// ambigmatrix[row][column] =
// difference(tonicscores, matrix[row][column]);
secondmatrix[row][column] = secondbestmax(scores, matrix[row][column]);
}
}
lastrrow = rrow;
}
}
//////////////////////////////////////////////////////////////////
void generatePictureKSOld(HumdrumFile& infile) {
initializeMatrices();
double startbeat; // absolute start beat of analysis
double stopbeat; // absolute ending beat of analysis
double laststartbeat = -1.0;
double laststopbeat = -1.0;
Array<double> scores(24); // storage for root scores
double centerbeat;
double beatwidth;
int laststartindex;
int laststopindex;
int startindex;
int stopindex;
int lastroot = -1;
int jj = -1;
int lastjj = -2;
int j, k;
for (j=0; j<imageHeight; j++) {
laststartbeat = -1.0;
laststopbeat = -1.0;
lastroot = -1;
if (linearQ) {
jj = imageHeight - j;
} else {
jj = (int)(imageHeight *
(1.0-log10((double)(imageHeight-j))/log10(imageHeight+1.0)));
jj = imageHeight - jj + 1;
}
if (jj == lastjj) {
for (k=0; k<imageWidth; k++) {
matrix[j][k] = matrix[j-1][k];
secondmatrix[j][k] = secondmatrix[j-1][k];
claritymatrix[j][k] = claritymatrix[j-1][k];
ambigmatrix[j][k] = ambigmatrix[j-1][k];
}
} else {
startindex = 0;
stopindex = 0;
laststartindex = -1;
laststopindex = -1;
for (k=0; k<imageWidth; k++) {
centerbeat = (k+0.5) * duration / imageWidth;
// beatwidth = duration * (1.0 - (double)jj/(imageHeight+1));
beatwidth = duration * (j+1)/imageHeight;
startbeat = centerbeat - fabs(beatwidth/2.0);
stopbeat = centerbeat + fabs(beatwidth/2.0) - 0.001;
if (startbeat < 0.0) {
stopbeat += -startbeat;
startbeat = 0.0;
matrix[j][k] = -1;
continue;
}
if (stopbeat > duration) {
startbeat -= stopbeat - duration;
stopbeat = duration;
matrix[j][k] = -1;
continue;
}
if (startbeat < 0.0) {
startbeat = 0.0;
}
if (startbeat == laststartbeat && stopbeat == laststopbeat) {
matrix[j][k] = lastroot;
continue;
}
startindex = getStartIndex(infile, startbeat);
stopindex = getStopIndex(infile, stopbeat);
if (debugQ) {
cout << "Window beats: " << startbeat
<< "(" << startindex << ")"
<< "\tto " << stopbeat
<< "(" << stopindex << ")"
<< "\trow = " << j
<< "\tcol = " << k
<< endl;
}
if (aardenQ) {
matrix[j][k] = infile.analyzeKeyKS2(scores, startindex,
stopindex, majorKeyAarden, minorKeyAarden, rhythmQ);
} else if (weightsQ) {
matrix[j][k] = infile.analyzeKeyKS2(scores, startindex,
stopindex, majorKey, minorKey, rhythmQ);
} else {
matrix[j][k] = infile.analyzeKeyKS(scores, startindex,
stopindex, rhythmQ);
}
int value = matrix[j][k];
if (value >= 0) {
// -1 means invalid analysis data. preserve it.
matrix[j][k] = (Convert::base12ToBase40(value)-2+40)% 40;
if (value >= 12) {
matrix[j][k] += 40;
}
}
if (matrix[j][k] != -1) {
// claritymatrix[j][k] = tonicscores[matrix[j][k]];
// ambigmatrix[j][k] =
// difference(tonicscores, matrix[j][k]);
secondmatrix[j][k] = secondbestmax(scores, matrix[j][k]);
}
laststartindex = startindex;
laststopindex = stopindex;
}
}
lastjj = jj;
}
}
//////////////////////////////
//
// printRoots -- print the roots instead of a picture
//
void printRoots(Array& matrix) {
int i, j;
char buffer[128];
for (i=matrix.getSize()-1; i>=0; i--) {
for (j=0; j<matrix[i].getSize(); j++) {
if (matrix[i][j] < 0) {
cout << "x ";
} else {
Convert::base40ToKern(buffer, matrix[i][j] + 2 + 3 * 40);
strcat(buffer, " ");
buffer[4] = '\0';
cout << buffer;
}
}
cout << "\n";
}
}
//////////////////////////////
//
// generateLineCoefficients -- list 40 sets of coefficients for a
// particular line in a plot
//
void generateLineCoefficients(HumdrumFile& infile) {
int j, k;
Array<ArrayDouble> matrix(imageWidth);
for (k=0; k<imageWidth; k++) {
matrix[k].setSize(40);
matrix[k].allowGrowth(0);
for (j=0; j<40; j++) {
matrix[k][j] = -1;
}
}
int analysisstart = -1;
int analysisstop = -1;
Array<double> parameters(3);
parameters[0] = sx;
parameters[1] = empirical1;
parameters[2] = empirical2;
double startbeat; // absolute start beat of analysis
double stopbeat; // absolute ending beat of analysis
double laststartbeat = -1.0;
double laststopbeat = -1.0;
double centerbeat;
double beatwidth;
int laststartindex;
int laststopindex;
int startindex;
int stopindex;
int lastroot = -1;
int jj = -1;
for (j=linenum; j<=linenum; j++) {
laststartbeat = -1.0;
laststopbeat = -1.0;
lastroot = -1;
if (!linearQ) {
jj = (int)(imageHeight * (1.0 -
log10((double)(imageHeight-j))/log10((double)(imageHeight+1))));
jj = imageHeight - jj + 1;
} else {
jj = imageHeight - j;
}
startindex = 0;
stopindex = 0;
laststartindex = -1;
laststopindex = -1;
for (k=0; k<imageWidth; k++) {
centerbeat = k * duration / imageWidth;
beatwidth = duration * (1.0 - (double)jj/(imageHeight+1));
startbeat = centerbeat - fabs(beatwidth/2.0);
stopbeat = centerbeat + fabs(beatwidth/2.0);
if (startbeat < 0.0) {
stopbeat += -startbeat;
startbeat = 0.0;
matrix[k].zero();
continue;
}
if (stopbeat > duration) {
startbeat -= stopbeat - duration;
stopbeat = duration;
if (analysisstop == -1) {
analysisstop = k - 1;
}
matrix[k].zero();
continue;
}
if (analysisstart == -1) {
analysisstart = k;
}
if (startbeat < 0.0) {
startbeat = 0.0;
}
startindex = getStartIndex(infile, startbeat);
stopindex = getStopIndex(infile, stopbeat);
int flag = infile.measureChordRoot(matrix[k],
parameters, startindex, stopindex, algorithm);
if (flag == -1) {
matrix[k].zero();
}
laststartindex = startindex;
laststopindex = stopindex;
}
}
int m, n;
for (m=0; m<40; m++) {
cout << "root" << m << " = Graphics[{RGBColor["
<< red[m]/256.0 << ", "
<< green[m]/256.0 << ", "
<< blue[m]/256.0 << "], "
<< "Line[{";
for (n=analysisstart; n<=analysisstop; n++) {
cout << " {" << n;
cout << ", " << 1.0/matrix[n][m] << "}";
if (n < analysisstop) {
cout << ",";
}
}
cout << "}]}];\n";
}
}
//////////////////////////////
//
// secondbest -- returns the second best root
//
int secondbest(Array& tonicscores, int max) {
int max2 = 0;
if (max == 0) max2 = 1;
for (int i=1; i<tonicscores.getSize(); i++) {
if (i == max) {
continue;
}
// actually looking for a minimum right now
if (tonicscores[i] < tonicscores[max2]) {
max2 = i;
}
}
return max2;
}
//////////////////////////////
//
// secondbestmax -- returns the second best root
//
int secondbestmax(Array& tonicscores, int max) {
int max2 = 0;
if (max == 0) max2 = 1;
for (int i=1; i<tonicscores.getSize(); i++) {
if (i == max) {
continue;
}
// actually looking for a minimum right now
if (tonicscores[i] > tonicscores[max2]) {
max2 = i;
}
}
return max2;
}
//////////////////////////////
//
// difference -- returns the difference between the highest score
// and the second highest score.
//
double difference(Array& tonicscores, int max) {
int max2 = 0;
if (max == 0) max2 = 1;
for (int i=1; i<40; i++) {
if (i == max) {
continue;
}
// actually looking for a minimum right now
if (tonicscores[i] < tonicscores[max2]) {
max2 = i;
}
}
return tonicscores[max2] - tonicscores[max];
}
//////////////////////////////
//
// printCPPM -- print only the clarity
//
void printCPPM(Array<ArrayInt>& matrix,
Array<ArrayFloat>& claritymatrix) {
int row, column;
int R, G, B;
int maxrow = imageHeight;
int maxcolumn = imageWidth;
int i, j;
int root;
cout << "P3\n";
cout << maxcolumn << " " << maxrow << "\n";
cout << "255\n";
double minc, maxc;
minc = maxc = 0.0;
int minit;
for (row=maxrow-1; row>=0; row--) {
i = row;
// find the max and min in claritymatrix
minit = 0;
for (column=0; column<maxcolumn; column++) {
j = column;
if (matrix[i][j] < 0) {
continue;
}
if (minit == 0) {
maxc = minc = claritymatrix[i][j];
minit = 1;
continue;
}
if (claritymatrix[i][j] < minc) minc = claritymatrix[i][j];
if (claritymatrix[i][j] > maxc) maxc = claritymatrix[i][j];
}
if (minc == maxc) {
minc -= 0.5;
maxc += 0.5;
}
double cfraction = 0;
for (column=0; column<maxcolumn; column++) {
j = column;
root = matrix[i][j];
if (root >= 0) {
// cfraction = 1.0 - (claritymatrix[i][j] - minc)/(maxc-minc);
cfraction = claritymatrix[i][j];
if (cfraction < 0.0) {
cfraction = 0.0;
} else {
cfraction = pow(cfraction, 1/sfactor);
}
R = (int)(cfraction * 255 + 0.5);
G = (int)(cfraction * 255 + 0.5);
B = (int)(cfraction * 255 + 0.5);
cout << " z" << claritymatrix[i][j] << " " << R << " " << G << " " << B << " ";
} else {
cout << " 255 0 0 ";
}
}
cout << "\n";
}
}
//////////////////////////////
//
// printAPPM -- print only the ambiguity
//
void printAPPM(Array<ArrayInt>& matrix,
Array<ArrayFloat>& ambigmatrix) {
int row, column;
int R, G, B;
int maxrow = imageHeight;
int maxcolumn = imageWidth;
int i, j;
int root;
cout << "P3\n";
cout << maxcolumn << " " << maxrow << "\n";
cout << "255\n";
double minc, maxc;
minc = maxc = 0.0;
int minit;
for (row=maxrow-1; row>=0; row--) {
i = row;
// find the max and min in ambigmatrix
minit = 0;
for (column=0; column<maxcolumn; column++) {
j = column;
if (matrix[i][j] < 0) {
continue;
}
if (minit == 0) {
maxc = minc = ambigmatrix[i][j];
minit = 1;
continue;
}
if (ambigmatrix[i][j] < minc) minc = ambigmatrix[i][j];
if (ambigmatrix[i][j] > maxc) maxc = ambigmatrix[i][j];
}
if (minc == maxc) {
minc -= 0.5;
maxc += 0.5;
}
double cfraction = 0;
for (column=0; column<maxcolumn; column++) {
j = column;
root = matrix[i][j];
if (root >= 0) {
cfraction = (ambigmatrix[i][j] - minc)/(maxc-minc);
cfraction = pow(cfraction, 1/sfactor);
R = (int)(cfraction * 256 + 0.5);
G = (int)(cfraction * 256 + 0.5);
B = (int)(cfraction * 256 + 0.5);
cout << R << " " << G << " " << B << " ";
} else {
cout << " 0 0 255 ";
}
}
cout << "\n";
}
}
//////////////////////////////
//
// printIAPPM --
//
void printIAPPM(Array<ArrayInt>& matrix,
Array<ArrayFloat>& ambigmatrix,
Array<ArrayInt>& secondmatrix) {
int row, column;
int R, G, B;
int maxrow = imageHeight;
int maxcolumn = imageWidth;
int i, j;
int root;
cout << "P3\n";
cout << maxcolumn << " " << maxrow << "\n";
cout << "255\n";
double mina, maxa;
mina = maxa = 0.0;
int minit;
for (row=maxrow-1; row>=0; row--) {
i = row;
// find the max and min in ambigmatrix
minit = 0;
for (column=0; column<maxcolumn; column++) {
j = column;
if (matrix[i][j] < 0) {
continue;
}
if (minit == 0) {
maxa = mina = ambigmatrix[i][j];
minit = 1;
continue;
}
if (ambigmatrix[i][j] < mina) mina = ambigmatrix[i][j];
if (ambigmatrix[i][j] > maxa) maxa = ambigmatrix[i][j];
}
if (mina == maxa) {
mina -= 0.5;
maxa += 0.5;
}
for (column=0; column<maxcolumn; column++) {
j = column;
root = matrix[i][j];
if (root >= 0) {
R = red[root];
G = green[root];
B = blue[root];
scaleColorInterpolate(R, G, B, secondmatrix[i][j],
(ambigmatrix[i][j] - mina)/(maxa-mina));
cout << R << " " << G << " " << B << " ";
} else {
// cout << " 0 0 0 ";
cout << " " << background << " ";
}
}
cout << "\n";
}
}
//////////////////////////////
//
// printCAPPM --
//
void printCAPPM(Array<ArrayInt>& matrix,
Array<ArrayFloat>& claritymatrix,
Array<ArrayFloat>& ambigmatrix) {
int row, column;
int R, G, B;
int maxrow = imageHeight;
int maxcolumn = imageWidth;
int i, j;
int root;
cout << "P3\n";
cout << maxcolumn << " " << maxrow << "\n";
cout << "255\n";
double minc, maxc;
double mina, maxa;
minc = maxc = mina = maxa = 0.0;
int minit;
for (row=maxrow-1; row>=0; row--) {
i = row;
// find the max and min in claritymatrix
minit = 0;
for (column=0; column<maxcolumn; column++) {
j = column;
if (matrix[i][j] < 0) {
continue;
}
if (minit == 0) {
maxc = minc = claritymatrix[i][j];
maxa = mina = ambigmatrix[i][j];
minit = 1;
continue;
}
if (claritymatrix[i][j] < minc) minc = claritymatrix[i][j];
if (claritymatrix[i][j] > maxc) maxc = claritymatrix[i][j];
if (ambigmatrix[i][j] < mina) mina = ambigmatrix[i][j];
if (ambigmatrix[i][j] > maxa) maxa = ambigmatrix[i][j];
}
if (minc == maxc) {
minc -= 0.5;
maxc += 0.5;
}
if (mina == maxa) {
mina -= 0.5;
maxa += 0.5;
}
for (column=0; column<maxcolumn; column++) {
j = column;
root = matrix[i][j];
if (root >= 0) {
R = red[root];
G = green[root];
B = blue[root];
scaleColor(R, G, B, (claritymatrix[i][j] - minc)/(maxc-minc),
(ambigmatrix[i][j] - mina)/(maxa-mina));
cout << R << " " << G << " " << B << " ";
} else {
// cout << " 0 0 0 ";
cout << " " << background << " ";
}
}
cout << "\n";
}
}
//////////////////////////////
//
// scaleColorInterpolate -- interpolate between second best and best
// root colors
//
void scaleColorInterpolate(int& R, int& G, int& B, int sec, double afraction) {
double rgbA[3];
rgbA[0] = R/256.0;
rgbA[1] = G/256.0;
rgbA[2] = B/256.0;
double hsiA[3] = {0};
rgb2hsi(hsiA, rgbA);
double rgbB[3];
rgbB[0] = red[sec]/256.0;
rgbB[1] = green[sec]/256.0;
rgbB[2] = blue[sec]/256.0;
double hsiB[3] = {0};
rgb2hsi(hsiB, rgbB);
double hsiC[3] = {0};
double rgbC[3] = {0};
// interpolate the hsi values by the ambiguity fraction
double difference = fabs(hsiA[0] - hsiB[0]);
double pointA = hsiA[0];
double pointM;
if (difference < 0.5) {
pointM = (pointA + hsiB[0])/2;
} else if (pointA < 0.5) {
pointM = (pointA + hsiB[0] - 1.0)/2;
} else {
pointM = (pointA + hsiB[0] + 1.0)/2;
}
if (pointA < pointM) {
hsiC[0] = (pointM - pointA) * (1.0 - afraction) + pointA;
} else {
hsiC[0] = (pointA - pointM) * afraction + pointM;
}
// hsiC[0] = (pointA + pointB)/2;
// hsiC[0] = pointM;
if (hsiC[0] > 1.0) hsiC[0] += -1.0;
if (hsiC[0] < 0.0) hsiC[0] += +1.0;
// hsiC[1] = hsiA[1] * (1.0 - afraction) + hsiB[1] * afraction;
//if (afraction < 0.5) {
// hsiC[2] = (hsiA[2] + hsiB[2])/2;
//} else {
// hsiC[2] = hsiA[2];
// }
hsiC[1] = (hsiA[1] + hsiB[1])/2;
hsiC[2] = (hsiA[2] + hsiB[2])/2;
if (hsiC[0] < 0) {
hsiC[0] += 1.0;
}
if (hsiC[0] < 0.0) {
hsiC[0] += 1.0;
}
if (hsiC[0] > 1.0) {
hsiC[0] -= 1.0;
}
if (hsiC[1] < 0.0) {
hsiC[1] += 1.0;
}
if (hsiC[1] > 1.0) {
hsiC[1] -= 1.0;
}
if (hsiC[2] < 0.0) {
hsiC[2] += 1.0;
}
if (hsiC[2] > 1.0) {
hsiC[2] -= 1.0;
}
hsi2rgb(rgbC, hsiC);
if (rgbA[0] < rgbB[0]) {
rgbC[0] = (rgbB[0] - rgbA[0])/2 * pow((1.0 - afraction), sfactor) + rgbA[0];
} else {
rgbC[0] = rgbA[0] - (rgbA[0] - rgbB[0])/2 * pow((1.0 - afraction), sfactor);
}
if (rgbA[1] < rgbB[1]) {
rgbC[1] = (rgbB[1] - rgbA[1])/2 * pow((1.0 - afraction), sfactor) + rgbA[1];
} else {
rgbC[1] = rgbA[1] - (rgbA[1] - rgbB[1])/2 * pow((1.0 - afraction), sfactor);
}
if (rgbA[2] < rgbB[2]) {
rgbC[2] = (rgbB[2] - rgbA[2])/2 * pow((1.0 - afraction), sfactor) + rgbA[2];
} else {
rgbC[2] = rgbA[2] - (rgbA[2] - rgbB[2])/2 * pow((1.0 - afraction), sfactor);
}
// rgbC[1] = (rgbA[1] - rgbB[1]) * afraction + rgbA[0];
// rgbC[2] = (rgbA[2] - rgbB[2]) * afraction + rgbA[0];
R = (int)(rgbC[0] * 256.0 + 0.5);
G = (int)(rgbC[1] * 256.0 + 0.5);
B = (int)(rgbC[2] * 256.0 + 0.5);
}
//////////////////////////////
//
// scaleColor -- add clarity and ambiguity color shifting to root
// analysis
//
void scaleColor(int& R, int& G, int& B, double cfraction, double afraction) {
double rgb[3];
rgb[0] = R/256.0;
rgb[1] = G/256.0;
rgb[2] = B/256.0;
double hsi[3] = {0};
rgb2hsi(hsi, rgb);
// scale the saturation by the clarity fraction
hsi[1] = hsi[1] * pow(1.0 - cfraction, 1/2.0);
if (hsi[1] < 0.0) {
hsi[1] = 0.0;
}
if (hsi[1] > 1.0) {
hsi[1] = 1.0;
}
// scale the intensity by the ambiguous fraction
hsi[2] = hsi[2] * pow(afraction, 1/sfactor);
if (hsi[2] < 0.0) {
hsi[2] = 0.0;
}
if (hsi[2] > 1.0) {
hsi[2] = 1.0;
}
hsi2rgb(rgb, hsi);
R = (int)(rgb[0] * 256.0 + 0.5);
G = (int)(rgb[1] * 256.0 + 0.5);
B = (int)(rgb[2] * 256.0 + 0.5);
}
//////////////////////////////
//
// printPPM --
//
void printPPM(Array& matrix) {
int row, column;
int maxrow = imageHeight;
int maxcolumn = imageWidth;
int i, j;
int root;
double modescale = 1.0;
cout << "P3\n";
cout << maxcolumn << " " << maxrow << "\n";
cout << "255\n";
for (row=maxrow-1; row>=0; row--) {
i = row;
// i = (int)(maxrow * (1.0 - log10((double)(maxrow-row)) /
// log10((double)(maxrow+1))));
// i = maxrow - i + 1;
for (column=0; column<maxcolumn; column++) {
j = column;
root = matrix[i][j];
if (root >= 40) {
root -= 40;
modescale = minorscale;
} else {
modescale = 1.0;
}
if (root >= 0) {
cout << (int)(red[root] * modescale + 0.5) << " "
<< (int)(green[root] * modescale + 0.5) << " "
<< (int)(blue[root] * modescale + 0.5) << " ";
} else {
if (ksalgorithmQ) {
// cout << " 255 255 255 ";
cout << " " << background << " ";
} else {
// cout << " 0 0 0 ";
cout << " " << background << " ";
}
}
}
cout << "\n";
}
}
//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("C|compound=b", "don't try to use compound meters");
opts.define("r|rhythm=i:4", "rhythm to measure root at");
opts.define("e1|empirical1=d:-4", "empirical rhythm value 1");
opts.define("e2|empirical2=d:-3", "empirical rhythm value 2");
opts.define("sx=d:0.578", "scale factor for the x-pitch space axis");
opts.define("sy=d:1.0", "scale factor for the y-pitch space axis");
opts.define("algorithm=i:0", "0 = default algorithm, 1+ = new algorithms");
opts.define("roots|root=b", "display root note analysis");
opts.define("ppm=b", "generate a ppm graphic lanscape image");
opts.define("d|dimension=s:10x10","pixel width and height of picture");
opts.define("k|ks=b", "generate Krumhansl-Schmuckler key picture");
opts.define("aarden=b", "use Aarden weightings in K-S algorithm");
opts.define("w|weights=s", "arbitrary set of pitch weights");
opts.define("minor=d:0.75", "make minor keys darker than major keys");
opts.define("c|clarity=b", "display clarity gradations");
opts.define("a|ambig=b", "display ambiguity gradations");
opts.define("second=b", "display second best roots");
opts.define("i|interpolate=b", "ambiguity gradations via interploation");
opts.define("g|gradient=b", "display gradations of tonal clarity");
opts.define("line=i:0", "print out root weightings for a horizontal line");
opts.define("bg|background=s:255 255 255", "background color of picture");
opts.define("q|quarter=b", "Calculate KS algorithm without durations");
opts.define("s|sfactor=d:5.0", "gradation scaling factor");
opts.define("l|linear=b", "linear plot instead of logarithmic");
opts.define("b|monochrome=b", "use black and white instead of color");
opts.define("x|matrix=b", "generate a matrix rather than a table");
opts.define("mirror=b","generate mirror matrix rather than cyclical matrix");
opts.define("divisions=i:-1", "the maximum number of divisions");
opts.define("average=b","generate average key picture");
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, Dec 2000" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: 6 Dec 2000" << 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);
}
algorithm = opts.getInteger("algorithm");
debugQ = opts.getBoolean("debug");
if (opts.getBoolean("compound")) {
compoundQ = 0;
} else {
compoundQ = 0;
}
empirical1 = opts.getDouble("empirical1");
empirical2 = opts.getDouble("empirical2");
sx = opts.getDouble("sx");
sy = opts.getDouble("sy");
ppmQ = opts.getBoolean("ppm");
sscanf(opts.getString("dimension"), "%dx%d", &imageWidth, &imageHeight);
linearQ = opts.getBoolean("linear");
matrixQ = opts.getBoolean("matrix");
mirrorQ = opts.getBoolean("mirror");
maxdivisions = opts.getInteger("divisions");
averageQ = opts.getBoolean("average");
clarityQ = opts.getBoolean("clarity");
ambigQ = opts.getBoolean("ambig");
secondQ = opts.getBoolean("second");
interpolateQ = opts.getBoolean("interpolate");
gradientQ = opts.getBoolean("gradient");
lineQ = opts.getBoolean("line");
linenum = opts.getInteger("line");
sfactor = opts.getDouble("sfactor");
ksalgorithmQ = opts.getBoolean("ks");
aardenQ = opts.getBoolean("aarden");
rhythmQ = !opts.getBoolean("quarter");
rootQ = opts.getBoolean("roots");
minorscale = opts.getDouble("minor");
background = opts.getString("background");
if (opts.getBoolean("monochrome")) {
makeGrayscale();
}
parameters[0] = sx; // alpha
parameters[1] = empirical1; // delta
parameters[2] = empirical2; // lambda
int i;
int j;
int key;
double value;
weightsQ = opts.getBoolean("weights");
if (weightsQ) {
HumdrumFile wfile;
wfile.read(opts.getString("weights"));
for (i=0; i<wfile.getNumLines(); i++) {
if (wfile[i].getType() != E_humrec_data) {
continue;
}
key = -1;
value = -1.0;
for (j=0; j<wfile[i].getFieldCount(); j++) {
if (strcmp(wfile[i].getExInterp(j), "**kern") == 0) {
key = Convert::kernToMidiNoteNumber(wfile[i][j]) % 12;
if (islower(wfile[i][j][0])) {
key += 12;
}
} else if (strcmp(wfile[i].getExInterp(j), "**weight") == 0) {
sscanf(wfile[i][j], "%lf", &value);
}
}
if ((key >= 0) && (key < 24) && (value != -1.0)) {
if (key < 12) {
majorKey[key] = value;
} else {
minorKey[key-12] = value;
}
}
}
}
}
//////////////////////////////
//
// example -- example usage of the maxent program
//
void example(void) {
cout <<
" \n"
<< endl;
}
//////////////////////////////
//
// usage -- gives the usage statement for the quality program
//
void usage(const char* command) {
cout <<
" \n"
<< endl;
}
//////////////////////////////
//
// makeGrayscale --
//
void makeGrayscale(void) {
for (int i=0; i<40; i++) {
red[i] = (int)((i+1.0)/40.0);
green[i] = red[i];
blue[i] = red[i];
}
}
//////////////////////////////
//
// getStartIndex --
//
int getStartIndex(HumdrumFile& infile, double startbeat) {
int index = 1;
while (index < infile.getNumLines() - 1) {
if (startbeat <= infile[index].getAbsBeat() + 0.001 &&
startbeat > infile[index-1].getAbsBeat() - 0.001) {
return index;
}
index++;
}
return infile.getNumLines() - 1;
}
//////////////////////////////
//
// getStopIndex --
//
int getStopIndex(HumdrumFile& infile, double stopbeat) {
int index = 1;
while (index < infile.getNumLines()) {
if (stopbeat <= infile[index].getAbsBeat() + 0.001 &&
stopbeat > infile[index-1].getAbsBeat() - 0.001) {
return index;
}
index++;
}
return infile.getNumLines() - 1;
}
//////////////////////////////
//
// rgb2hsi -- convert from RGB color space to HSI color space.
//
#ifndef PI
#define PI M_PI
#endif
void rgb2hsi(double *hsi, double *rgb) {
double& R = rgb[0];
double& G = rgb[1];
double& B = rgb[2];
double& H = hsi[0];
double& S = hsi[1];
double& I = hsi[2];
double min = R;
if (G < min) min = G;
if (B < min) min = B;
I = (R+G+B)/3.0;
S = 1 - min/I;
if (S == 0.0) {
H = 0.0;
} else {
H = ((R-G)+(R-B))/2.0;
H = H/sqrt((R-G)*(R-G) + (R-B)*(G-B));
H = acos(H);
if (B > G) {
H = 2*PI - H;
}
H = H/(2*PI);
}
}
//////////////////////////////
//
// hsi2rgb -- convert from HSI color space to RGB color space.
//
#ifndef PI
#define PI M_PI
#endif
void hsi2rgb(double* rgb, double* hsi) {
double& R = rgb[0];
double& G = rgb[1];
double& B = rgb[2];
double& H = hsi[0];
double& S = hsi[1];
double& I = hsi[2];
double r;
double g;
double b;
if (H < 1.0/3.0) {
b = (1-S)/3;
r = (1+S*cos(2*PI*H)/cos(PI/3-2*PI*H))/3.0;
g = 1 - (b + r);
} else if (H < 2.0/3.0) {
H = H - 1.0/3.0;
r = (1-S)/3;
g = (1+S*cos(2*PI*H)/cos(PI/3-2*PI*H))/3.0;
b = 1 - (r+g);
} else {
H = H - 2.0/3.0;
g = (1-S)/3;
b = (1+S*cos(2*PI*H)/cos(PI/3-2*PI*H))/3.0;
r = 1 - (g+b);
}
R = I * r * 3;
G = I * g * 3;
B = I * b * 3;
}
// md5sum: aa669e574e8b2fc5a7fa19ba1770e688 keyscape2.cpp [20120423]