//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon Dec 30 16:44:21 PST 2002
// Last Modified: Wed Jan 1 11:05:56 PST 2003
// Filename: ...sig/doc/examples/improv/synthImprov/rp.cpp
// Syntax: C++; synthImprov 2.0
//
// Description: Test performance program for "Responsoria prolixa"
//
#include "synthImprov.h"
#include <string.h>
class NoteData { public: double dur; double start; int voice; int vel; };
typedef Array<Array<Array<NoteData> > > NoteScore;
// function declarations:
int midilimit(int input);
void readScore(NoteScore& score);
void printScore(NoteScore& score);
void readScoreFile(const char* filename, Array<NoteData>& data);
void playNextPattern(EventBuffer& eventbuffer, NoteScore& score,
int pattern, int subpattern, double tempo);
// global variables:
double tempo = 80.0; // tempo for the performance
int nextpattern = 1; // next pattern group to play
int nextsubpattern = 0; // next subpattern to play in group
int nextpatterntime = 0; // time to play the next (sub)pattern
int patternbeats = 0; // number of beats in pattern
EventBuffer eventbuffer; // storage for notes to play in pattern
Array<int> voicetokey; // MIDI note number for each voice
NoteScore rpscore; // Score for reading during performance
Array<int> activevoice; // used to turn voices on/off
//////////////////////////////////////////////////////////////////////////
void description(void) {
cout << "Test performance program for Responsoria prolixa" << endl;
cout << "Type 0-6 to control pattern level " << endl;
cout << " a-d to control pattern sublevel " << endl;
cout << " [ to slow tempo, and ] to speed up tempo " << endl;
cout << " space to restart cycle " << endl;
cout << " q, w, e, r, t and then - or + for timbre " << endl;
cout << " y, u, i, o, p to turn voices on/off " << endl;
cout << " / to print score " << endl;
}
void initialization(void) {
eventbuffer.setPort(synth.getInputPort());
nextpatterntime = t_time;
voicetokey.setSize(5);
readScore(rpscore);
voicetokey[0] = GM_CLAVES;
voicetokey[1] = GM_COWBELL;
voicetokey[2] = GM_HIGH_AGOGO;
voicetokey[3] = GM_LOW_AGOGO;
voicetokey[4] = GM_HI_BONGO;
activevoice.setSize(5);
activevoice.setAll(1);
}
void finishup(void) { }
void mainloopalgorithms(void) {
eventbuffer.checkPoll();
if (nextpatterntime <= t_time) {
patternbeats = nextpattern + 2;
if (nextpattern == 0) {
patternbeats = 3;
}
nextpatterntime = t_time + (int)(60.0/tempo*1000*patternbeats + 0.5);
playNextPattern(eventbuffer, rpscore, nextpattern, nextsubpattern, tempo);
}
}
void keyboardchar(int key) {
static int controlvoice = 0;
switch (key) {
case '0': nextpattern = 0; break; // select level 0
case '1': nextpattern = 1; break; // select level 1
case '2': nextpattern = 2; break; // select level 2
case '3': nextpattern = 3; break; // select level 3
case '4': nextpattern = 4; break; // select level 4
case '5': nextpattern = 5; break; // select level 5
case '6': nextpattern = 6; break; // select level 6 (cadences)
case 'a': nextsubpattern = 0; break; // select subgroup a
case 'b': nextsubpattern = 1; break; // select subgroup b
case 'c': nextsubpattern = 2; break; // select subgroup c
case 'd': nextsubpattern = 3; break; // select subgroup d
case '/': printScore(rpscore); break; // for debugging
case '[': // slow down the tempo
tempo *= 0.99;
cout << "Tempo = " << tempo << endl;
break;
case ']': // speed up the tempo
tempo *= 1.01;
cout << "Tempo = " << tempo << endl;
break;
case '-': // lower the key number for the given voice
voicetokey[controlvoice]--;
voicetokey[controlvoice] = midilimit(voicetokey[controlvoice]);
cout << "Voice: " << controlvoice << "\tkey: "
<< voicetokey[controlvoice] << endl;
break;
case '+': // raise the key number for the given voice
case '=': // same as '+' without the shift key
voicetokey[controlvoice]++;
voicetokey[controlvoice] = midilimit(voicetokey[controlvoice]);
cout << "Voice: " << controlvoice << "\tkey: "
<< voicetokey[controlvoice] << endl;
break;
case ' ': // restart the pattern cycle
nextpatterntime = t_time;
break;
case 'q': controlvoice = 0; break; // control timber of voice 0 with -/=
case 'w': controlvoice = 1; break; // control timber of voice 1 with -/=
case 'e': controlvoice = 2; break; // control timber of voice 2 with -/=
case 'r': controlvoice = 3; break; // control timber of voice 3 with -/=
case 't': controlvoice = 4; break; // control timber of voice 4 with -/=
case 'y': // turn voice 0 on/off
activevoice[0] = !activevoice[0];
cout << "Voice 0 = " << activevoice[0] << endl;
break;
case 'u': // turn voice 1 on/off
activevoice[1] = !activevoice[1];
cout << "Voice 1 = " << activevoice[1] << endl;
break;
case 'i': // turn voice 2 on/off
activevoice[2] = !activevoice[2];
cout << "Voice 2 = " << activevoice[2] << endl;
break;
case 'o': // turn voice 3 on/off
activevoice[3] = !activevoice[3];
cout << "Voice 3 = " << activevoice[3] << endl;
break;
case 'p': // turn voice 4 on/off
activevoice[4] = !activevoice[4];
cout << "Voice 4 = " << activevoice[4] << endl;
break;
}
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// midilimit -- limit an integer into the range from 0 to 127.
//
int midilimit(int input) {
if (input < 0) {
return 0;
} else if (input > 127) {
return 127;
} else {
return input;
}
}
//////////////////////////////
//
// playNextPattern -- prepare the next measure of music.
//
void playNextPattern(EventBuffer& eventbuffer, NoteScore& score,
int pattern, int subpattern, double tempo) {
if (pattern == 0 && subpattern == 3) {
subpattern = 2;
}
if (pattern == 6 && subpattern == 3) {
subpattern = 2;
}
int p = pattern;
int sp = subpattern;
cout << "playing pattern " << pattern << (char)('a' + subpattern) << endl;
NoteEvent note; // temporary note for placing in buffer
int i;
for (i=0; i<score[pattern][subpattern].getSize(); i++) {
if (!activevoice[score[pattern][subpattern][i].voice]) {
continue;
}
note.setOnDur((int)(t_time + score[p][sp][i].start * 1000 * 60 / tempo +
0.5), (int)(score[p][sp][i].dur * 1000 * 60 / tempo + 0.5));
note.setChan(CH_10);
note.setKey(voicetokey[score[p][sp][i].voice]);
note.setVel(score[p][sp][i].vel);
note.activate();
if (score[p][sp][i].start == 0.0) {
note.action(eventbuffer); // play starting notes now, avoiding delay
}
eventbuffer.insert(note);
}
}
//////////////////////////////
//
// readScore -- read in the Responsoria Prolixa score
//
void readScore(NoteScore& score) {
score.setSize(7);
score[0].setSize(3); score[1].setSize(4);
score[2].setSize(4); score[3].setSize(4);
score[4].setSize(4); score[5].setSize(4);
score[6].setSize(3);
Array<NoteData> data;
data.setSize(100);
data.setGrowth(100);
data.setSize(0);
readScoreFile("rp0a.txt", data); score[0][0] = data;
readScoreFile("rp0b.txt", data); score[0][1] = data;
readScoreFile("rp0c.txt", data); score[0][2] = data;
readScoreFile("rp1a.txt", data); score[1][0] = data;
readScoreFile("rp1b.txt", data); score[1][1] = data;
readScoreFile("rp1c.txt", data); score[1][2] = data;
readScoreFile("rp1d.txt", data); score[1][3] = data;
readScoreFile("rp2a.txt", data); score[2][0] = data;
readScoreFile("rp2b.txt", data); score[2][1] = data;
readScoreFile("rp2c.txt", data); score[2][2] = data;
readScoreFile("rp2d.txt", data); score[2][3] = data;
readScoreFile("rp3a.txt", data); score[3][0] = data;
readScoreFile("rp3b.txt", data); score[3][1] = data;
readScoreFile("rp3c.txt", data); score[3][2] = data;
readScoreFile("rp3d.txt", data); score[3][3] = data;
readScoreFile("rp4a.txt", data); score[4][0] = data;
readScoreFile("rp4b.txt", data); score[4][1] = data;
readScoreFile("rp4c.txt", data); score[4][2] = data;
readScoreFile("rp4d.txt", data); score[4][3] = data;
readScoreFile("rp5a.txt", data); score[5][0] = data;
readScoreFile("rp5b.txt", data); score[5][1] = data;
readScoreFile("rp5c.txt", data); score[5][2] = data;
readScoreFile("rp5d.txt", data); score[5][3] = data;
readScoreFile("rp6a.txt", data); score[6][0] = data;
readScoreFile("rp6a.txt", data); score[6][1] = data;
readScoreFile("rp6a.txt", data); score[6][2] = data;
}
//////////////////////////////
//
// readscorefile -- read a score file.
//
/* Here is a sample score file. * ordering of data:
* note start-beat duration gm-channel10-key velocity
; Leader
note 0.0 0.5 0 84
note 3.0 1.0 1 64
; Left
note 2.5 0.5 2 64
note 3.5 0.5 2 64
note 4.0 0.75 2 64
*/
void readScoreFile(const char* filename, Array& data) {
data.setSize(0);
NoteData n;
#ifndef OLDCPP
ifstream input(filename, ios::in);
#else
ifstream input(filename, ios::in | ios::nocreate);
#endif
if (!input.is_open()) {
cout << "Error: could not read file: " << filename << endl;
exit(1);
}
char buffer[1024] = {0};
int count;
while (!input.eof()) {
input.getline(buffer, 1000, '\n');
if (strncmp(buffer, "note\t", 5) == 0) {
count = sscanf(buffer, "note\t%lf\t%lf\t%d\t%d", &n.start, &n.dur,
&n.voice, &n.vel);
if (count == 4) {
data.append(n);
}
}
}
}
//////////////////////////////
//
// printScore -- print the input score (for debugging purposes).
//
void printScore(NoteScore& score) {
int level;
int sublevel;
int note;
for (level=0; level<score.getSize(); level++) {
for (sublevel=0; sublevel<score[level].getSize(); sublevel++) {
for (note=0; note<score[level][sublevel].getSize(); note++) {
cout << level << (char)('a' + sublevel) << "\t"
<< score[level][sublevel][note].start << "\t"
<< score[level][sublevel][note].dur << "\t"
<< score[level][sublevel][note].voice << "\t"
<< score[level][sublevel][note].vel << "\n";
}
}
}
}
// md5sum: bba113ece30546dbbee31665fc6bbf9a rp.cpp [20050403]