// // 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]