//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Thu Jan 15 17:34:02 PST 2009
// Last Modified: Sun Jan 18 21:14:59 PST 2009
// Filename: ...sig/doc/examples/improv/synthImprov/jazzchord.cpp
// Syntax: C++; synthImprov 2.0
//
// Description: Chord recognition.
//
#include "humdrum.h"
#include "synthImprov.h" // includes the default Win95 console interface
// for the synthImprov environment
#ifndef OLDCPP
#include <iostream>
#include <sstream>
#define SSTREAM stringstream
#define CSTRING str().c_str()
using namespace std;
#else
#include <iostream.h>
#ifdef VISUAL
#include <strstrea.h>
#else
#include <strstream.h>
#endif
#define SSTREAM strstream
#define CSTRING str()
#endif
Array<int> keystates;
Array<int> currentchord;
Array<int> tempchord;
Array<Array<char> > names;
Array<Array<int> > chordset;
MidiMessage message;
long lastnotetime = 0;
long timedelta = 60;
char lastprintchord[1024] = {0};
// function declarations:
void getNewChordInfo(Array<int>& tempchord, Array<int>& keystates);
int equalChord(Array<int>& tempchord, Array<int>& currentchord);
void storeChordData(Array<int>& chordinfo, HumdrumFile& infile,
int index);
void prepareChordSet(Array<Array<char> >& names,
Array<Array<int> >& chordset);
int compareChords(int* chorda, int* chordb, int count);
void printNewChord(Array<int>& tempchord,
Array<Array<int> >& chordset,
Array<Array<char> >& names);
//////////////////////////////
//
// printNewChord --
//
void printNewChord(Array<int>& tempchord, Array<Array<int> >& chordset,
Array<Array<char> >& names) {
SSTREAM chordstream;
int i, j, k;
int length;
Array<int> testset(24);
testset.allowGrowth(0);
for (i=0; i<12; i++) {
testset[i] = tempchord[i];
testset[i+12] = tempchord[i];
}
int count = 0;
int equal;
char buffer[1024] = {0};
for (i=0; i<chordset.getSize(); i++) {
for (j=0; j<12; j++) {
equal = compareChords(chordset[i].getBase(), testset.getBase()+j, 12);
if (equal) {
if (count > 0) {
chordstream << " or ";
}
count++;
Convert::base12ToKern(buffer, j+4*12);
length = strlen(buffer);
for (k=0; k<length; k++) {
if (buffer[k] == '-') {
buffer[k] = 'b';
}
}
if (strcmp(buffer, "G#") == 0) {
strcpy(buffer, "Ab");
}
chordstream << buffer << names[i].getBase();
}
}
}
if (count > 0) {
chordstream << ends;
strcpy(lastprintchord, chordstream.CSTRING);
cout << lastprintchord;
cout << endl;
}
}
/////////////////////////////
//
// compareChords --
//
int compareChords(int* chorda, int* chordb, int count) {
int i;
for (i=0; i<count; i++) {
if (chorda[i] != chordb[i]) {
return 0;
}
}
return 1;
}
//////////////////////////////
//
// getNewChordInfo --
//
void getNewChordInfo(Array& tempchord, Array& keystates) {
int i;
tempchord.setAll(0);
for (i=0; i<keystates.getSize(); i++) {
if (keystates[i] != 0) {
tempchord[i%12] = 1;
}
}
}
//////////////////////////////
//
// equalChord --
//
int equalChord(Array& tempchord, Array& currentchord) {
int i;
for (i=0; i<tempchord.getSize(); i++) {
if (currentchord[i] != tempchord[i]) {
return 0;
}
}
return 1;
}
//////////////////////////////
//
// printNewChord --
//
void printNewChord(Array& chord) {
int i;
cout << "[";
for (i=0; i<chord.getSize(); i++) {
cout << chord[i];
if (i < 12-1) {
cout << " ";
}
}
cout << "]" << endl;
}
//////////////////////////////
//
// prepareChordSet --
//
void prepareChordSet(Array >& names, Array >& chordset) {
SSTREAM stream;
stream << "!! Column 1: Root of the chord\n";
stream << "!! Column 2: Other notes in the chord\n";
stream << "!! Column 3: Notated chord symbol\n";
stream << "!! Column 4: Full chord name\n";
stream << "!! substitutes with pitch in first column\n";
stream << "**kern\t**kern\t**label\t**name\n";
stream << "!root\t!others\t!\t!\n";
stream << "4C\t4e 4g\t.\tMajor\n";
stream << "4C\t4e- 4g\tm\tMinor\n";
stream << "4C\t4e 4g#\t+\tAugmented Triad\n";
stream << "4C\t4f 4g\tsus4\tSuspended 4th\n";
stream << "4C\t4e- 4g-\to\tDiminished Triad\n";
stream << "4C\t4e 4g 4a\t6\tMajor 6th\n";
stream << "4C\t4e- 4g 4a\tm6\tMinor 6th\n";
stream << "4C\t4e 4g 4b\tmaj7\tMajor 7th\n";
stream << "4C\t4e- 4g 4b-\tm7\tMinor 7th\n";
stream << "4C\t4e 4g 4b-\t7\tDominant 7th\n";
stream << "4C\t4f 4g 4b-\t7sus4\tDominant 7th sus4\n";
stream << "4C\t4e 4g# 4b-\t7+5\tDominant 7th Augmented 5th\n";
stream << "4C\t4e 4g- 4b-\t7-5\tDominant 7th Flattened 5th\n";
stream << "4C\t4e- 4g- 4b--\tdim7\tDiminished 7th\n";
stream << "4C\t4e- 4g- 4b-\tm7-5\tMinor 7th Flattened 5th\n";
stream << "4C\t4e- 4g 4b\tmmaj7\tMinor-Major 7th\n";
stream << "4C\t4e 4g 4b 4dd\tmaj9\tMajor 9th\n";
stream << "4C\t4e- 4g 4b- 4dd\tm9\tMinor 9th\n";
stream << "4C\t4e 4g 4b- 4dd\t9\tDominant 9th\n";
stream << "4C\t4e 4g# 4b- 4dd\t9+5\t9th Augmented 5th\n";
stream << "4C\t4e 4g- 4b- 4dd\t9-5\t9th Flattened 5th\n";
stream << "4C\t4e 4g 4a 4b- 4dd\t9/6\t9th Add 6th\n";
stream << "4C\t4e 4g 4b 4dd 4ff\tmaj11\tMajor 11th\n";
stream << "4C\t4e- 4g 4b- 4dd 4ff\tm11\tMinor 11th\n";
stream << "4C\t4e 4g 4b- 4dd 4ff\t11\tDominant 11th\n";
stream << "4C\t4e 4g 4b- 4dd- 4ff\t11-9\t11th Flattened 9th\n";
stream << "4C\t4e 4g 4b 4dd 4ff 4aa\tmaj13\tMajor 13th\n";
stream << "4C\t4e- 4g 4b- 4dd 4ff 4aa\tm13\tMinor 13th\n";
stream << "4C\t4e 4g 4b- 4dd 4ff 4aa\t13\tDominant 13th\n";
stream << "4C\t4e 4g 4b- 4dd- 4ff 4aa\t13-9\t13th Flattened 9th\n";
stream << "*-\t*-\t*-\t*-\n";
stream << ends;
HumdrumFile infile;
infile.read(stream);
chordset.setSize(infile.getNumLines());
chordset.setSize(0);
names.setSize(infile.getNumLines());
names.setSize(0);
int index;
int length;
int i;
for (i=0; i<infile.getNumLines(); i++) {
if (infile[i].getType() != E_humrec_data) {
continue;
}
index = chordset.getSize();
chordset.setSize(chordset.getSize() + 1);
names.setSize(names.getSize() + 1);
length = strlen(infile[i][2]);
names[index].setSize(length+1);
strcpy(names[index].getBase(), infile[i][2]);
if (names[index][0] == '.') {
names[index][0] = '\0';
}
storeChordData(chordset[index], infile, i);
}
chordset.allowGrowth(0);
}
//////////////////////////////
//
// storeChordData --
//
void storeChordData(Array& chordinfo, HumdrumFile& infile, int index) {
int i;
int note;
chordinfo.setSize(12);
chordinfo.allowGrowth(0);
chordinfo.setAll(0);
note = Convert::kernToMidiNoteNumber(infile[index][0]) % 12;
if (note != 0) {
cout << "Error reading chord: " << infile[index] << endl;
}
chordinfo[0] = 1;
char buffer[1024] = {0};
int count = infile[index].getTokenCount(1);
for (i=0; i<count; i++) {
infile[index].getToken(buffer, 1, i, 1000);
note = Convert::kernToMidiNoteNumber(buffer) % 12;
if (note < 0) {
continue;
}
chordinfo[note] = 1;
}
}
/*----------------- beginning of improvization algorithms ---------------*/
/*--------------------- maintenance algorithms --------------------------*/
//////////////////////////////
//
// description -- this function is called by the improv interface
// whenever a capital "D" is pressed on the computer keyboard.
// Put a description of the program and how to use it here.
//
void description(void) {
printboxtop();
psl("Identify chords played on the piano");
psl("");
psl(" space = mark last chord");
psl(" enter = add a blank line to display");
printboxbottom();
}
//////////////////////////////
//
// initialization -- this function is called by the improv
// interface once at the start of the program. Put items
// here which need to be initialized at the beginning of
// the program.
//
void initialization(void) {
keystates.setSize(128);
keystates.allowGrowth(0);
keystates.setAll(0);
currentchord.setSize(12);
currentchord.allowGrowth(0);
currentchord.setAll(0);
tempchord.setSize(12);
tempchord.allowGrowth(0);
tempchord.setAll(0);
prepareChordSet(names, chordset);
lastnotetime = 0;
}
//////////////////////////////
//
// finishup -- this function is called by the improv interface
// whenever the program is exited. Put items here which
// need to be taken care of when the program is finished.
//
void finishup(void) { }
/*-------------------- main loop algorithms -----------------------------*/
//////////////////////////////
//
// mainloopalgorithms -- this function is called by the improv interface
// continuously while the program is running. The global variable t_time
// which stores the current time is set just before this function is
// called and remains constant while in this functions.
//
void mainloopalgorithms(void) {
if (synth.getNoteCount() > 0) {
while (synth.getNoteCount() > 0) {
message = synth.extractNote();
if ((message.p2() == 0) || ((message.p0() & 0xf0) == 0x80)) {
keystates[message.p1()] = 0;
} else if (message.p2() != 0) {
keystates[message.p1()] = 1;
}
}
getNewChordInfo(tempchord, keystates);
lastnotetime = t_time;
}
if (t_time > lastnotetime + timedelta) {
if (!equalChord(tempchord, currentchord)) {
printNewChord(tempchord, chordset, names);
currentchord = tempchord;
}
}
}
/*-------------------- triggered algorithms -----------------------------*/
///////////////////////////////
//
// keyboardchar -- this function is called by the improv interface
// whenever a key is pressed on the computer keyboard.
// Put commands here which will be executed when a key is
// pressed on the computer keyboard.
//
void keyboardchar(int key) {
switch (key) {
case '\n':
cout << endl;
break;
case ' ':
if (strcmp(lastprintchord, "") != 0) {
cout << "\t\t\t" << lastprintchord << endl;
}
}
}
/*------------------ end improvization algorithms -----------------------*/
// md5sum: 163c49a5ec5e0bd41298728add43c609 jazzchord.cpp [20090129]