//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Fri Apr 28 14:37:28 PDT 2000
// Last Modified: Fri Apr 28 14:37:32 PDT 2000
// Filename: ...sig/doc/examples/all/ghost/ghost.cpp
// Syntax: C++; synthImprov 2.0
//
// Description: When a note is played on the keyboard, it generates
// a ghost image which will play sometime within the next
// minute on the same pitch, half as loud as the original pitch.
// The ghost note has the same duration as the original
// note.
//
#include "synthImprov.h"
#define MAXTIME 60 /* maximum time to start ghost note */
#define MINTIME 10 /* minimum time to start ghost note */
/*----------------- beginning of improvization algorithms ---------------*/
EventBuffer eventBuffer; // used to store algorithms for performance
Array<int> noteontimes(128); // list of the last time a note was played
Array<int> noteonvels(128); // list of the last time a note was played
// function declarations:
void sillyKeyboard(int key, int chan = 0);
void createGhost(int key, int velocity, int channel, int duration);
void processNote(MidiMessage message);
/*--------------------- maintenance algorithms --------------------------*/
void description(void) {
printboxtop();
psl(
" GHOST -- by Craig Stuart Sapp <craig@ccrma.stanford.edu> -- 28 April 2000");
psl("Description: When a note is played on the keyboard, it generates");
psl("a ghost image which will play sometime within the next");
psl("minute on the same pitch, half as loud as the original pitch.");
psl("The ghost note has the same duration as the original");
psl("note.");
psl("");
printboxbottom();
}
void initialization(void) {
eventBuffer.setPollPeriod(10);
noteontimes.zero();
noteonvels.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() / 2;
int channel = message.p0() & 0x0f;
int command = message.p0() & 0xf0;
int duration;
int status = 1;
if (command == 0x80 || velocity == 0) {
status = 0;
}
if (status == 0) {
duration = t_time - noteontimes[key];
createGhost(key, noteonvels[key], channel, duration);
} else {
noteontimes[key] = t_time;
noteonvels[key] = velocity;
}
}
////////////////////////////
//
// createGhost -- set up the parameters for the ghost note.
//
void createGhost(int key, int velocity, int channel, int duration) {
static NoteEvent tn; // a Temporary Note for copying into eventBuffer
// setting the fields of the function note
tn.setChannel(channel);
tn.setKeyno(key);
tn.setVelocity(velocity);
tn.setStatus(EVENT_STATUS_ACTIVE);
int starttime = (rand() % (MAXTIME - MINTIME) + MINTIME) * 1000;
tn.setOnDur(t_time + starttime, duration);
eventBuffer.insert(tn);
cout << "Ghost = " << key << " in " << starttime/1000
<< " seconds " << 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: 9931db184b6d501cc5ea77c4e9894a12 ghost.cpp [20050403]