//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon Jun 24 20:05:15 PDT 2002
// Last Modified: Thu Jul 11 14:48:23 2002
// Filename: ...sig/examples/all/chordset.cpp
// Web Address: http://sig.sapp.org/examples/museinfo/humdrum/chordset.cpp
// Syntax: C++; museinfo
//
// Description: Count and collect chords from **kern data using a
// **root interpretation indicating the segmentation of
// the chords.
//
#include "humdrum.h"
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
template<class type> int arrayEqual(Array<type>& a, Array<type>& b);
class ChordEntry {
public:
ChordEntry(void) { clear(); }
~ChordEntry() { clear(); }
void clear(void) { count = root = 0; pitches.setSize(0); }
int count;
int root;
Array<int> pitches;
};
class ChordData : public SigCollection<ChordEntry> {
public:
ChordData(void) { clear(); }
~ChordData() { clear(); }
void clear(void) { setSize(10000); setGrowth(10000); setSize(0); }
void addData(int root, Array<int>& pitches);
void addDataNoRoot(Array<int>& pitches);
};
void ChordData::addData(int root, Array<int>& pitches) {
ChordData& data = *this;
int i;
for (i=0; i<getSize(); i++) {
if (arrayEqual(data[i].pitches, pitches) && data[i].root == root) {
data[i].count++;
return;
}
}
// chord configuration not found -- add the data
ChordEntry ce;
ce.root = root;
ce.pitches = pitches;
ce.count = 1;
append(ce);
}
void ChordData::addDataNoRoot(Array<int>& pitches) {
ChordData& data = *this;
int i;
for (i=0; i<getSize(); i++) {
if (arrayEqual(data[i].pitches, pitches)) {
data[i].count++;
return;
}
}
// chord configuration not found -- add the data
ChordEntry ce;
ce.root = 0;
ce.pitches = pitches;
ce.count = 1;
append(ce);
}
///////////////////////////////////////////////////////////////////////////
// function declarations
void checkOptions(Options& opts, int argc, char* argv[]);
void example(void);
void usage(const char* command);
void addToTable(ChordData& chordData, HumdrumFile& infile);
int rootcompare(const void* a, const void* b);
void extractChord(ChordData& chordData, HumdrumFile& infile,
int startline, int stopline, int root);
void addToTable(ChordData& chordData, HumdrumFile& infile);
void printTable(ChordData& chordData);
void sortByCount(ChordData& chordData);
void sortByRoot(ChordData& chordData);
void sortByPitch(ChordData& chordData);
int countcompare(const void* a, const void* b);
int root2compare(const void* a, const void* b);
int pitchcompare(const void* a, const void* b);
// global variables
Options options; // database for command-line arguments
int debugQ = 0; // used with --debug option
int sortType = 0; // used with -s option
int rawQ = 0; // used with -r option
int transposeQ = 0; // used with -t option
int intervalQ = 0; // used with -i option
int uniqQ = 0; // used with -u option
int beatQ = 0; // used with -b option
int rootlessQ = 0; // used with -R option
const char* CurrentFile = ".";
///////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) {
HumdrumFile infile;
ChordData chordData;
// process the command-line options
checkOptions(options, argc, argv);
if (rawQ) {
if (rootlessQ) {
cout << "**file\t**line\t**root\t**kern\n";
} else {
cout << "**file\t**line\t**kern\n";
}
}
// build up the chord table from the input file(s):
int i;
for (i=1; i<=options.getArgCount() || options.getArgCount()==0; i++) {
infile.clear();
// if no command-line arguments read data file from standard input
if (options.getArgCount() < 1) {
infile.read(cin);
} else {
infile.read(options.getArg(i));
CurrentFile = options.getArg(i);
if (strrchr(options.getArg(i), '/') != NULL) {
CurrentFile = strrchr(options.getArg(i), '/') + 1;
}
}
if (beatQ) {
infile.analyzeRhythm();
}
addToTable(chordData, infile);
}
switch (sortType) {
case 'c': sortByCount(chordData); break;
case 'r': sortByRoot(chordData); break;
case 'p': sortByPitch(chordData); break;
}
if (rawQ) {
cout << "*-\t*-\t*-\t*-\n";
} else {
printTable(chordData);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// printTable --
//
void printTable(ChordData& chordData) {
int i, j;
char buffer[1024] = {0};
int totalchords = 0;
for (i=0; i<chordData.getSize(); i++) {
totalchords += chordData[i].count;
}
cout << "!! Total Chords: " << totalchords << "\n";
cout << "!! Unique Chords: " << chordData.getSize() << "\n";
if (rootlessQ) {
cout << "**count\t";
} else {
cout << "**count\t**root\t";
}
if (intervalQ) {
cout << "**rooti\n";
} else {
cout << "**kern\n";
}
for (i=0; i<chordData.getSize(); i++) {
cout << chordData[i].count << "\t";
if (!rootlessQ) {
cout << Convert::base40ToKern(buffer, chordData[i].root+3*40) << "\t";
}
if (chordData[i].pitches.getSize() == 0) {
cout << ".";
}
for (j=0; j<chordData[i].pitches.getSize(); j++) {
if (intervalQ) {
cout << Convert::base40ToIntervalAbbr(buffer, (chordData[i].pitches[j] -2 + 400) %40);
} else {
cout << Convert::base40ToKern(buffer, chordData[i].pitches[j] + 4*40);
}
if (j < chordData[i].pitches.getSize()-1) {
cout << " ";
}
}
cout << "\n";
}
cout << "*-\t*-\t*-\n";
}
//////////////////////////////
//
// addToTable -- add Humdrum file chords to the chord table.
//
void addToTable(ChordData& chordData, HumdrumFile& infile) {
int oldline = 0;
int init = 0;
int i, j;
int root = -1;
// go though the file and extract notes sets according to the
// **root segmentation of the music:
for (i=0; i<infile.getNumLines(); i++) {
if (!infile[i].isData()) {
continue;
}
for (j=0; j<infile[i].getFieldCount(); j++) {
if (strcmp(infile[i].getExInterp(j), "**root") == 0) {
if (strcmp(infile[i][j], ".") == 0) {
break;
}
if (strchr(infile[i][j], '_') != NULL) { // cont. ties
break;
}
if (strchr(infile[i][j], ']') != NULL) { // tie ends
break;
}
if (init == 0) {
init = 1;
oldline = i;
root = Convert::kernToBase40(infile[i][j]) % 40;
break;
}
extractChord(chordData, infile, oldline, i-1, root);
oldline = i;
root = Convert::kernToBase40(infile[i][j]) % 40;
break;
}
}
}
extractChord(chordData, infile, oldline, i-1, root);
}
//////////////////////////////
//
// extractChord -- extract the chord region from the file and store in
// the chordData structure.
//
void extractChord(ChordData& chordData, HumdrumFile& infile,
int startline, int stopline, int root) {
// ignore roots which are rests
if (root < 0) {
return;
}
// extract note data from Humdrum file
Array<double> absbeat;
Array<int> pitches;
Array<double> durations;
Array<double> levels;
infile.getNoteArray(absbeat, pitches, durations, levels, startline,
stopline);
// extract just pitch class info, and ignore rests
Array<int> npitches;
npitches.setSize(pitches.getSize());
npitches.setSize(0);
int value = 0;
int i;
for (i=0; i<pitches.getSize(); i++) {
if (pitches[i] < 0) {
continue;
}
value = pitches[i] % 40;
npitches.append(value);
}
// ignore empty pitch sets
if (npitches.getSize() == 0) {
return;
}
if (transposeQ) {
for (i=0; i<npitches.getSize(); i++) {
npitches[i] = (npitches[i] - root + 2 + 400) % 40;
}
}
// sort and store the chord entry
qsort(npitches.getBase(), npitches.getSize(), sizeof(int), rootcompare);
Array<int> nnpitches(npitches.getSize());
if (uniqQ == 0) {
if (rootlessQ) {
chordData.addDataNoRoot(npitches);
} else {
chordData.addData(root, npitches);
}
nnpitches = npitches;
} else {
nnpitches.setSize(0);
nnpitches.append(npitches[0]);
for (i=1; i<npitches.getSize(); i++) {
if (npitches[i] == npitches[i-1]) {
continue;
}
nnpitches.append(npitches[i]);
}
if (rootlessQ) {
chordData.addDataNoRoot(nnpitches);
} else {
chordData.addData(root, nnpitches);
}
}
char buffer[128] = {0};
if (rawQ) {
cout << CurrentFile << "\t";
if (beatQ) {
cout << infile.getAbsBeat(startline) << "\t";
} else {
cout << startline+1 << "\t";
}
cout << Convert::base40ToKern(buffer, root+3*40) << "\t";
for (i=0; i<nnpitches.getSize(); i++) {
// cout << Convert::base40ToKern(buffer, nnpitches[i]+4*40);
if (intervalQ) {
cout << Convert::base40ToIntervalAbbr(buffer, (nnpitches[i] -2 + 400) %40);
} else {
cout << Convert::base40ToKern(buffer, nnpitches[i] + 4*40);
}
if (i < nnpitches.getSize() - 1) {
cout << " ";
}
}
cout << "\n";
}
}
//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//
void checkOptions(Options& opts, int argc, char* argv[]) {
opts.define("s|sort=s:n", "sorting method");
opts.define("r|raw=b", "raw chord data only");
opts.define("b|beat=b", "display beat position instead of line no.");
opts.define("t|transpose=b", "transpose pitch class root to c");
opts.define("R|rootless=b", "do not display chord root spine");
opts.define("i|interval=b", "display root inteval abbreviations");
opts.define("u|unique|uniq=b", "unique pitch class only");
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, July 2002" << endl;
exit(0);
} else if (opts.getBoolean("version")) {
cout << argv[0] << ", version: Dec 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);
}
debugQ = opts.getBoolean("debug");
sortType = tolower(opts.getString("sort")[0]);
uniqQ = opts.getBoolean("unique");
beatQ = opts.getBoolean("beat");
rawQ = opts.getBoolean("raw");
transposeQ = opts.getBoolean("transpose");
intervalQ = opts.getBoolean("interval");
rootlessQ = opts.getBoolean("rootless");
if (intervalQ) {
transposeQ = 1;
}
}
//////////////////////////////
//
// 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;
}
//////////////////////////////
//
// arrayEqual -- return true all of the array elements are equal
// in the same positions in each array.
//
template<class type>
int arrayEqual(Array& a, Array& b) {
if (a.getSize() != b.getSize()) {
return 0;
}
int i;
for (i=0; i<a.getSize(); i++) {
if (a[i] != b[i]) {
return 0;
}
}
return 1;
}
//////////////////////////////
//
// sortByCount -- sort the chord data by occurance count.
//
void sortByCount(ChordData& chordData) {
qsort(chordData.getBase(), chordData.getSize(), sizeof(ChordEntry),
countcompare);
}
//////////////////////////////
//
// sortByRoot -- sort the chord data by root
//
void sortByRoot(ChordData& chordData) {
qsort(chordData.getBase(), chordData.getSize(), sizeof(ChordEntry),
root2compare);
}
//////////////////////////////
//
// sortByPitch -- sort the chord data by pitch class
//
void sortByPitch(ChordData& chordData) {
qsort(chordData.getBase(), chordData.getSize(), sizeof(ChordEntry),
pitchcompare);
}
//////////////////////////////
//
// root2compare --
//
int root2compare(const void* a, const void* b) {
ChordEntry& A = *((ChordEntry*)a);
ChordEntry& B = *((ChordEntry*)b);
if (A.root < B.root) {
return -1;
} else if (A.root > B.root) {
return 1;
} else {
return 0;
}
}
//////////////////////////////
//
// countcompare --
//
int countcompare(const void* a, const void* b) {
ChordEntry& A = *((ChordEntry*)a);
ChordEntry& B = *((ChordEntry*)b);
if (A.count < B.count) {
return 1;
} else if (A.count > B.count) {
return -1;
} else {
return 0;
}
}
//////////////////////////////
//
// picthcompare --
//
int pitchcompare(const void* a, const void* b) {
ChordEntry& A = *((ChordEntry*)a);
ChordEntry& B = *((ChordEntry*)b);
Array<int> x(A.pitches.getSize());
Array<int> y(A.pitches.getSize());
x.setSize(0);
y.setSize(0);
int i;
x.append(A.pitches[0]);
for (i=1; i<A.pitches.getSize(); i++) {
if (A.pitches[i] != A.pitches[i-1]) {
x.append(A.pitches[i]);
}
}
y.append(B.pitches[0]);
for (i=1; i<B.pitches.getSize(); i++) {
if (B.pitches[i] != B.pitches[i-1]) {
y.append(B.pitches[i]);
}
}
int size = 0;
if (x.getSize() < y.getSize()) {
size = x.getSize();
} else if (y.getSize() < x.getSize()) {
size = y.getSize();
} else {
size = x.getSize(); // equal sizes
}
for (i=0; i<size; i++) {
if (x[i] < y[i]) {
return -1;
} else if (x[i] > y[i]) {
return 1;
}
}
if (x.getSize() < y.getSize()) {
return -1;
} else if (x.getSize() > y.getSize()) {
return 1;
} else {
return 0;
}
}
//////////////////////////////
//
// rootcompare -- compare two roots for ordering
//
int rootcompare(const void* a, const void* b) {
if (*((int*)a) < *((int*)b)) {
return -1;
} else if (*((int*)a) > *((int*)b)) {
return 1;
} else {
return 0;
}
}
// md5sum: 6150564e474ef5f95c45d7468adb491a chordset.cpp [20090626]