//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sat Apr 22 16:04:00 PDT 2000
// Last Modified: Sat Apr 22 16:04:04 PDT 2000
// Filename: ...sig/doc/examples/all/decay/decay.cpp
// Syntax: C++; synthImprov 2.0
//
//
#include "synthImprov.h"
/*----------------- beginning of improvization algorithms ---------------*/
EventBuffer eventBuffer; // for future notes
MidiMessage message; // for reading keyno and velocity (and time)
Array<int> notestates(128); // for keeping track of note on/off
Array<double> decaystates(128); // for keeping track of note on/off
Array<int> onvels(128); // for keeping track of note on/off
int gap = 100; // spacing between notes (millisec)
double decayrate = 0.87; // echo decay rate.
// function declarations:
void sillyKeyboard(int key, int chan = 0);
void processNote(MidiMessage& message);
void createDecay(int channel, int key, int duration, int velocity);
/*--------------------- Event Algorithms --------------------------------*/
//////////////////////////////
//
// EchoAlgorithm --
//
// Global variables needed by this function:
// decaystates
//
// Local variables needed by this function:
// 14 short = gap between notes
//
static void EchoAlgorithm(FunctionEvent& p, EventBuffer& midiOutput) {
static NoteEvent note; // temporary note before placing in buffer
// check if pausing
if (decaystates[p.getKey()] < 0.0) {
p.setOnTime(p.getOnTime() + p.getDur() + p.shortValue(14));
return;
}
// set the parameters for the output note:
note.setOnDur(t_time, p.getOffTime()); // off time holds dur
note.setVel(p.getVel());
note.setChan(p.getChan());
note.setKey(p.getKey());
// update the parameters for the function:
decaystates[p.getKey()] *= decayrate;
p.setVel((int)decaystates[p.getKey()]);
// if note is too quiet, end the note
if (p.getVel() <= 2) {
p.off(midiOutput);
decaystates[p.getKey()] = 0.0;
}
// next time includes a gap so that key can raise on keyboard
p.setOnTime(p.getOnTime() + p.getDur() + p.shortValue(14));
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(
"DECAY -- by Craig Stuart Sapp <craig@ccrma.stanford.edu> -- 22 April 2000");
psl("");
printintermediateline();
printboxbottom();
}
void initialization(void) {
eventBuffer.setPollPeriod(10);
for (int i=0; i<notestates.getSize(); i++) {
notestates[i] = 0;
onvels[i] = 0;
decaystates[i] = 0.0;
}
}
void finishup(void) { }
/*-------------------- main loop algorithms -----------------------------*/
void mainloopalgorithms(void) {
if (eventBuffer.checkPoll()); // see if any notes to play
while (synth.getNoteCount() > 0) {
message = synth.extractNote();
processNote(message);
}
}
////////////////////////////
//
// processNote -- decide when to trigger a decay algorithm.
//
void processNote(MidiMessage& message) {
int velocity = message.p2();
int key = message.p1();
int channel = message.p0() & 0x0f;
int state = 1;
int duration = 0;
if (((message.p0() & 0xf0) == 0x80) || velocity == 0) {
state = 0;
}
if (key == A0) {
cout << "A0 Triggered" << endl;
for (int i=0; i<decaystates.getSize(); i++) {
decaystates[i] *= -1.0;
}
return;
}
if (state == 1) {
notestates[key] = message.time;
onvels[key] = velocity;
} else {
if (notestates[key] == 0) {
// do nothing
} else {
duration = message.time - notestates[key];
if (decaystates[key] == 0.0) {
createDecay(channel, key, duration, onvels[key]);
}
}
notestates[key] = 0;
}
}
////////////////////////////
//
// createDecay -- start a decay algorithm
//
void createDecay(int channel, int key, int duration, int velocity) {
static FunctionEvent tn; // temporary function for copying into eventBuffer
tn.shortValue(14) = gap; // gap between successive notes
tn.setFunction(EchoAlgorithm);
tn.setChannel(channel);
tn.setKeyno(key);
decaystates[key] = velocity * decayrate;
tn.setVelocity((int)decaystates[key]);
tn.setStatus(EVENT_STATUS_ACTIVE);
// start time of function and the duration between calling it
tn.setOnDur(t_time, duration);
eventBuffer.insert(tn);
cout << "Key= " << key
<< "\tDuration = " << duration + gap
<< "\tVelocity = " << velocity
<< endl;
}
/*-------------------- triggered algorithms -----------------------------*/
void keyboardchar(int key) {
switch (key) {
case 'p': // print eventbuffer info
eventBuffer.print();
cout << endl;
break;
case '[': // speed up echo rate
decayrate *= 0.99;
cout << "Decay rate = " << decayrate << endl;
break;
case ']': // slow down echo rate
decayrate /= 0.99;
cout << "Decay rate = " << decayrate << 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: 2b73be6f711d1022982de4323f34f32f decay.cpp [20050403]