//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sat Apr 22 17:12:00 PDT 2000
// Last Modified: Mon Apr 24 14:31:56 PDT 2000
// Filename: ...sig/doc/examples/all/trill/trill.cpp
// Syntax: C++; synthImprov 2.0
//
// Description: a trill is started when two notes occur within
// 1 second of each other. The trill is stopped
// when a note is played inside or on the boundary
// of the trill (for minor 2nd, within one note outside
// the range of the trill.
//
// There are several control keys on the keyboard.
// If a control key is hit harder, then its effect
// will occur at a faster rate.
// A0 = turn off all current trills
// A7 = make all trills quieter
// A#7 = make all trills louder
// B7 = make all trills faster
// C8 = make all trills slower
//
#include "synthImprov.h"
#define TRIGTIME 1000 /* maximum time between trill notes */
#define MINTRIGTIME 75 /* minimum time between trill notes */
/*----------------- beginning of improvization algorithms ---------------*/
EventBuffer eventBuffer; // used to store algorithms for performance
CircularBuffer<int> notetimes(100); // history of the last 100 note-on times
Array<int> noteontimes(128); // list of the last time a note was played
CircularBuffer<int> notes(100); // history of the pitches played
CircularBuffer<int> trills(100); // history of which notes create a trill
int trillcorrection = 0; // global speed control of trills
int velcorrection = 0; // golbal velocity control of trills
// function declarations:
void sillyKeyboard(int key, int chan = 0);
void createTrill(int key1, int key2, int velocity, int channel, int duration);
void processNote(MidiMessage message);
/*--------------------- Event Algorithms --------------------------------*/
//////////////////////////////
//
// TrillAlgorithm -- Play the next note in the trill, adjusting
// for the global control of
//
// Global variables needed by this function:
// notetimes : The time that keys are pressed on the keyboard
// trillcorrection : adjustment to the trill speed
// velcorrection : adjustment to the attack velocity
//
// Local variables:
// charValue(14) = upper key number of trill
// charValue(15) = current note to play
// intValue(10) = start time of trill
//
static void TrillAlgorithm(FunctionEvent& p, EventBuffer& midiOutput) {
static NoteEvent note; // temporary note before placing in buffer
int key1 = p.getKey(); // lower key of trill
int key2 = p.charValue(14); // upper key of trill
int state = p.charValue(15); // which note to play next
int starttime = p.intValue(10); // when trill was started
int i;
// turn off the trill if there is a note played inside the trill
int range1 = key1;
int range2 = key2;
if (range2 - range1 == 1) {
range1--;
range2++;
}
for (i=range1; i<=range2; i++) {
if (noteontimes[i] > starttime) {
p.off(midiOutput);
return;
}
}
// set the next note to play
int key = state ? key2 : key1;
state = !state;
p.charValue(15) = state;
// set the parameters for the output note:
note.setOnDur(t_time, p.getDur());
note.setVel(p.getVel());
note.setChan(p.getChan());
note.setKey(key);
// update the parameters for the gliss function:
p.setOnTime(p.getOnTime() + p.getDur());
int value = p.getVel() + velcorrection;
if (value < 100 && value > 3) {
p.setVel(value);
}
if (p.getDur() + trillcorrection > MINTRIGTIME) {
p.setDur(p.getDur() + trillcorrection);
}
note.activate();
note.action(midiOutput); // start right now, avoiding any buffer delay
midiOutput.insert(note); // the note off message is being buffered
}
/*--------------------- maintenance algorithms --------------------------*/
void description(void) {
printboxtop();
psl(
" TRILL -- by Craig Stuart Sapp <craig@ccrma.stanford.edu> -- 22 April 2000");
psl("");
printboxbottom();
}
void initialization(void) {
eventBuffer.setPollPeriod(10);
notetimes.reset();
notetimes.insert(0);
notetimes.insert(0);
notes.reset();
notes.insert(0);
notes.insert(0);
noteontimes.zero();
}
void finishup(void) { }
/*-------------------- main loop algorithms -----------------------------*/
void mainloopalgorithms(void) {
eventBuffer.checkPoll(); // see if any notes need playing
while (synth.getNoteCount() > 0) {
processNote(synth.extractNote());
}
}
////////////////////////////
//
// processNote --
//
void processNote(MidiMessage message) {
int key = message.p1();
int velocity = message.p2();
int channel = message.p0() & 0x0f;
int status = 1;
if (message.p0() - channel == 0x80 || velocity == 0) {
status = 0;
}
if (status == 0) {
if (key == C8 || key == B7) {
trillcorrection = 0;
}
if (key == As7 || key == A7) {
velcorrection = 0;
}
return;
}
if (key == C8) {
trillcorrection = velocity / 10;
return;
}
if (key == B7) {
trillcorrection = -(velocity / 10);
return;
}
if (key == As7) {
velcorrection = +(velocity / 10);
return;
}
if (key == A7) {
velcorrection = -(velocity / 10);
return;
}
if (key == A0) {
for (int j=0; j<128; j++) {
noteontimes[j] = t_time;
}
return;
}
if (key == A0) {
for (int j=0; j<128; j++) {
noteontimes[j] = t_time;
}
return;
}
noteontimes[key] = t_time;
notetimes.insert(message.time);
notes.insert(key);
if (notes[1] == 0) {
trills.insert(0);
return;
}
if (notes[2] != 0) {
if (trills[0] == 1 && notetimes[1] - notetimes[2] < TRIGTIME) {
trills.insert(0);
return;
}
}
trills.insert(1);
int duration = notetimes[0] - notetimes[1];
if (duration < TRIGTIME && duration > MINTRIGTIME &&
notes[0] - notes[1] != 0) {
createTrill(key, notes[1], velocity, channel, duration);
}
}
////////////////////////////
//
// createTrill -- set up the parameters for a trill
//
void createTrill(int key1, int key2, int velocity, int channel, int duration) {
static FunctionEvent tn; // a Temporary Note for copying into eventBuffer
// key1 should always be smaller than key2
int temp;
if (key1 > key2) {
temp = key1;
key1 = key2;
key2 = temp;
}
// setting the fields of the function note
tn.setFunction(TrillAlgorithm);
tn.setChannel(channel);
tn.setKeyno(key1);
tn.setVelocity(velocity);
// set extra parameters
tn.charValue(15) = 0; // 0 = play key1 next, 1 = play key2 next
tn.charValue(14) = key2; // secondary pitch
tn.intValue(10) = t_time; // initialization time
tn.setStatus(EVENT_STATUS_ACTIVE);
// start time of function and the duration between calling it
tn.setOnDur(t_time + duration, duration);
eventBuffer.insert(tn);
cout << "Trill = " << key1 << " to " << key2
<< "\tRate = " << duration
<< endl;
}
/*-------------------- triggered algorithms -----------------------------*/
void keyboardchar(int key) {
switch (key) {
case 'p':
cout << "current list in eventBuffer: " << endl;
eventBuffer.print();
cout << endl;
cout << "Event[0] status: " << eventBuffer[0].getStatus() << endl;
break;
default:
sillyKeyboard(key);
}
}
void sillyKeyboard(int key, int chan /* = 0 */) {
static int octave = 4;
static int newkey = 0;
static Voice voice;
static MidiMessage message;
// check to see if adjusting the octave:
if (isdigit(key)) {
octave = key - '0';
return;
}
switch (key) {
case 'z': newkey = 12 * octave + 0; break; // C
case 's': newkey = 12 * octave + 1; break; // C#
case 'x': newkey = 12 * octave + 2; break; // D
case 'd': newkey = 12 * octave + 3; break; // D#
case 'c': newkey = 12 * octave + 4; break; // E
case 'v': newkey = 12 * octave + 5; break; // F
case 'g': newkey = 12 * octave + 6; break; // F#
case 'b': newkey = 12 * octave + 7; break; // G
case 'h': newkey = 12 * octave + 8; break; // G#
case 'n': newkey = 12 * octave + 9; break; // A
case 'j': newkey = 12 * octave + 10; break; // A#
case 'm': newkey = 12 * octave + 11; break; // B
case ',': newkey = 12 * octave + 12; break; // C
case 'l': newkey = 12 * octave + 12; break; // C#
case '.': newkey = 12 * octave + 12; break; // D
case '\'': newkey = 12 * octave + 12; break; // D#
case '/': newkey = 12 * octave + 12; break; // E
default: return; // don't do anything if not a key
}
// prevent any invalid key numbers:
if (newkey < 0) {
newkey = 0;
} else if (newkey > 127) {
newkey = 127;
}
// put note-off message in synth's input buffer:
message.time = t_time;
message.p0() = 0x90 | voice.getChan();
message.p1() = voice.getKey();
message.p2() = 0;
synth.insert(message);
// turn off the last note:
voice.off();
// set parameters for next note-on:
voice.setChan(chan & 0x0f); // limit channel to range from 0 to 15
voice.setVel(rand() % 127 +1); // random attack in range from 1 to 127
voice.setKey(newkey); // use the newly selected key number
// play the MIDI note:
voice.play();
// insert the played note into synth's input MIDI buffer:
message.command() = 0x90 | voice.getChan();
message.p1() = voice.getKey();
message.p2() = voice.getVel();
synth.insert(message);
}
/*------------------ end improvization algorithms -----------------------*/
// md5sum: 967878f0601a855737b12812bbc08313 trill.cpp [20050403]