//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sat May 27 20:16:58 PDT 2000
// Last Modified: Sat May 27 20:44:30 PDT 2000
// Filename: ...sig/doc/examples/all/eco/eco.cpp
// Syntax: C++; synthImprov 2.0
//
// Description: Generates echos
// Echos are triggered whenever two notes of the same
// pitch are played within 10 seconds of each other.
//
// The duration of the echoed notes are related to the
// duration of the first note that starts the echo
//
// The decay amount of the echo is related to the
// change in attack velocity between the two notes
// which start the echo.
//
#include "synthImprov.h"
/*----------------- beginning of improvization algorithms ---------------*/
#define MAXECHOTIME 10000 /* max time for activating echos */
EventBuffer eventBuffer; // for future notes
MidiMessage message; // for reading keyno and velocity (and time)
int channel; // channel to play the echos on
int key; // the current MIDI key number
int duration; // duration of the echoing note
int period; // time between echoing notes
int command; // the current MIDI command
int velocity; // the current MIDI attack velocity
int decay; // decay rate for the echo algorithm
Array<int> ontimes(128); // time when the last note turned on for each pitch
Array<int> offtimes(128); // time with the last note turned off for each pitch
Array<int> attack(128); // attack velocity of the first note in the echo
// function declarations:
void sillyKeyboard(int key, int chan = 0);
void playecho(int key, int velocity, int decay, int channel, int duration,
int period);
/*--------------------- Event Algorithms --------------------------------*/
//////////////////////////////
//
// EchoFunction --
//
// Global variables needed by this function:
// None.
//
// Local variables needed by this function:
// 12 int = duration
// 8 float = decay
// 4 float = newvelocity
//
static void EchoFunction(FunctionEvent& p, EventBuffer& midiOutput) {
static NoteEvent note; // temporary note before placing in buffer
// set the parameters for the output note:
note.setOnDur(t_time, p.intValue(12)); // duration in int(12)
note.setVel(p.getVel());
note.setChan(p.getChan());
note.setKey(p.getKey());
// update the parameters for the function:
p.floatValue(4) = p.floatValue(4) * p.floatValue(8);
short newvelocity = (short)p.floatValue(4);
if (newvelocity <= 7) {
p.off(midiOutput);
} else {
p.setVel(newvelocity);
p.setOnTime(p.getOnTime() + p.getDur()); // OffTime stores duration
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(
"eco -- by Craig Stuart Sapp <craig@ccrma.stanford.edu> -- 28 May 2000");
psl("");
psl(" \"0\"-\"9\" = octave number of computer keyboard notes");
psl(" Notes: s d g h j ");
psl(" z x c v b n m ");
printboxbottom();
}
void initialization(void) {
eventBuffer.setPollPeriod(10);
ontimes.zero();
offtimes.zero();
attack.zero();
}
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();
channel = 0x0f & message.p0();
command = 0xf0 & message.p0();
key = message.p1();
velocity = message.p2();
if (command == 0x80 || velocity == 0) {
// note off message
offtimes[key] = t_time;
} else {
if (key == A0) {
eventBuffer.off();
return;
}
// note on message
duration = offtimes[key] - ontimes[key];
period = t_time - ontimes[key];
decay = attack[key] - velocity;
if (decay < 0) {
decay = -decay;
velocity = attack[key];
}
if (decay < 1) {
decay = 1;
}
if (period < MAXECHOTIME && ontimes[key] != 0) {
playecho(key, velocity, decay, channel, duration, period);
ontimes[key] = 0;
} else {
ontimes[key] = t_time;
attack[key] = message.p2();
}
}
}
}
////////////////////////////
//
// playecho -- inserts a FunctionEvent into the eventBuffer which
// plays an echo.
//
void playecho(int key, int velocity, int decay, int channel, int duration,
int period) {
static FunctionEvent tn; // a Temporary Note for copying into eventBuffer
// setting the fields of the function note
tn.intValue(12) = duration;
tn.floatValue(8) = 1.0 - decay/128.0;
tn.floatValue(4) = (float)velocity;
tn.setFunction(EchoFunction);
tn.setChannel(channel);
tn.setKeyno(key);
tn.setVelocity(velocity);
tn.setStatus(EVENT_STATUS_ACTIVE);
// start time of function and the duration between notes
tn.setOnDur(t_time, period);
eventBuffer.insert(tn);
cout << "Key = " << key
<< "\tLoudness = " << velocity
<< "\tdecay = " << decay
<< "\tduration = " << duration
<< "\tperiod = " << period
<< endl;
}
/*-------------------- triggered algorithms -----------------------------*/
void keyboardchar(int key) {
switch (key) {
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: bf0d0a22200a94eb60ed265119a3094f eco.cpp [20050403]